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, USSD, WALLET, OPAY, PAYOUTLET |
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.
Next steps
Was this page helpful?
Last updated on
