On this page
SiteGUI shop owners use payment gateways to accept and process payments from their customers. SiteGUI Commerce supports multiple payment gateways and new gateway can be added easily. Each payment gateway is a mini app that can display custom fields and HTML/Javascript code to obtain payment information from customers. Once the payment information is submitted, it should be able to process the payment automatically. Payment gateways may also support webhook (callback) method to receive payment notifications for delayed payments and refund method for refunding processed payments back to customers.
<?php
namespace SiteGUI\Gateway;
class Cash extends Base {
protected $settings;
public function __construct($config){
$this->settings = $config['app']??null;
}
// Custom config for each shop
public static function config($property = '') {
$config['visibility'] = 'hidden';
$config['fields'] = [
'display' => [
'label' => 'Display Name',
'type' => 'text',
'description' => 'Name displayed to customer',
'value' => 'Cash on Delivery',
],
'journal_alias' => [
'label' => 'Journal Account',
'type' => 'lookup',
'description' => 'Link to a journal account',
'value' => '',
],
'instruction' => [
'label' => 'Instruction',
'type' => 'text',
'description' => 'Instruction displayed to customer',
'value' => '',
],
'callback' => [
'label' => 'Webhook Listener',
'type' => 'text',
'description' => 'Enter any code to enable Webhook Listener to listen for payment notifications from external sources',
'value' => '',
],
'active' => [
'label' => 'Incoming Payment',
'type' => 'checkbox',
'description' => 'Shown as an incoming payment method',
'value' => 0,
],
'outgoing' => [
'label' => 'Outgoing Payment',
'type' => 'checkbox',
'description' => 'Shown as an outgoing payment method',
'value' => 0,
],
];
return ($property)? ($config[ $property ]??null) : $config;
}
// Capture payment
public function capture($charge = []) {
$data['status'] = 'success'; //return 'error' to request another payment attempt, 'success' to accept the payment attempt
$data['amount_received'] = 0; //the amount the gateway has processed/authorized
return $data;
}
// Transfer to an external recipient
public function send($payment = []) {
$data['status'] = 'success'; //return 'error' or 'success'
$data['amount_sent'] = 0; //the amount the gateway has processed/authorized
return $data;
}
// Refund processed payment
public function refund($charge = []) {
$data['refund'] = TRUE;
return $data;
}
//Use this callback for posting payment, callback URL is https://my.client-domain.com/account/cart/callback.json?app=Gateway/Cash&verifier=your_callback_key
public function callback($callback_post_input_array) {
$response['status']['result'] = 'error'; //default
if (empty($this->settings['callback']) OR empty($_GET['verifier']) OR $this->settings['callback'] != $_GET['verifier'] ){
$response['status']['code'] = 401;
$response['status']['message'] = 'Unauthorized';
} elseif ( !empty($_REQUEST['amount']) AND !empty($_REQUEST['transaction_id']) AND !empty($_REQUEST['reference']) ){
$response['status']['code'] = 200;
$response['status']['result'] = 'success';
$response['data']['reference'] = $_REQUEST['reference'];
$response['data']['transaction_id'] = $_REQUEST['transaction_id'];
$response['data']['amount'] = $_REQUEST['amount'];
$response['data']['fee'] = $_REQUEST['fee']??0;
$response['data']['currency'] = $_REQUEST['currency']??null;
$dt = new \DateTime();
$response['data']['timestamp'] = $_REQUEST['timestamp']??$dt->getTimestamp();
} else {
$response['status']['code'] = 422;
$response['status']['result'] = 'error';
$response['status']['message'] = 'Invalid request';
}
return $response;
}
// These protected methods are defined in Base class and you can use them directly.
// They are shown here for information purpose (your code should not need to redefine them)
// All HTML related values needs tag escaped to avoid XSS
protected function escape_html($v) {
return htmlspecialchars($v, ENT_QUOTES);
}
// All javascript values need quotes escaped to avoid XSS, redefined here because of HEREDOC
protected function json_encode($v) {
return trim(json_encode($v), '"'); //remove " at the beginning and the end so returned value can be used in query string
}
// Render payment interface
public function render($charge = []) {
if ( !empty($charge['amount']) ){
$response['render'] = $this->escape_html($this->settings['instruction']); //shop owner's input should be HTML escaped to avoid XSS
//your rendered code may contain safe HTML code (which will be checked) e.g:
//$response['render'] .= '<input type="hidden" name="cash[extra]" />';
}
return $response??[];
}
}
?>
Payment gateways should allow store owners to customize the gateway name to display it to their customers. Other settings such as gateway credentials can also be configured through custom fields. Each store will have different configured values and they are available to the gateway via $this->settings variable during runtime.
Payment gateways should display a web interface (input or select fields) to obtain payment information from customers. The Base class' render() method already includes common credit card fields, you are welcome to override it with your custom code. The render() method are provided with information similar to the following:
$charge = [
'amount' => 125,
'currency' => 'USD',
'customer' => '[id, name, email, company, street, city, state, zip, country]',
'return_url' => 'URL to return after payment is successful',
];
If you need to capture specific information for your gateway, make sure to prefix the name of the input fields with your gateway name e.g:
<input type="text" name="your_gateway[extra]" value="">
When customers enter payment information through your gateway's interface and submit it, SiteGUI will pass the payment information to your gateway's capture() method to process. You may also process the payment right at the customers' browser (like Stripe) and just return a payment ID for server side verification (use Javascript to attach that value to the hidden field named intent_id). At the server side, your gateway will receive the payment information similar to the following:
$charge = [
'amount' => 125,
'currency' => 'USD',
'card' => '[intent_id, card_number, card_expiry, card_cvc, your_custom_fields]',
'customer' => '[id, name, email, company, street, city, state, zip, country]',
'profile' => 'saved payment profile',
'shipping' => 'shipping information',
'return_url' => 'URL to return after payment is successful',
];
It is up to your gateway to decide how to process the payment. Your gateway should return an array containing a status key and the value should be either 'success' or 'error' to indicate whether the operation is successful or failed. If the payment is processed successfully, the returned array should contain payment_id and amount_received key and their values. If your gateway supports storing payment information into a profile for later processing, you can also return the identifier via payment_profile. If the payment fails, you may return an error message via message.
return [
'status' => 'success',
'payment_id' => '2390324-N334',
'amount_received' => '125',
'payment_profile' => 'PP20932',
'message' => 'Explanation in case payment is not successful',
];
When a payment cannot be captured immediately but delayed until the later time, the gateway may use webhook to notify SiteGUI when the payment is completed. The gateway should post a request to the webhook URL provided by the customer e.g: https://my.client-domain.com/account/cart/callback.json?app=Gateway/Cash&verifier=callback_key with the following information:
When an already processed payment needs to be refunded, the payment_id and the refund_amount to be refunded will be sent to refund() method to process. If it is refunded, just return true to indicate a successful refund.