Send your customer to a payment page hosted on AlliancePay's domain. No card data touches your servers. Sandbox and live share the same URL — the key prefix picks the environment.
curl -X POST https://hostedcheckoutapi.alliancepay.cloud/api/v1/session \
-H "Authorization: Bearer $AP_PUBLIC_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "customer@example.com",
"amount": 500000,
"currency": "NGN",
"reference": "order_1717000000",
"firstName": "Ada",
"lastName": "Okonkwo",
"mobile": "+2348012345678",
"country": "NG",
"callbackUrl": "https://yourstore.com/checkout/return"
}'Create a session, redirect the customer, verify server-side. A few dozen lines.
Card details entered on our page. Your servers never see a PAN or a CVV.
.NET, Node, PHP, Python, Ruby, Go, curl — plain HTTPS with Bearer auth.
Hosted Checkout gives you a drop-in payment page for card and bank-transfer payments. You call one endpoint — POST /api/v1/session — with the cart amount and customer details, receive an accessCode, then send the customer to https://hostedcheckout.alliancepay.cloud/pay/{accessCode}. AlliancePay handles the card form, 3-D Secure, virtual accounts, and the receipt. When the customer is done, we redirect them back to your callbackUrl. Your server confirms the outcome with a single call to GET /api/v1/transaction/verify/{reference}.
checkout-api.alliancepay.cloud lets you build your own checkout UI with the same merchant keys.https://hostedcheckoutapi.alliancepay.cloudhttps://hostedcheckout.alliancepay.cloud/pay/{accessCode}https://hostedcheckoutsdk.alliancepay.cloud/inline.js-dev hostnames (hostedcheckoutapi-dev.alliancepay.cloud, hostedcheckout-dev.alliancepay.cloud) — they route to the same service. If you already integrated against the -dev URLs nothing breaks; new integrations should use the production hostnames.Every request sends your Public Key as a bearer token:
Authorization: Bearer PGW-PUBLICKEY-<ENV>-XXXXXXXXXXXXXXXXXXXXXXXXXXThe prefix decides the environment. PGW-PUBLICKEY-TEST-… keys run in sandbox — no real money moves. PGW-PUBLICKEY-LIVE-… keys charge real cards. The API host and the UI host are the same either way.
Your Secret Key (PGW-SECRETKEY-…) is reserved for future server-to-server flows. Keep it out of the browser.
Every response uses the same shape:
{
"status": true,
"message": "OK",
"data": { … }
}status is true on success and false on failure. On non-2xx responses message is a merchant-facing string safe to show the customer, and data is null.
Amounts are integers in the minor unit of the currency. In Naira that means kobo: 500000 equals ₦5,000.00. In US dollars that means cents: 500000 equals $5,000.00. Supported currencies: NGN, USD.
https://hostedcheckoutapi.alliancepay.cloud/api/v1/sessionCall this from your server, once per payment attempt. Required fields are marked in the table below.
emailstring
Customer receipt email.
amountinteger
Minor units — kobo for NGN, cents for USD.
currencystring
ISO-4217. NGN or USD.
referencestring
Your unique order reference.
firstNamestring
Customer first name.
lastNamestring
Customer last name.
mobilestring
E.164 format (e.g. +2348012345678).
countrystring
ISO-3166 alpha-2 (NG).
descriptionstring
Shown to the customer on the checkout page.
channelsarray
Subset of ["card", "bank_transfer"]. Omit for both.
callbackUrlstring
HTTPS URL we redirect the customer back to after checkout completes.
metadataobject
Arbitrary JSON echoed back in verify responses.
curl -X POST https://hostedcheckoutapi.alliancepay.cloud/api/v1/session \
-H "Authorization: Bearer $AP_PUBLIC_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "customer@example.com",
"amount": 500000,
"currency": "NGN",
"reference": "order_1717000000",
"firstName": "Ada",
"lastName": "Okonkwo",
"mobile": "+2348012345678",
"country": "NG",
"description": "Yaba Coffee Order",
"channels": ["card", "bank_transfer"],
"callbackUrl": "https://yourstore.com/checkout/return"
}'{
"status": true,
"message": "Session created",
"data": {
"accessCode": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"authorizationUrl": "https://hostedcheckout.alliancepay.cloud/pay/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"reference": "order_1717000000",
"expiresAt": "2026-08-04T14:00:00Z"
}
}accessCode is a signed JWT with a 30-minute lifetime.
https://hostedcheckoutapi.alliancepay.cloud/api/v1/checkout/{accessCode}/sessionReads the customer-facing view of the session (merchant name, amount, allowed channels, expiry). No auth header — the accessCode in the URL is the credential. Handy for custom loading screens.
{
"status": true,
"message": "OK",
"data": {
"reference": "order_1717000000",
"amount": 500000,
"currency": "NGN",
"merchantName": "Yaba Coffee Roasters",
"customerEmail": "customer@example.com",
"channels": ["card", "bank_transfer"],
"status": "pending",
"expiresAt": "2026-08-04T14:00:00Z",
"callbackUrl": "https://yourstore.com/checkout/return"
}
}https://hostedcheckoutapi.alliancepay.cloud/api/v1/transaction/verify/{reference}This is the endpoint your server should call to decide whether to fulfil an order. You look it up by your own reference — the same value you passed when you created the session. Nothing else needs to be stored: no access codes, no JWTs, no per-session state.
Call it from your server after onSuccess, after the redirect back to your site, or on a schedule for pending orders. Only mark the order paid when status == "success" and the amount, currency and reference match what you expect.
curl -X GET \
"https://hostedcheckoutapi.alliancepay.cloud/api/v1/transaction/verify/$REFERENCE" \
-H "Authorization: Bearer $AP_PUBLIC_KEY"HTTP/1.1 200 OK
{
"status": "success",
"message": "OK",
"data": {
"reference": "order_1717000000",
"status": "success",
"amount": 500000,
"currency": "NGN",
"channel": "card"
}
}pending — no settlement yet. success — payment settled; fulfill. failed — declined or expired; do not fulfill.Once you have an accessCode, choose how to open the checkout. All three options behave the same for the customer.
HTTP/1.1 303 See Other
Location: https://hostedcheckout.alliancepay.cloud/pay/{accessCode}<iframe
src="https://hostedcheckout.alliancepay.cloud/pay/{accessCode}"
style="width:100%;height:720px;border:0"
allow="payment">
</iframe>The SDK does the session creation for you. Ideal for e-commerce.
<script src="https://hostedcheckoutsdk.alliancepay.cloud/inline.js"></script>
<button id="pay">Pay ₦5,000</button>
<script>
document.getElementById('pay').onclick = function () {
AlliancePay.setup({
key: 'PGW-PUBLICKEY-TEST-XXXXXXXXXXXXXXXXXXXXXXXXXX',
email: 'customer@example.com',
amount: 500000,
currency: 'NGN',
reference: 'order_' + Date.now(),
firstName: 'Ada', lastName: 'Okonkwo',
mobile: '+2348012345678', country: 'NG',
onSuccess: r => console.log('paid', r),
onFailed: r => console.log('declined', r),
onClose: () => console.log('dismissed'),
}).openIframe();
};
</script>success and failed fire callbacks but do not close the modal — the customer clicks "Return to merchant" themselves. Listen for close if you need to know when the iframe has been removed.
Webhooks are handled by the AlliancePay platform, not by Hosted Checkout. If you want push notifications instead of (or as a backup to) polling GET /api/v1/transaction/verify/{reference}, register a single webhook URL once in your AlliancePay merchant dashboard (Settings → API keys and Webhooks → Add URL). AlliancePay will POST the same JSON body for every payment made under your merchant, including those started through Hosted Checkout.
Delivered as POST <your webhook URL> with Content-Type: application/json. The Data.OrderReference field is the same reference you sent when you created the session — use it to look up the order in your database.
{
"Status": "success",
"StatusCode": "00",
"Message": "Order details fetched successfully",
"Data": {
"OrderReference": "order_1717000000",
"PaymentReference": "PA-REF-CFE3DE37-29FB-46DE-84A0-D0E849EEF1D8",
"TotalAmountCharged": 50.00,
"CurrencyName": "NGN",
"PaymentStatus": 5,
"Status": "Successful",
"PaymentMethod": "Card Payment",
"PaymentResponseCode": "00",
"PaymentResponseMessage": "Payment completed successfully",
"DatePaymentConfirmed": "2026-08-04T13:15:11Z",
"Customer": { "EmailAddress": "customer@example.com", "…": "…" },
"OrderPayments": [ { "…": "…" } ]
}
}The full schema (card details, customer object, order payments) is documented at alliance-pay.readme.io/docs/handling-asynchronous-events-webhooks.
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/alliancepay", async (req, res) => {
const { Status, Data } = req.body ?? {};
if (Status === "success" && Data?.Status === "Successful") {
// Look the order up in YOUR database by YOUR reference:
await markOrderPaidIdempotent(Data.OrderReference, {
amount: Data.TotalAmountCharged,
currency: Data.CurrencyName,
channel: Data.PaymentMethod
});
}
// Acknowledge quickly — don't do slow work here.
res.sendStatus(200);
});OrderReference may arrive more than once.2xx quickly; do slow work in a queue.GET /api/v1/transaction/verify/{reference} for any order still in pending after a few minutes — this covers webhook downtime.Any PGW-PUBLICKEY-TEST-… key enables sandbox. No real money moves.
Customer-related parameters like firstName, lastName, email, mobile and country can take any value. They must still be in the right format to avoid validation errors during testing.
5123 4500 0000 0008CARD HOLDER
AlliancePay Test
VALID THRU
01/39
CVV
100PIN
11114508 7500 1574 1019CARD HOLDER
AlliancePay Test
VALID THRU
01/39
CVV
100PIN
11114000 0000 0000 2503CARD HOLDER
AlliancePay Test
VALID THRU
03/50
CVV
111PIN
11115060 9905 8000 0217 499CARD HOLDER
AlliancePay Test
VALID THRU
03/50
CVV
111PIN
1111OTP
1234565060 9905 8000 0000 390CARD HOLDER
AlliancePay Test
VALID THRU
03/50
CVV
111PIN
1111OTP
123456Complete AlliancePay merchant onboarding (KYC, settlement account, acquirer enablement for the card schemes × currencies you plan to accept).
Swap the -TEST- keys for -LIVE- in your frontend and server code.
Point callbackUrl at your production HTTPS return page.
Run a real ₦100 transaction end-to-end and confirm GET /api/v1/transaction/verify/{reference} returns success.
(Optional) Register your webhook URL in the AlliancePay dashboard so you get pushed settlement events.
Add monitoring on your verify calls (retries, alerting on non-success).
Nothing else changes — the URLs are the same for test and live.
400Missing required field. Check email, amount, firstName, lastName, mobile, country.
401Bad or missing Bearer key, or session token expired (30 min). Create a new session.
402Acquirer relationship not enabled for this card scheme × currency on your subsidiary. Contact AlliancePay.
409You reused a reference. For bank transfer we return the previously allocated virtual account.
5xxTransient upstream error. Retry with exponential back-off; verify status before creating a new session.
Full AlliancePay documentation lives at alliance-pay.readme.io. Include your subsidiary ID and (redacted) reference in every ticket.