Complete checkout and take payment with the Booking API
- Last verified
- Last verified Sep 23, 2026
Use this guide to turn a filled cart into a paid order. It covers the checks before checkout, legal consent, the hosted payment handoff, payment status and ticket delivery. The Booking API quickstart shows the shortest path; this guide explains each decision along the way.
Before you start
- A cart with at least one item and its cart token.
- The customer's name, email address and billing address.
Check the cart before checkout
Read the cart with request(id: <cart token>) and act on these fields:
| Field | Meaning and action |
|---|---|
checkoutPolicy.targetState | BOOKED completes with requestPaymentInitiate. RESERVED and REQUESTED cannot be completed through the Booking API yet; link to the offer in the hosted shop instead. |
checkoutPolicy.compatible and conflicts | When compatible is false, remove the lines in conflicts or check them out in a separate cart. |
requiresShipping | When true, the customer needs a shipping address in one of shop.allowedShippingCountries. |
customFieldsCompletion.allRequiredComplete | When false, do not start checkout. The Booking API cannot collect these answers; send the customer to the hosted shop to complete this purchase. |
requestItems.requiresWithdrawalEarlyStartConsent | When true for any line, ask for the early-start consent described below. |
checkoutHold.secondsRemaining | Time left before an unpaid order is canceled. Keep it visible until the payment succeeds. |
The hosted shop's address for an offer is its domain, the two-letter language code and the offer's shopSlug.slugTranslated, for example https://tickets.example.com/en/guided-tour.
Add the customer
Send the customer with requestUpdate, as shown in the quickstart. Mark the billing address with billing: true and, when the cart requires shipping, a shipping address with shipping: true. The same address can have both.
Collect legal consent
Load the documents customers must accept with legalDocuments(required: [true]). For each document, show a checkbox with checkboxLabelTranslated, or nameTranslated when no label is set. Show the document text from contentTranslated; when isUrl is true, the content is a link to the document.
Send one acceptance per required document with checkout. legalDocumentVersion must be the publishedAt value the customer saw. When a document changes between display and checkout, checkout fails with an error for legalDocumentAcceptances; reload the documents and ask the customer again.
Ask for early-start consent
Some services can only be sold when the customer agrees that the service starts before the legal withdrawal period ends. When a cart line has requiresWithdrawalEarlyStartConsent, show a consent checkbox with your consent text and send the result with checkout:
{
"withdrawalEarlyStartConsent": {
"accepted": true,
"requestItemIds": ["<cart line id>"],
"text": "I agree that the service starts before the withdrawal period ends."
}
}text must be the exact text the customer saw.
Complete checkout
Call requestPaymentInitiate with the cart token, the legal acceptances and, when needed, the early-start consent. The quickstart shows the full mutation.
| Result | What to do |
|---|---|
invoice with paymentLink | Redirect the customer to paymentLink. |
invoice.paymentState is PAID | Nothing is left to pay, for example when a voucher covers the full amount. The order is complete; do not redirect. |
errors | Show messageTranslated. The cart is unchanged; fix the input and try again. |
Hand payment to the hosted page
paymentLink opens the shop's payment page for this invoice, in the language of your Accept-Language header. The customer chooses a payment method and pays there. The page handles payment retries. It does not collect required booking custom fields: if customFieldsCompletion.allRequiredComplete is false, use the hosted shop to complete the purchase before starting checkout.
Keep the invoice token from invoice.accessToken. You need it to follow the payment.
Follow the payment
Read the invoice with the invoice token, for example with the PaymentStatus query from the quickstart. Poll every few seconds while the customer pays and stop when the payment is final.
| Field | Meaning |
|---|---|
paymentState: PAID | The payment succeeded. |
paymentState: REQUIRES_PAYMENT | No successful payment yet. payments lists the attempts and their state. |
paymentIntentCreateError | Why a new payment cannot start: already paid, already in progress, or the order was canceled. |
payments.providerInitializationFailed | The payment provider could not start this attempt. The customer can open paymentLink again. |
request.state: CANCELED | The order was canceled, for example because the checkout hold ended before payment. Start a new cart. |
Rely on the invoice, not on the customer returning to a page: payments can succeed after the customer closes the browser.
Deliver tickets
After payment, request.shareablePublicTicketsUrl(format: PDF) returns a customer-facing link to the tickets page. Ticket activation can still be pending when this URL is present; the page makes the tickets available when activation finishes. Use format: PKPASSES for Apple Wallet passes when they are available.
Handle an expired checkout hold
When checkoutHold.secondsRemaining reaches zero before payment, KORONA Event cancels the order and releases its places. Call requestCancelExpiredCheckoutHold with the cart token as requestId, or the invoice token as invoiceToken, to release them immediately, then offer the customer a new cart.
Troubleshooting
| Problem | What to check |
|---|---|
Checkout fails with legalDocumentAcceptances | Every required document is accepted with its current publishedAt as legalDocumentVersion. |
A cart change fails with checkoutHold | The hold ended and the order was canceled. Start a new cart. |
| A response is missing after checkout | Read the cart with its token before trying again. When state is BOOKED, checkout already succeeded; read the invoice token from invoices { accessToken }. |