Bank transfer (ACH, Wire and Swift)

Collect payments in USD from customers globally.

USD collection

Give a customer abroad a single-use US bank account. They pay in dollars by ACH or wire, and you receive naira in your Paga balance, converted at the prevailing exchange rate when the payment arrives.

EnvironmentBase URL
Sandboxhttps://qa1-collect.paga.com
Livehttps://collect.paga.com

Authenticate with HTTP Basic and a hash header, as in Authentication. Set your callback URL to an HTTPS endpoint that responds with HTTP 200 within 5 seconds.

How it works

Create a payment request

Send the amount you want to collect in naira. Paga returns single-use USD account details for three payment rails, with the dollar amount to request on each.

Show your customer how to pay

Display the instructions for the rail they’ll use, including the exact dollar amount, which already includes that rail’s fee.

Your customer sends dollars

By ACH, US domestic wire or international (SWIFT) wire, from their own bank.

Paga notifies you

A CAPTURED webhook when the payment clears, or FUNDING_FAILED if it can’t be accepted.

Naira settles to your Paga balance

Paga converts the dollars at the prevailing rate when the payment arrives. Verify the webhook, then fulfil the order.

Three ways your customer can pay

US bank account

ACH

For customers with a US checking account. The lowest fee, and the slowest to clear.

US bank account

US domestic wire

For customers in the US who want the payment to clear faster.

Any country

International wire (SWIFT)

For customers outside the US. They must include the reference Paga - <account number> in the transfer memo.

Each rail has its own fee and minimum amount, returned when you create the request. Show your customer the total for the rail they choose.

1. Create a payment request

POST /paymentRequest
Authorization: Basic base64(publicKey:secretKey)
hash: sha512(referenceNumber + amount + currency + payer.phoneNumber + payer.email + hashKey)
Content-Type: application/json

Use an empty string for payer.email in the hash if you don’t send it. The hash is lowercase hex.

{
  "referenceNumber": "INV-2026-0042",
  "amount": "80000",
  "currency": "NGN",
  "payer": {
    "name": "Jill Scott",
    "phoneNumber": "+19175247723",
    "currency": "USD"
  },
  "payee": {
    "name": "Your business name"
  },
  "expiryDateTimeUTC": "2026-11-10T15:35:00",
  "payerCollectionFeeShare": "1.0",
  "recipientCollectionFeeShare": "0.0",
  "isAllowPartialPayments": "false",
  "isAllowOverPayments": "true",
  "callBackUrl": "https://yourstore.com/webhooks/paga",
  "narration": "Invoice 0042",
  "paymentMethods": ["BANK_TRANSFER"]
}
All request parameters (17)
ParameterRequiredDescription
referenceNumberYesYour unique reference. Use it to match webhooks and check status
amountYesThe amount you want to collect, in naira
currencyYesYour settlement currency. Must be NGN
payer.nameYesCustomer’s full legal name
payer.phoneNumberYesWith country code, for example +19175247723
payer.currencyYesMust be USD
payer.emailNoCustomer’s email address
payee.nameYesName shown as the recipient on the USD account
payerCollectionFeeShareYesShare of fees the customer pays. 1.0 means all of it
recipientCollectionFeeShareYesShare of fees you absorb. 0.0 means none
isAllowOverPaymentsNoSet to true. Intermediary banks often change wire amounts slightly
isAllowPartialPaymentsNoSet to false to reject underpayments
expiryDateTimeUTCNoWhen the account details stop accepting payments, in UTC
isSuppressMessagesNotrue stops Paga emailing or texting the customer
callBackUrlYesWhere Paga sends webhooks
narrationNoA short description of the payment
paymentMethodsYesMust be ["BANK_TRANSFER"]

The response

You get one entry per rail in paymentMethods, each with the account details and the dollar amount for that rail.

Response fields you’ll use most (7)
FieldWhat it’s for
totalPaymentAmountThe exact dollar amount to show your customer, fee included
transferAccountNumberThe single-use USD account number
transferFinancialInstitutionRoutingNumber1ACH routing number, or SWIFT code for international wires
transferAccountInstructions.mdReady-made instructions for international wires, including the memo reference
minimumSourceAmountThe smallest payment that rail accepts
foreignExchangeRateThe indicative naira-per-dollar rate used to calculate the dollar amount. The final rate is set when the payment arrives
rateExpiryMillisWhen the quoted dollar amount stops being valid. Create a new request after this

2. Show your customer how to pay

Show the details for the rail your customer will use. These are from the example response:

Amount to send: $51.50 USD

Account typeChecking
Bank nameRegent Bank
Account number104723884136673
Routing number (ACH)359867415
RecipientYour business name

The amounts differ because each rail’s fee is added on top. Show the total for the rail your customer picks, so the payment isn’t short.

3. Handle webhooks

Paga sends one of two events to your callback URL. Respond with HTTP 200 within 5 seconds, or Paga retries.

CAPTURED

The payment cleared and naira is on its way to your Paga balance. Verify, then fulfil.

FUNDING_FAILED

Paga received money but couldn’t accept it, for example an amount mismatch. Read errorCode and fundsReturnStatus.

Webhook fields to read first (8)
FieldMeaning
eventCAPTURED or FUNDING_FAILED
referenceNumberYour reference, to match the webhook to your order
paymentIdPaga’s ID for the payment. Store it with your reference
receivedAmountsDollars that arrived from the customer
serviceDetails.destinationAmountNaira credited to your Paga balance
serviceDetails.foreignExchangeRateThe prevailing rate applied when the payment arrived
errorCode, errorMessageWhy a payment failed. Only on FUNDING_FAILED
fundings[].fundsReturnStatusWhether the money went back to the customer. NOT_RETURNED means you need to contact support
{
  "event": "CAPTURED",
  "referenceNumber": "INV-2026-0042",
  "paymentId": "01a130c8-fec1-78e9-9bb1-a90e5797e9a0",
  "paymentStatus": "CAPTURED",
  "receivedAmounts": { "currency": "USD", "amounts": { "BASE": 515.06 } },
  "serviceDetails": {
    "sourceCurrency": "USD",
    "destinationCurrency": "NGN",
    "foreignExchangeRate": 1670.5475,
    "destinationAmount": 800000.0
  }
}

The full payload also includes fees, fundings and payment method details.

4. Check payment status

Use the status endpoint to confirm a payment if a webhook hasn’t arrived. For USD collection, include the payment currency.

POST /status
Authorization: Basic base64(publicKey:secretKey)
hash: sha512(referenceNumber + paymentCurrency + hashKey)
Content-Type: application/json
{
  "referenceNumber": "INV-2026-0042",
  "paymentCurrency": "USD"
}
Request parameters (2)
ParameterRequiredDescription
referenceNumberYesThe referenceNumber you used when you created the payment request. Hash value 1
paymentCurrencyYesUSD. Hash value 2
{
  "data": {
    "statusCode": "1",
    "statusMessage": "pending",
    "requestAmount": 400.0,
    "totalPaymentAmount": 400.0
  },
  "statusCode": "0",
  "statusMessage": "success"
}
Response fields (6)
FieldMeaning
statusCode0 means your status request worked. It doesn’t tell you whether the payment was made
statusMessageReadable status of your status request
data.statusCodeThe payment’s own status. 0 means paid, 1 means still pending. See Collect API status codes for the others
data.statusMessageReadable status of the payment
data.requestAmountThe amount you asked for
data.totalPaymentAmountThe amount to be paid, fee included

Read data.statusCode, not the outer statusCode. The outer statusCode only says whether your status request worked. Fulfil the order only when data.statusCode is 0.

Wires can take a day or more to arrive, so don’t poll on a timer. Wait for the webhook, and only check status if it hasn’t arrived when you’d expect it.

Good to know

Allow overpayments

Set isAllowOverPayments to true. Intermediary banks deduct varying fees from wires, and strict amounts cause rejections.

Store both IDs

Index payments by referenceNumber and paymentId. If a webhook arrives twice, respond 200 without fulfilling again.

Expect the naira amount to vary

Dollars convert at the prevailing rate when they arrive, so the naira you receive can differ from your request. Read destinationAmount in the webhook.

Failed wires aren’t returned automatically

When fundsReturnStatus is NOT_RETURNED, contact support with the paymentId and referenceNumber.

Need help? We’re here 24/7

Talk to our support team any time, or ask questions in the developer community.