Docs

Merchant API · Public contract 2026-06

VeloraPay developer documentation

Guides, examples, and API reference for creating hosted checkout sessions, redirecting payers, and confirming final payment state.

Version
2026-06
Staging
https://api.staging.velorapay.com
Auth
Server-side bearer key
Cards
Hosted checkout only

Explore by product area

Find the guide by integration job.

Integration setup

Create the integration in Merchant Dashboard.

Create the integration, test API key, and webhook receiver in the VeloraPay Merchant Dashboard. Your backend then uses the generated API key for the payment APIs below.

Setup sequence

Complete setup in the dashboard, then store generated secrets on your server.

  1. 1 Merchant Dashboard Select integration

    Name the app, ERP, website, or back-office system.

  2. 2 Merchant Dashboard Create API key

    Copy the one-time key into your backend secret manager.

  3. 3 Merchant Dashboard Add webhook

    Register the HTTPS receiver and store the signing secret.

  1. Create or select the integration

    Use the Merchant Dashboard to create the container for the app or system that will connect to VeloraPay.

  2. Create the test API key

    API keys are scoped to one integration. Copy the one-time key into your backend secret manager and use it only from your server.

  3. Add the webhook endpoint

    Register your HTTPS receiver in the Merchant Dashboard. Copy the one-time signing secret into your webhook verifier before accepting payment events.

  4. Use the API key from your backend

    After dashboard setup, call the payment APIs below with Authorization: Bearer ak_test_example in staging. Do not expose merchant API keys in browser or mobile clients.

Build your first payment

Create and confirm a hosted payment.

This is the standard hosted-checkout path: create the session from your backend, send the payer to VeloraPay, then confirm the final state before fulfilment.

First-payment sequence

Make authenticated calls from your backend. Use webhooks as the primary confirmation signal.

  1. 1 POST /api/v1/invoice-lines Record invoice lines

    Attach service-code line items to your invoice reference.

  2. 2 POST /api/v1/sessions Create checkout

    Return a hosted checkout URL for the payer.

  3. 3 checkout_url Send the payer to hosted checkout

    VeloraPay collects the selected payment details, including hosted card entry.

  4. 4 VeloraPay checkout VeloraPay collects payment

    Hosted checkout handles payer prompts, card entry, transfer details, and slips.

  5. 5 checkout.session.completed Receive webhook

    Use the signed event as the primary fulfilment signal.

  6. 6 GET /api/v1/sessions/{session_id} Fallback lookup

    Use server-side session lookup if the payer returns before the webhook arrives.

  7. 7 amount_minor Verify final state

    Match amount, currency, invoice reference, and terminal status.

  1. Record the invoice line items

    Use the same invoice_ref when you create the checkout session. Invoice lines help the payer recognize the bill and help your finance team settle collections by service code.

    Request
    curl https://api.staging.velorapay.com/api/v1/invoice-lines \
      -H "Authorization: Bearer ak_test_example" \
      -H "Idempotency-Key: invoice-EPA-2026-001" \
      -H "Content-Type: application/json" \
      -d '{
        "invoice_ref": "EPA-2026-001",
        "currency": "GHS",
        "lines": [
          { "code": "EPA-PERMIT", "name": "Permit fee", "amount_minor": 300000 },
          { "code": "EPA-LEVY", "name": "Environmental levy", "amount_minor": 7038 }
        ]
      }'
    Expected response
    {
      "object": "invoice.line_items",
      "invoice_ref": "EPA-2026-001",
      "ingested": 2,
      "billed_minor": 307038,
      "errors": []
    }
  2. Create the checkout session

    The response includes checkout_url. Redirect the payer there, or send the link through your own payment notification flow.

    Node.js
    const response = await fetch("https://api.staging.velorapay.com/api/v1/sessions", {
      method: "POST",
      headers: {
        "Authorization": "Bearer ak_test_example",
        "Idempotency-Key": "checkout-EPA-2026-001",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        invoice_ref: "EPA-2026-001",
        amount_minor: 307038,
        currency: "GHS",
        description: "EPA permit payment",
        customer: { name: "Kwame Asante", email: "kwame@example.com" },
        callback_url: "https://epa.gov.gh/payments/return"
      })
    });
    
    const session = await response.json();
    return session.checkout_url;
    Expected response
    {
      "id": "cs_example",
      "status": "open",
      "amount_minor": 307038,
      "currency": "GHS",
      "invoice_ref": "EPA-2026-001",
      "description": "EPA permit payment",
      "checkout_url": "https://velorapay-checkout-staging-fxzcszzkha-uc.a.run.app/pay/cs_example",
      "expires_at": "2026-06-14T12:30:00Z"
    }

    Set callback_url to your return route and store the VeloraPay session id, invoice_ref, and your order id before redirecting the payer. After checkout completes, VeloraPay can return the payer to EPA's callback URL. EPA must still confirm final state by webhook or server-side session lookup before marking the order paid.

  3. Let VeloraPay-hosted checkout collect the payment

    After you redirect the payer to checkout_url, VeloraPay-hosted checkout handles payment-method selection, fee display, Mobile Money prompts, card entry, cash slip creation, and payment collection. Your backend waits for a webhook or session lookup; it does not start payment-method calls.

  4. Confirm session status from your backend

    Signed webhooks are the primary fulfilment signal. If EPA needs a fallback lookup after the payer returns, call the merchant session endpoint with the integration API key. This path does not require an attempt_reference.

    Session lookup request
    curl https://api.staging.velorapay.com/api/v1/sessions/cs_example \
      -H "Authorization: Bearer ak_test_example"
    Checkout session response
    {
      "id": "cs_example",
      "status": "pending",
      "amount_minor": 307038,
      "currency": "GHS",
      "invoice_ref": "EPA-2026-001",
      "description": "EPA permit payment",
      "checkout_url": "https://velorapay-checkout-staging-fxzcszzkha-uc.a.run.app/pay/cs_example",
      "expires_at": "2026-06-14T12:30:00Z"
    }
  5. Fulfil only after confirmation

    A return to your site is not proof of payment. Fulfil only after the signed webhook or final checkout status confirms the payment amount, currency, invoice reference, and terminal state.

    Safe to fulfil

    checkout.session.completed, terminal success, matching amount, matching currency, and matching invoice reference.

    Do not fulfil

    Pending, failed, expired, amount mismatch, currency mismatch, duplicate event with changed body, or invalid signature.

Authentication and retries

Use API keys only from your server.

Merchant API keys identify your account and must stay on your server or in a secret manager. VeloraPay checkout pages use public session endpoints and never need your key.

Authorization header

Authorization: Bearer ak_test_example

Use separate staging and go-live credentials. Rotate a key if it is ever exposed in app code, logs, screenshots, or support tickets.

Idempotency-Key

Send a stable idempotency key on checkout and invoice-line writes. If a network call fails, retry with the same key and same body.

Invoice model

The invoice is the start of the payment flow.

VeloraPay links each checkout to your invoice_ref. Revenue lines name the service codes you collect for; invoice lines attach amounts to those codes for one bill.

Catalog

Revenue lines

Sync the service codes your organisation collects against.

Invoice

Invoice lines

Record the exact amount due for each code on one invoice.

Payment

Checkout session

Create the payer page and lock the amount to collect.

Payment flow

Track asynchronous payment state.

Mobile Money, bank transfer, and cash confirmation can be asynchronous. Your system should treat the checkout as pending until VeloraPay returns a terminal status through a signed webhook or server-side session lookup.

open

The payer is on VeloraPay-hosted checkout and can choose an enabled payment method.

pending

A payer action, provider update, or teller review is still in progress.

success

Payment is final for fulfilment after amount, currency, and invoice reference match.

failed or expired

Do not release value. Ask the payer to create a new payment path if needed.

Payment methods

VeloraPay-hosted checkout renders the payment methods.

For standard merchant integrations, your backend does not quote, start, or poll payment-method calls. Redirect the payer to checkout_url; VeloraPay checkout shows enabled methods, computes fees, collects payer input, and drives the payment.

Mobile Money

Hosted checkout collects the payer's network and number, then asks them to approve or verify the payment.

Hosted checkout endpoints

Bank transfer

Hosted checkout returns account details for this invoice and confirms when the transfer is matched.

Read finality rules

Cash deposit

Hosted checkout creates or reuses one active deposit slip. A teller review can make the checkout pending before final approval.

Cash deposit guide

Card

Card entry belongs on VeloraPay-hosted checkout. Do not collect card numbers or security codes in your merchant flow.

Go-live requirement
Mobile Money

Complete through hosted checkout. The payer enters network and phone details there, then approves or verifies the payment before VeloraPay sends a terminal update.

Bank transfer

Hosted checkout shows the account instructions and waits for matched status or a terminal webhook before fulfilment.

Cash deposit

Hosted checkout creates one active slip for an open checkout. Teller review can keep the checkout pending before the final completed event.

Card

Redirect to hosted checkout. Do not collect card numbers, security codes, or cardholder authentication data in your merchant application.

Cash deposits

One payable slip per open checkout.

Retrying a cash deposit slip request returns the active slip with replayed: true. If a teller is already reviewing the deposit, ask the payer to wait instead of issuing another slip.

Cash slip response
{
  "slip_token": "SLIP-ABC123DEF456",
  "reference": "s-cash-example",
  "amount_minor": 307038,
  "currency": "GHS",
  "invoice_ref": "EPA-2026-001",
  "customer_name": "Kwame Asante",
  "status": "active",
  "expires_at": "2026-06-15T12:00:00Z",
  "replayed": true
}

Webhooks

Use webhooks to decide when to fulfil.

Register the HTTPS receiver in the Merchant Dashboard and store the signing secret in your backend secret manager. Verify every delivery before you update an order, release a service, or mark an invoice as paid.

Dashboard setup
Webhook URL: https://merchant.example/webhooks/velorapay
Events: checkout.session.completed, checkout.session.failed, checkout.session.expired
Secret storage: backend secret manager
Configure in dashboard

Create, rotate, or disable webhook endpoints from the Merchant Dashboard.

Store the secret once

Copy the signing secret into your secret manager before accepting payment events.

Use final events

Fulfil only after a valid terminal event matches the invoice reference, amount, and currency.

Headers

Read the VeloraPay headers

Each delivery includes X-VeloraPay-Event-Id, X-VeloraPay-Event-Type, and X-VeloraPay-Signature.

Events

Handle terminal states

Expect checkout.session.completed, checkout.session.failed, and checkout.session.expired.

Fulfilment

Verify before value

Confirm the session status, amount_minor, currency, and invoice_ref before releasing goods or services.

Signature contract
X-VeloraPay-Signature: t=<unix_timestamp>,v1=<hex_hmac_sha256>

v1 = HMAC_SHA256(signing_secret, "<t>.<raw_request_body>")

Use the raw request body exactly as received. Do not verify a parsed and
re-stringified JSON object.
Terminal event payload
{
  "id": "evt_example",
  "type": "checkout.session.completed",
  "data": {
    "session": {
      "id": "cs_example",
      "status": "success",
      "amount_minor": 307038,
      "currency": "GHS",
      "invoice_ref": "EPA-2026-001",
      "metadata": {}
    }
  }
}
Webhook handler checklist
1. Read the raw request body.
2. Parse t and v1 from X-VeloraPay-Signature.
3. Reject timestamps more than 5 minutes from your server clock.
4. Recompute HMAC_SHA256(signing_secret, "<t>.<raw_request_body>").
5. Compare signatures with a constant-time comparison.
6. Store X-VeloraPay-Event-Id and return 200 for already-processed events.
7. Treat delivery as at-least-once; your handler must be idempotent.
8. For checkout.session.completed, confirm amount_minor, currency,
   invoice_ref, and terminal status before fulfilment.
Node.js verification handler
import crypto from "node:crypto";

function verifyVeloraPayWebhook({ rawBody, signature, signingSecret }) {
  const parts = Object.fromEntries(signature.split(",").map((part) => part.split("=")));
  const timestamp = Number(parts.t);
  const received = parts.v1 || "";
  const ageSeconds = Math.abs(Date.now() / 1000 - timestamp);
  if (!timestamp || ageSeconds > 300) throw new Error("stale webhook");

  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  if (
    received.length !== expected.length ||
    !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
  ) {
    throw new Error("invalid webhook signature");
  }
}

export async function handleVeloraPayWebhook(request, response) {
  const rawBody = await getRawBody(request);
  verifyVeloraPayWebhook({
    rawBody,
    signature: request.headers["x-velorapay-signature"],
    signingSecret: process.env.VELORAPAY_WEBHOOK_SECRET
  });

  const eventId = request.headers["x-velorapay-event-id"];
  if (await alreadyProcessed(eventId)) return response.status(200).end();

  const event = JSON.parse(rawBody);
  if (event.type === "checkout.session.completed") {
    await fulfilIfAmountCurrencyAndInvoiceMatch(event.data.session);
  }
  await markProcessed(eventId);
  response.status(200).end();
}

Staging testing

Test on staging without moving real money.

Use staging API keys, synthetic invoices, test payer details, and signed webhook samples. Staging supports test integrations and test API keys only. Keep screenshots and support tickets free of real customer or payment data.

1

Create a test invoice

Use a unique invoice reference and service-code lines for each test.

2

Complete each hosted method

Exercise success, pending, failure, retry, and expiry paths through hosted checkout.

3

Replay webhook handling

Confirm dedupe by sending the same event id more than once.

4

Compare records

Match checkout, invoice, webhook, and settlement evidence by invoice_ref.

Staging workspace

Use predictable fixtures before requesting live credentials.

Keep test data synthetic and repeatable. The staging collection includes the same invoice reference, idempotency keys, payer fixture, return route, and webhook receiver shape used throughout this guide.

API key ak_test_example
Invoice reference EPA-2026-001
Currency GHS
Webhook timestamp tolerance 300 seconds
Staging base URL https://api.staging.velorapay.com
Successful checkout

Create invoice lines, create checkout, send the payer to hosted checkout, receive checkout.session.completed, and fulfil only after the amount, currency, and invoice reference match.

Pending payment

Use hosted checkout for Mobile Money or bank transfer, keep the order pending while finalized is false, and show the payer a recoverable status.

Failure and expiry

Confirm failed or expired sessions never release value and can be restarted with a new payment path when the merchant policy allows it.

Webhook replay

Send the same event id twice. The second delivery must return 200 without fulfiling the invoice again.

Signature rejection

Change one byte in the raw body or timestamp. Your handler must reject the delivery before touching order state.

Idempotent retry

Retry session and invoice-line writes with the same Idempotency-Key and body. Reusing the key with a different body must fail.

Versioning and changelog

Keep integrations stable while the platform grows.

The public API is documented as an explicit versioned contract. New optional response fields can be added without notice, but required request fields, event names, webhook signature rules, and error-code meanings need a migration note before they change.

Current public contract 2026-06

Checkout sessions, invoice lines, hosted payment methods, cash deposits, webhooks, errors.

Latest docs change Hosted checkout guide

Clearer setup, redirect, webhook, fallback lookup, and staging-test guidance.

Breaking change policy Migration note first

Ship an upgrade path before changing required fields or finality semantics.

Errors

Handle stable error codes.

Error responses use the same envelope across the merchant API. Use the code to decide whether to retry, refresh the checkout, or ask an operator to correct the request.

UNAUTHORIZED Check that the API key belongs to the right environment.
IDEMPOTENCY_KEY_REUSED Generate a new key or retry with the exact same request body.
SESSION_NOT_PAYABLE Refresh the checkout session before asking the payer to continue.
PAYMENT_AMOUNT_MISMATCH Refresh hosted checkout state and retry with the current amount.
RATE_LIMITED Wait for retry_after_seconds before retrying.

The full error-code enum is published in the OpenAPI component MerchantErrorCode.

Finality and settlement

Payment success is the fulfilment boundary.

A successful checkout means the payment has reached VeloraPay's final collection state for fulfilment. Payout, settlement, and merchant reconciliation records can arrive later and should be used for finance operations, not for first fulfilment.

Fulfilment

Requires terminal success plus matching amount, currency, and invoice reference.

Refunds and reversals

Confirm the supported process with VeloraPay before promising a reversal or refund flow.

Reconciliation

Use invoice_ref, checkout id, event id, and payment rail to match finance records.

Developer assets

Use the reference and staging assets when you need exact fields.

The guide explains the payment journey. The reference and machine-readable assets carry exact endpoint fields, response shapes, and contract enums.

Go-live checklist

Move to live money only after the staging path is proven.

Before go-live, prove the full checkout path, signed webhooks, cash review, reconciliation evidence, key storage, and support workflow with test data.

  • Staging checkout succeeds and failed/expired cases do not release value.
  • Webhook receiver verifies raw-body signatures and dedupes event ids.
  • Idempotency keys are used on checkout and invoice-line writes.
  • Merchant API keys are stored in a backend secret store.
  • Support team can trace an invoice_ref across checkout, webhook, and settlement evidence.

Reference

Open the endpoint reference for exact fields and responses.

Use the reference when you are mapping request fields, handling response states, or wiring generated clients.