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