Collect via wallet
Collect in-app from a wallet your customer already holds. Create a virtual_wallet payment, settle it with /wallets/:id/pay: a pure internal transfer, no bank rail.
Sometimes the money is already inside Acute. Your customer holds a wallet, they want to
pay another wallet, and there's no reason to round-trip through the banks. A
virtual_wallet payment collects wallet → wallet, in-app, instantly: a pure internal
ledger transfer with no bank rail and no provider fee.
Two steps: create the payment to say "₦X is owed to this wallet," then have the payer's wallet settle it.
Same POST /payments, but method: "virtual_wallet". There's no NUBAN to mint: the
response is just the amount owed and the fee breakdown.
# create a virtual_wallet payment"
curl -X POST https://sandbox.api.acute.network/v1/payments \
-H "Authorization: Bearer acuinf_test_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"method": "virtual_wallet",
"baseAmount": 250000,
"targetWalletId": "acuinf830192847561wlt",
"description": "Tip for ride #92"
}'{
"success": true,
"statusCode": 201,
"data": {
"id": "acuinf558712049336pay",
"method": "virtual_wallet",
"status": "pending",
"baseAmount": 250000,
"fee": 3750,
"payableAmount": 253750,
"amountReceived": 0,
"currency": "NGN",
"targetWalletId": "acuinf830192847561wlt",
"description": "Tip for ride #92",
"expiresAt": null,
"settledAt": null,
"createdAt": "2026-06-26T12:00:00.000Z",
"virtualAccount": null
}
}No provider fee, no NUBAN
virtual_wallet payments never touch the bank rail, so there's no provider fee and
virtualAccount is null. The fee on this payment is the transfer fee
(clamp(₦10, 1.5%, ₦100)), billed to the paying wallet, not the bank-collection fee.
The payer's wallet pays the payment by its reference. This is a payment-scoped call
with the payment id in the query:
# payer wallet settles the payment
curl -X POST "https://sandbox.api.acute.network/v1/wallets/acuinf661043820917wlt/pay?paymentId=acuinf558712049336pay" \
-H "Authorization: Bearer acuinf_test_..." \
-H "Idempotency-Key: $(uuidgen)"Both wallets must be kycStatus: tier1, they must belong to the same org, and the
payer can't be the target (PAYMENT_SELF_PAY → 422). Settlement is synchronous: the
response comes back settled.
Settling a virtual_wallet payment is one internal transfer. The payer's wallet is
debited the full payableAmount; the target gets the base; the Acute fee is remitted.
No suspense account, because no money left Acute.
Wallet payment settles
₦2,500.00 base · ₦37.50 Acute (transfer) fee · no provider fee
- DRpayer covers base + Acute fee
- CRbase credited to the target
- CRAcute fee remitted
If that diagram looks familiar, good: it is a transfer, just framed
as a payment so you get a payment record, a payment status, and a payment.settled
webhook. The accounting is the same three legs.
Shorter than the bank-transfer story: there's no expiry window and no NUBAN to wait on.
A virtual_wallet payment is pending until a wallet settles it, then settled.
pendingentry statusCreated and waiting for a wallet to pay it via /wallets/:id/pay. No money has moved.
Transitions out
pendingsettledA wallet calls /wallets/:id/pay?paymentId=… and covers payableAmount
fires
payment.settledpendingfailedThe payer wallet can't cover the amount, or settlement otherwise faults
{
"id": "acuinf913660472518evt",
"type": "payment.settled",
"createdAt": "2026-06-26T12:01:09.000Z",
"data": {
"id": "acuinf558712049336pay",
"status": "settled",
"baseAmount": 250000,
"amountReceived": 253750,
"currency": "NGN"
}
}Both wallets need tier-1 KYC
In-app collection moves real ledger balances, so both the payer and target wallets must
be tier1. A wallet at kycStatus: none can't pay or be paid: you'll get a 403
WALLET_KYC_REQUIRED. Submit KYC on the wallet first
(POST /wallets/:id/kyc).
bank_transfer | virtual_wallet | |
|---|---|---|
| Money source | external bank, via NIP | a wallet already in your org |
| Provider fee | yes (clamp(₦0.5, 0.5%, ₦200)) | none |
| Settlement | async (webhook on inbound) | sync (the pay call returns settled) |
| Has a NUBAN | yes, expiring | no |
| Use it for | first-touch collection from anyone | in-app payments between your users |