Bank Transfer Events
When a buyer chooses to pay by bank transfer, the payment is not confirmed immediately. WePay sends a sequence of webhooks so you can follow the transfer from start to finish.
See Payment Summary for what the buyer sees during this flow.
The sequence (Normal path only)
Buyer chooses bank transfer
|
v
[ bank_transfer.initiated ] ---> account details issued to the buyer
|
v
Buyer transfers from their own bank
|
v
[ bank_transfer.buyer_acknowledged ] ---> buyer says they have sent it
|
| ... waiting for the funds to arrive ...
v
[ payment.completed ] ---> contract is funded
| Event | Meaning | Contract funded? |
|---|---|---|
bank_transfer.initiated | Bank account details were issued to the buyer | No |
bank_transfer.buyer_acknowledged | The buyer says they have sent the transfer | No |
payment.completed or milestone.payment.completed | The transfer arrived | Yes |
payment.completed or milestone.payment.completed means the money arrivedbank_transfer.buyer_acknowledged is the buyer telling the checkout page that they have made the transfer. It is not proof that any funds were sent or received.
Never release goods, services, or the contract itself on bank_transfer.buyer_acknowledged. Wait for payment.completed or milestone.payment.completed.
bank_transfer.initiated
Sent when bank account details are issued for the contract and shown to the buyer.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event": "bank_transfer.initiated",
"createdAt": "2026-09-22T10:15:00Z",
"data": {
"contractId": "CNT-2609-00100021",
"reference": "your-reference-123",
"status": "Escrow",
"previousStatus": "Approved",
"amount": 1057.5,
"currency": "SAR",
"paymentMethod": "BankTransfer",
"paymentBankInfo": {
"iban": "SA03****************7519",
"accountNumber": "*********7519",
"bankName": "Arab National Bank",
"bankCode": "30100",
"beneficiaryName": "COMPANY RABITAT ALDFA FINANCE WEPAY"
},
"timestamp": "2026-09-22T10:15:00Z",
"metadata": {
"reference": "your-reference-123"
}
}
}
paymentBankInfo to show the account details on your own platformpaymentBankInfo carries the full account details issued for this contract, including the IBAN and account number. You can display these to your buyer inside your own app, so they do not have to return to the WePay checkout page to see where to transfer.
The account is unique to this contract, which is how WePay matches the incoming transfer back to it. Show it exactly as received.
Note: IBANs and account numbers are masked in the examples on this page for illustration. Real webhooks contain the full, unmasked values.
bank_transfer.buyer_acknowledged
Sent when the buyer confirms on the checkout page that they have made the transfer.
{
"id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"event": "bank_transfer.buyer_acknowledged",
"createdAt": "2026-09-22T10:22:00Z",
"data": {
"contractId": "CNT-2609-00100021",
"reference": "your-reference-123",
"status": "Approved",
"previousStatus": "Pending",
"amount": 1057.5,
"currency": "SAR",
"paymentMethod": "BankTransfer",
"buyerAcknowledged": true,
"buyerAcknowledgedAt": "2026-09-22T10:22:00Z",
"paymentBankInfo": {
"iban": "SA03****************7519",
"accountNumber": "*********7519",
"bankName": "Arab National Bank",
"bankCode": "30100",
"beneficiaryName": "COMPANY RABITAT ALDFA FINANCE WEPAY"
},
"timestamp": "2026-09-22T10:22:00Z",
"metadata": {
"reference": "your-reference-123"
}
}
}
Use this event to update your own UI, for example showing "awaiting bank confirmation" to your user. Do not treat it as payment.
payment.completed
Sent when the transfer has arrived and the contract is funded.
{
"id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"event": "payment.completed",
"createdAt": "2026-09-22T11:40:00Z",
"data": {
"contractId": "CNT-2609-00100021",
"reference": "your-reference-123",
"status": "Escrow",
"previousStatus": "Approved",
"amount": 1057.5,
"currency": "SAR",
"paymentMethod": "BankTransfer",
"paymentId": "PAY-123456",
"transactionId": "TXN-789012",
"vIBAN": "SA03****************7519",
"transferReference": "PT2609220001",
"senderName": "Kareem Hasan",
"senderIBAN": "SA03********************1234",
"timestamp": "2026-09-22T11:40:00Z",
"metadata": {
"reference": "your-reference-123"
}
}
}
This is the event to act on. At this point the contract is funded and behaves exactly as it would after an online payment.
When the amount does not match
A bank transfer is entered manually by the buyer, so the amount received may not match the amount expected.
| Event | Meaning |
|---|---|
contract.payment.underpaid | Less than the expected amount arrived. The contract stays unpaid, and a refund request is created automatically for the amount received |
contract.payment.overpaid | More than the expected amount arrived. The contract is funded, and a refund request is created automatically for the excess |
payment.unmatched | A payment arrived that could not be matched to any contract |
milestone.payment.underpaid | Less than the expected amount arrived. The milestone stays unpaid, and a refund request is created automatically for the amount received |
milestone.payment.overpaid | More than the expected amount arrived. The milestone is funded, and a refund request is created automatically for the excess |
refund_request.cancelled | After an underpayment, the buyer transferred the remaining balance. The refund request is cancelled and the contract/milestone is funded |
These events carry additional fields:
| Field | Description |
|---|---|
amountExpected | The amount the buyer was expected to pay |
amountReceived | The total amount received so far |
amountDifference | amountReceived minus amountExpected. Negative when underpaid, positive when overpaid |
vIBAN | The contract account that received the credit |
transferReference | The bank's reference for the transfer |
senderName | Name on the sending account |
senderIBAN | Sending account IBAN, masked |
refund | Refund details, when a refund has been raised for the difference |
Example: underpayment
{
"id": "d4e5f6a7-b8c9-0123-defa-456789012345",
"event": "contract.payment.underpaid",
"createdAt": "2026-09-22T11:40:00Z",
"data": {
"contractId": "CNT-2609-00100021",
"reference": "your-reference-123",
"status": "Approved",
"previousStatus": "Pending",
"currency": "SAR",
"paymentMethod": "BankTransfer",
"amountExpected": 1057.5,
"amountReceived": 1000.0,
"amountDifference": -57.5,
"vIBAN": "SA03****************7519",
"transferReference": "PT2609220001",
"senderName": "Kareem Hasan",
"senderIBAN": "SA03********************1234",
"timestamp": "2026-09-22T11:40:00Z",
"refund": {
"id": 1411,
"amount": 1000,
"status": "Pending"
},
"metadata": {
"reference": "your-reference-123"
}
}
}
An underpaid contract stays unpaid. Its refund request is cancelled once the remaining balance arrives
Milestone equivalents are milestone.payment.underpaid and milestone.payment.overpaid, identified by data.milestoneId. See Payload Reference for how milestone events differ.
Example: overpayment
{
"id": "3bc5f6a7-b8c9-01d3-d11a-4ab789012746",
"event": "contract.payment.overpaid",
"createdAt": "2026-09-22T11:40:00Z",
"data": {
"contractId": "CNT-2609-00100021",
"reference": "your-reference-864",
"status": "Escrow",
"previousStatus": "Approved",
"currency": "SAR",
"paymentMethod": "BankTransfer",
"amountExpected": 1500,
"amountReceived": 1600,
"amountDifference": 100,
"vIBAN": "SA03****************7519",
"transferReference": "PT2609739501",
"senderName": "Kareem Hasan",
"senderIBAN": "SA03********************1234",
"timestamp": "2026-09-22T11:40:00Z",
"refund": {
"id": 1411,
"amount": 100,
"status": "Pending"
},
"metadata": {
"reference": "your-reference-864"
}
}
}
Handling recommendations
Wait for payment.completed. It is the only event that means funds are paid. Treat initiated and buyer_acknowledged as progress updates for your UI.
Be idempotent. Webhooks are retried on failure, so the same event may arrive more than once. Use X-WePay-Webhook-Id to detect repeats.
Do not assume ordering. Events are delivered independently and may arrive out of order. Decide based on data.status and data.timestamp rather than on the order of arrival.
Expect a delay. Depending on the buyer's bank, confirmation may take from minutes to considerably longer. Design your user experience around a pending state rather than an immediate result.
Handle amount mismatches. Build a path for underpaid and overpaid transfers. They are more common with bank transfer than with online payment.
Reconcile with the API. If you miss an event, call Get Contract to read the current status and transactions.
Testing
You can simulate a bank transfer in the sandbox without moving real money. See Test Data.