Initial commit
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 NekoMonci12
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
Photos/Cover.png
Normal file
BIN
Photos/Cover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
BIN
Photos/Example.png
Normal file
BIN
Photos/Example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
BIN
Photos/Logo.png
Normal file
BIN
Photos/Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## Installation
|
||||
Download the code and place it in the `app/Extensions/Gateways/Midtrans` directory.
|
||||
|
||||
## Configuration
|
||||
In the admin panel, go to `Settings > Payment Gateways` and click on the `Midtrans` gateway. Enter Midtrans `MerchantID`/`Server-Key`/`Client-Key` then `Save`.
|
||||
|
||||
## Usage
|
||||
When a user selects the `Midtrans` gateway, they will be redirected to the Midtrans payment page. After the payment is completed, the user will be redirected back to the site.
|
||||
|
||||
## Setup Callback On Midtrans
|
||||
1. Settings > SNAP Preferences > System Settings
|
||||
```
|
||||
Finish URL = https://yourdomain.com
|
||||
Unfinish URL = https://yourdomain.com
|
||||
Error Payment URL = https://yourdomain.com
|
||||
```
|
||||
2. Settings > Payment > Finish Redirect URL
|
||||
```
|
||||
Finish Redirect URL = https://yourdomain.com
|
||||
```
|
||||
3. Settings > Payment > Notification URL
|
||||
```
|
||||
Notification URL = https://yourdomain.com/extensions/midtrans/webhook
|
||||
Recurring payment notification URL = https://yourdomain.com
|
||||
Account linking notification URL = https://yourdomain.com
|
||||
```
|
||||
134
Upload/Midtrans.php
Normal file
134
Upload/Midtrans.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace App\Extensions\Gateways\Midtrans;
|
||||
|
||||
use App\Classes\Extensions\Gateway;
|
||||
use App\Helpers\ExtensionHelper;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Midtrans extends Gateway
|
||||
{
|
||||
public function getMetadata()
|
||||
{
|
||||
return [
|
||||
'display_name' => 'Midtrans',
|
||||
'version' => '1.0.0',
|
||||
'author' => 'NekoMonci12',
|
||||
'website' => 'https://github.com/NekoMonci12',
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($total, $products, $invoiceId)
|
||||
{
|
||||
$hash = substr(hash('sha256', time()), 0, 16);
|
||||
$orderId = 'PAYMENTER-' . $invoiceId . '-' . $hash;
|
||||
|
||||
$serverKey = ExtensionHelper::getConfig('Midtrans', 'server_key');
|
||||
$merchantId = ExtensionHelper::getConfig('Midtrans', 'merchant_id');
|
||||
$clientKey = ExtensionHelper::getConfig('Midtrans', 'client_key');
|
||||
$debugMode = ExtensionHelper::getConfig('Midtrans', 'debug_mode');
|
||||
if ($debugMode) {
|
||||
$url = 'https://app.sandbox.midtrans.com/snap/v1/transactions';
|
||||
} else {
|
||||
$url = 'https://app.midtrans.com/snap/v1/transactions';
|
||||
}
|
||||
$transactionDetails = [
|
||||
'order_id' => $orderId,
|
||||
'gross_amount' => round($total, 2),
|
||||
];
|
||||
$itemDetails = [];
|
||||
foreach ($products as $product) {
|
||||
$itemDetails[] = [
|
||||
'id' => $product['id'] ?? uniqid(),
|
||||
'price' => $product['price'] ?? 0,
|
||||
'quantity' => $product['quantity'] ?? 1,
|
||||
'name' => $product['name'] ?? 'Product',
|
||||
];
|
||||
}
|
||||
$payload = [
|
||||
'transaction_details' => $transactionDetails,
|
||||
'item_details' => $itemDetails,
|
||||
];
|
||||
$headers = [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Basic ' . base64_encode($serverKey . ':'),
|
||||
];
|
||||
$response = Http::withHeaders($headers)
|
||||
->post($url, $payload);
|
||||
|
||||
$responseJson = $response->json();
|
||||
if ($response->failed() || isset($responseJson['error_messages'])) {
|
||||
ExtensionHelper::error('Midtrans', $responseJson);
|
||||
}
|
||||
|
||||
if (!isset($responseJson['redirect_url'])) {
|
||||
ExtensionHelper::error('Midtrans', [
|
||||
'error' => 'Missing redirect_url in response.',
|
||||
'response' => $responseJson,
|
||||
]);
|
||||
}
|
||||
|
||||
return $responseJson['redirect_url'];
|
||||
}
|
||||
|
||||
public function webhook(Request $request)~
|
||||
{
|
||||
if (!$request->isMethod('post')) {
|
||||
return response('Method Not Allowed', 405);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true);
|
||||
if (
|
||||
isset($data['status_code'], $data['transaction_status']) &&
|
||||
$data['status_code'] === "200" &&
|
||||
in_array($data['transaction_status'], ['capture', 'settlement'])
|
||||
) {
|
||||
try {
|
||||
$orderId = $data['order_id'];
|
||||
$parts = explode('-', $orderId);
|
||||
if (count($parts) >= 3) {
|
||||
$invoiceId = $parts[1];
|
||||
} else {
|
||||
\Log::error('Invalid order_id format: ' . $orderId);
|
||||
return response('Invalid order id format', 400);
|
||||
}
|
||||
ExtensionHelper::paymentDone($invoiceId, 'Midtrans', $data['transaction_id']);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Error processing paymentDone: ' . $e->getMessage());
|
||||
return response('Error processing webhook', 500);
|
||||
}
|
||||
}
|
||||
return response('OK', 200);
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'name' => 'server_key',
|
||||
'type' => 'text',
|
||||
'friendlyName' => 'Server Key',
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'merchant_id',
|
||||
'type' => 'text',
|
||||
'friendlyName' => 'Merchant ID',
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'client_key',
|
||||
'type' => 'text',
|
||||
'friendlyName' => 'Client Key',
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'debug_mode',
|
||||
'type' => 'boolean',
|
||||
'friendlyName' => 'Should Debug Mode be enabled?',
|
||||
'required' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
5
Upload/routes.php
Normal file
5
Upload/routes.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::post('/midtrans/webhook', [App\Extensions\Gateways\Midtrans\Midtrans::class, 'webhook'])->name('midtrans.webhook');
|
||||
Reference in New Issue
Block a user