This guide will describe the process, step by step:

  1. Create the payment intent PaymentIntent
  2. Attempt a payment
  3. (Optional) On client’s side, authenticate the payment attempt
  4. Hub2’s side: Processing the payment with the providers/payment platforms
  5. Fetching the payment intent PaymentIntent’s status

Here are the detailed steps on how to proceed with these actions.

Create a payment intent

The PaymentIntent object is used to represent the intent to collect a payment from a customer. It keeps track of fees and various payment attempts throughout the processing.

The PaymentIntent must be created on the merchant server with an amount, a currency, your customer reference and your purchase reference.

Create a payment intent

Here is a sample of request to that endpoint:

This ID and this token should be saved somewhere in the merchant infrastructure, it will be required for the next steps.

  • The id is the unique reference to the PaymentIntent, it will be used to attempt payments
  • The token is a JSON Web Token (JWT) that will be used to authenticate the payment request
{
    "id": "pi_tffiDaXyWQu203rIXhujW",
    "createdAt": "2022-07-18T06:35:25.932Z",
    "updatedAt": "2022-07-18T06:35:25.944Z",
    "merchantId": "[REDACTED]",
    "purchaseReference": "Test_YYYY_MM_DD_01",
    "customerReference": "Test_01",
    "amount": 200,
    "currency": "XOF",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnRlbnRJZCI6InBpX3RmZmlEYVh5V1F1MjAzcklYaHVqVyIsIm1lcmNoYW50SWQiOiIzIiwibW9kZSI6InNhbmRib3giLCJpYXQiOjE2NTgxMjYxMjV9.1eB6ifC2ldfRw5UstnVa-bQqIdx9_IGLRdwWyXzZR4o",
    "status": "payment_required",
    "payments": [],
    "mode": "sandbox"
}

Attempt a payment on the Payment Intent

Once the PaymentIntent is successfully created, it will be possible to collect payment information with the final customer and attempt a Payment.

To attempt a Payment, the paymentMethod and all required field that are described in the API reference must be specified .

Attempt a payment

As this route does not require your private API_KEY, the payment request could be made directly on the client side (from the client himself), using the previously retrieved JWT token to authenticate the request.

If the request is successful, a HTTP 201 with the full PaymentIntent object in the response body will be returned.

Notice that the status is now processing and the payments array contains the newly created Payment attempt.

The payment id can now be saved to identify your payment attempt in the list as more than one payment may be attempted for one PaymentIntent.

{
    "id": "pi_tffiDaXyWQu203rIXhujW",
    "createdAt": "2022-07-18T06:35:25.932Z",
    "updatedAt": "2022-07-18T06:39:21.471Z",
    "merchantId": "XXXXX",
    "purchaseReference": "Test_YYYY_MM_DD_01",
    "customerReference": "Test_01",
    "amount": 200,
    "currency": "XOF",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnRlbnRJZCI6InBpX3RmZmlEYVh5V1F1MjAzcklYaHVqVyIsIm1lcmNoYW50SWQiOiIzIiwibW9kZSI6InNhbmRib3giLCJpYXQiOjE2NTgxMjYxMjV9.1eB6ifC2ldfRw5UstnVa-bQqIdx9_IGLRdwWyXzZR4o",
    "status": "processing",
    "payments": [
        {
            "id": "pay_GBc53h6dHvuuq4vlcP6dY",
            "intentId": "pi_tffiDaXyWQu203rIXhujW",
            "createdAt": "2022-07-18T06:39:20.721Z",
            "updatedAt": "2022-07-18T06:39:21.471Z",
            "amount": 207,
            "currency": "XOF",
            "status": "created",
            "method": "mobile_money",
            "country": "CI",
            "provider": "orange",
            "number": "00000001",
            "fees": [
                {
                    "currency": "XOF",
                    "id": "fee_drJQNmGYKQAqT7oulY519",
                    "label": "payments.customer_fee",
                    "rate": 3,
                    "rateType": "percent",
                    "amount": 7
                }
            ]
        }
    ],
    "mode": "sandbox"
}

Retrieve Payment status

After attempting the payment, HUB2 will contact the payment provider and update the payment object according to its response.

From this point, the payment status can be retrieved in two different ways.

Instead of polling PaymentIntent for status updates, we recommend to use webhooks.

Please take a look at Webhooks Integration to see how to implement them.

Register to payment or payment_intents related events.

By using this method, the merchant server will be notified once the Payment or the PaymentIntent is updated.

This way, implementing mechanisms to poll the status is not necessary, rate limiting won’t be applied and potential latency issues will be avoided.

Also in case of high payment traffic, it will reduce the load on the merchant server and consequently on HUB2 servers.

Start polling the payment status using the dedicated API endpoint.

Get Payment

This method is not recommended unless the status is checked directly on the client side (Payment modal, for instance).

To prevent DoS attack, this route is rate limited, a HTTP 429 - Too Many Requests will be returned in this case. Please take this in consideration while implementing the check status polling.

Here is a sample of request on that endpoint:

Important

Polling should be used only if PaymentIntent.status is processing OR action_required. Continuously polling payment_required, successful or failed PaymentIntent will result in source IP address being throttled or temporarily banned.

In most cases, the next PaymentIntent status will be action_required. This status mean that the payment attempt requires a manual action from the user to authenticate or validate the payment.

Handle user action

The Payment object will contain a nextAction object that will describe the type of the action that will be required from the user.

{
    "id": "pi_tffiDaXyWQu203rIXhujW",
    "...": "...",
    "amount": 200,
    "currency": "XOF",
    "status": "action_required",
    "payments": [
        {
            "id": "pay_GBc53h6dHvuuq4vlcP6dY",
            "intentId": "pi_tffiDaXyWQu203rIXhujW",
            "amount": 207,
            "currency": "XOF",
            "status": "pending",
            "method": "mobile_money",
            "country": "CI",
            "provider": "orange",
            "number": "00000001",
            "fees": ["..."],
            "nextAction": {
                "type": "otp",
                "message": "Mode Sandbox. Entrez un numéro à 4 chiffres pour authentifier le paiement."
            }
        }
    ],
    "nextAction": {
        "type": "otp",
        "message": "Mode Sandbox. Entrez un numéro à 4 chiffres pour authentifier le paiement."
    }
}

Important

This is the part we are interested in : "nextAction": { "type": "otp", "message": "Mode Sandbox. Entrez un numéro à 4 chiffres pour authentifier le paiement." }.

The nextAction.message should now be displayed to the final customer to let him know what he should do. The action indicates the customer to validate the Payment. There are different types for the action :

  • A redirection action type provides all the information to redirect the customer to an external page.
  • A ussd action type indicates to the user the USSD procedure to follow to validate the payment.
  • A otp action type indicates to the final customer how to generate an One Time Password (OTP). This type of action requires the client to enter the OTP code into a field that will be sent to the dedicated API Route. Please note that some providers allow to pass the OTP code directly in the payment attempt request (see otp field mobileMoney object).