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:
- A webpage that initiates a request to Fena to make a payment.
- A page on your webserver that Fena to notify you when a payment has been made.
- 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:
- JWT token
- Create a JWT payload
- Sign a JWT Payload
- 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 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:
- Verify the signature
- Verify it has the correct payload
- Verify the terminal
- 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:
- order
- status
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 |