Skip to content

Charges

Take a payment from a customer by mobile money, bank or card.

Endpoint What it does
POST /v1/charges Create a charge
GET /v1/charges/{id} Retrieve a charge
POST /v1/charges/{id}/otp Submit a charge’s one-time code
POST /v1/charges/{id}/otp/resend Re-send a charge’s one-time code

Try any of them in the API tester.

POST /v1/charges

Collects money from a customer over mobile money, bank or card.

Requires an Idempotency-Key header. Resending the same key with the same body replays the first response rather than charging again, so a retry after a timeout is always safe.

A 202-like outcome is normal: most charges return pending or awaiting_action and complete asynchronously. Wait for the webhook or poll the charge; do not treat the absence of succeeded as a failure.

Authentication: a secret key (server-side only) with the scope collection.create.

Field Type Required Allowed values and limits What it is
Idempotency-Key string yes Unique per operation. Resend the same value to retry safely.
Field Type Required Allowed values and limits What it is
amount integer yes min 1; e.g. 1250 Amount in minor units — 1250 is GHS 12.50.
bank string no at most 30 characters The bank’s code. Required for bank unless bank_id is sent instead. Codes are listed at /v1/reference/banks.
bank_id string (uuid) no The bank’s id from /v1/reference/banks. An alternative to bank; send one, not both.
callback_url string (uri) no HTTPS only.
channel string yes One of: mobile_money, bank, card How the customer pays: mobile_money (an approval prompt on their phone), bank (a debit from their bank account) or card (the customer enters their card on a hosted page). Your account must be enabled for the channel.
currency string yes exactly 3 characters; e.g. GHS The currency of amount, as an ISO 4217 code such as GHS. Supported currencies are listed at /v1/reference/currencies.
customer object yes
customer.email string (email) no at most 255 characters; e.g. [email protected] Required for card: the partner’s checkout and receipt go to it.
customer.id string (uuid) no An existing PaySigna customer id, to record the payment against them. identifier (or email for card) is still required.
customer.identifier string no at most 100 characters; e.g. 0244123456 Required for mobile_money (the number: 9 to 15 digits, at most 20 characters as sent) and bank (the account number). Optional for card, where the email is used: the card itself is entered on the partner’s page.
customer.name string no at most 150 characters; e.g. Ama Mensah The payer’s name. Saved on the customer record and returned as customer.name.
description string no at most 500 characters; e.g. Order 1042 What the payment is for. Stored on the transaction and sent with the payment request; depending on the channel the customer may see it.
fee_bearer string no One of: merchant, customer Only honoured if your account permits per-transaction choice.
metadata object no Returned unchanged on every read and webhook.
momo_network string no at most 30 characters; e.g. mtn The network’s code, such as mtn, telecel or airteltigo. Required for mobile_money unless momo_network_id is sent instead. Codes are listed at /v1/reference/momo-networks.
momo_network_id string (uuid) no The network’s id from /v1/reference/momo-networks. An alternative to momo_network; send one, not both.
reference string no at most 100 characters Your own identifier for this payment.
return_url string (uri) no Where to send the customer after a redirect flow.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/charges \
-H "Authorization: Bearer sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount": 1250,
"channel": "mobile_money",
"currency": "GHS",
"customer": {
"email": "[email protected]",
"identifier": "0244123456",
"name": "Ama Mensah"
},
"description": "Order 1042",
"momo_network": "mtn"
}'
Field Type What it is
action object
action.attempts_remaining integer otp only: how many more incorrect codes are allowed before the charge fails.
action.expires_at string (date-time) When the partner stops accepting this action, where it says. For otp, a code sent after this is refused; send a new one first if resendable.
action.message string What to tell the customer, when the type alone does not say: for otp, where the code is (e-mailed to them, or shown in their bank’s app). Safe to show as is.
action.resendable boolean otp only: the code can be sent to the customer again with POST /v1/charges/{id}/otp/resend.
action.type string prompt: the customer approves a prompt on their phone. otp: they enter a one-time code, which you send to POST /v1/charges/{id}/otp. 3ds_redirect or hosted_checkout: send them to url. ussd: they dial a code (reserved, not currently used). One of: prompt, otp, 3ds_redirect, hosted_checkout, ussd
action.url string Open this to complete authentication. Absent for a handset prompt.
amount integer The amount requested, in minor units.
channel string How the customer pays: mobile_money, bank or card.
created_at string (date-time) When the charge was created.
currency string ISO 4217 code of every amount on this charge.
customer object
customer.masked string A partial identifier. The full value is never returned.
customer.name string The payer’s name, when one was given.
customer_amount integer What the customer is charged, including any fee they bear.
expires_at string (date-time) After this, the charge expires and cannot complete.
fee object
fee.bearer string Who pays the fees: merchant (deducted from merchant_amount) or customer (added to customer_amount). One of: merchant, customer
fee.commission integer Commission you accepted for a company that referred you, on this payment. Follows bearer, like platform.
fee.platform integer PaySigna’s processing fee, in minor units.
fee.tax integer Tax charged on the fee, in minor units.
id string The charge’s id. Use it to read the charge, refund it, or find it in /v1/transactions.
merchant_amount integer What will be credited to your balance.
metadata object The metadata you sent, unchanged.
object string
reference string PaySigna’s reference. Quote it to support.
status string initiated: created, not yet sent. pending or processing: in progress. awaiting_action: the customer must act (see action). succeeded: the money was collected. failed, cancelled or expired: nothing was collected. Wait for the webhook or read the charge for the final state. One of: initiated, pending, processing, awaiting_action, succeeded, failed, cancelled, expired
your_reference string The reference you sent when creating the charge.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 402: your balance cannot cover it
  • 403: the key lacks the scope, or your account may not do this
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 502: the payment partner refused it
  • 503: the payment partner is unreachable; safe to retry
  • 504: the partner timed out; read the resource before retrying

Every error is a JSON problem with a stable code. See Errors.


GET /v1/charges/{id}

Reads a charge’s current state. This is the authority after a timeout: if a create request did not return, read the charge rather than resending it.

Authentication: a secret key (server-side only) with the scope collection.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The charge’s id, as returned when it was created.
Field Type What it is
action object
action.attempts_remaining integer otp only: how many more incorrect codes are allowed before the charge fails.
action.expires_at string (date-time) When the partner stops accepting this action, where it says. For otp, a code sent after this is refused; send a new one first if resendable.
action.message string What to tell the customer, when the type alone does not say: for otp, where the code is (e-mailed to them, or shown in their bank’s app). Safe to show as is.
action.resendable boolean otp only: the code can be sent to the customer again with POST /v1/charges/{id}/otp/resend.
action.type string prompt: the customer approves a prompt on their phone. otp: they enter a one-time code, which you send to POST /v1/charges/{id}/otp. 3ds_redirect or hosted_checkout: send them to url. ussd: they dial a code (reserved, not currently used). One of: prompt, otp, 3ds_redirect, hosted_checkout, ussd
action.url string Open this to complete authentication. Absent for a handset prompt.
amount integer The amount requested, in minor units.
channel string How the customer pays: mobile_money, bank or card.
created_at string (date-time) When the charge was created.
currency string ISO 4217 code of every amount on this charge.
customer object
customer.masked string A partial identifier. The full value is never returned.
customer.name string The payer’s name, when one was given.
customer_amount integer What the customer is charged, including any fee they bear.
expires_at string (date-time) After this, the charge expires and cannot complete.
fee object
fee.bearer string Who pays the fees: merchant (deducted from merchant_amount) or customer (added to customer_amount). One of: merchant, customer
fee.commission integer Commission you accepted for a company that referred you, on this payment. Follows bearer, like platform.
fee.platform integer PaySigna’s processing fee, in minor units.
fee.tax integer Tax charged on the fee, in minor units.
id string The charge’s id. Use it to read the charge, refund it, or find it in /v1/transactions.
merchant_amount integer What will be credited to your balance.
metadata object The metadata you sent, unchanged.
object string
reference string PaySigna’s reference. Quote it to support.
status string initiated: created, not yet sent. pending or processing: in progress. awaiting_action: the customer must act (see action). succeeded: the money was collected. failed, cancelled or expired: nothing was collected. Wait for the webhook or read the charge for the final state. One of: initiated, pending, processing, awaiting_action, succeeded, failed, cancelled, expired
your_reference string The reference you sent when creating the charge.
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/charges/{id}/otp

Completes a charge whose action.type is otp: send the code the customer entered. action.message says where the customer finds it.

The answer is the charge as it now stands. Usually processing, settling like any other charge; it can also ask for ANOTHER code (action.type still otp, with a new action.message), as when a first payment from a bank account is preceded by a one-time enrolment. Ask the customer again and send the new code here.

A code the bank refuses is a 422 with otp_rejected, and nothing changes: the customer can try again while action.attempts_remaining lasts. At the limit the charge fails and the answer is otp_attempts_exceeded. A 409 means the charge is not waiting for a code, or a code for it is already being checked.

Requires an Idempotency-Key header. In the sandbox, a bank charge to an account number ending 6000 asks for a code and one ending 6001 asks for an enrolment code first; the code 123456 is accepted and any other is refused.

Authentication: a secret key (server-side only) with the scope collection.create.

Field Type Required Allowed values and limits What it is
Idempotency-Key string yes Unique per code. Resend the same value to retry safely; a retry replays the first answer rather than sending the code again.
Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The charge’s id.
Field Type Required Allowed values and limits What it is
code string yes at least 4 characters; at most 12 characters; e.g. 123456 The one-time code the customer entered, as typed. Never stored or logged.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/charges/{id}/otp \
-H "Authorization: Bearer sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"code": "123456"
}'
Field Type What it is
action object
action.attempts_remaining integer otp only: how many more incorrect codes are allowed before the charge fails.
action.expires_at string (date-time) When the partner stops accepting this action, where it says. For otp, a code sent after this is refused; send a new one first if resendable.
action.message string What to tell the customer, when the type alone does not say: for otp, where the code is (e-mailed to them, or shown in their bank’s app). Safe to show as is.
action.resendable boolean otp only: the code can be sent to the customer again with POST /v1/charges/{id}/otp/resend.
action.type string prompt: the customer approves a prompt on their phone. otp: they enter a one-time code, which you send to POST /v1/charges/{id}/otp. 3ds_redirect or hosted_checkout: send them to url. ussd: they dial a code (reserved, not currently used). One of: prompt, otp, 3ds_redirect, hosted_checkout, ussd
action.url string Open this to complete authentication. Absent for a handset prompt.
amount integer The amount requested, in minor units.
channel string How the customer pays: mobile_money, bank or card.
created_at string (date-time) When the charge was created.
currency string ISO 4217 code of every amount on this charge.
customer object
customer.masked string A partial identifier. The full value is never returned.
customer.name string The payer’s name, when one was given.
customer_amount integer What the customer is charged, including any fee they bear.
expires_at string (date-time) After this, the charge expires and cannot complete.
fee object
fee.bearer string Who pays the fees: merchant (deducted from merchant_amount) or customer (added to customer_amount). One of: merchant, customer
fee.commission integer Commission you accepted for a company that referred you, on this payment. Follows bearer, like platform.
fee.platform integer PaySigna’s processing fee, in minor units.
fee.tax integer Tax charged on the fee, in minor units.
id string The charge’s id. Use it to read the charge, refund it, or find it in /v1/transactions.
merchant_amount integer What will be credited to your balance.
metadata object The metadata you sent, unchanged.
object string
reference string PaySigna’s reference. Quote it to support.
status string initiated: created, not yet sent. pending or processing: in progress. awaiting_action: the customer must act (see action). succeeded: the money was collected. failed, cancelled or expired: nothing was collected. Wait for the webhook or read the charge for the final state. One of: initiated, pending, processing, awaiting_action, succeeded, failed, cancelled, expired
your_reference string The reference you sent when creating the charge.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 502: the payment partner refused it
  • 503: the payment partner is unreachable; safe to retry
  • 504: the partner timed out; read the resource before retrying

Every error is a JSON problem with a stable code. See Errors.


POST /v1/charges/{id}/otp/resend

Asks the bank to send the code again, when action.resendable is true: a code sent by e-mail or SMS. A code the customer reads from their bank’s app cannot be re-sent, and the answer is 422. The answer is the charge; its action may carry a new expires_at.

Requires an Idempotency-Key header.

Authentication: a secret key (server-side only) with the scope collection.create.

Field Type Required Allowed values and limits What it is
Idempotency-Key string yes Unique per resend. A retry with the same key does not send another code.
Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The charge’s id.
Field Type What it is
action object
action.attempts_remaining integer otp only: how many more incorrect codes are allowed before the charge fails.
action.expires_at string (date-time) When the partner stops accepting this action, where it says. For otp, a code sent after this is refused; send a new one first if resendable.
action.message string What to tell the customer, when the type alone does not say: for otp, where the code is (e-mailed to them, or shown in their bank’s app). Safe to show as is.
action.resendable boolean otp only: the code can be sent to the customer again with POST /v1/charges/{id}/otp/resend.
action.type string prompt: the customer approves a prompt on their phone. otp: they enter a one-time code, which you send to POST /v1/charges/{id}/otp. 3ds_redirect or hosted_checkout: send them to url. ussd: they dial a code (reserved, not currently used). One of: prompt, otp, 3ds_redirect, hosted_checkout, ussd
action.url string Open this to complete authentication. Absent for a handset prompt.
amount integer The amount requested, in minor units.
channel string How the customer pays: mobile_money, bank or card.
created_at string (date-time) When the charge was created.
currency string ISO 4217 code of every amount on this charge.
customer object
customer.masked string A partial identifier. The full value is never returned.
customer.name string The payer’s name, when one was given.
customer_amount integer What the customer is charged, including any fee they bear.
expires_at string (date-time) After this, the charge expires and cannot complete.
fee object
fee.bearer string Who pays the fees: merchant (deducted from merchant_amount) or customer (added to customer_amount). One of: merchant, customer
fee.commission integer Commission you accepted for a company that referred you, on this payment. Follows bearer, like platform.
fee.platform integer PaySigna’s processing fee, in minor units.
fee.tax integer Tax charged on the fee, in minor units.
id string The charge’s id. Use it to read the charge, refund it, or find it in /v1/transactions.
merchant_amount integer What will be credited to your balance.
metadata object The metadata you sent, unchanged.
object string
reference string PaySigna’s reference. Quote it to support.
status string initiated: created, not yet sent. pending or processing: in progress. awaiting_action: the customer must act (see action). succeeded: the money was collected. failed, cancelled or expired: nothing was collected. Wait for the webhook or read the charge for the final state. One of: initiated, pending, processing, awaiting_action, succeeded, failed, cancelled, expired
your_reference string The reference you sent when creating the charge.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 502: the payment partner refused it
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.