From baacaed999629e2d1268f114481fe5ca0cfd70ed Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 24 Aug 2022 08:32:07 +0700 Subject: [PATCH 1/2] Update WHMCS order status when customer redirected back to success_redirect_url --- modules/gateways/callback/xendit.php | 14 +-- modules/gateways/xendit.php | 10 +- modules/gateways/xendit/lib/ActionBase.php | 90 -------------- modules/gateways/xendit/lib/Callback.php | 15 --- modules/gateways/xendit/lib/CreditCard.php | 2 +- .../xendit/lib/{Link.php => PaymentLink.php} | 114 ++++++++++++------ modules/gateways/xendit/lib/Webhook.php | 94 +++++++++++++++ .../xendit/tests/lib/ActionBaseTest.php | 10 +- 8 files changed, 189 insertions(+), 160 deletions(-) delete mode 100644 modules/gateways/xendit/lib/Callback.php rename modules/gateways/xendit/lib/{Link.php => PaymentLink.php} (68%) create mode 100644 modules/gateways/xendit/lib/Webhook.php diff --git a/modules/gateways/callback/xendit.php b/modules/gateways/callback/xendit.php index 9fcc265..76241e0 100644 --- a/modules/gateways/callback/xendit.php +++ b/modules/gateways/callback/xendit.php @@ -4,11 +4,11 @@ require_once __DIR__ . '/../../../includes/invoicefunctions.php'; require_once __DIR__ . '/../xendit/autoload.php'; -use Xendit\Lib\Callback; +use Xendit\Lib\Webhook; use Xendit\Lib\CreditCard; use Xendit\Lib\XenditRequest; -$callback = new Callback(); +$webhook = new Webhook(); $creditCard = new CreditCard(); $xenditRequest = new XenditRequest(); @@ -106,11 +106,11 @@ ); } } else { - // use for callback + // use for webhook $arrRequestInput = json_decode(file_get_contents("php://input"), true); if (!empty($arrRequestInput) && isset($arrRequestInput['external_id']) && !empty($arrRequestInput['external_id'])) { - $invoiceId = $callback->getInvoiceIdFromExternalId($arrRequestInput['external_id']); - $transactions = $callback->getTransactionFromInvoiceId($invoiceId); + $invoiceId = $webhook->getInvoiceIdFromExternalId($arrRequestInput['external_id']); + $transactions = $webhook->getTransactionFromInvoiceId($invoiceId); try { // Get invoice from Xendit @@ -118,13 +118,13 @@ if (isset($arrRequestInput['credit_card_token'])) { $xenditInvoice['credit_card_token'] = $arrRequestInput['credit_card_token']; } - $result = $callback->confirmInvoice( + $result = $webhook->confirmInvoice( $invoiceId, $xenditInvoice, $xenditInvoice["status"] == "PAID" || $xenditInvoice["status"] == "SETTLED" ); if ($result) { - $callback->updateTransactions($transactions); + $webhook->updateTransactions($transactions); echo 'Success'; exit; } diff --git a/modules/gateways/xendit.php b/modules/gateways/xendit.php index 9f6e0b7..826e821 100644 --- a/modules/gateways/xendit.php +++ b/modules/gateways/xendit.php @@ -10,12 +10,12 @@ require __DIR__ . '/xendit/autoload.php'; // defines -define('XENDIT_PAYMENT_GATEWAY_VERSION', '1.0.9'); +const XENDIT_PAYMENT_GATEWAY_VERSION = '1.1.0'; use WHMCS\Billing\Invoice; use Xendit\Lib\ActionBase; use Xendit\Lib\CreditCard; -use Xendit\Lib\Link; +use Xendit\Lib\PaymentLink; use Xendit\Lib\Model\XenditTransaction; use Xendit\Lib\Recurring; use Xendit\Lib\XenditRequest; @@ -73,11 +73,11 @@ function xendit_deactivate() */ function xendit_link($params) { - $link = new Link(); + $paymentLink = new PaymentLink(); try { - return $link->generatePaymentLink($params); + return $paymentLink->generatePaymentLink($params); } catch (\Exception $e) { - return $link->errorMessage($e->getMessage()); + return $paymentLink->errorMessage($e->getMessage()); } } diff --git a/modules/gateways/xendit/lib/ActionBase.php b/modules/gateways/xendit/lib/ActionBase.php index 63ccbb8..c2b03f2 100644 --- a/modules/gateways/xendit/lib/ActionBase.php +++ b/modules/gateways/xendit/lib/ActionBase.php @@ -219,83 +219,6 @@ public function extractPaidAmount($xenditTotal, $whmcsTotal): float return $decimalAmount > 0 && $decimalAmount < 1 ? (float)$whmcsTotal : (float)$xenditTotal; } - /** - * @param int $invoiceId - * @param array $xenditInvoiceData - * @param bool $success - * @return bool - * @throws \Exception - */ - public function confirmInvoice(int $invoiceId, array $xenditInvoiceData, bool $success = true): bool - { - try { - if (!$success) { - return false; - } - - /* - * Verify the invoice need to update is correct - * Avoid update wrong WHMCS invoice - */ - if ($invoiceId != $this->getInvoiceIdFromExternalId($xenditInvoiceData['external_id'])) { - 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"]; - $transactionStatus = 'Success'; - - // Save credit card token - if (isset($xenditInvoiceData['credit_card_charge_id']) && isset($xenditInvoiceData['credit_card_token'])) { - $cardInfo = $this->xenditRequest->getCardInfo($xenditInvoiceData['credit_card_charge_id']); - $cardExpired = $this->xenditRequest->getCardTokenInfo($xenditInvoiceData['credit_card_token']); - - if (!empty($cardInfo) && !empty($cardExpired)) { - $lastDigit = substr($cardInfo["masked_card_number"], -4); - invoiceSaveRemoteCard( - $invoiceId, - $lastDigit, - $cardInfo["card_brand"], - sprintf("%s/%s", $cardExpired["card_expiration_month"], $cardExpired["card_expiration_year"]), - $xenditInvoiceData['credit_card_token'] - ); - } - } - - $invoiceId = checkCbInvoiceID($invoiceId, $this->getDomainName()); - checkCbTransID($transactionId); - - addInvoicePayment( - $invoiceId, - $transactionId, - $paymentAmount, - $paymentFee, - $this->getDomainName() - ); - - // Save payment method - $transactions = $this->getTransactionFromInvoiceId($invoiceId); - if (!empty($transactions)) { - $this->updateTransactions( - $transactions, - [ - "status" => XenditTransaction::STATUS_PAID, - "payment_method" => $xenditInvoiceData["payment_method"] - ] - ); - } - - logTransaction($this->getDomainName(), $_POST, $transactionStatus); - return true; - } catch (\Exception $exception) { - throw new \Exception($exception->getMessage()); - } - } - /** * @param int $invoiceId * @return mixed @@ -305,19 +228,6 @@ public function getRecurringBillingInfo(int $invoiceId) return getRecurringBillingValues($invoiceId); } - /** - * @param int $invoiceId - * @return bool - */ - public function isRecurring(int $invoiceId): bool - { - $recurringData = $this->getRecurringBillingInfo($invoiceId); - if (!isset($recurringData["firstpaymentamount"]) && !isset($recurringData['firstcycleperiod'])) { - return true; - } - return false; - } - /** * @param float $total * @return float diff --git a/modules/gateways/xendit/lib/Callback.php b/modules/gateways/xendit/lib/Callback.php deleted file mode 100644 index 1b70395..0000000 --- a/modules/gateways/xendit/lib/Callback.php +++ /dev/null @@ -1,15 +0,0 @@ - true ]; - if(!empty($billingDetailObject)){ + if (!empty($billingDetailObject)) { $payload['billing_details'] = $billingDetailObject; } if (!empty($auth_id)) { diff --git a/modules/gateways/xendit/lib/Link.php b/modules/gateways/xendit/lib/PaymentLink.php similarity index 68% rename from modules/gateways/xendit/lib/Link.php rename to modules/gateways/xendit/lib/PaymentLink.php index f55bba7..8ccafb1 100644 --- a/modules/gateways/xendit/lib/Link.php +++ b/modules/gateways/xendit/lib/PaymentLink.php @@ -4,7 +4,7 @@ use Xendit\Lib\Model\XenditTransaction; -class Link extends ActionBase +class PaymentLink extends ActionBase { /** @var string $callbackUrl */ protected $callbackUrl = 'modules/gateways/callback/xendit.php'; @@ -32,7 +32,7 @@ protected function generateInvoicePayload(array $params, bool $retry = false): a 'should_charge_multiple_use_token' => true ]; - if(!empty($customerObject)){ + if (!empty($customerObject)) { $payload['customer'] = $customerObject; } @@ -136,6 +136,69 @@ protected function generateFormParam(array $params, string $invoiceUrl): string return $htmlOutput; } + /** + * @param array $params + * @param $transactions + * @param bool $isForced + * @return false|string + * @throws \Exception + */ + protected function createXenditInvoice(array $params, $transactions, bool $isForced = false) + { + try { + $payload = $this->generateInvoicePayload($params, $isForced); + $xenditInvoice = $this->xenditRequest->createInvoice($payload); + $this->updateTransactions( + $transactions, + [ + 'transactionid' => $xenditInvoice["id"], + 'status' => XenditTransaction::STATUS_PENDING, + 'external_id' => $payload["external_id"] + ] + ); + return $xenditInvoice; + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + } + + /** + * @param array $params + * @param array $xenditInvoice + * @param $transactions + * @return string|null + * @throws \Exception + */ + protected function updateInvoiceStatus(array $params, array $xenditInvoice, $transactions) + { + if (empty($xenditInvoice)) { + throw new \Exception('Xendit invoice not found.'); + } + + try { + $xenditInvoiceStatus = $xenditInvoice['status'] ?? ''; + switch ($xenditInvoiceStatus) { + case 'PAID': + case 'SETTLED': + $webhook = new \Xendit\Lib\Webhook(); + $confirmed = $webhook->confirmInvoice($params['invoiceid'], $xenditInvoice); + if (!$confirmed) { + throw new \Exception('Cannot update invoice status.'); + } + return $this->redirectUrl($xenditInvoice['success_redirect_url']); + + case 'EXPIRED': + $this->updateTransactions($transactions, ['status' => XenditTransaction::STATUS_EXPIRED]); + return $this->generatePaymentLink($params, true); + + default: + return $this->generateFormParam($params, $xenditInvoice['invoice_url']); + } + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + } + /** * @param array $params * @param bool $force @@ -149,24 +212,13 @@ public function generatePaymentLink(array $params, bool $force = false): string return false; } - // Get transaction + // Get transactions by WHMCS invoice $transactions = $this->getTransactionFromInvoiceId($params["invoiceid"]); - // If force create new invoice + // Create a new Xendit invoice in case the previous invoice is EXPIRED if ($force) { - $payload = $this->generateInvoicePayload($params, true); - $createInvoice = $this->xenditRequest->createInvoice($payload); - $url = $createInvoice['invoice_url']; - - $this->updateTransactions( - $transactions, - [ - 'transactionid' => $createInvoice["id"], - 'status' => XenditTransaction::STATUS_PENDING, - 'external_id' => $payload["external_id"] - ] - ); - return $this->generateFormParam($params, $url); + $xenditInvoice = $this->createXenditInvoice($params, $transactions, true); + return $this->generateFormParam($params, $xenditInvoice['invoice_url']); } // Get Xendit Invoice by transaction (Xendit invoice_id) @@ -175,28 +227,16 @@ public function generatePaymentLink(array $params, bool $force = false): string $xenditInvoice = $this->xenditRequest->getInvoiceById($transactions[0]->transactionid); } - // Check xendit invoice status + /* + * Check if Xendit invoice exists then update WHMCS invoice status + */ if (!empty($xenditInvoice)) { - if ($xenditInvoice['status'] == "EXPIRED") { - $this->updateTransactions($transactions, ['status' => XenditTransaction::STATUS_EXPIRED]); - return $this->generatePaymentLink($params, true); - } else { - $url = $xenditInvoice['invoice_url']; - } - } else { - $createInvoice = $this->xenditRequest->createInvoice( - $this->generateInvoicePayload($params) - ); - $url = $createInvoice['invoice_url']; - $this->updateTransactions( - $transactions, - [ - 'transactionid' => $createInvoice["id"], - 'status' => XenditTransaction::STATUS_PENDING - ] - ); + return $this->updateInvoiceStatus($params, $xenditInvoice, $transactions); } - return $this->generateFormParam($params, $url); + + // If Xendit invoice not exist then create a new Xendit invoice + $xenditInvoice = $this->createXenditInvoice($params, $transactions); + return $this->generateFormParam($params, $xenditInvoice['invoice_url']); } catch (\Exception $e) { /* * If currency is error diff --git a/modules/gateways/xendit/lib/Webhook.php b/modules/gateways/xendit/lib/Webhook.php new file mode 100644 index 0000000..04a0736 --- /dev/null +++ b/modules/gateways/xendit/lib/Webhook.php @@ -0,0 +1,94 @@ +getInvoiceIdFromExternalId($xenditInvoiceData['external_id'])) { + 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"]; + $transactionStatus = 'Success'; + + // Save credit card token + if (isset($xenditInvoiceData['credit_card_charge_id']) && isset($xenditInvoiceData['credit_card_token'])) { + $cardInfo = $this->xenditRequest->getCardInfo($xenditInvoiceData['credit_card_charge_id']); + $cardExpired = $this->xenditRequest->getCardTokenInfo($xenditInvoiceData['credit_card_token']); + + if (!empty($cardInfo) && !empty($cardExpired)) { + $lastDigit = substr($cardInfo["masked_card_number"], -4); + invoiceSaveRemoteCard( + $invoiceId, + $lastDigit, + $cardInfo["card_brand"], + sprintf("%s/%s", $cardExpired["card_expiration_month"], $cardExpired["card_expiration_year"]), + $xenditInvoiceData['credit_card_token'] + ); + } + } + + $invoiceId = checkCbInvoiceID($invoiceId, $this->getDomainName()); + checkCbTransID($transactionId); + + addInvoicePayment( + $invoiceId, + $transactionId, + $paymentAmount, + $paymentFee, + $this->getDomainName() + ); + + // Save payment method + $transactions = $this->getTransactionFromInvoiceId($invoiceId); + if (!empty($transactions)) { + $this->updateTransactions( + $transactions, + [ + "status" => XenditTransaction::STATUS_PAID, + "payment_method" => $xenditInvoiceData["payment_method"] + ] + ); + } + + logTransaction($this->getDomainName(), $_POST, $transactionStatus); + return true; + } catch (\Exception $exception) { + throw new \Exception($exception->getMessage()); + } + } +} diff --git a/modules/gateways/xendit/tests/lib/ActionBaseTest.php b/modules/gateways/xendit/tests/lib/ActionBaseTest.php index da1d6f6..72623da 100644 --- a/modules/gateways/xendit/tests/lib/ActionBaseTest.php +++ b/modules/gateways/xendit/tests/lib/ActionBaseTest.php @@ -23,7 +23,7 @@ public function testCustomerAddressObjectReturnEmpty() 'postcode' => '' ]; - $link = new \Xendit\Lib\Link(); + $link = new \Xendit\Lib\PaymentLink(); $customerAddressObject = $link->extractCustomerAddress($mockCustomerDetails); $this->assertIsArray($customerAddressObject, 'customerAddressObject should be array'); $this->assertEquals([], $customerAddressObject); @@ -46,7 +46,7 @@ public function testCustomerAddressObjectReturnValues() 'postcode' => 'test postcode', ]; - $link = new \Xendit\Lib\Link(); + $link = new \Xendit\Lib\PaymentLink(); $customerAddressObject = $link->extractCustomerAddress($mockCustomerDetails); $this->assertIsArray($customerAddressObject, 'customerAddressObject should be array'); @@ -55,7 +55,7 @@ public function testCustomerAddressObjectReturnValues() 'country' => 'ID', 'street_line1' => 'test address1', 'city' => 'test city', - 'province_state' => 'test state', + 'state' => 'test state', 'postal_code' => 'test postcode' ], $customerAddressObject @@ -78,7 +78,7 @@ public function testCustomerObjectReturnEmpty() 'phonenumber' => '' ]; - $link = new \Xendit\Lib\Link(); + $link = new \Xendit\Lib\PaymentLink(); $customerObject = $link->extractCustomer($mockCustomerDetails); $this->assertIsArray($customerObject, 'customerObject should be array'); @@ -100,7 +100,7 @@ public function testCustomerObjectReturnValues() 'email' => 'test@example.com' ]; - $link = new \Xendit\Lib\Link(); + $link = new \Xendit\Lib\PaymentLink(); $customerObject = $link->extractCustomer($mockCustomerDetails); $this->assertIsArray($customerObject, 'customerObject should be array'); From f5e6206166a2f9904c9c283642f3e0234eb85431 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 24 Aug 2022 13:40:05 +0700 Subject: [PATCH 2/2] some small changes --- modules/gateways/xendit.php | 2 +- modules/gateways/xendit/lib/PaymentLink.php | 2 +- modules/gateways/xendit/lib/Webhook.php | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/gateways/xendit.php b/modules/gateways/xendit.php index 826e821..966a82b 100644 --- a/modules/gateways/xendit.php +++ b/modules/gateways/xendit.php @@ -9,7 +9,7 @@ require __DIR__ . '/xendit/autoload.php'; -// defines +// Module version const XENDIT_PAYMENT_GATEWAY_VERSION = '1.1.0'; use WHMCS\Billing\Invoice; diff --git a/modules/gateways/xendit/lib/PaymentLink.php b/modules/gateways/xendit/lib/PaymentLink.php index 8ccafb1..dbb281f 100644 --- a/modules/gateways/xendit/lib/PaymentLink.php +++ b/modules/gateways/xendit/lib/PaymentLink.php @@ -234,7 +234,7 @@ public function generatePaymentLink(array $params, bool $force = false): string return $this->updateInvoiceStatus($params, $xenditInvoice, $transactions); } - // If Xendit invoice not exist then create a new Xendit invoice + // If Xendit invoice does not exist, create a new Xendit invoice $xenditInvoice = $this->createXenditInvoice($params, $transactions); return $this->generateFormParam($params, $xenditInvoice['invoice_url']); } catch (\Exception $e) { diff --git a/modules/gateways/xendit/lib/Webhook.php b/modules/gateways/xendit/lib/Webhook.php index 04a0736..4a09830 100644 --- a/modules/gateways/xendit/lib/Webhook.php +++ b/modules/gateways/xendit/lib/Webhook.php @@ -11,7 +11,7 @@ class Webhook extends ActionBase */ public function getInvoiceIdFromExternalId(string $external_id) { - $externalArray = array_map("trim", explode("-", $external_id)); + $externalArray = array_map('trim', explode('-', $external_id)); return end($externalArray); } @@ -42,7 +42,7 @@ public function confirmInvoice(int $invoiceId, array $xenditInvoiceData, bool $s $transactionId = $xenditInvoiceData['id']; $paymentAmount = $this->extractPaidAmount($xenditInvoiceData['paid_amount'], $invoice->total); - $paymentFee = $xenditInvoiceData['fees'][0]["value"]; + $paymentFee = $xenditInvoiceData['fees'][0]['value']; $transactionStatus = 'Success'; // Save credit card token @@ -51,12 +51,12 @@ public function confirmInvoice(int $invoiceId, array $xenditInvoiceData, bool $s $cardExpired = $this->xenditRequest->getCardTokenInfo($xenditInvoiceData['credit_card_token']); if (!empty($cardInfo) && !empty($cardExpired)) { - $lastDigit = substr($cardInfo["masked_card_number"], -4); + $lastDigit = substr($cardInfo['masked_card_number'], -4); invoiceSaveRemoteCard( $invoiceId, $lastDigit, - $cardInfo["card_brand"], - sprintf("%s/%s", $cardExpired["card_expiration_month"], $cardExpired["card_expiration_year"]), + $cardInfo['card_brand'], + sprintf('%s/%s', $cardExpired['card_expiration_month'], $cardExpired['card_expiration_year']), $xenditInvoiceData['credit_card_token'] ); } @@ -79,8 +79,8 @@ public function confirmInvoice(int $invoiceId, array $xenditInvoiceData, bool $s $this->updateTransactions( $transactions, [ - "status" => XenditTransaction::STATUS_PAID, - "payment_method" => $xenditInvoiceData["payment_method"] + 'status' => XenditTransaction::STATUS_PAID, + 'payment_method' => $xenditInvoiceData['payment_method'] ] ); }