X-API-Key / X-API-Secret headers
because the customer paying the invoice is not your API client. The base URL is
the same: https://api.hyparrow.cloud/api/v1.
Amounts are in kobo.
payableAmount, totalAmount and friends are in the
minor unit (100000 = ₦1,000.00).Payment methods
The customer-facing options on the checkout page are:
Which methods appear depends on the merchant’s payment preferences and the
invoice currency, the NGN-only rails (bank transfer, USSD) are hidden for
non-NGN invoices. The active set is returned as
allowedPaymentMethods from
GET /checkout/{invoiceId}.
End-to-end flow
1
Customer opens the checkout page
The page calls
GET /checkout/{invoiceId} to load the invoice, amounts and
allowed payment methods.2
Customer picks a method
Depending on the choice, the page calls
…/va, …/ussd, …/card,
…/opay or …/crypto to start that payment.3
Customer pays
Transfer to the virtual account, dial the USSD code, complete the card /
OPay redirect, or send crypto to the address.
4
Page polls for status
The page polls
GET /checkout/{invoiceId}/status until paid is true.5
Callback fires
Hyparrow
POSTs a checkout.payment.* event to the invoice’s
checkoutCallbackUrl (if set) as the payment progresses.Load the checkout
GET /checkout/{invoiceId}
Returns sanitized invoice data for rendering the page. Canceled invoices return
404.
payableAmount is what the customer actually pays, always collect this figure,
never totalAmount.
The service fee and VAT are settled independently. serviceFee and vatAmount
are what the customer is charged for each: a component the merchant absorbs
reports 0 and is deducted from their payout instead. serviceFeeDirection and
vatDirection tell you which side each one landed on, and taxDirection
summarizes them as merchant, customer, or split when they differ.
So a merchant who absorbs the 2.5% but passes VAT to the customer produces
serviceFee: 0, a non-zero vatAmount, and taxDirection: "split". Render
whichever fee rows are non-zero rather than keying off taxDirection, see
Fees & tax direction.
Bank transfer (virtual account)
POST /checkout/{invoiceId}/va
Creates (or returns the existing active) virtual account for the invoice.
expiryMinutes accepts 10, 15 or 30; anything else defaults to 15.
200 instead of creating
a duplicate.
GET /checkout/{invoiceId}/va returns the current active, non-expired account,
or 404 if there is none (e.g. it expired).
USSD
POST /checkout/{invoiceId}/ussd
Generates a USSD dial code for the customer’s bank.
checkout.payment.pending callback fires when the code is generated.
Card
POST /checkout/{invoiceId}/card
Charges a card via the card network. Card details are encrypted before they
leave the browser.
responseCode:
00, approved. The invoice is settled and acheckout.payment.completedcallback fires.T0/S0, processing (e.g. an OTP/3-DS step). Acheckout.payment.pendingcallback fires; keep polling status.- anything else, declined. A
checkout.payment.failedcallback fires.
OPay
POST /checkout/{invoiceId}/opay
Initializes an OPay payment and returns a redirectUrl to send the customer to.
checkout.payment.pending callback fires on
initialization.
Crypto
POST /checkout/{invoiceId}/crypto
Generates a stablecoin payment address. The fiat total is converted to USD.
token is USDC or USDT. The customer sends the asset to address before
expiresAt (30 minutes). GET /checkout/{invoiceId}/crypto returns the active
address. Settlement arrives asynchronously once the on-chain transfer confirms.
Poll status
GET /checkout/{invoiceId}/status
The checkout page polls this until the payment lands.
paid is true once the invoice is paid (or a receipt exists). Use this as
the source of truth on the page; the asynchronous methods (transfer, USSD, OPay,
crypto) flip it when settlement confirms.
Payment links
A payment link lets you collect a payment without pre-creating an invoice, useful for a shareable “pay me” page. The public URL isCHECKOUT_BASE_URL/pay/{identifier}, where identifier is the link’s custom
slug or its id.
GET /checkout/pay/{identifier} returns the link’s page data (name,
description, amount, allowed methods):
POST /checkout/pay/{identifier} turns the customer’s submission into an invoice
so the normal payment rails can settle it. If the link has a fixed amount it
wins; otherwise the customer-entered amount is used.
invoiceId to the standard checkout flow above
(…/va, …/card, etc.) to complete payment.
Edge cases
Checkout callback webhook
If the invoice has acheckoutCallbackUrl, Hyparrow POSTs an event to it as
the payment progresses. The body is:
checkout.payment.pending, payment started, awaiting settlement (transfer pending, USSD/OPay in progress, card in 3-DS).checkout.payment.completed, settled; the invoice ispaid.checkout.payment.failed, the attempt failed (e.g. a declined card).
GET /checkout/{invoiceId}/status before fulfilling.
Sandbox
In the sandbox, the hosted checkout URL carries?sandbox=true and
points at the sandbox API (https://sandbox.hyparrow.cloud/api/v1). You don’t
need a real bank transfer or card to test, complete the payment instantly:
Use the “Simulate payment” button on the sandbox checkout page, or call
POST /checkout/{invoiceId}/simulate-payment directly. It runs the invoice
through the normal payment path, marking it paid, issuing a receipt and
firing the checkout.payment.completed callback, with simulated money. It is
available only on the sandbox deployment (returns 404 in production).200 with "invoice already paid".
