diff --git a/modules/gateways/xendit.php b/modules/gateways/xendit.php index 1e12646..c57d805 100644 --- a/modules/gateways/xendit.php +++ b/modules/gateways/xendit.php @@ -10,7 +10,7 @@ require __DIR__ . '/xendit/autoload.php'; // Module version -const XENDIT_PAYMENT_GATEWAY_VERSION = '2.0.0'; +const XENDIT_PAYMENT_GATEWAY_VERSION = '2.1.0'; use WHMCS\Billing\Invoice; use Xendit\Lib\ActionBase; diff --git a/modules/gateways/xendit/composer.json b/modules/gateways/xendit/composer.json index 2d9740d..ee449d4 100644 --- a/modules/gateways/xendit/composer.json +++ b/modules/gateways/xendit/composer.json @@ -8,9 +8,16 @@ "email": "thirdpartyintegrations@xendit.co" } ], + "scripts": { + "test": "export IS_TEST=true & vendor/bin/phpunit tests" + }, "homepage": "https://xendit.co", "require-dev": { "phpunit/phpunit": "@stable", "squizlabs/php_codesniffer": "^3.7" + }, + "require": { + "illuminate/database": "^11.34", + "vlucas/phpdotenv": "^5.6" } } diff --git a/modules/gateways/xendit/hooks.php b/modules/gateways/xendit/hooks.php index 6f7af9b..6a05422 100644 --- a/modules/gateways/xendit/hooks.php +++ b/modules/gateways/xendit/hooks.php @@ -8,6 +8,7 @@ * * @return This depends on the hook function point. */ +require_once __DIR__ . '/autoload.php'; if (!defined('WHMCS')) { die('You cannot access this file directly.'); @@ -58,3 +59,48 @@ function hookClientAreaPageCart($vars) } add_hook("ClientAreaPageCart", 1, 'hookClientAreaPageCart'); + +/** + * Hook to show make Xendit invoice expired when the order is canceled + * + * @param $vars + * @return boolean|void + */ +add_hook('CancelOrder', 1, 'cancelXenditInvoice'); + +/** + * Hook to show make Xendit invoice expired when the invoice is canceled + * + * @param $vars + * @return boolean|void + */ +add_hook('InvoiceCancelled', 1, 'cancelXenditInvoice'); + +function cancelXenditInvoice($vars) { + if (!class_exists('\Xendit\Lib\ActionBase') || !class_exists('\Xendit\Lib\Model\XenditTransaction')) { + return false; + } + + $actionBase = new \Xendit\Lib\ActionBase(); + try { + $flag = "invoiceid"; + + if ($vars["orderid"]) { + $flag = "orderid"; + } + + // if the invoice is still active we need to expire the invoice from here + $xenditRequest = $actionBase->getXenditRequest(); + + $xenditTransactions = $actionBase->getTransactionFromInvoiceId($vars[$flag], $flag); + + if ($actionBase->isTransactionsDataValid($xenditTransactions)) { + $xenditRequest->expire($xenditTransactions[0]['transactionid']); + + $actionBase->setTransactionsToExpired($xenditTransactions); + } + } catch (\Exception $e) { + logActivity('Error at cancel event hooks >>> '. $e->getMessage(), 0); + } + return true; +} diff --git a/modules/gateways/xendit/lib/ActionBase.php b/modules/gateways/xendit/lib/ActionBase.php index 79b705a..4a27d36 100644 --- a/modules/gateways/xendit/lib/ActionBase.php +++ b/modules/gateways/xendit/lib/ActionBase.php @@ -181,9 +181,9 @@ public function storeTransaction(array $params = []) * @param int $invoiceId * @return mixed */ - public function getTransactionFromInvoiceId(int $invoiceId) + public function getTransactionFromInvoiceId(int $invoiceId, string $type = "invoiceid") { - return XenditTransaction::where("invoiceid", $invoiceId)->get(); + return XenditTransaction::where($type, $invoiceId)->get(); } /** @@ -363,4 +363,36 @@ public function extractCustomerAddress(array $params): array ]; return array_filter($customerAddressObject); } + + /** + * get xendit request object + * + * @return XenditRequest + */ + public function getXenditRequest() + { + return $this->xenditRequest; + } + + /** + * @name setTransactionsToExpired + * @param $transactionsToCancel object + * @return void + */ + public function setTransactionsToExpired($transaction) { + $this->updateTransactions($transaction, + [ + 'status' => XenditTransaction::STATUS_EXPIRED + ] + ); + } + + /** + * @name isTransactionsDataValid + * @param $transactionsData array + * @return boolean + */ + public function isTransactionsDataValid($transactionsData) { + return !empty($transactionsData) && $transactionsData[0]["transactionid"] !== ""; + } } diff --git a/modules/gateways/xendit/lib/PaymentLink.php b/modules/gateways/xendit/lib/PaymentLink.php index 1cf015a..d353bb0 100644 --- a/modules/gateways/xendit/lib/PaymentLink.php +++ b/modules/gateways/xendit/lib/PaymentLink.php @@ -169,7 +169,7 @@ protected function updateInvoiceStatus(array $params, array $xenditInvoice, $tra case 'EXPIRED': $this->updateTransactions($transactions, ['status' => XenditTransaction::STATUS_EXPIRED]); - return $this->generatePaymentLink($params, true); + return $this->generateFormParam($params, $xenditInvoice['invoice_url']); default: return $this->generateFormParam($params, $xenditInvoice['invoice_url']); @@ -195,12 +195,6 @@ public function generatePaymentLink(array $params, bool $force = false): string // Get transactions by WHMCS invoice $transactions = $this->getTransactionFromInvoiceId($params["invoiceid"]); - // Create a new Xendit invoice in case the previous invoice is EXPIRED - if ($force) { - $xenditInvoice = $this->createXenditInvoice($params, $transactions, true); - return $this->generateFormParam($params, $xenditInvoice['invoice_url']); - } - // Get Xendit Invoice by transaction (Xendit invoice_id) $xenditInvoice = false; if ($transactions->count() && !empty($transactions[0]->transactionid)) { diff --git a/modules/gateways/xendit/lib/Webhook.php b/modules/gateways/xendit/lib/Webhook.php index 4a09830..d061fea 100644 --- a/modules/gateways/xendit/lib/Webhook.php +++ b/modules/gateways/xendit/lib/Webhook.php @@ -25,7 +25,24 @@ public function getInvoiceIdFromExternalId(string $external_id) public function confirmInvoice(int $invoiceId, array $xenditInvoiceData, bool $success = true): bool { try { - if (!$success) { + // Load WHMCS invoice + $invoice = $this->getInvoice($invoiceId); + // Load Xendit transactions by order id column + $transactionsDataByOrder = $this->getTransactionFromInvoiceId($invoiceId, "orderid"); + + if (!$success) { // that means it's expired and need to cancel the order + if (!empty($transactionsDataByOrder)) { + // update xendit transactions + $this->setTransactionsToExpired($transactionsDataByOrder); + + localAPI('CancelOrder', array( + 'orderid' => $transactionsDataByOrder[0]['orderid'], + )); + + echo 'Success'; + } else { + echo 'Transaction not found'; + } return false; } @@ -37,9 +54,6 @@ public function confirmInvoice(int $invoiceId, array $xenditInvoiceData, bool $s throw new \Exception('Invoice id is incorrect!'); } - // Load WHMCS invoice - $invoice = $this->getInvoice($invoiceId); - $transactionId = $xenditInvoiceData['id']; $paymentAmount = $this->extractPaidAmount($xenditInvoiceData['paid_amount'], $invoice->total); $paymentFee = $xenditInvoiceData['fees'][0]['value']; diff --git a/modules/gateways/xendit/lib/XenditRequest.php b/modules/gateways/xendit/lib/XenditRequest.php index 348e72a..c5e74d7 100644 --- a/modules/gateways/xendit/lib/XenditRequest.php +++ b/modules/gateways/xendit/lib/XenditRequest.php @@ -168,6 +168,27 @@ public function createInvoice(array $param = []) } } + /** + * @param array $param + * @return false|string + * @throws \Exception + */ + public function expire(string $invoice_id) + { + try { + $response = $this->request( + '/tpi/payment/xendit/invoice/' . $invoice_id . '/expire', + [ + 'headers' => $this->defaultHeader() + ], + "POST" + ); + return $this->processResponse($response); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + } + /** * @param array $param * @return false|string