Skip to main content
Storefront API checkout uses the same buyer-facing checkout concepts as the Scalev-hosted storefront. A browser storefront can prepare delivery options, compute the checkout summary, create a guest order, read the public order, and create or reuse payment instructions without a merchant backend. The Storefront API is designed so your storefront can render its own payment page. Use the public order and payment responses to show buyer-facing instructions directly in your UI. payment_url is still returned as a hosted fallback for storefronts that have not implemented a method-specific renderer or for provider flows that must open a hosted payment page. Use only payment methods returned by GET /v3/stores/{store_id}/public/payment-methods. Storefront checkout does not return no_payment, and checkout endpoints reject it if submitted directly.

Guest checkout flow

  1. Read or create the guest cart and store X-Scalev-Guest-Token.
  2. Ask the buyer for delivery location and postal code when the cart contains physical items.
  3. Call POST /public/checkout/shipping-options.
  4. Let the buyer select a shipping option.
  5. Call POST /public/checkout/summary.
  6. Call POST /public/checkout with buyer details and the selected checkout fields.
  7. Read the order with GET /public/orders/{secret_slug}.
  8. For manual bank_transfer, request a transfer-proof upload URL, upload the image, and patch the order with the returned file URL.
  9. Call POST /public/orders/{secret_slug}/payment and render the returned payment data on your own payment page.

Shipping options

POST /v3/stores/{store_id}/public/checkout/shipping-options
Headers:
X-Scalev-Storefront-Api-Key: sfpk_...
X-Scalev-Guest-Token: <guest-token>
Send direct typed items, or omit items and let X-Scalev-Guest-Token select the guest cart:
{
  "items": [
    {
      "type": "variant",
      "variant_id": 494535,
      "quantity": 1
    }
  ],
  "destination": {
    "location_id": 9089,
    "postal_code": "10510"
  },
  "payment_method": "bank_transfer"
}
Direct items[] supports variants and bundle price options. Bundle price option items use { "type": "bundle_price_option", "bundle_price_option_id": 123, "quantity": 1 }. Response:
{
  "data": [
    {
      "courier_service_id": 123,
      "courier_code": "jne",
      "service_code": "REG",
      "name": "Regular",
      "cost": 12000,
      "etd": "2-3",
      "is_cod": false,
      "warehouse_unique_id": "warehouse_...",
      "courier_aggregator_code": null
    }
  ],
  "is_paginated": false
}
Digital-only carts return:
{
  "data": [],
  "is_paginated": false
}

Checkout summary

POST /v3/stores/{store_id}/public/checkout/summary
Send the selected shipping option. Scalev recomputes the shipping cost server-side and ignores any client-supplied shipping_cost.
{
  "destination": {
    "location_id": 9089,
    "postal_code": "10510"
  },
  "courier_service_id": 123,
  "warehouse_unique_id": "warehouse_...",
  "courier_aggregator_code": null,
  "payment_method": "bank_transfer"
}
Response fields match the existing checkout summary:
{
  "product_price": "125000.00",
  "shipping_cost": "12000",
  "other_income": "0",
  "other_income_name": "Biaya Lainnya",
  "gross_revenue": "137000.00"
}
Digital-only carts return shipping_cost: "0".

Discount code check

POST /v3/stores/{store_id}/public/discount-codes/check
Use a JSON body to validate a discount code before checkout:
{
  "code": "SAVE10",
  "items": [
    {
      "type": "variant",
      "variant_id": 494535,
      "quantity": 1
    }
  ],
  "destination": {
    "location_id": 9089,
    "postal_code": "10510"
  },
  "courier_service_id": 123,
  "warehouse_unique_id": "warehouse_abc",
  "courier_aggregator_code": null,
  "payment_method": "bank_transfer"
}
If direct items are supplied, the API uses them as the item source. Otherwise, provide X-Scalev-Guest-Token for the guest cart. Unknown or ineligible codes return a normal validation response with is_eligible: false and discount_code: null.

Public checkout

Create the order with:
POST /v3/stores/{store_id}/public/checkout
The endpoint accepts direct typed items, the guest cart referenced by X-Scalev-Guest-Token, or both. If both are supplied, direct items create the order and the referenced guest cart is cleared after success. Use the same selected checkout fields:
{
  "items": [
    {
      "type": "variant",
      "variant_id": 494535,
      "quantity": 1
    }
  ],
  "customer_name": "Customer Name",
  "customer_email": "customer@example.com",
  "customer_phone": "08123456789",
  "shipping_address": "Jl. Example 1",
  "shipping_location_id": 9089,
  "shipping_subdistrict": "Cempaka Putih",
  "shipping_postal_code": "10510",
  "courier_service_id": 123,
  "warehouse_unique_id": "warehouse_...",
  "courier_aggregator_code": null,
  "payment_method": "bank_transfer"
}
The checkout endpoint uses the selected courier service, warehouse, destination, payment method, and checkout items to recompute the shipping cost internally. Do not treat a client-supplied shipping_cost as the source of truth. On success, the response includes the created slim public order data, including secret_slug, public_order_url, payment_url, status, totals, the existing variants and bundle_price_options object maps, line items, shipping details, and payment fields. Internal order IDs, dashboard-only revenue fields, platform fees, payment-status history, and affiliate attribution are not returned. Use secret_slug to read or update the order. Use payment_url only as a hosted fallback if your storefront does not render the payment instructions itself.

Payment instructions

After checkout, call:
POST /v3/stores/{store_id}/public/orders/{secret_slug}/payment
The endpoint is idempotent. If payment instructions already exist, the response returns the order again with the existing buyer-facing payment data and URLs. Use this order of preference in a browser storefront:
  1. Render the order’s method-specific instructions from payment_method, sub_payment_method, epayment_provider, and pg_payment_info.
  2. For provider-hosted methods, open the provider URL from pg_payment_info when the provider requires a redirect.
  3. Use payment_url only as a fallback to the Scalev-hosted payment page when your storefront does not yet support that method or provider response.
  4. Poll the public order when the method needs gateway data that may not exist immediately.

Method rendering

The Scalev-hosted success page is the reference implementation for method-specific rendering. A Storefront API storefront can replace that page by applying the same pg_payment_info mappings in its own UI. The hosted success page first derives a small set of display fields from the current Xendit/default pg_payment_info. This is not a separate API response. It is a useful reference if your storefront wants one payment renderer instead of branching directly on raw provider payload fields:
{
  "qrString": "000201010212...",
  "qrImageUrl": "https://api.qrserver.com/v1/create-qr-code/?...",
  "vaNumber": "88081234567890",
  "vaAccountHolder": "Customer Name",
  "invoiceUrl": "https://provider.example/pay/...",
  "renderUrlAs": "button"
}
The actual source fields come from the order and payment response. For the current Xendit/default payload, map them this way:
Display valueSource field
qrStringpg_payment_info.qr_string or pg_payment_info.payment_method.qr_code.channel_properties.qr_string
vaNumberpg_payment_info.payment_method.virtual_account.channel_properties.virtual_account_number or pg_payment_info.payment_method.over_the_counter.channel_properties.payment_code
vaAccountHolderpg_payment_info.payment_method.virtual_account.channel_properties.customer_name or pg_payment_info.payment_method.over_the_counter.channel_properties.customer_name
invoiceUrlpg_payment_info.invoice_url or pg_payment_info.redirect_url
qrImageUrlGenerate a QR image from qrString in your frontend when you need a downloadable QR image.
For gopay, dana, linkaja, shopeepay, and ovo, choose an action URL from pg_payment_info.actions: mobile prefers url_type WEB or DEEPLINK; desktop prefers MOBILE or DEEPLINK. Set renderUrlAs to button for invoice, card, and all mobile views. For other desktop views, render the URL as a QR code. bank_transfer: Display the order total, manual bank transfer accounts, expiry countdown when enabled, and a payment-confirmation link to /o/{secret_slug}?showPaymentConfirmation=true. The hosted page gets bank accounts from the store payment_accounts where method is bank_transfer and the method is still active. pg_payment_info can be {}. For manual transfer proof, follow the same flow as the Scalev-hosted public order page:
  1. Let the buyer choose a bank account from store.payment_accounts where method is bank_transfer.
  2. Let the buyer upload a jpg, png, or webp proof image.
  3. Request upload metadata:
POST /v3/stores/{store_id}/public/orders/{secret_slug}/transfer-proof-upload
{
  "filename": "transfer-proof.png",
  "content_type": "image/png",
  "content_length": 12345
}
  1. Upload the file bytes directly to the returned upload_url with PUT and the same Content-Type.
  2. Patch the public order with the returned file_url:
PATCH /v3/stores/{store_id}/public/orders/{secret_slug}
{
  "transferproof_url": "https://files.scalev.com/uploads/.../transfer-proof.png",
  "transfer_time": "2026-05-15T04:00:00Z",
  "financial_entity_id": 12,
  "payment_account_holder": "PT Example",
  "payment_account_number": "1234567890"
}
  1. Refetch the order and show the submitted proof from transferproof_url.
cod: Display the COD success body and do not show a payment countdown. Payment creation may return an empty pg_payment_info. Virtual account (va): Render the virtual account bank/channel from sub_payment_method, the vaNumber, and the optional vaAccountHolder. Show a copy button and a payment tutorial link. Known bank codes include BRI, MANDIRI, PERMATA, BNI, BCA, MAYBANK, CIMB, BNC, DANAMON, BSI, AG, BJB, SAHABAT_SAMPOERNA, ARTAJASA, BMI, and BTN. QRIS (qris): Render a QR code from qrString. Also offer a QR download action from qrImageUrl and show mobile instructions for saving or screenshotting the QR image before paying in a QRIS app. If QR generation fails or qrString is missing, show a retry action that calls the public payment endpoint again. E-wallets except OVO (gopay, dana, linkaja, shopeepay): Use invoiceUrl from provider actions. On mobile, open it as a button/deep link. On desktop, render the invoiceUrl as a QR code when renderUrlAs is qr_code; otherwise open it as a button. If no URL can be mapped, send the buyer back to the public order page. OVO (ovo): Show an instruction to check the OVO app tied to the buyer phone number. The hosted success component does not render OVO through the same e-wallet QR component. Card (card) and invoice/hosted payment (invoice): Open the mapped invoiceUrl as a button/redirect. The hosted success page redirects automatically when invoiceUrl exists and renderUrlAs is button. Alfamart and Indomaret: Render vaNumber as No Pembayaran, show vaAccountHolder when present, and provide a copy action.

Polling and status checks

For e-payment methods (gopay, card, invoice, va, qris, ovo, dana, shopeepay, linkaja, alfamart, and indomaret), the hosted success page waits for pg_payment_info to appear by refetching the public order up to 15 times with a 1.5 second delay. For pending unpaid orders with card, invoice, shopeepay, dana, qris, linkaja, gopay, bank_transfer, indomaret, and alfamart, the hosted success page then refreshes the public order every 5 seconds while waiting for payment confirmation. For va, bank_transfer, qris, alfamart, and indomaret, it also shows a manual “check payment status” action.

Authenticated customer checkout

Customer checkout endpoints stay under:
/v3/stores/{store_id}/customers/me/checkout/*
They use Authorization: Bearer <customer_access_token>, not the Storefront API key. Addresses, payment methods, shipping options, and summary use the same checkout preparation fields as public checkout. Create the order with POST /v3/stores/{store_id}/customers/me/checkout; the request accepts direct typed items, cart_id, or both. The response uses the same public order shape returned by public checkout and public order reads, so every checkout completion response has the same order fields. Customer checkout summary uses the selected courier service, warehouse, destination, payment method, and either direct items or the referenced customer cart to recompute shipping cost server-side. A client-supplied shipping_cost is only a compatibility value and is not the authoritative amount.