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
- Read or create the guest cart and store
X-Scalev-Guest-Token. - Ask the buyer for delivery location and postal code when the cart contains physical items.
- Call
POST /public/checkout/shipping-options. - Let the buyer select a shipping option.
- Call
POST /public/checkout/summary. - Call
POST /public/checkoutwith buyer details and the selected checkout fields. - Read the order with
GET /public/orders/{secret_slug}. - For manual
bank_transfer, request a transfer-proof upload URL, upload the image, and patch the order with the returned file URL. - Call
POST /public/orders/{secret_slug}/paymentand render the returned payment data on your own payment page.
Shipping options
items and let X-Scalev-Guest-Token select the guest cart:
items[] supports variants and bundle price options. Bundle price option items use { "type": "bundle_price_option", "bundle_price_option_id": 123, "quantity": 1 }.
Response:
Checkout summary
shipping_cost.
shipping_cost: "0".
Discount code check
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: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:
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:- Render the order’s method-specific instructions from
payment_method,sub_payment_method,epayment_provider, andpg_payment_info. - For provider-hosted methods, open the provider URL from
pg_payment_infowhen the provider requires a redirect. - Use
payment_urlonly as a fallback to the Scalev-hosted payment page when your storefront does not yet support that method or provider response. - 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 samepg_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:
| Display value | Source field |
|---|---|
qrString | pg_payment_info.qr_string or pg_payment_info.payment_method.qr_code.channel_properties.qr_string |
vaNumber | pg_payment_info.payment_method.virtual_account.channel_properties.virtual_account_number or pg_payment_info.payment_method.over_the_counter.channel_properties.payment_code |
vaAccountHolder | pg_payment_info.payment_method.virtual_account.channel_properties.customer_name or pg_payment_info.payment_method.over_the_counter.channel_properties.customer_name |
invoiceUrl | pg_payment_info.invoice_url or pg_payment_info.redirect_url |
qrImageUrl | Generate a QR image from qrString in your frontend when you need a downloadable QR image. |
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:
- Let the buyer choose a bank account from
store.payment_accountswheremethodisbank_transfer. - Let the buyer upload a
jpg,png, orwebpproof image. - Request upload metadata:
- Upload the file bytes directly to the returned
upload_urlwithPUTand the sameContent-Type. - Patch the public order with the returned
file_url:
- 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: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.
