Accept payments

Create & verify payments

Initialize a charge, direct the customer to checkout, and confirm what happened.

Markdown

Create on your server

POST /payments starts a payment through a connected provider. The provider field selects a provider; its connection must belong to the key’s environment. Calculate the amount from trusted order data.

curl https://api.veyqo.org/api/v1/payments \
  -H "Authorization: Bearer $VEYQO_SECRET_KEY" \
  -H "Idempotency-Key: $ORDER_PAYMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "currency": "NGN",
    "reference": "ORDER-1001",
    "provider": "paystack",
    "customer": { "email": "customer@example.com" },
    "metadata": { "order_id": "1001" }
  }'

Payment request fields

FieldMeaning
amountRequired positive integer in the smallest currency unit.
currencyRequired three-letter currency code, supported by the provider.
customer.emailRequired customer email for the nested customer form.
referenceYour order reference; if omitted, the payment ID becomes the reference.
providerProvider slug, such as paystack.
callback_urlOptional HTTPS URL the provider can return the customer to.
metadataOptional application context, such as an internal order ID.

Retry the same operation safely

Send an Idempotency-Key on POST requests. Generate and persist it before the first attempt. If the request times out, retry with the same key and exactly the same body. A different body with the same key returns a conflict.

Use a new key for a new logical operation. Your order reference and idempotency key serve different purposes: one identifies the order; the other identifies a request attempt across retries.

Verify before fulfilment

GET /payments/{id} retrieves the stored payment. POST /payments/{id}/verify queries the provider and updates the payment result. Both require payments:read.

Pending is not a successful payment. Store the payment ID and reconcile webhooks against the order’s reference, expected amount and currency. Deduplicate fulfilment even if the same confirmation arrives more than once.

Read the real payment response

POST /payments returns HTTP 201 with a payment in data. Its checkout address is data.checkout_url. It is not data.checkout.url, and its initial status is usually pending. The provider URL in the example is illustrative.

{
  "success": true,
  "data": {
    "id": "pay_01M6EB66SC61Q3RQYAN0693DGM",
    "object": "payment",
    "reference": "ORDER-1001",
    "status": "pending",
    "amount": 50000,
    "amount_refunded": 0,
    "currency": "NGN",
    "customer": {
      "email": "customer@example.com",
      "name": "Ada Lovelace",
      "phone": null
    },
    "description": "Order 1001",
    "checkout_url": "https://checkout.paystack.com/example",
    "provider": "paystack",
    "provider_reference": "provider_reference_example",
    "metadata": {
      "order_id": "1001"
    },
    "failure_code": null,
    "failure_message": null,
    "authorized_at": null,
    "succeeded_at": null,
    "failed_at": null,
    "created_at": "2026-11-02T12:00:00+00:00"
  }
}

Keep the payment ID and merchant reference

Persist the Veyqo payment ID alongside your order before redirecting the customer. GET /payments/{id} and POST /payments/{id}/verify accept either that ID or your URL-encoded reference directly in the path. There is no ?reference= lookup on the single-payment endpoint.

Use the Veyqo ID for unambiguous reconciliation, especially when order references are reused across sandbox and live. Cross-project resources stay not-found; a known payment in the other environment of the same project can produce ENVIRONMENT_MISMATCH.

Payment states

StatusMeaning / action
createdRecorded by Veyqo before a provider result. Do not fulfil.
pendingWaiting for the customer or provider. Do not fulfil.
processingProvider outcome is not settled yet. Verify later.
succeededPayment confirmed. Compare amount, currency and order ownership before fulfilling once.
failedPayment failed. Inspect failure_code and failure_message.
cancelledPayment was cancelled. Do not fulfil.
partially_refundedSome captured funds have been refunded. Track amount_refunded.
refundedThe captured payment has been fully refunded.

API failures and payment failures are different

An error envelope with success:false describes a rejected or failed API operation. A 200 response can return a payment whose status is failed, and a 201 response can return a pending payment. Inspect both layers. Payment failure_code is not the same catalogue as error.code.

gateway_declined means the provider rejected the payment; show an appropriate customer message and let the customer choose another method. amount_mismatch means the provider-reported amount failed reconciliation; do not fulfil, do not override it from browser data, and investigate the stored order and provider transaction. A creation failure can store the corresponding API error code on the payment. failure_message contains provider-specific detail and may be null; do not use its wording as a stable machine code.