NAV

Introduction

Welcome to the Fena online payment guide. Before we dive into integration, let us explain how this system works.

How Fena online payments works

The Fena Online Payment system consists of three main parts:

  1. A webpage that initiates a request to Fena to make a payment.
  2. A page on your webserver that Fena to notify you when a payment has been made.
  3. A webpage that confirms the above payment and passes the customer on to the next phase of your application, such as your ‘order confirmation’ or 'Thank You' page. Parts 1 and 3 can be accessed by customers on your website/app. Part 2 is only visible to Fena. This diagram illustrates the interaction between your customer, Fena and your application.

JWT

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

In our case between Fena and your platform.

Fena uses JWT tokens extensively for API integration while maintaining ease of use and security.

New Payment

To create a new payment simply follow these two steps:

  1. JWT token
    1. Create a JWT payload
    2. Sign a JWT Payload
  2. Create a url for customer redirection

We highly recommend using the library for JWT encryption. Here are some example for PHP and Node.

Platform Library Link
PHP PHP-JWT https://github.com/firebase/php-jwt
Node jwt-simple https://github.com/hokaccha/node-jwt-simple

Create a JWT payload

Your JWT token should contains these fields:

Payload

{
  "iat": 1613942693,
  "exp": 1613949893,
  "terminalID": "4af9b080-b6d3-4206-a10f-f2162f15b655",
  "orderID": "222",
  "amount": "0.50",
  "email": "user@example.com",
  "firstName": "John",
  "lastName": "Doe",
  "contactNumber": "07000000000",
  "bankID": "ob-lloyds-personal",
  "sortCode": "123456",
  "accountNumber": "12345678"
}

Payload Definition

Name Definition Can be empty?
iat The time at which the request was issued by the integrator expressed as "seconds since the epoch" No
exp The time at which the request expires expressed as seconds since the epoch. No
terminalID The terminal key from Fena platform No
orderID Your unique persisted id for this payment. Must not be more than 255 characters. Cannot be Null. No
amount Amount expressed in string with 2 decimal places. Must be greater than 0.50 and lower than 5000. Do not include any currency symbols No
email Email of the customer. Must not be more than 255 characters. Cannot be Null. Yes
firstName First name of the customer. Must not be more than 255 characters. Cannot be Null. Yes
lastName Last name of the paying customer. Must not be more than 255 characters. Cannot be Null. Yes
contactNumber Phone number of the customer. Must not be more than 255 characters. Cannot be Null. Yes
bankID Bank id from our provider API (https://app.fena.co/api/providers/). This is used if you want to force customer to pay with specific bank / account provider. Cannot be Null. Yes
sortCode Sort code of paying customer. This will force the customer to pay from this specific sort code. Do not include spaces or -. This should be 6 digits. Yes
accountNumber Account number of paying customer. This will force customer the to pay from this specific account number. Do not include spaces or -. This should be 8 digits. Cannot be Null. Yes

Please note bankID, sortCode and accountNumber are all required to force customer to pay from a specific account number.

Sign a JWT Payload

Please use your terminal secret key to sign the payload and use HS512 algorithm.

Please find the example below for PHP

Examples

PHP

use Firebase\JWT\JWT;

$payload = [
          'iat' => $currentUnixTimeStamp,
          'exp' => $tokenExpiryTime,
          'terminalID' => $terminalId,
          'orderID' => $orderId,
          'amount' => $amount,
          'email' => $email,
          'firstName' => $firstName,
          'lastName' => $lastName,
          'contactNumber' => $contactNumber,
          'bankID' => $bankID,
          'sortCode' => $sortCode,
          'accountNumber' => $accountNumber
  ];
$jwt = JWT::encode($payload, $terminalSecret, 'HS512');

Javascript

const jwt = require('jwt-simple');
const payload = {
      iat: currentUnixTimeStamp,
      exp: tokenExpiryTime,
      terminalID: terminalId,
      orderID: this.orderId,
      amount: amount,
      email: "",
      firstName: "",
      lastName: "",
      contactNumber: "",
      bankID: "",
      sortCode: "",
      accountNumber: "",
};
const jwtToken = jwt.encode(payload, terminalSecret, "HS512");

Create a url for customer redirection

This redirects a user to the following link:

https://app.fena.co/pay/?token=YOUR_JWT_TOKEN

Webhook

Fena calls the webhook or notification endpoint with post request of encoded as "application/x-www-form-urlencoded" with token in key called "token".

Fena will only make a notification request if the payment is successful.

Only one request will be made per your unique order id. This is setup to prevent duplicated payments.

The token is signed JWT token. This token is signed with the same secret key used for creating a new payment.

Fena recommends performing the following checks on tokens:

  1. Verify the signature
  2. Verify it has the correct payload
  3. Verify the terminal
  4. Verify the amount

Payload Definition

Name Definition
iat The time at which the request was issued by the Fena expressed as "seconds since the epoch"
exp The time at which the request expires expressed as seconds since the epoch
orderID Your unique persisted id for this payment
requestAmount Amount expressed in string with 2 decimal places.
netAmount Amount expressed in string with 2 decimal places.
terminal The terminal key from Fena platform
completedAt Timestamp of when the payment was completed. Formatted as string. (YYYY-mm-dd HH:mm:ss)

PHP Example (Sudo Code)

use Firebase\JWT\JWT;

try {
    $token = JWT::decode($token, $terminalSecret, ['HS512']);
    if ($token instanceof stdClass) {
        $token = json_decode(json_encode($token), true);
    }
} catch (Exception $exception) {
    // ERROR handling here
    return;
}


// verify if token has field
if (!is_array($token) ||
    !isset($token['id']) ||
    !isset($token['orderID']) ||
    !isset($token['requestAmount']) ||
    !isset($token['netAmount']) ||
    !isset($token['terminal'])
) {
    // ERROR handling here
    return;
}

// validate the terminal
if ($token['terminal'] != $terminalId) {
    return;
}

// validate the amount
if ($token['netAmount'] != $amount) {
    return;
}

Redirect Handling

On successful payment completion or rejection the user will be redirected to "Redirect Endpoint" configured in terminal setting along with two query parameters:

Your Order ID as "order"

Payment Status as "status"

Example:

https://www.test.com/thankyou?order=aa-bb-cc&status=executed

Status Description

Status Description
executed Payment has been successfully processed
rejected Payment has been rejected by the account provider