Payment-first purchases
Maintain an existing tickets integration without changing its payment contract.
For existing integrations
For new implementations, use checkout reservations. Do not submit one payment to both flows.
How this flow differs
| Recommended checkout | Existing payment-first | |
|---|---|---|
| First call | POST /raffles/{id}/checkouts | GET /raffles/{id} |
| Memo | Exact returned payment.memo | payment.memo_prefix + : + buyer reference |
| Submit signature | Promptly after submission, before the checkout deadline | After finalized commitment |
| Idempotency | Your checkout key, then the same signature | Transaction signature; do not send Idempotency-Key |
| Success | Purchase status completed | HTTP 200 |
| Tickets | Objects with ticket_no and code | Ticket-code strings |
Claim tickets after payment
After the buyer's payment is finalized, call POST /raffles/{raffleId}/tickets from your server:
{"external_user_ref":"buyer_123","qty":2,"tx_signature":"<finalized transaction signature>"}Use the same transfer rules as wallet payments, but with the payment-first memo above. Allow at least 30 seconds for the request.
On a timeout, 5xx or TX_NOT_FOUND, retry the same signature and body with backoff. replayed: true means you received the original successful purchase. Changing raffle, quantity or buyer for an issued signature returns IDEMPOTENCY_KEY_REUSED.
Refund handling
An unissuable, attributable payment can enter asynchronous refund handling. Show refund_status, not just the HTTP status or presence of a signature:
in_flight: refund pending.paid: refund completed;refund_tx_signatureprovides its transaction.failed: contact Verity support.
Re-post the same tickets request to read the outcome. Once refunded, it returns PAYMENT_REFUNDED and the original rejection in reason. Do not take another payment while the original outcome is unresolved.
A memo mismatch or an unresolved payer can require support if funds moved; an automatic refund is not guaranteed for those cases.