Files
This commit is contained in:
233
modules/Store/gateways/Tripay/gateway.php
Normal file
233
modules/Store/gateways/Tripay/gateway.php
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Tripay_Gateway class
|
||||||
|
*
|
||||||
|
* @package Modules\Store
|
||||||
|
* @version 1.0.0
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class Tripay_Gateway extends GatewayBase {
|
||||||
|
|
||||||
|
private $endpoint;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$name = 'Tripay';
|
||||||
|
$author = '<a href="https://tripay.co.id" target="_blank" rel="nofollow noopener">Tripay</a>';
|
||||||
|
$gateway_version = '1.0.0';
|
||||||
|
$store_version = '1.0.0';
|
||||||
|
$settings = ROOT_PATH . '/modules/Store/gateways/Tripay/gateway_settings/settings.php';
|
||||||
|
parent::__construct($name, $author, $gateway_version, $store_version, $settings);
|
||||||
|
$this->endpoint = StoreConfig::get('tripay.debug_mode')
|
||||||
|
? 'https://tripay.co.id/api-sandbox/transaction'
|
||||||
|
: 'https://tripay.co.id/api/transaction';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the checkout page is loaded.
|
||||||
|
* Not required for Tripay closed payment.
|
||||||
|
*/
|
||||||
|
public function onCheckoutPageLoad(TemplateBase $template, Customer $customer): void {
|
||||||
|
// Not required for Tripay closed payment.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the order by creating a Tripay closed payment transaction.
|
||||||
|
*/
|
||||||
|
public function processOrder(Order $order): void {
|
||||||
|
$tripay_api_key = StoreConfig::get('tripay.api_key');
|
||||||
|
$tripay_api_secret = StoreConfig::get('tripay.api_secret');
|
||||||
|
$tripay_merchant_code = StoreConfig::get('tripay.merchant_code');
|
||||||
|
$tripay_channel = StoreConfig::get('tripay.channel');
|
||||||
|
|
||||||
|
$redirect_url = URL::getSelfURL();
|
||||||
|
$domain = rtrim(parse_url($redirect_url, PHP_URL_HOST), '/');
|
||||||
|
|
||||||
|
if (empty($tripay_api_key) || empty($tripay_api_secret) || empty($tripay_merchant_code)) {
|
||||||
|
$this->addError('Tripay configuration is missing.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$transaction_endpoint = $this->endpoint . '/create';
|
||||||
|
$amount = (string)(int)Store::fromCents($order->getAmount()->getTotalCents());
|
||||||
|
$order_id = $order->data()->id;
|
||||||
|
|
||||||
|
$customerData = $order->customer()->data();
|
||||||
|
$customer_name = $customerData->username;
|
||||||
|
$customer_email = $customerData->username . '@' . $domain;
|
||||||
|
$customer_phone = '081234567890';
|
||||||
|
|
||||||
|
$merchant_ref = 'MC-' . $customer_name . '-' . time() . '-' . $order_id;
|
||||||
|
|
||||||
|
$order_items = [
|
||||||
|
[
|
||||||
|
'sku' => 'ITEM001',
|
||||||
|
'name' => $order->getDescription(),
|
||||||
|
'price' => $amount,
|
||||||
|
'quantity' => 1
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$signature = hash_hmac('sha256', $tripay_merchant_code . $merchant_ref . $amount, $tripay_api_secret);
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'method' => $tripay_channel,
|
||||||
|
'merchant_ref' => $merchant_ref,
|
||||||
|
'amount' => $amount,
|
||||||
|
'customer_name' => $customer_name,
|
||||||
|
'customer_email' => $customer_email,
|
||||||
|
'customer_phone' => $customer_phone,
|
||||||
|
'order_items' => $order_items,
|
||||||
|
'api_key' => $tripay_api_key,
|
||||||
|
'merchant_code' => $tripay_merchant_code,
|
||||||
|
'return_url' => $redirect_url,
|
||||||
|
'expired_time' => (time() + (15 * 60)),
|
||||||
|
'signature' => $signature
|
||||||
|
];
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $transaction_endpoint);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Authorization: Bearer ' . $tripay_api_key,
|
||||||
|
'Content-Type: application/x-www-form-urlencoded'
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
$this->addError('Curl error: ' . curl_error($ch));
|
||||||
|
curl_close($ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
if (StoreConfig::get('tripay.debug_mode')) {
|
||||||
|
if (!isset($result['data']['checkout_url'])) {
|
||||||
|
echo '<pre>';
|
||||||
|
echo "Sent Params:\n";
|
||||||
|
print_r($params);
|
||||||
|
|
||||||
|
echo "\nRequest Headers:\n";
|
||||||
|
print_r([
|
||||||
|
'Authorization: Bearer ' . $tripay_api_key,
|
||||||
|
'Content-Type: application/x-www-form-urlencoded'
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo "\n\nRaw Tripay response:\n";
|
||||||
|
print_r($response);
|
||||||
|
|
||||||
|
echo "\n\nParsed JSON:\n";
|
||||||
|
print_r($result);
|
||||||
|
echo '</pre>';
|
||||||
|
|
||||||
|
$error_message = isset($result['message']) ? $result['message'] : 'Unknown error';
|
||||||
|
$this->addError('Tripay error: ' . $error_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->addError('ERROR: Please Contact The Web Administrator');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payment = new Payment($result['data']['reference'], 'transaction');
|
||||||
|
$payment->create([
|
||||||
|
'order_id' => $order->data()->id,
|
||||||
|
'gateway_id' => $this->getId(),
|
||||||
|
'transaction' => $result['data']['reference'],
|
||||||
|
'created' => time(),
|
||||||
|
'last_updated' => time(),
|
||||||
|
'status_id' => 0,
|
||||||
|
'amount_cents' => $order->getAmount()->getTotalCents(),
|
||||||
|
'currency' => $order->getAmount()->getCurrency(),
|
||||||
|
'fee_cents' => 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
header('Location: ' . $result['data']['checkout_url']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the return URL after a successful payment.
|
||||||
|
* Modify as necessary based on Tripay's redirect/return behavior.
|
||||||
|
*/
|
||||||
|
public function handleReturn(): bool {
|
||||||
|
Redirect::to(URL::build('/'));
|
||||||
|
die();
|
||||||
|
|
||||||
|
return true; // Leave it For Compability Reason
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles Tripay's listener (webhook/notification) for payment updates.
|
||||||
|
*/
|
||||||
|
public function handleListener(): void {
|
||||||
|
$json = file_get_contents('php://input');
|
||||||
|
$headers = getallheaders();
|
||||||
|
|
||||||
|
$callbackSignature = $headers['X-Callback-Signature'] ?? '';
|
||||||
|
$callbackEvent = $headers['X-Callback-Event'] ?? '';
|
||||||
|
$privateKey = StoreConfig::get('tripay.api_secret');
|
||||||
|
|
||||||
|
$expectedSignature = hash_hmac('sha256', $json, $privateKey);
|
||||||
|
|
||||||
|
if ($callbackSignature !== $expectedSignature) {
|
||||||
|
http_response_code(403);
|
||||||
|
$this->logError('[Tripay] Invalid signature');
|
||||||
|
die(json_encode(['success' => false, 'message' => 'Invalid signature']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = json_decode($json, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE || !isset($payload['merchant_ref'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
$this->logError('[Tripay] Invalid JSON payload');
|
||||||
|
die(json_encode(['success' => false, 'message' => 'Invalid JSON']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($callbackEvent !== 'payment_status') {
|
||||||
|
http_response_code(400);
|
||||||
|
$this->logError('[Tripay] Unexpected event type: ' . $callbackEvent);
|
||||||
|
die(json_encode(['success' => false, 'message' => 'Unexpected callback event']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$merchant_ref = $payload['merchant_ref'];
|
||||||
|
$tripay_ref = $payload['reference'];
|
||||||
|
$status = strtoupper((string) $payload['status']);
|
||||||
|
$amount_cents = Store::toCents($payload['amount_received']);
|
||||||
|
$currency = 'IDR';
|
||||||
|
$order_id = (int) substr(strrchr($merchant_ref, '-'), 1);
|
||||||
|
|
||||||
|
$payment = new Payment($tripay_ref, 'transaction');
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'order_id' => $order_id,
|
||||||
|
'gateway_id' => $this->getId(),
|
||||||
|
'transaction' => $tripay_ref,
|
||||||
|
'amount_cents' => $amount_cents,
|
||||||
|
'currency' => $currency,
|
||||||
|
'fee_cents' => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
switch ($status) {
|
||||||
|
case 'PAID':
|
||||||
|
$payment->handlePaymentEvent(Payment::COMPLETED, $data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'EXPIRED':
|
||||||
|
case 'FAILED':
|
||||||
|
$payment->handlePaymentEvent(Payment::REFUNDED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->logError("[Tripay] Unknown status: $status");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$gateway = new Tripay_Gateway();
|
||||||
46
modules/Store/gateways/Tripay/gateway_settings/settings.php
Normal file
46
modules/Store/gateways/Tripay/gateway_settings/settings.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Tripay Closed Payment Gateway Settings Page
|
||||||
|
*
|
||||||
|
* @package Modules\Store
|
||||||
|
* @version 1.0.0
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once(ROOT_PATH . '/modules/Store/classes/StoreConfig.php');
|
||||||
|
|
||||||
|
if (Input::exists()) {
|
||||||
|
if (Token::check()) {
|
||||||
|
$enabled = (isset($_POST['enable']) && $_POST['enable'] === 'on') ? 1 : 0;
|
||||||
|
|
||||||
|
StoreConfig::set('tripay.api_key', $_POST['tripay_api_key']);
|
||||||
|
StoreConfig::set('tripay.api_secret', $_POST['tripay_api_secret']);
|
||||||
|
StoreConfig::set('tripay.merchant_code', $_POST['tripay_merchant_code']);
|
||||||
|
StoreConfig::set('tripay.channel', $_POST['tripay_channel']);
|
||||||
|
StoreConfig::set('tripay.debug_mode', isset($_POST['tripay_debug_mode']) && $_POST['tripay_debug_mode'] === 'on' ? 1 : 0);
|
||||||
|
|
||||||
|
DB::getInstance()->update('store_gateways', $gateway->getId(), [
|
||||||
|
'enabled' => $enabled,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Session::flash('gateways_success', $language->get('admin', 'successfully_updated'));
|
||||||
|
} else {
|
||||||
|
$errors = [$language->get('general', 'invalid_token')];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate Tripay Callback URL
|
||||||
|
$domain = URL::getSelfURL();
|
||||||
|
$callback_url = $domain . 'store/listener/?gateway=Tripay';
|
||||||
|
|
||||||
|
// Assign to Smarty template
|
||||||
|
$smarty->assign([
|
||||||
|
'SETTINGS_TEMPLATE' => ROOT_PATH . '/modules/Store/gateways/Tripay/gateway_settings/settings.tpl',
|
||||||
|
'ENABLE_VALUE' => (isset($enabled)) ? $enabled : $gateway->isEnabled(),
|
||||||
|
'TRIPAY_API_KEY' => StoreConfig::get('tripay.api_key'),
|
||||||
|
'TRIPAY_API_SECRET' => StoreConfig::get('tripay.api_secret'),
|
||||||
|
'TRIPAY_MERCHANT_CODE' => StoreConfig::get('tripay.merchant_code'),
|
||||||
|
'TRIPAY_CHANNEL' => StoreConfig::get('tripay.channel'),
|
||||||
|
'TRIPAY_DEBUG_MODE' => StoreConfig::get('tripay.debug_mode'),
|
||||||
|
'CALLBACK_URL' => $callback_url
|
||||||
|
]);
|
||||||
41
modules/Store/gateways/Tripay/gateway_settings/settings.tpl
Normal file
41
modules/Store/gateways/Tripay/gateway_settings/settings.tpl
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<form action="" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputTripayApiKey">Tripay API Key</label>
|
||||||
|
<input class="form-control" type="text" id="inputTripayApiKey" name="tripay_api_key" value="{$TRIPAY_API_KEY}" placeholder="Tripay API Key">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputTripayApiSecret">Tripay API Secret</label>
|
||||||
|
<input class="form-control" type="text" id="inputTripayApiSecret" name="tripay_api_secret" value="{$TRIPAY_API_SECRET}" placeholder="Tripay API Secret">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputTripayMerchantCode">Tripay Merchant Code</label>
|
||||||
|
<input class="form-control" type="text" id="inputTripayMerchantCode" name="tripay_merchant_code" value="{$TRIPAY_MERCHANT_CODE}" placeholder="Tripay Merchant Code">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputTripayChannel">Tripay Payment Channel</label>
|
||||||
|
<input class="form-control" type="text" id="inputTripayChannel" name="tripay_channel" value="{$TRIPAY_CHANNEL}" placeholder="QRIS">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group custom-control custom-switch">
|
||||||
|
<input id="inputDebugMode" name="tripay_debug_mode" type="checkbox" class="custom-control-input"{if $TRIPAY_DEBUG_MODE eq 1} checked{/if} />
|
||||||
|
<label class="custom-control-label" for="inputDebugMode">Enable Debug Mode</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group custom-control custom-switch">
|
||||||
|
<input id="inputEnabled" name="enable" type="checkbox" class="custom-control-input"{if $ENABLE_VALUE eq 1} checked{/if} />
|
||||||
|
<label class="custom-control-label" for="inputEnabled">Enable Payment Method</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="hidden" name="token" value="{$TOKEN}">
|
||||||
|
<input type="submit" value="{$SUBMIT}" class="btn btn-primary">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="callbackURL">Tripay Callback URL</label>
|
||||||
|
<input class="form-control" type="text" id="callbackURL" value="{$CALLBACK_URL}" readonly>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
Reference in New Issue
Block a user