Skip to main content

Payment request events

Payment request events use the v1.collection.payment_request.* prefix. The current lifecycle events are created, part_paid, paid, cancelled and expired.

Overview​

Payment Requests let you collect money via a pre-generated link. Whenever a request moves through its life-cycle we emit a webhook so your system can react in real-time (update UI, trigger fulfilment, etc.).


Event catalogue​

EventWhen it triggers
v1.collection.payment_request.createdThe payment request is first generated
v1.collection.payment_request.part_paidA partial payment is captured but the payable balance > 0
v1.collection.payment_request.paidThe request balance reaches 0 from payments in RECEIVED or COMPLETE
v1.collection.payment_request.cancelledThe request is cancelled by the user or after a payment is rejected
v1.collection.payment_request.expiredA HOSTED_PAGE or EMBEDDED request closed without being paid in full; see Expiry below for when this happens

Event flow​

payment_request.paid is an aggregate balance signal: it means the requested balance is covered even while an individual payment remains RECEIVED. For crypto, use the individual payment.completed event and confirm its API status is COMPLETE. A later crypto compliance rejection can cancel a previously paid or part-paid request. See Payment lifecycle and webhooks.


Additional data​

payment_request.part_paid, payment_request.paid and payment_request.expired include the amount received so far in the data object:

{
"amountPaid": "400.00",
"currency": "AED"
}
FieldMeaning
amountPaidCumulative amount received against the payment request so far, as a decimal string
currencyCurrency of amountPaid, always the payment request's currency

payment_request.created and payment_request.cancelled have an empty data object. The payment request id is relatedObject.id on every event.

Use these fields to update your records from the event. Before taking a business action such as fulfilment, confirm the latest state with Get payment request. See Event format for the webhook envelope.

Expiry​

HOSTED_PAGE and EMBEDDED payment requests carry an expiresAt set to 60 minutes after creation. PAYMENT_LINK requests have no expiresAt and do not expire.

  • Up to 45 minutes after creation (expiresAt minus 15 minutes) the payer can start a payment. A crypto quote taken during this time runs until that 45 minute mark, however late it was taken.
  • Between 45 minutes and expiresAt no new payment can start. This window lets funds already sent arrive and confirm.
  • The request becomes EXPIRED, and payment_request.expired is sent with amountPaid and currency, at the earlier of two points: the 45 minute mark, if the payer never took a quote or started a card or bank payment; otherwise expiresAt, if the request is still UNPAID or PART_PAID and no deposit sent in time is still confirming. A confirming deposit holds the request open past expiresAt: the request becomes PAID if the deposit covers the balance, and expires once nothing is confirming otherwise. expiresAt always reports the 60 minute instant, so an early expiry arrives before it.
  • A request that reached PAID is unaffected and receives no expiry event.

Worked example​

A payment request is created at 10:00, so expiresAt is 11:00 and the last moment a payment can be started is 10:45. Time the payer spends before reaching the quote screen comes off the quote window rather than extending the request:

Payer's journeyWhat happens
Reaches the quote screen at 10:15The quote shown is valid until 10:45, a 30 minute window
Reaches the quote screen at 10:40The quote shown is valid until 10:45, a 5 minute window
Sends crypto at 10:44, confirmed on-chain at 10:52Inside the arrival window, so the payment is processed normally and the request becomes PART_PAID or PAID
Has not taken a quote or started a card or bank payment by 10:45There is no longer time to take a quote and pay, so the request becomes EXPIRED at 10:45 and payment_request.expired is sent. A payer who took a quote at 10:40 and sent funds at 10:44 is not in this case: their request waits until 11:00, and longer while the deposit is visible but unconfirmed
Took a quote but never sent fundsThe request becomes EXPIRED at 11:00 and payment_request.expired is sent with amountPaid of zero
Paid in full at 10:30The request is PAID; nothing expires and no expiry event is sent

If your own system also times the request out, key it off the same moment of creation so the payer can never be mid-checkout with a valid quote on screen while your side has already written the request off, and treat payment_request.expired or the API status, not the clock alone, as the signal to close it: a deposit still confirming can hold a request open past expiresAt and then pay it.

An expired request does not reopen. Funds that arrive after expiry are not lost: depending on the payment method they are either credited to your Hubpay wallet and shown against the payment request in the API, or held by Hubpay for review, in which case our operations team will contact you to complete or refund the payment.