Accept Payments
Initialize transactions, redirect customers to pay, and verify completed payments.
This guide covers the complete payment flow - from initializing a transaction to verifying the result. Credo supports two integration approaches: the hosted checkout (redirect) and direct card charge (API-only).
Payment flow overview
Step 1: Initialize a transaction
Send a POST request to create a payment session. The response includes a URL where the customer completes payment.
curl -X POST https://api.credodemo.com/transaction/initialize \
-H "Authorization: YOUR_PUBLIC_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"email": "customer@example.com",
"currency": "NGN",
"bearer": 0,
"channels": ["CARD", "BANK"],
"initializeAccount": 0,
"reference": "ORD-20260207-001",
"callbackUrl": "https://yoursite.com/payment/callback"
}'Response
{
"status": 200,
"message": "Transaction initialized successfully",
"data": {
"authorizationUrl": "https://pay.credocentral.com/checkout/xxx",
"reference": "ORD-20260207-001",
"credoReference": "vs_xxxxxxxxxxxx",
"crn": "0000298483"
}
}Required fields
| Field | Type | Description |
|---|---|---|
amount | integer | Amount in lowest currency unit (kobo for NGN, cents for USD) |
email | string | Customer's email address |
currency | string | NGN or USD |
bearer | integer | 0 = customer bears fee, 1 = merchant bears fee |
channels | array | Payment methods: CARD, BANK |
initializeAccount | integer | 0 = No, 1 = Yes (generate virtual account for bank transfer) |
Optional fields
| Field | Type | Description |
|---|---|---|
reference | string | Your unique reference for the transaction. Auto-generated if omitted. |
callbackUrl | string | Where to redirect the customer after payment. |
customerPhoneNumber | string | Customer phone number. |
customerFirstName | string | Customer first name. |
customerLastName | string | Customer last name. |
narration | string | Description shown on the payment page. |
metadata | object | Custom data attached to the transaction. |
serviceCode | string | Pre-configured split settlement code. |
splitConfiguration | array | Dynamic split settlement config. |
pauseSettlement | integer | 1 to hold funds in escrow. |
pauseSettlementDate | string | Date when paused funds will be settled (YYYY-MM-DD). |
Step 2: Redirect to checkout
Send the customer to the authorizationUrl returned in step 1. Credo's hosted checkout handles card entry, bank transfer instructions, OTP verification, and 3D Secure - you don't need to build any of this.
// Express.js example
app.post("/pay", async (req, res) => {
const response = await fetch("https://api.credocentral.com/transaction/initialize", {
method: "POST",
headers: {
"Authorization": process.env.CREDO_PUBLIC_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: req.body.amount * 100, // Convert to kobo
email: req.body.email,
currency: "NGN",
bearer: 0,
channels: ["CARD", "BANK"],
initializeAccount: 0,
callbackUrl: `${process.env.BASE_URL}/payment/callback`,
}),
});
const { data } = await response.json();
res.redirect(data.authorizationUrl);
});After payment, Credo redirects the customer to your callbackUrl with the transaction reference appended as a query parameter.
Step 3: Verify the transaction
Always verify server-side
Never trust the redirect alone. A customer could manipulate the callback URL or close their browser before the redirect. Always verify the transaction on your server using the secret key.
curl https://api.credodemo.com/transaction/vs_xxxxxxxxxxxx/verify \
-H "Authorization: YOUR_SECRET_KEY"Verification response
{
"status": 200,
"message": "Transaction fetched successfully",
"data": {
"transRef": "vs_xxxxxxxxxxxx",
"businessRef": "ORD-20260207-001",
"debitedAmount": 153250,
"transAmount": 150000,
"transFeeAmount": 3250,
"settlementAmount": 150000,
"customerId": "customer@example.com",
"transactionDate": "2026-02-07T14:30:00.000Z",
"currencyCode": "NGN",
"status": 0
}
}Verification checklist
Before marking an order as paid, confirm:
- Status is
0(successful) - see Concepts for all status codes - Amount matches - compare
transAmountto what you expected - Currency matches - ensure
currencyCodeis what you charged - Reference matches -
businessRefshould match your order reference - Not already processed - check your database to avoid double-crediting
app.get("/payment/callback", async (req, res) => {
const { transRef } = req.query;
const response = await fetch(
`https://api.credocentral.com/transaction/${transRef}/verify`,
{ headers: { "Authorization": process.env.CREDO_SECRET_KEY } }
);
const { data } = await response.json();
if (data.status === 0 && data.transAmount === expectedAmount) {
// Payment confirmed - fulfill the order
await markOrderAsPaid(data.businessRef);
res.redirect("/order/success");
} else {
res.redirect("/order/failed");
}
});Direct card charge
For PCI-DSS compliant merchants who want to collect card details on their own form, Credo offers a direct charge API. This is a two-step process.
Direct card charge requires PCI-DSS compliance. Most merchants should use the hosted checkout instead. Contact Credo support to enable this feature on your account.
Step 1: Initiate the charge
curl -X POST https://api.credodemo.com/transaction/direct/initiate \
-H "Authorization: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"currency": "NGN",
"reference": "ORD-20260207-002",
"card": {
"pan": "5399838383838381",
"cvv": "470",
"expiryYear": 2028,
"expiryMonth": 10
},
"customer": {
"email": "customer@example.com",
"phoneNumber": "08012345678",
"firstName": "John",
"lastName": "Doe"
},
"authorization": {
"mode": "PIN",
"pin": "1234"
},
"callbackUrl": "https://yoursite.com/payment/callback"
}'If the card requires additional verification (OTP), the response will indicate this.
Step 2: Authorize with OTP
If prompted, submit the OTP the customer received:
curl -X POST https://api.credodemo.com/transaction/direct/authorize \
-H "Authorization: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"transRef": "vs_xxxxxxxxxxxx",
"authorization": {
"mode": "OTP",
"otp": "123456"
}
}'After authorization, verify the transaction using the same verification endpoint.
Adding metadata
Attach custom data to any transaction for your own record-keeping:
{
"amount": 150000,
"email": "customer@example.com",
"currency": "NGN",
"bearer": 0,
"channels": ["CARD"],
"initializeAccount": 0,
"metadata": {
"customFields": [
{
"variable_name": "order_id",
"value": "ORD-001",
"display_name": "Order ID"
},
{
"variable_name": "product",
"value": "Premium Plan",
"display_name": "Product"
}
]
}
}Metadata is returned in the verification response and webhook payload. Use it to link transactions to orders, products, or any business context.
