Online Checkout
Integrate online payments into any application using Xtopay's hosted checkout page or embedded onsite iframe.
Online Checkout
The Online Checkout API allows merchants to accept online payments for goods and services. It creates a secure, hosted payment session that supports:
- Mobile Money: MTN Mobile Money, Telecel Cash, AT Money (AirtelTigo)
- Bank Cards: Visa, Mastercard, Verve
- Wallets: Xtopay Wallets, G-Money, Zeepay
- GhQR & Bank Transfers
This API offers RESTful endpoints that integrate online payments into any application. You may implement the Xtopay Online Checkout using one of two patterns:
- Redirect Checkout: Redirect the customer to a secure checkout page hosted by Xtopay to take payment.
- Onsite Checkout: Load the Xtopay checkout flow directly on your own website inside an iframe or modal.
Redirect Checkout - How It Works
- A customer arrives on your website and clicks on a pay button.
- Your backend server sends a request to
POST /v1/paymentsto initiate a payment session. - Xtopay returns a secure
checkoutUrland a unique transactionreference. - You redirect the customer to the
checkoutUrl. - The customer selects how they wish to pay (e.g. entering their mobile money number).
- The customer verifies their identity and authorizes the payment (e.g. by completing a USSD push PIN prompt on their phone or entering an OTP).
- When payment is completed, a success or failure notification is presented to the customer.
- Xtopay dispatches a webhook notification to your registered webhook URL.
- The customer is redirected back to your website via the return URL specified in your request metadata.
Redirect Checkout Request Flow
sequenceDiagram
autonumber
actor Customer
participant MerchantSite as Merchant Website
participant MerchantServer as Merchant Server
participant Xtopay as Xtopay API
participant Telco as Telco / Bank Partner
Customer->>MerchantSite: Click Pay Button
MerchantSite->>MerchantServer: Initiate payment request
MerchantServer->>Xtopay: POST /v1/payments (Auth: Bearer secret_key)
Xtopay-->>MerchantServer: Return checkoutUrl & reference
MerchantServer-->>MerchantSite: Redirect to checkoutUrl
MerchantSite-->>Customer: Redirect to Xtopay hosted page
Customer->>Xtopay: Select payment method & submit details
Xtopay->>Telco: Trigger charge request (USSD Push / Card Auth)
Telco-->>Customer: Receive USSD PIN Prompt / OTP
Customer->>Telco: Approve transaction (Enter PIN / OTP)
Telco-->>Xtopay: Authorization success notification
Xtopay-->>Customer: Display success/failure status screen
Xtopay->>MerchantServer: Webhook callback (POST payment.succeeded)
Customer->>MerchantSite: Redirect back via returnUrl (metadata)Onsite Checkout - How It Works
- A customer arrives on your website and clicks on a pay button.
- Your backend server initiates the payment session with Xtopay and returns the
checkoutUrlto your frontend. - Your website loads the
checkoutUrlinside an iframe or modal directly on your checkout page. - The customer selects their payment method and completes the transaction inside the iframe (e.g. scanning a GhQR code or entering their mobile wallet details).
- The customer completes the OTP or USSD authentication prompt on their mobile device.
- Xtopay dispatches a webhook notification to your registered webhook URL.
- The iframe emits completion events back to the parent window (via
window.parent.postMessage), allowing you to smoothly transition the checkout page onsite.
Onsite Checkout Request Flow
sequenceDiagram
autonumber
actor Customer
participant MerchantSite as Merchant Website
participant Iframe as Xtopay Iframe
participant MerchantServer as Merchant Server
participant Xtopay as Xtopay API
Customer->>MerchantSite: Click Pay Button
MerchantSite->>MerchantServer: Request payment session
MerchantServer->>Xtopay: POST /v1/payments
Xtopay-->>MerchantServer: Return checkoutUrl & reference
MerchantServer-->>MerchantSite: Return checkoutUrl
MerchantSite->>Iframe: Load checkoutUrl in Iframe
Customer->>Iframe: Select method & authorize transaction
Xtopay->>MerchantServer: Webhook callback (POST payment.succeeded)
Iframe-->>MerchantSite: postMessage event (payment.succeeded)
MerchantSite->>Customer: Show custom success view onsite[!NOTE] Mandatory Fallback Check In instances where a merchant does not receive the final status webhook callback within five (5) minutes from Xtopay, it is mandatory to perform a transaction status check using the Transaction Status Check API to determine the final status of the transaction.
API Reference
To initiate a transaction, send an HTTP POST request to the below URL with the required parameters in the JSON payload.
| Detail | Description |
|---|---|
| API Endpoint | https://api.xtopay.co/v1/payments |
| Request Type | POST |
| Content Type | application/json |
| Authentication | Basic Base64(clientId:clientSecret) |
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | Integer | Yes | Specifies the total amount expected to be paid for the items being purchased, in minor units (e.g. 5000 = GHS 50.00). Decimals are not allowed. |
currency | String | No | Three-letter ISO 4217 currency code (e.g. GHS, NGN, USD). Case-insensitive. Defaults to GHS. |
description | String | No | A brief description of the transaction shown to the customer on the checkout page (max 255 characters). |
customer | Object | No | Recommended. An object containing the customer details. Structure detailed below. |
customer.name | String | Yes* | Customer's full name. *Mandatory if the customer object is provided. |
customer.email | String | Yes* | Customer's email address. *Mandatory if the customer object is provided. |
customer.phone | String | No | Customer's mobile number. |
metadata | Object | No | Flat JSON key-value store to attach merchant references. Tip: include orderId and returnUrl (e.g., { "orderId": "inv0012", "returnUrl": "https://merchant.com/checkout/success" }). |
Response Parameters
| Parameter | Type | Description |
|---|---|---|
success | Boolean | Indicates if the checkout session was created successfully. |
data | Object | Contains the checkout session details. |
data.id | String | Unique transaction ID generated by Xtopay. |
data.reference | String | Unique reference matching the transaction ID. |
data.status | String | Initial status of the checkout session (always PENDING). |
data.amount | Integer | Ingested amount in minor units. |
data.currency | String | Ingested currency code. |
data.description | String | Ingested description. |
data.checkoutUrl | String | Secure URL where the payee should be redirected to make payment. |
data.metadata | Object | Flattened custom key-values attached to this payment. |
data.createdAt | String | ISO 8601 creation timestamp. |
data.customer | Object | Sanitized customer profile matching the created session. |
Code Examples
curl https://api.xtopay.co/v1/payments \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"currency": "GHS",
"description": "Book Shop Checkout",
"customer": {
"name": "Kwame Mensah",
"email": "kwame@example.com",
"phone": "+233241234567"
},
"metadata": {
"orderId": "inv0012",
"returnUrl": "https://merchant.com/checkout/success"
}
}'Sample Response
201 Created
{
"success": true,
"data": {
"id": "pay_cl8z2e8z20000g",
"reference": "cl8z2e8z20000g",
"status": "PENDING",
"environment": "LIVE",
"method": null,
"amount": 10000,
"currency": "GHS",
"fee": 0,
"net": 10000,
"description": "Book Shop Checkout",
"metadata": {
"orderId": "inv0012",
"returnUrl": "https://merchant.com/checkout/success"
},
"failureCode": null,
"failureMessage": null,
"paidAt": null,
"createdAt": "2026-06-06T12:00:00Z",
"updatedAt": "2026-06-06T12:00:00Z",
"customer": {
"id": "cust_cl8z2e9z",
"name": "Kwame Mensah",
"email": "kwame@example.com",
"phone": "+233241234567"
},
"refunds": [],
"checkoutUrl": "https://pay.xtopay.co/cl8z2e8z20000g"
}
}Checkout Callback (Webhooks)
You should register a webhook endpoint in your merchant dashboard to receive asynchronous payment status notifications. When a customer completes payment, Xtopay dispatches an HTTP POST payload containing the transaction's final status to your webhook url.
Sample Webhook Payload
{
"event": "payment.succeeded",
"data": {
"id": "pay_cl8z2e8z20000g",
"reference": "cl8z2e8z20000g",
"status": "SUCCEEDED",
"environment": "LIVE",
"method": "MOBILE_MONEY",
"amount": 10000,
"currency": "GHS",
"fee": 150,
"net": 9850,
"description": "Book Shop Checkout",
"metadata": {
"orderId": "inv0012",
"returnUrl": "https://merchant.com/checkout/success"
},
"paidAt": "2026-06-06T12:02:11Z",
"customer": {
"id": "cust_cl8z2e9z",
"name": "Kwame Mensah",
"email": "kwame@example.com",
"phone": "+233241234567"
},
"mobileMoneyDetail": {
"network": "MTN",
"phone": "+233241234567",
"externalRef": "0000006824852622"
}
}
}Transaction Status Check
It is mandatory to implement the Transaction Status Check API. Use this to poll the transaction state in rare instances where your server does not receive the webhook callback from Xtopay within five (5) minutes.
To check the status of a transaction, send an HTTP GET request to the endpoint below, appending the transaction's reference as a path parameter.
| Detail | Description |
|---|---|
| API Endpoint | https://api.xtopay.co/v1/payments/:reference |
| Request Type | GET |
| Content Type | application/json |
| Authentication | Basic Base64(clientId:clientSecret) |
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
:reference | String | Yes | The unique reference of the payment session (e.g. cl8z2e8z20000g). |
Sample GET Request
curl https://api.xtopay.co/v1/payments/cl8z2e8z20000g \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET"Response Parameters
The GET endpoint returns the fully resolved payment record, including processor details (e.g. mobileMoneyDetail or cardDetail) and platform fees once processed.
Sample Response (Paid)
200 OK
{
"success": true,
"data": {
"id": "pay_cl8z2e8z20000g",
"reference": "cl8z2e8z20000g",
"status": "SUCCEEDED",
"environment": "LIVE",
"method": "MOBILE_MONEY",
"amount": 10000,
"currency": "GHS",
"fee": 150,
"net": 9850,
"description": "Book Shop Checkout",
"metadata": {
"orderId": "inv0012"
},
"paidAt": "2026-06-06T12:02:11Z",
"createdAt": "2026-06-06T12:00:00Z",
"customer": {
"id": "cust_cl8z2e9z",
"name": "Kwame Mensah",
"email": "kwame@example.com"
},
"mobileMoneyDetail": {
"network": "MTN",
"phone": "+233241234567",
"externalRef": "0000006824852622"
}
}
}Sample Response (Unpaid / Pending)
200 OK
{
"success": true,
"data": {
"id": "pay_cl8z2e8z20000g",
"reference": "cl8z2e8z20000g",
"status": "PENDING",
"environment": "LIVE",
"method": null,
"amount": 10000,
"currency": "GHS",
"fee": 0,
"net": 10000,
"description": "Book Shop Checkout",
"metadata": {
"orderId": "inv0012"
},
"paidAt": null,
"createdAt": "2026-06-06T12:00:00Z",
"customer": {
"id": "cust_cl8z2e9z",
"name": "Kwame Mensah",
"email": "kwame@example.com"
}
}
}Response Codes & Actions
The Xtopay Sales API uses standard HTTP error reporting. Successful requests return HTTP status codes in the 2xx range. Failed requests return status codes in 4xx and 5xx ranges. Standard response error bodies contain a message describing the failure. In transaction callbacks and status reports, you may consult the following code mapping:
| Response Code | Description | Required Action |
|---|---|---|
0000 | Request accepted or processed successfully. | None. You can proceed with fulfilling the order. |
0005 | connection error occurred when reaching the telco or card network. | The transaction status is unknown. Perform a Status Check or contact support to confirm. |
2001 | Transaction failed (e.g. customer entered an invalid PIN, has insufficient funds, or the session timed out). | Instruct customer to verify their wallet balance, check their PIN, or try another payment method. |
4000 | Validation errors. Something is incorrect in the request payload. | Check that the amount is a positive integer and that customer credentials are correct. |
4070 | declined by gateway. Gateway fees not configured or minimum transaction amount not met. | Contact your Xtopay relationship manager to verify fee configurations. |
How is this guide?