Core concepts

Payments

Customers pay approved invoices online with Paystack. You start the payment; Flux verifies it with Paystack and tells you by webhook.

  1. You

    Start the payment

    POST /invoices/{id}/checkout returns a Paystack URL. Or give the payer the signed payUrl.

  2. Payer

    Pays on Paystack

    By card, transfer or any channel Paystack offers. Paystack redirects them back.

  3. Flux

    Verifies

    Flux checks the payment with Paystack: the money must cover the invoice total, in its currency.

  4. Flux → you

    Tells you

    payment.succeeded, invoice.paid and receipt.issued. Or payment.failed.

Online and offline invoices#

When an invoice is approved, the organization decides how it's paid: online (Paystack) or offline (bank transfer). Only online invoices can be paid through the API or a payUrl. Starting a checkout for an offline invoice gets 409 not_online.

Starting a payment#

There are two ways. Both reach the same Paystack checkout.

  • Checkout: call POST /invoices/{id}/checkout (scope payments:write) and redirect the payer to checkoutUrl.
  • Pay link: give the payer the signed payUrl from POST /invoices/{id}/links. Opening it starts the checkout. No payment scope is needed. See Signed links.
curl -X POST "$FLUX_BASE_URL/api/v1/invoices/5012/checkout" \
  -H "Authorization: ApiKey $FLUX_API_KEY"
200 OK
200 OK
{
  "checkoutUrl": "https://checkout.paystack.com/7xk2m9q4fz0b1ac"
}
ErrorMeaning
409 invoice_voidedThe invoice is voided.
409 already_paidThe invoice is already paid.
409 not_onlineThe invoice wasn't approved for online payment.
409 payment_not_availableOnline payment can't start, for example because the organization's payment settings aren't complete. The message explains.
502 gateway_errorPaystack refused to start the payment. details holds its response. Retry later.

Confirmation is asynchronous#

Don't trust the redirect

Paystack redirecting the payer back to you isn't proof of payment. Show a “processing” state until Flux confirms it.

  • Flux verifies every payment with Paystack itself.
  • An invoice is marked paid only when the amount paid covers the invoice total in the invoice's currency. Paying more, for example when the payer covers gateway fees, is accepted.
  • A short payment, or one in another currency, isn't applied. The invoice stays unpaid, and Flux sends payment.failed (with problem saying why) and invoice.payment_review. The organization sorts it out in Flux.
  • Listen for payment.succeeded, invoice.paid or receipt.issued. If you can't receive webhooks, poll GET /invoices/{id} until status is paid.
OutcomeEvents, in the order Flux emits themInvoice
Checkout startedpayment.initiatedunchanged
Paid in fullpayment.succeeded, invoice.paid, receipt.issuedpaid
Short, or wrong currencypayment.failed (with problem), invoice.payment_reviewunchanged, paymentStatus explains
Declined by Paystackpayment.failed (problem is null)unchanged
Refunded in Paystackpayment.refundedstill paid; paymentStatus notes the refund

Events can arrive in a different order than they were emitted. Re-read the invoice when order matters.

Fees#

  • The payer bears Paystack's fee.
  • When the organization collects through Flux's Paystack account: Flux adds a platform fee to the amount the payer pays: a flat amount plus a percentage, capped. It's shown as platformFeeMinor on payment.initiated. whether to publish the fee figures
  • Organizations on their own Paystack account pay no platform fee.

Refunds#

Refunds are made in Paystack. Flux records them and sends payment.refunded, with the amount in minor units. Whether to reverse the invoice is an accounting decision taken in Flux, not by the API.