Skip to content

Confirm the payment

The checkout_session.succeeded browser event (Payment components) and the status=success redirect parameter (Hosted checkout) are convenience signals — neither is proof of payment. Always confirm server-side before fulfilling, and treat the Checkout Session as the source of truth.

Retrieve the Checkout Session

Retrieve a single Checkout Session by its ID in the CheckoutSessionId header. No request body is required:

curl https://api.shift4.com/api/rest/v1/checkoutsessions/retrieve \
  -H "AccessToken: YOUR_ACCESS_TOKEN" \
  -H "InterfaceVersion: 1.0" \
  -H "InterfaceName: YourApp" \
  -H "CompanyName: YourCompany" \
  -H "CheckoutSessionId: chse_wMCUGVZgpf45fWHzKbYWMKmZ"

A completed Checkout Session has status: "paid" and a populated lastTransaction — the processor response for the resulting charge:

{
  "result": [
    {
      "dateTime": "2026-05-20T10:44:02Z",
      "checkoutSession": {
        "id": "chse_wMCUGVZgpf45fWHzKbYWMKmZ",
        "action": "payment",
        "status": "paid",
        "expiresAt": "2026-05-20T11:30:00Z",
        "vendorReference": "vendor_reference",
        "lineItems": [
          {
            "product": {
              "name": "Test Product",
              "amount": { "total": 12.99 },
              "currencyCode": "USD"
            },
            "quantity": 1
          }
        ],
        "lastTransaction": {
          "dateTime": "2026-05-20T03:44:01.000-07:00",
          "card": {
            "type": "card_type",
            "token": { "value": "card_token_value" },
            "number": "XXXXXXXXXXXX4242",
            "expirationDate": 1299
          },
          "amount": { "total": 12.99 },
          "merchant": { "mid": "merchant_mid" },
          "transaction": {
            "invoice": "invoice_number",
            "saleFlag": "S",
            "responseCode": "A",
            "authorizationCode": "authorization_code",
            "retrievalReference": "retrieval_reference",
            "hostResponse": {
              "reasonCode": "00",
              "reasonDescription": "Transaction Approved"
            },
            "cardOnFile": {
              "type": "S01",
              "transactionId": "transaction_id"
            }
          }
        }
      }
    }
  ]
}

A Checkout Session that never completes ends in one of two terminal statuses instead: expired (its lifetime elapsed before payment) or failed (the maximum number of failed payment attempts was reached).

To review or reconcile activity in bulk rather than one Checkout Session at a time, list Checkout Sessions with /checkoutsessions/list.

Key fields on a paid Checkout Session

Inside lastTransaction, these are the fields you'll reach for most. For the complete response schema — every field on the Checkout Session and its lastTransaction — see the Retrieve Checkout Session API reference.

Field Description
checkoutSession.statuspaid confirms the payment completed successfully.
transaction.responseCodeA = approved, D = declined.
transaction.invoiceInvoice number assigned by the processor for this transaction — the key you need for a later capture, refund, or void.
transaction.authorizationCodeAuthorization code returned by the card network.
transaction.cardOnFile.transactionIdNetwork transaction ID for card-on-file linkage.
transaction.saleFlagS = sale, C = credit (refund).
card.numberMasked PAN of the card used.
checkoutSession.vendorReferenceYour internal reference, as supplied at create time.

Webhooks

For a source of truth that doesn't depend on the customer's browser, consume webhooks instead of (or alongside) polling retrieve.

How it works

Set webhookUrl when you create the Checkout Session and Shift4 sends an HTTP POST to that URL for each Checkout Session event — checkout_session_created, checkout_session_completed, and checkout_session_expired — carrying the full object. Use event.type to route (the checkoutSession below is abbreviated):

{
  "dateTime": "2026-06-26T10:15:30.123456Z",
  "event": {
    "type": "checkout_session_created"
  },
  "paymentLink": null,
  "invoice": null,
  "metadata": {
    "orderId": "ORD-10042"
  },
  "checkoutSession": {
    "id": "chse_wMCUGVZgpf45fWHzKbYWMKmZ",
    "staticFields": {
      "department": "Sales"
    }
  }
}

Acknowledging and retries

Respond with any 2xx within 90 seconds to acknowledge a webhook. Anything else — a non-2xx status or a timeout — counts as undelivered, and Shift4 retries indefinitely with exponential backoff (after 1, 2, 3, 8, 15, 30, and 60 minutes, then hourly). Return the 2xx as soon as you've recorded the event and do slower processing afterwards.

Webhooks are delivered in order across your whole account — the sequence in which the objects were created, regardless of type. Because delivery is sequential, an undelivered webhook halts the queue: no later webhooks are sent until it succeeds, at which point delivery resumes immediately.

Delivery is at least once: a dropped connection before your acknowledgement can cause the same event to arrive twice. There is no idempotency key, so make your handler idempotent — dedupe on the Checkout Session id and event.type.

Requests carry no authentication header, so allowlist Shift4's sending endpoints if you need to verify origin. Each webhook carries the full object, in the same format as the API response.

Webhooks are sent per Checkout Session, to the webhookUrl you set when you create it.

Note that opening a Payment Link automatically creates a Checkout Session — so Payment Links produce these same Checkout Session webhooks too. For those sessions the link's own webhookUrl is used, and it takes precedence over any webhookUrl on the session itself.

API reference: Notification.

After the payment

The charge behind a paid Checkout Session is managed through the standard transaction operations, keyed by lastTransaction.transaction.invoice. An auth-only Checkout Session (capture: false) is finalised with a capture, and a settled payment is returned with a refund — see Capturing Sales and Processing Refunds.

Next steps