FleetbaseFleetbase

Checkout

The Storefront checkout is a two-step API flow — `/checkouts/before` to initialize and produce a token, then `/checkouts/capture` to finalize the order after payment is confirmed.

Checkout

Checkout converts a cart into a paid order. Storefront uses a two-step flow:

Storefront app — checkout screen with delivery details, tip selection, and order total

  1. /checkouts/before — submit the cart, service quote, gateway, tip, and pickup/delivery flag. Storefront returns a token (and a Stripe clientSecret if the gateway is Stripe).
  2. /checkouts/capture — once payment is confirmed by the customer's frontend, finalize by sending back the same token. Storefront creates one or more Fleet-Ops orders.

This split exists so the frontend can confirm payment with the gateway SDK (which keeps card data out of Fleetbase) and Storefront only needs to verify the gateway-confirmed token at capture time.

Step 1 — /checkouts/before

POST /storefront/v1/checkouts/before
Authorization: Bearer store_your_store_key
Customer-Token: 1|VlKK7lZ...

{
  "cart": "cart_abc123",
  "gateway": "gateway_stripe_id",
  "serviceQuote": "service_quote_id",
  "customer": "customer_abc123",
  "pickup": false,
  "tip": 200,
  "deliveryTip": 100
}

Required:

  • cart — the cart's public id
  • gateway — the gateway public id selected by the customer
  • serviceQuote — the Service Quote id (omit for pickup orders)
  • customer — the customer's public id

Optional:

  • pickup — true for in-store pickup, defaults to delivery
  • tip — driver tip in minor currency units (e.g. 100 = $1.00 USD)
  • deliveryTip — separate delivery tip when delivery_tips_enabled
  • is_cod — true if the customer is paying cash on delivery

Returns:

{
  "token": "checkout_token_abc",
  "clientSecret": "pi_xxx_secret_yyy",
  "socket_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

When the Fleetbase instance has socket authentication enabled, the response also carries a socket_token: a short-lived realtime token that may subscribe only to this checkout's own checkout.{checkout public id} channel. Use it to follow the payment status (see Checkout Status Events). The field is absent on instances without socket authentication, where the channel can be subscribed to without a token.

For Stripe, the frontend uses clientSecret with the Stripe SDK to confirm the PaymentIntent (cards, Apple Pay, Google Pay). For QPay, the response includes the QPay invoice payload. For Cash on Delivery, no client-side payment is required.

Step 2 — /checkouts/capture

POST /storefront/v1/checkouts/capture
Authorization: Bearer store_your_store_key
Customer-Token: 1|VlKK7lZ...

{
  "token": "checkout_token_abc",
  "transactionDetails": { ... },
  "notes": "Leave at the front gate."
}

Required:

  • token — the token returned by before

Optional:

  • transactionDetails — gateway-specific confirmation payload
  • notes — order notes for the merchant

On success, Storefront finalizes the cart and creates a Fleet-Ops order (or one order per store, for multi-cart checkouts in a network). The response contains the created order(s).

The order's payment is recorded in meta.payment. Card and bank payments are recorded as paid; cash on delivery orders are recorded as unpaid until the order is completed and the cash collected. See Payment Status.

QPay Callback

QPay payments are completed by redirecting the customer to the bank app. After the bank confirms payment, QPay calls a public callback that Storefront uses to capture:

POST /storefront/v1/checkouts/capture-qpay

You don't call this directly — QPay does — but it must be reachable from QPay's network.

Checkout Status Events

While a QPay payment is pending, Storefront publishes the result on the realtime channel checkout.{checkout public id}. Connect with the socket_token from /checkouts/before (in the SocketCluster handshake or via socket.authenticate(socket_token)) and subscribe to that channel. Each event payload has this shape:

{
  "checkout": "checkout_abc123",
  "status": "completed",
  "order": { "id": "order_abc123", "...": "..." },
  "error": null
}
FieldDescription
checkoutThe checkout's public id
statuspaid (payment received), completed (order captured), or failed
orderThe created order once the checkout is captured, otherwise null
errorA message when status is failed, otherwise null

The checkout token cannot subscribe to any other channel. To follow the order afterwards, use a customer socket token (POST /storefront/v1/customers/socket-token) and the order.{order id} channel.

Pickup Orders

If the customer selects in-store pickup:

  • No service quote is required (the serviceQuote field can be omitted)
  • The order's activity flow uses the pickup_ready status instead of dispatching a driver
  • The customer-facing app polls or subscribes to receive the ready notification, then collects in-store

Pickup is available when the store/network has options.pickup_enabled toggled on.

Promotions

When the checkout is created, Storefront prices the cart's promotions — automatic promotions plus any promo codes applied to the cart — and subtracts the discount from the amount charged. Codes can also be passed to before (and to the Stripe payment intent update) directly:

{
  "cart": "cart_abc123",
  "gateway": "stripe",
  "customer": "customer_abc123",
  "promo_codes": ["SUMMER25"]
}

promo_codes accepts an array or a comma-separated string; promo_code and discount_code are accepted for a single code.

  • The discounted amount is what Stripe's PaymentIntent, the QPay invoice (which gets a Discount line) and cash orders are charged, and capture verifies the payment against the same amount.
  • Percentage tips are calculated on the subtotal before the discount.
  • Each promotion use is reserved for the checkout, so limits and budgets can't be overspent by concurrent checkouts. Abandoned checkouts release their reservation after 60 minutes.
  • The created order's meta.discount holds the total discount and meta.promotions lists the promotions applied (promotion, name, type, code, amount, delivery_amount). The transaction gets a discount line, stored as a positive amount with meta.direction: credit. In a multi-store network checkout, each store's order carries its share of the discount.

If a code can't be applied — invalid, expired, used up, or the cart doesn't meet its conditions — before returns 400 with the reason in promotions.rejected, so the customer is never charged without a discount they expected. See Promo Codes for the reasons.

Required Minimum Amount

A store or network can require a minimum cart subtotal. Toggle options.required_checkout_min and set options.required_checkout_min_amount. If the cart subtotal is below the threshold, before returns a validation error.

Stock and Booking Checks

When the checkout is initialized, Storefront also:

  • holds the stock of every cart line whose product tracks stock, for 15 minutes. Capture takes the held stock. If any line is short, nothing is held and before returns 422 with code: "cart_items_unavailable" and the short lines in items (each with the quantity still available).
  • checks that a booked service's slot still has room. If it filled up, before returns 422 with code: "booking_slot_unavailable". See Bookings.

These checks apply to cash, Stripe and QPay checkouts and to orders placed from the console.

Error Cases

The /before step validates that:

  • The cart exists and is not expired
  • The selected gateway is configured for the store/network
  • The customer is authenticated (when authentication is required by your flow)
  • The cart subtotal meets the minimum order amount (if configured)
  • Every promo code can be applied, and the promotions still have uses and budget left
  • Products that track stock have enough at the line's location, and booked slots still have room (see above)

The /capture step validates that:

  • The token is valid and not yet used
  • The gateway has confirmed the payment (for Stripe, that the PaymentIntent succeeded)

Handle errors by surfacing the API message to the customer and letting them retry without losing cart state.

Checkout | Fleetbase