Wiki
Clone wikiSOP / Home
#Optimize Allow your customers to check out and key in their credit/debit card details directly on merchant website. This eliminates the need to redirect customer to different pages and the best part is that Merchant does not need to be PCI certified. These features enabled merchants to host their own payment/order page.
eGHL are PCI certified, and by using Library(and API) will allows your server to never touch any of the sensitive data.
- How It all works
- Getting started
- Recurring payments
- Populate other payment methods
- Preloader
- Going live
Optimize - Allow your customers to check out and key in their credit/debit card details directly on merchant website. This eliminates the need to redirect customer to different pages and the best part is that merchant does not need to be PCI certified. This repository is intended as a technical guide for merchant developers and system integrators who are responsible for designing or programming the respective applications to integrate with Payment Gateway.
eGHL | Wiki | Downloads | Follow eGHL Repos
Change Log
**v1.3
- release v1.3.
- support MGate Browser Data
- Steps for Changes:
1) Change your JavaScript link from /eGHL_SOP-1.6.js to /eGHL_SOP-1.7.js
#!html <!-- Make following change --> <script type="text/javascript" src="https://securepay.e-ghl.com/IPG/optimize/eGHL_SOP-1.7.js?v=20221014"></script>
2) Add the following script into your checkout PHP page to fetch Browser Data
#!PHP <?php ... // Added 2022-10-04 for Mgate Requirement: Browser Data if (array_key_exists('BrowserData', $_REQUEST) && !empty($_REQUEST['BrowserData'])) { $browser_data = $_REQUEST['BrowserData']; } else{ // Send Default Value $browser_data = 'FALSE|en-MY|24|873|393|-420|TEXT/HTML'; } ...
3) In your checkout PHP page, Make sure you hash the value according to this format:
#!PHP <?php ... $data = [ 'ServiceID' => $config['ServiceID'], // String: Service ID comes from your Merchant ID with eGHL 'PaymentID' => $PaymentID, // String: Payment ID from $_REQUEST['PaymentID'] 'MerchantReturnURL' => $merchantReturnURL, // String: Your own Return URL 'MerchantCallBackURL' => $merchantCallBackURL, // String: Your own Call Back URL 'Amount' => $amount, // String: Amount from your customer's checkout 'CurrencyCode' => $currencyCode, // String: Currency Code from your customer's checkout 'CustIP' => $custIP, // String: IP from your server 'PageTimeout' => $PageTimeout // String: Page time out as 600 seconds // Important: Do not include browserData here ]; ...
4) In your input (to be submitted to eGHL), add in hidden input for Browser Data
... <input type="hidden" name="HashValue" value="<?php echo $hashValue;?>"/> <!-- Added 2022-10-06 for MGate Browser Data--> <input type="hidden" name="BrowserData" value="<?php echo $browser_data;?>"/> ...
**v1.2
- release v1.2.
- support recurring payments
- added populate other payment methods options
**v1.1
- Fixed getting js error 'undefined is not an object' when using jquery 1.1.0
[**v1.0]
- First public release
How It all works
Collecting credit cards would be handle by eGHL Javascript Library (eGHL JS). Integration of this library will be very easy and it wont modify the interface of your forms.
How it actually work is that eGHL JS will be tokenising the card before it is send to your server.
- User fill in their credit card & checkout.
- eGHL JS sends information to our server to be tokenised.
- Our Server send back token to eGHL JS.
- Payment information (exclude card info) and the token send to your server.
- Your server will then submit this token to eGHL to charge the user.
#####Back to top
Getting started
Include the code in your web. As of Oct 2022, the current JavaScript version is upgraded to 1.7.
#!html <script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.min.js"></script> <script type="text/javascript" src="https://pay.e-ghl.com/IPGSG/optimize/eGHL_SOP-1.7.js"></script>
Preparing the form
You will need to follow the following name in order for our script to work
- CardHolder - Card holder name
- CardNo - Card number
- CardExp - Expiry date for the card
- CardCVV2 - Card CVV*
Form Example
In the following example, we are going to make a plain Credit card form. You may change it to your desire design as long the name are remain the same.
#!html <form id="frmPayment" method="POST" action=""> <table> <tr> <td>Card Holder:</td><td>:</td><td><input type="text" name="CardHolder"/></td> </tr> <tr> <td>Card Number:</td><td>:</td><td><input type="text" name="CardNo" data-sop-hash="<?php echo $SOPHash;?>"/></td> </tr> <tr> <td>Card Expiry(MMYYYY):</td><td>:</td><td><input type="text" name="CardExp"/></td> </tr> <tr> <td>CVV</td><td>:</td><td><input type="text" name="CardCVV2"/></td> </tr> <tr> <td colspan="3"> <input type="submit" id="submitButton" /> </td> </tr> </table> </form>
#####Back to top
On load Javascript
Next, you will need to add the following code to initialise our library
#!javascript eGHL_SOP.init(<form ID>, <service ID>, <payment ID>, <Customer IP>,<Redirect Page>)
<form>
tag
Service ID | Service ID give by eGHL Support.
Payment ID | Unique Payment ID (must be the one used when submitting payment request)
Customer IP | Customer’s IP address captured by merchant system
Redirect Page | Page to redirect after eGHL tokenised the card
Example
#!html <script type="text/javascript"> window.onload=function(){ ... eGHL_SOP.init( 'frmPayment', '<?php echo $config["ServiceID"];?>', '<?php echo $PaymentID;?>', '<?php echo $CustIP;?>', '<?php echo $config["base_url"].'checkout.php';?>' ); ... }; </script>
Payment ID
& Cust IP
must be the same with the payment used in eGHL_SOP.init(..), eGHL_Hash & payment request
** Please note that starting v1.2, you no longer need to send parameter hash value to init function. The hash value will be added as html attributes.
Generate Hash
You will need to generate hash value for optimize(SOP) before proceed.
Example of how to generate the hash value in the server side script(example in PHP):
#!PHP <?php ... require_once('config.php'); require_once('lib/eGHL_HASH.php'); $paymentID = time(); $custIP = (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR']; $eGHLHashObj = new eGHL_Hash([ 'PaymentID' => $paymentID, 'ServiceID' => $config['ServiceID'], 'CustIP' => $custIP ]); $sopHash = $eGHLHashObj->generateHashValueForPaymentInfo('sop', $config['MerchantPass']); ...
Note:
- If you wish to use your own hash algorithm library (according to our API documentation), follow the following structure: Hash key = Password + ServiceID + PaymentID + Customer IP
- The
Payment ID
&Cust IP
must be the same with the payment used in eGHL_SOP.init(..), eGHL_Hash & payment request*
#####Back to top
Charging the card
In order to charge the card, you will need to include Token=<'Token' in the post body>
& TokenType=SOP
in the normal Payment Request.
#!PHP <?PHP $Token = $_REQUEST['Token']; $PaymentID = $_REQUEST['PaymentID']; // Added 2022-10-04 for Mgate Requirement: Browser Data if (array_key_exists('BrowserData', $_REQUEST) && !empty($_REQUEST['BrowserData'])) { $browser_data = $_REQUEST['BrowserData']; } else{ // Send Default Value $browser_data = 'FALSE|en-MY|24|873|393|-420|TEXT/HTML'; } // ------------------------------------------------------------------- // Example ONLY: The following are sample code for your reference only // ------------------------------------------------------------------- if (array_key_exists('customer_consent', $_REQUEST) && !empty($_REQUEST['customer_consent'])) { $custOCP = $_REQUEST['customer_consent']; } if (array_key_exists('amount', $_REQUEST) && !empty($_REQUEST['amount'])) { $amount = $_REQUEST['amount']; } if (array_key_exists('currencyCode', $_REQUEST) && !empty($_REQUEST['currencyCode'])) { $currencyCode = $_REQUEST['currencyCode']; } if (array_key_exists('productName', $_REQUEST) && !empty($_REQUEST['productName'])) { $productName = $_REQUEST['productName']; } if (array_key_exists('CustEmail', $_REQUEST) && !empty($_REQUEST['CustEmail'])) { $custEmail = $_REQUEST['CustEmail']; } if (array_key_exists('CustPhone', $_REQUEST) && !empty($_REQUEST['CustPhone'])) { $custPhone = $_REQUEST['CustPhone']; } if (array_key_exists('PymtMethod', $_REQUEST) && !empty($_REQUEST['PymtMethod'])) { $pymtMethod = $_REQUEST['PymtMethod']; } if (array_key_exists('IssuingBank', $_REQUEST) && !empty($_REQUEST['IssuingBank'])) { $issuingBank = $_REQUEST['IssuingBank']; } if (array_key_exists('Token', $_REQUEST) && !empty($_REQUEST['Token'])) { $token = $_REQUEST['Token']; } $orderDesc = 'Purchasing ' . $productName; $merchantReturnURL = $config['base_url'].'return.php?urlType=return'; $merchantCallBackURL = $config['base_url'].'return.php?urlType=callback'; $custName = "John Doe"; $PageTimeout = 600; $custIP = (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR']; $custIP = '127.0.0.1'; // FOR TESTING ONLY: Use your own IP // ------------------------------------------------------------------- // End Example // ------------------------------------------------------------------- // Include eGHL_HASH.php provided if not included include_once 'lib/eGHL_HASH.php'; // You need to hash the values according to this format $data = [ 'ServiceID' => $config['ServiceID'], // Service ID in String 'PaymentID' => $PaymentID, // Payment ID in String from $_REQUEST['PaymentID']; 'MerchantReturnURL' => $merchantReturnURL, // Your own Return URL 'MerchantCallBackURL' => $merchantCallBackURL, // Your own Call Back URL 'Amount' => $amount, // Amount from your customer's checkout 'CurrencyCode' => $currencyCode, // Currency Code from your customer's checkout 'CustIP' => $custIP, // IP from your server 'PageTimeout' => $PageTimeout // Page time out as 600 seconds ]; // Add Token field when you have the token if($pymtMethod == "CC") { if(!empty($token)) { $data['Token'] = $token; } $tokenType = "SOP"; } else { $token = ""; $tokenType = ""; } if(!empty($custOCP)) { $data['CustOCP'] = $custOCP; } $eGHLHashObj = new eGHL_HASH($data); $hashValue = $eGHLHashObj->generateHashValueForPaymentInfo( 'sale', $config['MerchantPass']); ?>
#!HTML ... <form method="POST" name='form1' action="<?php echo $config['payment_url'];?>"> <input type="hidden" name="Token" value="<?php echo $Token;?>"/> <input type="hidden" name="TokenType" value="SOP"/> <input type="hidden" name="TransactionType" value="SALE"/> <input type="hidden" name="PymtMethod" value="<?php echo $pymtMethod;?>"/> <input type="hidden" name="ServiceID" value="<?php echo $config['ServiceID'];?>"/> <input type="hidden" name="PaymentID" value="<?php echo $PaymentID;?>"/> <input type="hidden" name="OrderNumber" value="<?php echo $OrderNumber;?>"/> <input type="hidden" name="PaymentDesc" value="<?php echo $orderDesc;?>"/> <input type="hidden" name="MerchantReturnURL" value="<?php echo $merchantReturnURL;?>"/> <input type="hidden" name="MerchantCallBackURL" value="<?php echo $merchantCallBackURL;?>"/> <input type="hidden" name="Amount" value="<?php echo $amount;?>"/> <input type="hidden" name="CurrencyCode" value="<?php echo $currencyCode;?>"/> <input type="hidden" name="CustName" value="<?php echo $custName;?>"/> <input type="hidden" name="CustPhone" value="<?php echo $custPhone;?>"/> <input type="hidden" name="CustEmail" value="<?php echo $custEmail;?>"/> <input type="hidden" name="CustIP" value="<?php echo $custIP;?>"/> <input type="hidden" name="IssuingBank" value="<?php if (!empty($issuingBank)) {echo $issuingBank;} ?>"/> <input type="hidden" name="PageTimeout" value="<?php echo $PageTimeout;?>"/> <input type="hidden" name="LanguageCode" value="en"/> <input type="hidden" name="HashValue" value="<?php echo $hashValue;?>"/> <!-- Added 2022-10-06 for MGate Browser Data--> <input type="hidden" name="BrowserData" value="<?php echo $browser_data;?>"/> <?PHP if (!empty($custOCP)) { ?> <input type="hidden" name="CustOCP" value="<?PHP echo $custOCP;?>"/> <?PHP } ?> <input type='submit' value="proceed.."/> </form> <script type="text/javascript"> document.form1.submit(); </script> ...
* Please note that the Payment ID
& Cust IP
must be the same with the payment used in eGHL_SOP.init(..), eGHL_Hash & payment request
#####Back to top
Sending additional field
Add class name eghl-field
to the input field that you wish the library send together with generated SOP token
to the Redirect Page
.
Example of sending customer consent for saving the card for future purchases.
#!HTML <input type="checkbox" id="custOCP" name="customer_consent" class="eghl-field" checked="checked" /> I agree to have my payment details securely saved for future purchases
Card tokenization
To tokenize a card, you will need to include CustOCP=<'customer_consent' in the post body>
when charging the card. Please note that you are required to get customer consent to tokenize the card. For example including a checkbox before the submit button.
Include customer consent when charging the card:
#!HTML ... <form method="POST" name='form1' action="<?php echo $config['payment_url'];?>"> ... <input type="hidden" name="CustOCP" value="<?PHP echo $custOCP;?>"/> ... </form> ...
#####Back to top
Card info validation
By default, library will validate format, minimum length and maximum length of required field. However, the library will only print out to console log. Function name submitHandler
will be trigger every time the library completes processing the card. Any error will be stated in the info
object.
#!javascript eGHL_SOP.submitHandler = function (info) { console.log(info); if (typeof info == 'object') { for (var key in info) { $spanEL = jQuery("#error" + key + ""); switch (info[key].status){ case eGHL_SOP.responseCode.empty:{ $spanEL.html(key + ' is empty'); $spanEL.show(); } break; case eGHL_SOP.responseCode.minLength:{ $spanEL.html(key + ' minimum length: ' + info[key].minLength); $spanEL.show(); } break; case eGHL_SOP.responseCode.maxLength:{ $spanEL.html(key + ' maximum length: ' + info[key].maxLength); $spanEL.show(); } break; case eGHL_SOP.responseCode.invalidFormat:{ $spanEL.html(key + ' invalid format: ' + info[key].dataType); $spanEL.show(); } break; case 0: default: { $spanEL.html(''); $spanEL.hide(); } break; } } } }
You can also add additional checking such as LUHN check in the same function. Set preventCardSubmit
to true to prevent the library from submitting the form.
* Library does not validate card using LUHN algorithm
#!javascript ... default: { if (key == 'CardNo') { var cardNo = jQuery("form#frmPayment input[name='CardNo']").val() if(!luhnCheck(cardNo)){ $spanEL.html(key + ' entered is invalid'); $spanEL.show(); eGHL_SOP.preventCardSubmit = true; } else { $spanEL.html(''); $spanEL.hide(); eGHL_SOP.preventCardSubmit = false; } } else { $spanEL.html(''); $spanEL.hide(); } } ...
Default validation:
eGHL_SOP.fieldValidations
#!javascript { CardExp: { dataType: 'N', minLength: 6, maxLength: 6 }, CardCVV2: { dataType: 'N', minLength: 3, maxLength: 4 }, CustEmail: { dataType: 'EM', maxLength: 60 }, CustPhone: { dataType: 'AN', maxLength: 25 }, CardHolder: { dataType: 'AN-name', maxLength: 30 }, CardNo: { dataType: 'N', minLength: 16, maxLength: 19 }, HashValue: { dataType: 'AN', maxLength: 100 }, TokenType: { dataType: 'AN', maxLength: 3 }, Token: { dataType: 'ANS', maxLength: 50 } };
Data Types:
eGHL_SOP.dataTypes
#!javascript { "A": { "regex": "^[A-Za-z]$", "error-title": "Invalid %s", "error-message": "This fields only accept alphabet" }, "AN": { "regex": "^[A-Za-z0-9]+$", "error-title": "Invalid %s", "error-message": "This fields only accept alphabet & numeric" }, "N": { "regex": "^\\d+$", "error-title": "Invalid %s", "error-message": "This fields only accept numeric" }, "ND": { "regex": "^d+.+[0-9]{2}$", "error-title": "Invalid %s", "error-message": "Wrong format. Expected format 1000.00\nInvalid format: 1,000.00 or 100" }, "EM": { "regex": "[A-Z0-9a-z!#$'*-/=?^_`{|}~.]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,5}$", "error-title": "Invalid %s", "error-message": "Valid format person@company-website.com.my\nOnly !#$'*-/=?^_`{|}~. are acceptable for the email id\ne.g person!#$'*-/=?^_`{|}~.@company-website.com.my" }, "AN-name": { "regex": "^[A-Za-z0-9 ,.'-]+$", "error-title": "Invalid %s", "error-message": "This field only accept alphabet, numeric, the following symbol: \"[space],.'-\"" }, "ANS": { "regex": "^[A-Za-z0-9!#$'*-/=?^_`{|}~.]+$", "error-title": "Invalid %s", "error-message": "This field only accept alphabet, numeric, the following symbol: \"!#$'*-/=?^_`{|}~.\"" } }
Recurring payments
The following fields are required to Fetch card info
data-date-time
data-card-hash
The following fields are required for recurring payment
data-sop-hash
#!php // data-date-time $SOPDateTime = time();
#!php // data-card-hash $eGHLHashObj = new eGHL_Hash(array( 'ServiceID' => $config['ServiceID'], 'CustEmail' => $email, 'CustPhone' => $phone, 'Token' => $postData['token'], 'DateTime' => $SOPDateTime )); $cardHash = $eGHLHashObj->generateHashValueForPaymentInfo('sopcard', $config['MerchantPass']);
#!php // data-sop-hash $custIP = (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR']; $custIP = '127.0.0.1'; $eGHLHashObj = new eGHL_Hash(array( 'PaymentID' => $postData['paymentId'], 'ServiceID' => $config['ServiceID'], 'CustIP' => $custIP, 'CustEmail' => $email, 'CustPhone' => $phone, 'Token' => $postData['token'] )); $sop2Hash = $eGHLHashObj->generateHashValueForPaymentInfo('sop2', $config['MerchantPass']);
Assign required field to input name TokenType. Library will read these attributes when fetchCardInfo()
is call.
#!html <input type="text" name="TokenType" data-card-hash="92af3f609c5de23c6b1e634a350355ba7f9ced6d09d9d7d7f913bfa60350fb7e" data-sop-hash="4c262be9a92d6460314a09b9a83f9f771bb307170334d02dec8f2de2da3046c0" data-date-time="1531370964">
Displaying the Card info
Mask card number, card expiry & card holder will be fetch by calling fetchCardInfo()
.
The library will do the following:
- add a drop down above
CardNo
. - hide
CardNo
&custOCP
's parent - update image for
cardType
- update
CardExp
value
* You can also manage the card info by overriding the library function. Kindly refer the example in index.php
Example of dropdown selection generated by the library
#!html <div class="dropdown" id="dropdown-token"> <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown"><span class="dropdown-button-label" id="dropdown-button-label">XXXXXXXXXXXX2112</span> <span class="caret"></span></button> <ul class="dropdown-menu"> <li class="dropdown-item" data-card-token="xfypazs0LZUfF4sPY5M5sw==" data-card-exp="012029" data-card-type="V" data-card-holder="John Doe">XXXXXXXXXXXX2112</li> <li class="dropdown-item">Other</li> </ul> </div>
#####Back to top
Charging the card for recurring payments
Charging the card will be similar to normal payments.
Populate other payment methods
The following fields are required to generate other payment methods:
data-date-time
data-opm-hash
#!php // data-date-time $SOPDateTime = time(); $product = [ 'name' => 'pencil', 'currencyCode' => 'MYR', 'price' => '1.00' ];
#!PHP $opmHash = $eGHLHashObj->generateHashValueForPaymentInfo('sopbl', $config['MerchantPass']);
#!html <div id="eghl-opm" data-amount="<?PHP echo $product['price'];?>" data-currency="<?PHP echo $product['currencyCode'];?>" data-opm-hash="<?PHP echo $opmHash"> </div>
Library will populate other payment methods in eghl-opm
by calling function generateOtherPaymentMethod()
Preloader
The library will look for html element that contains class name eghl-card-preload
when fetching the card info and eghl-opm-preload
when fetching & populating other payment methods to display the loader
#!html <div class="eghl-card-preload"></div> <div class="eghl-opm-preload"></div>
#####Back to top
Going Live
Once all completed, eGHL would provide live credential to contact person registered in the staging account. He/she will be given the following:
- Login credential to Merchant's Admin portal
Merchant ID
*Merchant Password
*- Payment gateway production link
- eGHL JS production link
* Please note that Merchant ID
& Merchant Password
are not the login credential to Admin portal
For more info, please contact us
#####Back to top
Updated