Skip to main content

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
EventMeaningContract funded?
bank_transfer.initiatedBank account details were issued to the buyerNo
bank_transfer.buyer_acknowledgedThe buyer says they have sent the transferNo
payment.completed or milestone.payment.completedThe transfer arrivedYes
Only payment.completed or milestone.payment.completed means the money arrived

bank_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"
}
}
}
Use paymentBankInfo to show the account details on your own platform

paymentBankInfo 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.

EventMeaning
contract.payment.underpaidLess than the expected amount arrived. The contract stays unpaid, and a refund request is created automatically for the amount received
contract.payment.overpaidMore than the expected amount arrived. The contract is funded, and a refund request is created automatically for the excess
payment.unmatchedA payment arrived that could not be matched to any contract
milestone.payment.underpaidLess than the expected amount arrived. The milestone stays unpaid, and a refund request is created automatically for the amount received
milestone.payment.overpaidMore than the expected amount arrived. The milestone is funded, and a refund request is created automatically for the excess
refund_request.cancelledAfter an underpayment, the buyer transferred the remaining balance. The refund request is cancelled and the contract/milestone is funded

These events carry additional fields:

FieldDescription
amountExpectedThe amount the buyer was expected to pay
amountReceivedThe total amount received so far
amountDifferenceamountReceived minus amountExpected. Negative when underpaid, positive when overpaid
vIBANThe contract account that received the credit
transferReferenceThe bank's reference for the transfer
senderNameName on the sending account
senderIBANSending account IBAN, masked
refundRefund 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.