HTML Mode runtime

Use the window.Scalev runtime methods available inside Scalev HTML Mode pages.

Scalev HTML Mode pages can use window.Scalev from browser JavaScript. The runtime gives your page safe page context, selected store context, checkout helpers, analytics forwarding, form prefill helpers, and diagnostics while keeping private API tokens server-side.

This page is written for developers and AI agents that generate HTML Mode files. Use these methods instead of calling private Scalev URLs directly.

Availability

The runtime is inserted near body-close, before the trailing top-level script block. Earlier authored scripts should wait for DOMContentLoaded before accessing window.Scalev. Keep third-party script order and attributes intact; external resources also need the appropriate page CSP permission.

Editor previews simulate commerce, location, and prefill behavior. They do not create real orders or send platform analytics. Simulated orders include redirectUrl: "#scalev-preview-order".

const scalev = window.Scalev;

if (!scalev) {
  // Keep the page usable while runtime data loads.
}

Most methods are asynchronous and return a Promise. Scalev.data.get() and Scalev.diagnostics.get() are synchronous.

Runtime method payloads and responses use camelCase object keys. The runtime handles the backend casing internally, so page JavaScript should not send snake_case keys. Runtime methods also return the JavaScript-visible result directly. They remove backend collection containers such as data and is_paginated before returning arrays.

String enum values and provider-owned blobs keep their original values. For example, success_page, direct_to_whatsapp, analytics parameters, metadata, and customData are not renamed.

Store context

When Store Context is selected, Scalev.data.get().store includes only the selected store summary, products, and bundle price options. Treat it as the page-specific checkout catalog.

When HTML Checkout Page is selected, Scalev.data.get().afterCheckout describes the selected after-checkout type and its public destination configuration. It is editor state, not the resolved destination for an order. After checkout, use order.redirectUrl from Scalev.checkout.createOrder() instead of rebuilding a URL from this configuration.

When Store Context is unselected, treat store as unavailable. Build a regular landing page and rely only on page-safe methods such as Scalev.data.get(), Scalev.analytics.track(), and Scalev.prefill.get().

Security rules

  • Keep API keys, business-user JWTs, storefront API keys, customer tokens, cookies, and credentials out of page JavaScript.
  • Use documented window.Scalev methods instead of private Scalev URLs or private APIs.
  • Create orders only after a visitor intentionally submits a form.
  • Prefer Scalev.checkout.createOrder() on submit; it validates the payload before the order is persisted.
  • Use only selected products, bundles, prices, inventory, payment methods, shipping options, and customer identity values present in the runtime data.

Scalev.data.get()

Returns public page data injected into the rendered page.

const data = Scalev.data.get();

Response example:

{
  "page": {
    "id": 1,
    "uniqueId": "pageUid",
    "username": "brand"
  },
  "store": {
    "id": 1,
    "uniqueId": "storeUid",
    "name": "Main Store",
    "paymentMethodOptions": [
      {
        "value": "bt:BCA:paymentAccountUid",
        "display": "Bank Central Asia",
        "logoUrl": "https://cdn.scalev.com/icons/BT_BCA.png"
      },
      {
        "value": "cod",
        "display": "COD",
        "logoUrl": "https://cdn.scalev.com/icons/cod.png"
      }
    ],
    "products": [
      {
        "id": 1,
        "name": "Product",
        "variants": [
          {
            "id": 10,
            "uniqueId": "variantUid",
            "name": "Default",
            "price": 99000,
            "availableQty": 12
          }
        ]
      }
    ],
    "bundlePriceOptions": [
      {
        "id": 20,
        "uniqueId": "bundleOptionUid",
        "name": "Bundle",
        "price": 179000
      }
    ]
  },
  "afterCheckout": {
    "type": "other_page",
    "otherPagePath": "/thank-you"
  }
}

Use store.paymentMethodOptions to render payment choices. Render display, show logoUrl, and submit the selected value as paymentMethod. Scalev omits e-payment methods that cannot be executed with a verified fee schedule when the store passes provider transaction fees to the customer; do not recreate hidden options from saved store configuration.

Payment option values are already flattened:

  • va:BRI means virtual account for BRI.
  • bt:BCA:paymentAccountUid means manual bank transfer for the represented BCA account.
  • Regular values such as cod, qris, invoice, alfamart, or gopay keep their original values.

Location methods

Use these methods for address and shipping forms.

const provinces = await Scalev.location.provinces();
const cities = await Scalev.location.cities(provinces[0].id);
const subdistricts = await Scalev.location.subdistricts(cities[0].id);
const postalCodes = await Scalev.location.postalCodes(subdistricts[0].id);

provinces, cities, and subdistricts accept optional query objects:

const provinces = await Scalev.location.provinces({ search: "Jawa" });

Province and city responses use { id, name }. Subdistrict responses use id and name for the subdistrict, plus cityName, provinceName, and display for address labels.

Response examples:

[
  {
    "id": 31,
    "name": "DKI Jakarta"
  }
]
[
  {
    "id": 3171,
    "name": "Kota Jakarta Pusat"
  }
]
[
  {
    "id": 9090,
    "name": "Gambir",
    "cityName": "Kota Jakarta Pusat",
    "provinceName": "DKI Jakarta",
    "display": "Gambir, Kota Jakarta Pusat, DKI Jakarta"
  }
]
[
  {
    "postalCode": "10110"
  }
]

Scalev.checkout.validateDiscount(payload)

Validates a discount code for a checkout payload.

const result = await Scalev.checkout.validateDiscount({
  discountCode: "PROMO10",
  grossRevenue: 179000,
  netProductPrice: 179000,
  shippingCost: 0,
  paymentMethod: store.paymentMethodOptions?.[0]?.value || "cod"
});

Response example:

{
  "discountCode": {
    "code": "PROMO10",
    "appliedTo": "product_price",
    "amountType": "fixed",
    "amount": 10000,
    "isEnabled": true
  },
  "eligibilityDetails": {
    "isEnabled": true,
    "isExpiryCheck": true,
    "isUsageLimitCheck": true,
    "isMinimumRevenueCheck": true,
    "isLimitedToPagesCheck": true,
    "isLimitedToPaymentMethodsCheck": true,
    "isPageDiscountActive": true,
    "isAmountNotZero": true
  },
  "isEligible": true,
  "discountCodeDiscount": 10000
}

Scalev.checkout.shippingOptions(payload)

Returns shipping options for the selected items, destination, and payment method. Use this as the only shipping lookup method.

const shippingOptions = await Scalev.checkout.shippingOptions({
  paymentMethod: selectedPaymentOption.value,
  destination: {
    subdistrictId: selectedSubdistrictId,
    postalCode: selectedPostalCode
  },
  items: [
    {
      type: "product",
      variantUniqueId: firstVariant.uniqueId,
      quantity: 1
    }
  ]
});

Pass the chosen option back as one shipping field in estimateSummary and createOrder. The runtime prepares the checkout request from that option. Options include logoUrl when a courier code is available.

Response example:

[
  {
    "courierServiceId": 48,
    "courierCode": "anteraja",
    "serviceCode": "ND",
    "name": "Next Day",
    "cost": 15300,
    "etd": "1",
    "isCod": false,
    "warehouseUniqueId": "warehouseUid",
    "courierAggregatorCode": null,
    "logoUrl": "https://cdn.scalev.com/assets/images/kurir/anteraja.png"
  }
]

Recommended method payload:

{
  "items": [
    { "type": "product", "variantUniqueId": "variantUid", "quantity": 1 },
    {
      "type": "bundle",
      "bundlePriceOptionUniqueId": "bundleOptionUid",
      "quantity": 1
    }
  ],
  "destination": { "subdistrictId": 12345, "postalCode": "10110" },
  "paymentMethod": "cod"
}

Scalev.checkout.estimateSummary(payload)

Returns a checkout summary for pages that show a buyer-facing breakdown before submit.

This method is optional. Pass the same item, destination, payment, and shipping that you will send to Scalev.checkout.createOrder(payload).

The result does not replace order creation validation. Scalev still validates and applies final shipping, discount, extra-fee, and total amounts during order creation before the order is persisted.

const shipping = shippingOptions[0];

const payload = {
  destination: { subdistrictId: 12345, postalCode: "10110" },
  items: [
    { type: "product", variantUniqueId: firstVariant.uniqueId, quantity: 1 },
    {
      type: "bundle",
      bundlePriceOptionUniqueId: firstBundlePriceOption.uniqueId,
      quantity: 1
    }
  ],
  paymentMethod: selectedPaymentOption.value,
  shipping
};

const summary = await Scalev.checkout.estimateSummary(payload);

Recommended method payload:

{
  "items": [
    { "type": "product", "variantUniqueId": "variantUid", "quantity": 1 },
    {
      "type": "bundle",
      "bundlePriceOptionUniqueId": "bundleOptionUid",
      "quantity": 1
    }
  ],
  "destination": { "subdistrictId": 12345, "postalCode": "10110" },
  "paymentMethod": "cod",
  "shipping": {
    "courierServiceId": 48,
    "courierCode": "anteraja",
    "serviceCode": "ND",
    "name": "Next Day",
    "cost": 15300,
    "etd": "1",
    "isCod": false,
    "warehouseUniqueId": "warehouseUid",
    "courierAggregatorCode": null,
    "logoUrl": "https://cdn.scalev.com/assets/images/kurir/anteraja.png"
  }
}

Response example:

{
  "productPrice": 268000,
  "shippingCost": 15300,
  "otherIncome": 6500,
  "otherIncomeName": "Biaya Penanganan",
  "serviceFee": 2500,
  "grossRevenue": 292300
}

Use estimateSummary when the checkout UI displays totals before submit. Refresh it whenever the selected items, quantities, destination, shipping option, discount, or payment method changes. Render otherIncome with otherIncomeName when it is non-zero, and render one Biaya layanan or Service fee row from serviceFee when it is non-zero.

otherIncome remains the store's existing Other Charges concept. serviceFee is one combined customer-facing fee for direct e-payment. Both can be non-zero in the same checkout. Scalev calculates Other Charges first, then uses the checkout amount plus otherIncome as the base for the Service Fee, and grossRevenue is the displayed and charged total.

Both fees are calculated by Scalev from the store's saved Service Fee and Other Charges settings. The page does not send a fee policy, a fee amount, or a fee quote, and there is no fee field to echo back into createOrder. Order creation recalculates both fees from the same store settings, so treat every summary value as a preview for the buyer. serviceFee is zero when the store charges no customer fee for the selected method, and it is always zero for cod, other non-e-payment methods, and payment_link, while saved Other Charges rules still apply.

A payment_link order is always created with otherIncome of zero, whatever the request contains. The Payment Link snapshots the store's Other Charges configuration and quotes both fees on the Scalev-hosted payment page once the buyer picks a concrete method.

Scalev.checkout.createOrder(payload)

Creates a real public order and validates the payload. Call this only after a visitor intentionally submits the form.

async function submitOrder(event) {
  event.preventDefault();

  const data = Scalev.data.get();
  const store = data.store;
  const firstVariant = store.products[0].variants[0];
  const selectedPaymentOption = store.paymentMethodOptions[0];
  const selectedSubdistrictId = 12345;
  const selectedPostalCode = "10110";
  const items = [
    { type: "product", variantUniqueId: firstVariant.uniqueId, quantity: 1 }
  ];
  const destination = {
    subdistrictId: selectedSubdistrictId,
    postalCode: selectedPostalCode
  };
  const shippingOptions = await Scalev.checkout.shippingOptions({
    paymentMethod: selectedPaymentOption.value,
    destination,
    items
  });
  const shipping = shippingOptions[0];

  const payload = {
    customer: {
      name: event.currentTarget.elements.customerName.value,
      phone: event.currentTarget.elements.customerPhone.value,
      email: event.currentTarget.elements.customerEmail.value
    },
    destination: {
      address: event.currentTarget.elements.address.value,
      ...destination
    },
    items,
    paymentMethod: selectedPaymentOption.value,
    shipping
  };

  const order = await Scalev.checkout.createOrder(payload);
  return order;
}

Supported payload fields:

  • Customer object: customer.name, customer.phone, customer.email
  • Destination object: destination.address, destination.subdistrictId, destination.postalCode
  • Payment field: paymentMethod
  • Items list: items[] with { type: "product", variantUniqueId, quantity } or { type: "bundle", bundlePriceOptionUniqueId, quantity }
  • Discount field: discountCode
  • Optional post-payment override: isPostPaymentRedirectEnabled and postPaymentRedirectUrl

The post-payment fields are creation-only API inputs; Scalev does not add them as controls to your public form. Omit both fields to inherit the Checkout Page setting. Send isPostPaymentRedirectEnabled: false to force Scalev's hosted success flow for that order. Send isPostPaymentRedirectEnabled: true with an absolute HTTPS postPaymentRedirectUrl to replace the page setting for that order. Scalev snapshots the result, so an order update cannot change it later.

Do not submit top-level product or shipping discount fields. Product price and item-level adjustments belong on items[] when needed. Use validateDiscount to preview eligible discount code amounts, then pass the selected code as discountCode to createOrder.

For shipping selection, call Scalev.checkout.shippingOptions(payload), let the visitor choose one option, then pass that whole option as shipping into summary and order calls.

Recommended method payload:

{
  "customer": {
    "name": "Customer Name",
    "phone": "08123456789",
    "email": "[email protected]"
  },
  "destination": {
    "address": "Customer address",
    "subdistrictId": 12345,
    "postalCode": "10110"
  },
  "items": [
    { "type": "product", "variantUniqueId": "variantUid", "quantity": 1 }
  ],
  "paymentMethod": "cod",
  "shipping": {
    "courierServiceId": 48,
    "courierCode": "anteraja",
    "serviceCode": "ND",
    "name": "Next Day",
    "cost": 15300,
    "etd": "1",
    "isCod": false,
    "warehouseUniqueId": "warehouseUid",
    "courierAggregatorCode": null,
    "logoUrl": "https://cdn.scalev.com/assets/images/kurir/anteraja.png"
  },
  "discountCode": "PROMO10",
  "eventSourceUrl": "https://brand.myscalev.com/page",
  "utmSource": "ads"
}

For payment selection, render store.paymentMethodOptions[].display, render payment images from store.paymentMethodOptions[].logoUrl, and submit only the selected option value as paymentMethod.

Response example:

{
  "orderId": "260601HPQSPBX",
  "status": "pending",
  "paymentStatus": "unpaid",
  "secretSlug": "orderSecretSlug",
  "redirectUrl": "https://brand.myscalev.com/o/orderSecretSlug/success",
  "publicOrderUrl": "https://brand.myscalev.com/o/orderSecretSlug",
  "paymentUrl": null,
  "productPrice": "90000.00",
  "productDiscount": "0.00",
  "shippingCost": "15300.00",
  "shippingDiscount": "0.00",
  "discountCodeDiscount": "10000.00",
  "otherIncome": "0.00",
  "otherIncomeName": "Biaya Lainnya",
  "serviceFee": "0.00",
  "grossRevenue": "95300.00",
  "paymentMethod": "bank_transfer",
  "paymentAccount": {
    "uniqueId": "paymentAccountUid",
    "financialEntityName": "Bank Central Asia",
    "accountNumber": "1234567890"
  },
  "courierService": {
    "id": 48,
    "name": "Next Day",
    "courier": {
      "name": "AnterAja",
      "code": "anteraja"
    }
  },
  "variants": {},
  "bundlePriceOptions": {
    "Bundle": {
      "bundlePriceOptionUniqueId": "bundleOptionUid",
      "quantity": 1
    }
  },
  "handlerPhone": null,
  "chatMessage": null
}

After order creation

createOrder returns the order directly and does not navigate. The Scalev API resolves the immediate destination as redirect_url; the runtime exposes it as order.redirectUrl. This covers the configured After Checkout destination and payment-specific behavior, including PayLink and WhatsApp. If the configured destination cannot be resolved, the server returns the Scalev-hosted payment-instructions page (/o/{secret_slug}/success) as order.redirectUrl.

const order = await Scalev.checkout.createOrder(payload);
const target = typeof order.redirectUrl === "string"
  ? order.redirectUrl.trim()
  : "";

if (target) {
  if (window.self !== window.top) {
    // Use the embedding parent's exact origin when you control it.
    window.parent.postMessage(target, "*");
  } else {
    window.location.assign(target);
  }
} else {
  // Handle older or unexpected responses without a redirect. Offer the returned
  // order.publicOrderUrl as a link; do not submit the order again.
  showOrderCreated(order);
}

Open the returned URL unchanged. Do not derive the destination from afterCheckout, infer it from a payment-method list, re-encode a WhatsApp message, or append the current query string. order.paymentUrl (the API's order-level payment_url) is deprecated and returned for backward compatibility only. Always use order.redirectUrl for checkout navigation, including PayLink. If an older or unexpected response has a missing or empty redirectUrl, the order can still have been created successfully; keep the buyer on the page, show a confirmation, and offer order.publicOrderUrl when available. Do not fall back to paymentUrl. This deprecation does not apply to provider URL fields inside a raw gateway payload.

This immediate redirect is separate from the post-payment destination snapshot. The page's post-payment setting stays private; hosted Scalev pages use the order's snapshot only after its payment status becomes paid or settled. Neither redirect proves payment. Provision external access from a verified payment.received webhook.

See HTML Mode checkout success types for the destination types, embedding behavior, and a reusable navigation helper.

Error handling

Runtime methods reject the Promise with a JavaScript Error. Use error.message for UI copy. Use error.status, error.data, and Scalev.diagnostics.get() only for debugging.

try {
  const order = await Scalev.checkout.createOrder(payload);
  return order;
} catch (error) {
  showFormError(error.message || "Order creation failed.");
}

Scalev.analytics.track(provider, payload)

Forwards configured advertising-provider events through Scalev. These events are separate from Web Analytics reports; this method does not create a first-party page view. Use the Web Analytics reporting API from your backend to read hosted-page traffic.

await Scalev.analytics.track("facebook", {
  firstName: "Customer",
  phone: "08123456789",
  variants: [{ uniqueId: "variantUid", quantity: 1, price: 179000 }],
  events: [
    {
      eventName: "Lead",
      parameters: { value: 179000, currency: "IDR" }
    }
  ]
});

Supported provider names:

  • facebook
  • tiktok
  • kwai

Response example:

null

If no provider is passed, the runtime skips the call without contacting Scalev:

{
  "skipped": true,
  "reason": "missing_analytics_provider"
}

If the analytics request fails, the checkout flow should continue and the runtime returns:

{
  "skipped": true,
  "reason": "analytics_request_failed"
}

Scalev.prefill.get()

Reads safe browser-scoped form prefill data.

const prefill = await Scalev.prefill.get();
const customerName = prefill.customer?.name || "";
const customerPhone = prefill.customer?.phone || "";
const destinationAddress = prefill.destination?.address || "";

Response example:

{
  "customer": {
    "name": "Customer Name",
    "phone": "08123456789",
    "email": "[email protected]"
  },
  "destination": {
    "address": "Jl. Contoh No. 1",
    "subdistrictId": 9104,
    "postalCode": "12250"
  }
}

Scalev.prefill.save(form, metadata)

Persists safe browser-scoped form prefill data in the encrypted scv_fd cookie. This is handled locally by fe-public and does not call a Storefront or page runtime API endpoint.

await Scalev.prefill.save(
  {
    customer: {
      name: "Customer Name",
      phone: "08123456789",
      email: "[email protected]"
    },
    destination: {
      address: "Jl. Contoh No. 1",
      subdistrictId: 9104,
      postalCode: "12250"
    }
  },
  {
    source: "checkout_form"
  }
);

Response example:

{
  "saved": true
}

Scalev.diagnostics.get()

Returns recent runtime diagnostics for debugging.

const diagnostics = Scalev.diagnostics.get();

Response example:

[
  {
    "level": "error",
    "code": "runtime_request_failed",
    "message": "Scalev runtime request failed.",
    "details": {
      "path": "/pages/pageUid/orders",
      "status": 422,
      "response": {
        "message": "customer.phone: can't be blank"
      }
    },
    "timestamp": "2026-06-01T08:00:00.000Z"
  }
]

Did this page help you?