Skip to main content

Cards Guide

This guide details how to process Card payments (Credit/Debit) directly via the Little Pay API, including handling 3D-Secure authentication. For testing purposes, you can use the card numbers provided in the Test Cards guide.

1. Setup & Credentials

Before getting started, ensure you have an account:

  1. Create an account and a sandbox merchant at https://pay.little.africa.
  2. Navigate to Merchants -> Merchant Settings to obtain your credentials:
    • App ID
    • Client ID
    • Client Secret
    • Token ID
    • Secret Key (Used as X-API-KEY)

Note: The Secret Key can be found in the Security tab of the Merchant Settings.

2. Create Payment Intent

First, create a payment intent.

  • URL: https://pay.little.africa/api/payments/{tokenId}/pay
  • Method: POST
  • Authentication: Basic Auth (use ClientId as username and ClientSecret as password).

Request Body

The payload should be a JSON object with the following fields:

Important: While the billing address fields are marked as optional, providing complete and accurate billing address information is strongly recommended for card payments. The billing address is used for Address Verification System (AVS) checks, which can significantly impact payment success rates. Incomplete or incorrect billing address information may lead to payment declines during processing.

{
"amount": 0, //required - amount (can include decimals, some payment providers may not support decimals eg MPESA and amount will be rounded to nearest whole number)
"currency": "string", //required ISO 4217 currency code eg "KES" for Kenyan Shillings
"description": "string", // required
"callbackUrl": "string", // required
"key": "string", // required (alphanumeric string, no special characters eg (-,/,*,+,\))
"returnUrl": "string", // optional - will redirect to this url after payment is completed
"expiresAt": 0, // optional - minutes after which the payment intent will expire
"payload": {
"billingAddress": {
"firstName": "string", //optional
"lastName": "string", //optional
"address1": "string", //optional - street address
"locality": "string", //optional - city/town
"administrativeArea": "string", //optional - state/province/county
"postalCode": "string", //optional - zip code
"country": "string", //optional - ISO 3166-1 alpha-2 country code eg "KE" for Kenya
"email": "string", //optional if phone is provided
"phoneNumber": "string" //optional if email is provided (ISO format eg +254712345678)
},
"customData": null // optional - a json with custom data that will be included in the callback
},
"metadata": {
"authenticationRedirectUrl": "string" // optional - url to redirect user to after step-up authentication
}
}

Code Examples

const https = require('https');

const data = JSON.stringify({
amount: 100,
currency: "KES",
description: "Card Payment",
callbackUrl: "https://your-domain.com/callback",
key: "CARD12345",
payload: {
billingAddress: {
firstName: "John",
lastName: "Doe",
email: "john@example.com"
}
},
metadata: {
authenticationRedirectUrl: "https://your-domain.com/3ds-complete"
}
});

const options = {
hostname: 'pay.little.africa',
path: '/api/payments/{tokenId}/pay', // Replace {tokenId}
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + Buffer.from('YOUR_CLIENT_ID:YOUR_CLIENT_SECRET').toString('base64'),
'Content-Length': data.length
}
};

const req = https.request(options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
console.log(JSON.parse(body));
});
});

req.write(data);
req.end();

Response

{
"date": "2024-03-07T13:03:05.141Z",
"data": {
"reference": "YOUR_REFERENCE_ID",
"checkoutUrl": "",
"message": "Request processed successfully"
}
}

The reference typically serves as the identifier for the payment process. In the following steps, replace {reference} with the value returned here.

3. Get Device Details Collection URL

To process a 3D-Secure card payment, you must first get a device details collection URL. This URL is used to collect device details from the cardholder's device.

You can get the device details collection URL by making a POST request to the /pay/{reference}/cspaToken endpoint. The request body should contain the card details.

The response will contain the deviceDataCollectionUrl and the accessToken that you can use to collect the device details.

  • URL: https://pay.little.africa/pay/{reference}/cspaToken
  • Method: POST

You can get the test cards here Test Cards

Request Body

{
"type": "CARDS",
"payment": {
"cc_number": "4000000000002701", //Card number
"cc_cvv": "000", //CVV
"cc_exp": "12/25", //Expiry date (MM/YY) or (MM/YYYY) or (MMYY)
"cc_name": "John Doe" //Cardholder name
}
}

Response

{
"data": {
"reference": "YOUR_REFERENCE_ID",
"paToken": {
"accessToken": "ACCESS_TOKEN",
"deviceDataCollectionUrl": "https://centinelapi.cardinalcommerce.com/V1/Cruise/Collect",
"referenceId": "",
"token": ""
}
}
}

4. Collect Device Details

You can collect device details by making a POST request to the deviceDataCollectionUrl that you received in the previous step. The request body should contain the accessToken.

Please note that this step is MANDATORY.

The following JavaScript code demonstrates how to create a hidden form and iframe to submit the request. The response is an event callback that contains a message with the status of the device data collection process.

Make a POST request to the deviceDataCollectionUrl from the cardholder's browser using the accessToken.

// This code runs in the browser
const form = document.createElement("form");
form.id = "deviceDataCollectionForm";
form.method = "POST";
form.action = paToken.deviceDataCollectionUrl; // From Step 3 response
form.style.display = "none";
form.target = "collectionIframe";

const input = document.createElement("input");
input.type = "hidden";
input.name = "JWT";
input.value = paToken.accessToken; // From Step 3 response
input.id = "cardinal_collection_form_input";
form.appendChild(input);

const iframe = document.createElement("iframe");
iframe.name = "collectionIframe";
iframe.style.display = "none";
iframe.id = "cardinal_collection_iframe";
iframe.width = "10";
iframe.height = "10";

document.body.appendChild(form);
document.body.appendChild(iframe);
form.submit();

window.addEventListener("message", (event) => {
// Verify origin:
// Test: https://centinelapistag.cardinalcommerce.com
// Prod: https://centinelapi.cardinalcommerce.com
if (event.origin.includes("cardinalcommerce.com")) {
console.log("Device details collected:", event.data);
// Proceed to Step 5
}
});

5. Check 3DS Enrollment

You can check if the cardholder requires step-up authentication by making a POST request to the /pay/{reference}/enroll endpoint.

You must pass manually collected device information as a backup in case the device details collection (Step 4) fails. The response will contain the action. If the action is AUTHENTICATE, it will also return an accessToken and stepUpUrl that you can use to render the step-up authentication page.

  • URL: https://pay.little.africa/pay/{reference}/enroll
  • Method: POST

Request Body

{
"deviceInformation": {
"httpBrowserColorDepth": 24,
"httpBrowserJavaEnabled": true,
"httpBrowserJavaScriptEnabled": true,
"httpBrowserLanguage": "en-US",
"httpBrowserScreenHeight": 1080,
"httpBrowserScreenWidth": 1920,
"httpBrowserTimeDifference": -300
}
}

Response

{
"data": {
"action": "AUTHENTICATE", // MAKE_PAYMENT, AUTHENTICATE, or FAILED
"accessToken": "STEP_UP_ACCESS_TOKEN",
"stepUpUrl": "URL"
}
}

The action field determines your next step:

  • MAKE_PAYMENT: The cardholder does not require step-up authentication. Proceed to Step 7.
  • AUTHENTICATE: The cardholder requires step-up authentication. Proceed to Step 6.
  • FAILED: The request was rejected. The customer should be prompted to try using another payment method.

6. Render Step-Up Authentication

If the action returned in Step 5 was AUTHENTICATE, you must render the step-up authentication page.

You can do this by creating a form that submits a POST request to the stepUpUrl using the accessToken (passed as JWT) and the Payment Intent reference (passed as MD).

The form can be targeted to a new tab (_blank) or an iframe.

If you provided a metadata.authenticationRedirectUrl when creating the payment intent (Step 2), the user will be redirected to that URL after step-up authentication is completed. The redirect will include the following query parameters:

  • intentId: This is the payment intent reference.
  • action: The outcome of the authentication process.
const form = document.createElement("form");
form.id = "stepUpForm";
form.method = "POST";
form.action = stepUpUrl; // From Step 5
form.target = "_blank"; // Or iframe name

const jwtInput = document.createElement("input");
jwtInput.type = "hidden";
jwtInput.name = "JWT";
jwtInput.value = accessToken; // From Step 5
form.appendChild(jwtInput);

const mdInput = document.createElement("input");
mdInput.type = "hidden";
mdInput.name = "MD";
mdInput.value = reference; // The Payment Intent Reference
form.appendChild(mdInput);

document.body.appendChild(form);
form.submit();

If you provided a metadata.authenticationRedirectUrl in Step 2, the user will be redirected there after authentication.

7. Process Payment

Finally, process the payment.

  • URL: https://pay.little.africa/pay/{reference}/process
  • Method: POST

Request Body

{
"type": "CARDS"
}

Response

{
"data": {
"status": "COMPLETED", // COMPLETED, FAILED
"message": "Payment successful",
"provider": "CARDS",
"reference": "..."
}
}