On this page

Introduction

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. 

Sample Code

  <?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??[];    
  }    
}
?>    

Gateway Settings

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.

Rendering Payment Interface to display on the shopping cart or invoice payment

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="">    

Processing Payments

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',
];    

Payment Notification Webhook/Callback

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:

  • reference: the payment's reference e.g: Invoice ID, Payment Memo etc
  • transaction_id: the unique ID of the payment
  • amount: the amount of the payment
  • fee: the gateway fee for processing the payment 
  • currency: the currency of the payment
  • timestamp: the Unix timestamp when the payment is made

Refunding Payments

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.