Skip to content

Commit

Permalink
Implement Xendit TPI service
Browse files Browse the repository at this point in the history
  • Loading branch information
andykimlong committed Mar 25, 2022
1 parent df5aa90 commit 5aecd68
Show file tree
Hide file tree
Showing 12 changed files with 1,136 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ https://docs.whmcs.com/System_Requirements
## Configuration ##
1. Access your WHMCS admin page.
2. Go to menu Setup -> Payments -> Payment Gateways.
3. There are will be `**Xendit Payment Gateway Module**`
3. There are will be `Xendit Payment Gateway Module`
4. Then choose Setup -> Payments -> Payment Gateways -> Manage Existing Gateways
5. Put the `secretKey` and `publicKey` (Open Xendit Dashboard > Settings > API Keys > Generate Secret Key > Copy SecretKey & PublicKey)
6. Click Save Changes
Expand Down
14 changes: 14 additions & 0 deletions modules/gateways/xendit/autoload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
if (!defined('XENDIT_ROOT')) {
define('XENDIT_ROOT', __DIR__);
}

if (!defined('WHMCS_ROOT')) {
define('WHMCS_ROOT', dirname(__DIR__, 3));
}

spl_autoload_register(function ($className) {
if (strpos($className, 'Xendit') !== false) {
require __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . str_replace('\\', '/', mb_strcut($className, 11)) . '.php';
}
});
168 changes: 168 additions & 0 deletions modules/gateways/xendit/form/update-card-remote-iframe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php
/**
* Remote iFrame Demo.
*
* This sample file demonstrates how a payment gateway might render a
* payment form to be displayed via iFrame within WHMCS.
*
* In a real world scenario, this file/page would be hosted by the payment
* gateway being implemented. On submission they would validate the input
* and return the user to the callback file with a success confirmation.
*
* @see https://developers.whmcs.com/payment-gateways/remote-input-gateway/
*
* @copyright Copyright (c) WHMCS Limited 2019
* @license http://www.whmcs.com/license/ WHMCS Eula
*/
// Parameters posted from remote input gateway module.
$apiUsername = isset($_POST['api_username']) ? $_POST['api_username'] : '';
$action = isset($_POST['action']) ? $_POST['action'] : '';
$customerId = isset($_POST['customer_id']) ? $_POST['customer_id'] : '';
$cardToken = isset($_POST['card_token']) ? $_POST['card_token'] : '';
$invoiceId = isset($_POST['invoice_id']) ? $_POST['invoice_id'] : '';
$amount = isset($_POST['amount']) ? $_POST['amount'] : '';
$currencyCode = isset($_POST['currency']) ? $_POST['currency'] : '';
$firstname = isset($_POST['first_name']) ? $_POST['first_name'] : '';
$lastname = isset($_POST['last_name']) ? $_POST['last_name'] : '';
$email = isset($_POST['email']) ? $_POST['email'] : '';
$address1 = isset($_POST['address1']) ? $_POST['address1'] : '';
$address2 = isset($_POST['address2']) ? $_POST['address2'] : '';
$city = isset($_POST['city']) ? $_POST['city'] : '';
$state = isset($_POST['state']) ? $_POST['state'] : '';
$postcode = isset($_POST['postcode']) ? $_POST['postcode'] : '';
$country = isset($_POST['country']) ? $_POST['country'] : '';
$phonenumber = isset($_POST['phonenumber']) ? $_POST['phonenumber'] : '';
$returnUrl = isset($_POST['return_url']) ? $_POST['return_url'] : '';
$customReference = isset($_POST['custom_reference']) ? $_POST['custom_reference'] : '';
$verificationHash = isset($_POST['verification_hash']) ? $_POST['verification_hash'] : '';

// Validate Verification Hash. Uncomment for production use.
// $apiPassword = 'sharedsecret';
// $comparisonHash = sha1(
// implode('|', [
// $apiUsername,
// $customerId,
// $invoiceId,
// $amount,
// $currencyCode,
// $apiPassword,
// ])
// );
// if ($verificationHash !== $comparisonHash) {
// die('Invalid hash.');
// }

if ($action === 'payment') {
$title = 'Make a payment';
$buttonLabel = "Pay {$amount} {$currencyCode} Now";
} else {
$title = 'Add/Update card details';
$buttonLabel = 'Save Changes';
}

?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title><?= $title ?></title>

<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>

<form method="post" action="" style="margin:0 auto;width:80%;">
<input type="hidden" name="action" value="<?= $action ?>">
<input type="hidden" name="card_token" value="<?= $cardToken ?>">
<input type="hidden" name="invoice_id" value="<?= $invoiceId ?>">
<input type="hidden" name="customer_id" value="<?= $customerId ?>">
<input type="hidden" name="return_url" value="<?= $returnUrl ?>">
<input type="hidden" name="custom_reference" value="<?= $customReference ?>">
<div class="form-group">
<label>First Name</label>
<input type="text" name="firstname" class="form-control" value="<?= $firstname ?>" required>
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" name="lastname" class="form-control" value="<?= $lastname ?>" required>
</div>
<div class="form-group">
<label>Address 1</label>
<input type="text" name="address1" class="form-control" value="<?= $address1 ?>" required>
</div>
<div class="form-group">
<label>Address 2</label>
<input type="text" name="address2" class="form-control" value="<?= $address2 ?>">
</div>
<div class="form-group">
<label>City</label>
<input type="text" name="city" class="form-control" value="<?= $city ?>" required>
</div>
<div class="form-group">
<label>State</label>
<input type="text" name="state" class="form-control" value="<?= $state ?>" required>
</div>
<div class="form-group">
<label>Postcode</label>
<input type="text" name="postcode" class="form-control" value="<?= $postcode ?>" required>
</div>
<div class="form-group">
<label>Country</label>
<input type="text" name="country" class="form-control" value="<?= $country ?>" required>
</div>
<div class="form-group">
<label>Card Type</label>
<select name="card_type" class="form-control">
<option>Visa</option>
<option>MasterCard</option>
<option>American Express</option>
<option>Discover</option>
</select>
</div>
<div class="form-group">
<label>Card Number</label>
<input type="text" name="card_number" class="form-control" required>
</div>
<div class="form-group">
<label>Expiry Date</label>
<div class="row">
<div class="col-sm-6">
<select name="card_exp_month" class="form-control">
<?php for ($x = 1; $x <= 12; $x++) { ?>
<option><?= str_pad($x, 2, STR_PAD_LEFT, '0') ?></option>
<?php } ?>
</select>
</div>
<div class="col-sm-6">
<select name="card_exp_year" class="form-control">
<?php for ($x = date('y'); $x <= date('y') + 12; $x++) { ?>
<option><?= $x ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="form-group">
<label>CVV Number</label>
<input type="text" name="card_cvv" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</body>
</html>
67 changes: 67 additions & 0 deletions modules/gateways/xendit/handler/update-card.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
/**
* Remote iFrame Demo Submit Handler.
*
* This sample file demonstrates how a payment gateway might process a
* payment form submission from an iFrame displayed within WHMCS.
*
* In a real world scenario, this file/page would be hosted by the payment
* gateway being implemented. On submission they would:
* * Validate the input
* * Create a token
* * Process the payment (if applicable)
* * Redirect back to WHMCS with the newly created token
*
* @see https://developers.whmcs.com/payment-gateways/remote-input-gateway/
*
* @copyright Copyright (c) WHMCS Limited 2019
* @license http://www.whmcs.com/license/ WHMCS Eula
*/

$apiUsername = isset($_POST['api_username']) ? $_POST['api_username'] : '';
$action = isset($_POST['action']) ? $_POST['action'] : '';
$invoiceId = isset($_POST['invoice_id']) ? $_POST['invoice_id'] : '';
$amount = isset($_POST['amount']) ? $_POST['amount'] : '';
$currencyCode = isset($_POST['currency']) ? $_POST['currency'] : '';
$customerId = isset($_POST['customer_id']) ? $_POST['customer_id'] : '';
$firstname = isset($_POST['first_name']) ? $_POST['first_name'] : '';
$lastname = isset($_POST['last_name']) ? $_POST['last_name'] : '';
$email = isset($_POST['email']) ? $_POST['email'] : '';
$address1 = isset($_POST['address1']) ? $_POST['address1'] : '';
$address2 = isset($_POST['address2']) ? $_POST['address2'] : '';
$city = isset($_POST['city']) ? $_POST['city'] : '';
$state = isset($_POST['state']) ? $_POST['state'] : '';
$postcode = isset($_POST['postcode']) ? $_POST['postcode'] : '';
$country = isset($_POST['country']) ? $_POST['country'] : '';
$phonenumber = isset($_POST['phonenumber']) ? $_POST['phonenumber'] : '';

$cardType = isset($_POST['card_type']) ? $_POST['card_type'] : '';
$cardNumber = isset($_POST['card_number']) ? $_POST['card_number'] : '';
$cardExpiryMonth = isset($_POST['card_exp_month']) ? $_POST['card_exp_month'] : '';
$cardExpiryYear = isset($_POST['card_exp_year']) ? $_POST['card_exp_year'] : '';
$cardCvv = isset($_POST['card_cvv']) ? $_POST['card_cvv'] : '';
$customReference = isset($_POST['custom_reference']) ? $_POST['custom_reference'] : '';

$returnUrl = isset($_POST['return_url']) ? $_POST['return_url'] : '';

// Payment gateway performs input validation, creates a token, process the
// payment (if applicable).

// Redirect back to WHMCS.
$redirectUri .= $returnUrl . '?' . http_build_query([
'success' => true,
'action' => $action,
'invoice_id' => $invoiceId,
'customer_id' => $customerId,
'amount' => $amount,
'currency' => $currencyCode,
'transaction_id' => rand(100000, 999999),
'card_token' => 'abc' . rand(100000, 999999),
'card_type' => $cardType,
'card_last_four' => substr($cardNumber, -4, 4),
'card_expiry_date' => $cardExpiryMonth . $cardExpiryYear,
'custom_reference' => $customReference,
]);

header('Location: ' . $redirectUri);
exit;
29 changes: 29 additions & 0 deletions modules/gateways/xendit/hooks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use Xendit\Lib\Recurring;

/**
* Hook invoice created
*
* @param $vars
* @return void
*/
function hookInvoiceCreated($vars)
{
$xenditRecurring = new Recurring();
$xenditRecurring->storeTransactions($vars['invoiceid']);

if($xenditRecurring->isRecurring($vars['invoiceid'])){
$previousInvoice = $xenditRecurring->getPreviousInvoice($vars['invoiceid']);
if(!empty($previousInvoice) && !empty($previousInvoice->paymethodid))
{
$invoice = $xenditRecurring->getInvoice($vars['invoiceid']);
$invoice->setAttribute("paymethodid", $previousInvoice->paymethodid);
$invoice->save();

// Capture invoice payment
$xenditRecurring->capture($invoice->id);
}
}
}
add_hook('InvoiceCreated', 1, 'hookInvoiceCreated');
Loading

0 comments on commit 5aecd68

Please sign in to comment.