Payment API

Overview

The Payment API attempts to provide a unified way of integration with different payment gateways.

The API provides an abstract definition of payment gateway API, this means it will not implement specific processing workflow – this is achieved by extending it.

The API consists of:

  • Library
  • API allowing you to extend the functionality of the library

Types of integrations

    • Gateway Integration – all communication is done between Joc5 web server and gateway, customer enters CC information on Joc5.
    • Hosted Page – all payment (addresses, items) information transferred to gateway and transaction is completed there

Workflow

  1. Create a new php gateway class using the examples in this document
  2. Code any custom functionality required by payment gateway
  3. Place the class in the gateway folder
  4. Open the admin and configure and  enable offers for the new payment option

As an example, consider a typical payment gateway. You might need to:

  • set up a merchant ID and a password
  • add custom product codes for each offer
  • request users to write down their phone number before purchasing tokens

All these can be accomplished with simple tasks:

  • specify the 2 fields, one for merchant ID, one for the password in the generateAdminForm() metdod; they will automatically show up in admin edit screen
  • specify the product code field in the generateAdminOffersForm() method; it will automatically show up in admin edit screen, for every system offer
  • specify the phone # field in the generatePaymentForm() method; it will automatically show up in front-end, on buy process page

Conventions

 

New gateways must extend the abstract gateway class:

Joc_Payment_Gateway

The naming convention require Joc_Payment_Gateway_prefix being added to all new gateway class names, followed by a capitalized name of the payment gateway:

Joc_Payment_Gateway_Demo

Files must be placed in the gateway folder, for example:

libs/
    classes/
        joc/
            payment/
                gateway/
                    Demo.class.php
                    Epoch.class.php
                    <- add new class here

Following the convention will assure the new gateway automatically shows up in admin, where it can be enabled and set up. No more, and no less, is required.

 

The Gateway class

 

Now that we’ve discussed the minimum requirements for creating a new gateway and its structure, let’s discuss the minimum requirement: the Gateway class.

The gateway class, as noted previously, should exist in the gateway’s folder. Beyond that, however, there are no real requirements, other that, at a minimum, it mus implement the following structure:

class Joc_Payment_Gateway_Demo extends Joc_Payment_Gateway
{
    protected $_gateway = 'Demo';

    public function generateAdminForm()
    {

    }
     public function generatePaymentForm()
    {

    }
     public function generateAdminOffersForm()
    {

    }
     public function processOrder()
    {

    }
     public function processTransaction(array $response)
    {

    }
}

So, what do gateway classes do, then?

The gateway class fulfills three key purposes:

  • It generates forms, either for configuration or for payment
  • It processes an order
  • It processes a transaction

 

Forms

 

Most gateways require some sort of configuration and parameters to be passed to each request. This can be achieved by generating forms.

There are 4 types of form elements that can be generated, each of them supports labels, custom css class, custom id, a chain of validators and other few specific options such as rows for testareas. Below are exampled for each type.

 

Example of text input

 

// set options
$options = array(
    'label' => 'Text Input',
    '3' => 1,
    'id' => 'txt1',
    'class' => 'text',
    'size' => 28,
    'maxlength' => 24
);

// init element
$element = new Joc_Form_Element_Text('text', $options);

// get value, fallback to default
$value = $this->getOption('text', 'default text value');

// set value, required, validator
$element->setValue($value)
		->setRequired(true)
		->addValidator('alpha');

// add element
$this->getPaymentForm()->addElement($element);

Example of textarea

 

// set options
$options = array(
    'label' => array('value'=>'Textarea', 'class'=>'textarea'),
    'id' => 'textarea1',
    'class' => 'text',
    'rows' => 5,
    'cols' => 20,
);

// init element
$element = new Joc_Form_Element_Textarea('textarea', $options);

// get value, fallback to default
$value = $this->getOption('textarea', 'default value for textarea');

// set value, required, validator
$element->setValue($value)
		->setRequired(true)
		->addValidator('alpha');

// add element
$this->getPaymentForm()->addElement($element);

Example of select box

 

// set options
$options = array(
    'label' => array('value'=>'Select', 'class'=>'select'),
    'id' => 'select1',
    'class' => 'text',
    'options' => array(10=>'Option 10', 20=>'Option 20'),
);

// init element
$element = new Joc_Form_Element_Select('select', $options);

// get value, fallback to default
$value = $this->getOption('select', 20);

// set value, required, validator
$element->setValue($value)
		->setRequired(true)
		->addValidator('numeric');

// add element
$this->getPaymentForm()->addElement($element);

Example of multi-select box

 

// set options
$options = array(
    'label' => array('value'=>'Multi Select', 'class'=>'multiselect'),
    'id' => 'multi1',
    'class' => 'text',
    'multiple' => true,
);

$opt = array(10=>'Option 10', 20=>'Option 20', 30=>'Option 30');

// init element
$element = new Joc_Form_Element_Multiselect('multiselect', $options);

// get value, fallback to default
$value = $this->getOption('multiselect', array(10, 30));

// set value, required, validator
$element->setValue($value)
		->setRequired(true)
		->addValidator('numeric')
		->setMultiOptions($opt);

// add element
$this->getPaymentForm()->addElement($element);

Processing orders

 

Different gateways require different parameters for the order. The implementation can follow two distinct directions:

  1. Compose a URL with gateway url and a list of parameters, then redirect to gateway url. The gateway will use a callback script (URL available in admin, edit gateway screen) to respond with the transaction details. Based on response, display ok/error message.
    OR
  2. Compose an array of parameters and call the gateway. Based on response, display ok/error message.

Processing transactions

 

Different gateways respond in specific formats after a transaction. Based on the validity of response source, of response parameters, display ok/error message.

 

Php demo class file

 

You can see below an example of a php demo class file:

<?php

class Joc_Payment_Gateway_Demo extends Joc_Payment_Gateway
{
   const RESPONSE_APPROVED = 1;
   const RESPONSE_DECLINED = 2;
   const RESPONSE_ERROR = 3;
   const RESPONSE_HELD = 4;

   protected $_gateway = 'Demo';

   /* Set up forms */

   /**
   * Generate a form to be loaded in admin on the gateway set up page
   * @return Joc_Form
   */
   public function generateAdminForm()
   {
      /* example of text input */

      // set options
      $options = array(
         'label' => 'Text Input',
         'id' => 'txt1',
         'class' => 'text',
         'size' => 28,
         'maxlength' => 24
      );

      // init element
      $element = new Joc_Form_Element_Text('text_admin', $options);

      // get value, fallback to default
      $value = $this->getOption('text', 'default value');

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('alphanumeric');

      // add element
      $this->getAdminForm()->addElement($element);

      /* example of textarea */

      // set options
      $options = array(
         'label' => array('value'=>'Textarea', 'class'=>'textarea'),
         'id' => 'textarea1',
         'rows' => 5,
         'cols' => 20,
      );

      // init element
      $element = new Joc_Form_Element_Textarea('textarea_admin', $options);

      // get value, fallback to default
      $value = $this->getOption('textarea', 'default value');

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('notEmpty');

      // add element
      $this->getAdminForm()->addElement($element);

      /* example of select box */

      // set options
      $options = array(
         'label' => array('value'=>'Select', 'class'=>'select'),
         'id' => 'select1',
         'options' => array(10=>'Option 10', 20=>'Option 20'),
      );

      // init element
      $element = new Joc_Form_Element_Select('select_admin', $options);

      // get value, fallback to default
      $value = $this->getOption('select', 20);

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('numeric');

      // add element
      $this->getAdminForm()->addElement($element);

      /* example of multi-select box */

      // set options
      $options = array(
         'label' => array('value'=>'Multi Select', 'class'=>'multiselect'),
         'id' => 'multi1',
         'multiple' => true,
      );

      $opt = array(10=>'Option 10', 20=>'Option 20', 30=>'Option 30');

      // init element
      $element = new Joc_Form_Element_Multiselect('multiselect_admin', $options);

      // get value, fallback to default
      $value = $this->getOption('multiselect', array(10, 30));

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('numeric')
              ->setMultiOptions($opt);

      // add element
      $this->getAdminForm()->addElement($element);

  }

   /**
    * Generate a form to be loaded in front-end on the payment page
    * @return Joc_Form
    */
   public function generatePaymentForm()
   {
      /* example of text input */
      // set options
      $options = array(
         'label' => 'Text Input',
         '3' => 1,
         'id' => 'txt1',
         'class' => 'text',
         'size' => 28,
         'maxlength' => 24
      );

      // init element
      $element = new Joc_Form_Element_Text('text', $options);

      // get value, fallback to default
      $value = $this->getOption('text', 'default text value');

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('alpha');

      // add element
      $this->getPaymentForm()->addElement($element);

      /* example of textarea */

      // set options
      $options = array(
         'label' => array('value'=>'Textarea', 'class'=>'textarea'),
         'id' => 'textarea1',
         'class' => 'text',
         'rows' => 5,
         'cols' => 20,
      );

      // init element
      $element = new Joc_Form_Element_Textarea('textarea', $options);

      // get value, fallback to default
      $value = $this->getOption('textarea', 'default value for textarea');

     // set value, required, validator
     $element->setValue($value)
             ->setRequired(true)
             ->addValidator('alpha');

     // add element
     $this->getPaymentForm()->addElement($element);

     /* example of select box */

     // set options
     $options = array(
        'label' => array('value'=>'Select', 'class'=>'select'),
        'id' => 'select1',
        'class' => 'text',
        'options' => array(10=>'Option 10', 20=>'Option 20'),
     );

      // init element
      $element = new Joc_Form_Element_Select('select', $options);

      // get value, fallback to default
      $value = $this->getOption('select', 20);

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('numeric');

      // add element
      $this->getPaymentForm()->addElement($element);

      /* example of multi-select box */

      // set options
      $options = array(
         'label' => array('value'=>'Multi Select', 'class'=>'multiselect'),
         'id' => 'multi1',
         'class' => 'text',
         'multiple' => true,
      );

      $opt = array(10=>'Option 10', 20=>'Option 20', 30=>'Option 30');

      // init element
      $element = new Joc_Form_Element_Multiselect('multiselect', $options);

      // get value, fallback to default
      $value = $this->getOption('multiselect', array(10, 30));

      // set value, required, validator
      $element->setValue($value)
              ->setRequired(true)
              ->addValidator('numeric')
              ->setMultiOptions($opt);

      // add element
      $this->getPaymentForm()->addElement($element);
   }

   /**
    * Generate a form to be loaded in admin on the payment page
    * @return Joc_Form
    */
   public function generateAdminOffersForm()
   {
      foreach ($this->getOffers(true) as $package) {
          // x_code
          $options = array(
              'label' => 'Xcode'
          );
          $element = new Joc_Form_Element_Text('x_code__' . $package, $options);

          $value = $this->getOption('x_code__' . $package, 'x');
          $element->setValue($value)
                  ->setRequired(true)
                  ->addValidator('alpha');
          $this->getAdminOffersForm()->addElement($element);
      }
   }

   /* Process */

   /**
    * Processes an order coming from member
    * Also known as SALE or AUTHORIZE+CAPTURE
    */
   public function processOrder()
   {
      exit('processing....');

      $appUrl = 'http://example.com';
      $request = Joc_Payment_Request::get($appUrl)
          ->addParameter('text', $this->getOption('text'))
          ->addParameter('select', $this->getOption('select'))
          ->addParameter('offer', $this->getOption('offer_id'))
      ;

      $offerId = (int) $this->getOption('offer_id');
      $merchantId = $this->getOption('id');

      // generate an invoice
      $transactionObj = new Joc_Payment_Transaction();
      $invoiceId = $transactionObj->generateInvoice($merchantId, $request->getParameters());

      // set invoice in request
      $request->addParameter('x_invoice_id', $invoiceId);

      // may redirect

      // or may return
      $response = $request->getResponse();
      return $this->processTransaction($response);
   }

   /**
    * Process transaction response coming from payment gateway
    */
   public function processTransaction(array $response)
   {
      $responseMap = array(
          'code', 'subcode', 'reason_code', 'message', 'auth_code', 'avs_code', 'transaction_id',
          'invoice_id', 'description', 'amount', 'method', 'transaction_type', 'customer_id',
          37 => 'md5_hash', 38 => 'card_verification', 39 => 'cavr_code', 50 => 'account_number',
          51 => 'card_type', 52 => 'tendet_id', 53 => 'requested_amount', 54 => 'card_balabce'
      );

      $result = new Joc_Payment_Response();
      $result->import($response, $responseMap);

      switch ((int)$result->get('code')) {
          case self::RESPONSE_APPROVED:
              $status = Joc_Payment_Response::STATUS_APPROVED;
              break;
          case self::RESPONSE_DECLINED:
              $status = Joc_Payment_Response::STATUS_DECLINED;
              break;
          case self::RESPONSE_ERROR:
              $status = Joc_Payment_Response::STATUS_ERROR;
              break;
          case self::RESPONSE_HELD:
              $status = Joc_Payment_Response::STATUS_PENDING;
              break;
          default:
              $status = Joc_Payment_Response::STATUS_ERROR;
              break;
      }

      $result->setStatus($status);
      $result->setMessages($result->get('message'));

      return $result->isSuccess();
   }
}

 

Handling Callbacks

 

The callback URL is defined in admin, merchant edit screen.

The logic for the callback has to go into processTransaction() method of the merchant class. Here you can validate the request and give tokens, for example:

($this->_isValid())
   {
         $amount = (float) $_POST['initialPrice'];
         $invoiceId = (int) $_POST['invoice_id'];
         $transactionId = (int) $_POST['subscription_id'];

$this->confirmPayment($invoiceId, $transactionId, $amount);
   }