> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scalev.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Runtime HTML Mode

> Gunakan method window.Scalev yang tersedia di halaman HTML Mode Scalev.

Halaman HTML Mode Scalev dapat memakai `window.Scalev` dari JavaScript browser. Runtime ini memberi konteks halaman yang aman, konteks store terpilih, helper checkout, forwarding analytics, helper prefill form, dan diagnostics tanpa mengekspos token API privat ke browser.

Halaman ini ditulis untuk developer dan AI agent yang membuat file HTML Mode. Gunakan method ini daripada memanggil URL Scalev privat secara langsung.

## Ketersediaan

Runtime tersedia di halaman HTML Mode yang sudah dirender. Di preview editor lokal, beberapa method dapat mengembalikan data preview.

```js theme={null}
const scalev = window.Scalev;

if (!scalev) {
  // Jaga halaman tetap bisa dipakai saat data runtime dimuat.
}
```

Sebagian besar method asynchronous dan mengembalikan `Promise`. `Scalev.data.get()` dan `Scalev.diagnostics.get()` bersifat synchronous.

Payload dan response method runtime memakai key object camelCase. Runtime menangani casing backend secara internal, jadi JavaScript halaman tidak perlu mengirim key snake\_case. Runtime method juga mengembalikan hasil yang terlihat oleh JavaScript secara langsung. Container backend seperti `data` dan `is_paginated` dihapus sebelum array dikembalikan.

String enum dan blob milik provider tidak diubah. Contohnya `success_page`, `direct_to_whatsapp`, analytics `parameters`, `metadata`, dan `customData` tetap memakai nilai aslinya.

## Konteks store

Saat Store Context dipilih, `Scalev.data.get().store` berisi ringkasan store terpilih, produk, dan bundle price option yang dipilih. Perlakukan data ini sebagai katalog checkout khusus halaman.

Saat HTML Checkout Page dipilih, `Scalev.data.get().afterCheckout` berisi tipe sukses setelah checkout yang dipilih dan konfigurasi tujuan publiknya.

Saat Store Context tidak dipilih, perlakukan `store` sebagai data yang tidak tersedia. Buat landing page biasa dan gunakan hanya method aman seperti `Scalev.data.get()`, `Scalev.analytics.track()`, dan `Scalev.prefill.get()`.

## Aturan keamanan

* Jangan taruh API key, JWT business user, storefront API key, customer token, cookie, atau credential di JavaScript halaman.
* Gunakan method `window.Scalev` yang terdokumentasi, bukan URL Scalev privat atau API privat.
* Buat order hanya setelah pengunjung sengaja submit form.
* Utamakan `Scalev.checkout.createOrder()` saat submit; method ini memvalidasi payload sebelum order disimpan.
* Gunakan hanya produk, bundle, harga, inventory, payment method, opsi pengiriman, dan identitas customer yang ada di runtime data.

## `Scalev.data.get()`

Mengembalikan data publik halaman yang diinjeksi ke halaman yang dirender.

```js theme={null}
const data = Scalev.data.get();
```

Contoh response:

```json theme={null}
{
  "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"
  }
}
```

Gunakan `store.paymentMethodOptions` untuk render pilihan payment. Render `display`, tampilkan `logoUrl`, dan submit `value` yang dipilih sebagai `paymentMethod`.

Value payment option sudah diratakan:

* `va:BRI` berarti virtual account BRI.
* `bt:BCA:paymentAccountUid` berarti bank transfer manual untuk akun BCA tersebut.
* Value seperti `cod`, `qris`, `invoice`, `alfamart`, atau `gopay` tetap memakai nilai aslinya.

## Method lokasi

Gunakan method ini untuk form alamat dan pengiriman.

```js theme={null}
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`, dan `subdistricts` menerima query object opsional:

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

Response province dan city memakai `{ id, name }`. Response subdistrict memakai `id` dan `name` untuk subdistrict, ditambah `cityName`, `provinceName`, dan `display` untuk label alamat.

Contoh response:

```json theme={null}
[
  {
    "id": 31,
    "name": "DKI Jakarta"
  }
]
```

```json theme={null}
[
  {
    "id": 3171,
    "name": "Kota Jakarta Pusat"
  }
]
```

```json theme={null}
[
  {
    "id": 9090,
    "name": "Gambir",
    "cityName": "Kota Jakarta Pusat",
    "provinceName": "DKI Jakarta",
    "display": "Gambir, Kota Jakarta Pusat, DKI Jakarta"
  }
]
```

```json theme={null}
[
  {
    "postalCode": "10110"
  }
]
```

## `Scalev.checkout.validateDiscount(payload)`

Memvalidasi kode diskon untuk payload checkout.

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

Contoh response:

```json theme={null}
{
  "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)`

Mengembalikan opsi pengiriman untuk item, tujuan, dan payment method yang dipilih. Gunakan ini sebagai satu-satunya method lookup pengiriman.

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

Kirim option yang dipilih sebagai satu field `shipping` di `estimateSummary` dan `createOrder`. Runtime akan menyiapkan request checkout dari option tersebut. Opsi dapat berisi `logoUrl` saat courier code tersedia.

Contoh response:

```json theme={null}
[
  {
    "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"
  }
]
```

Payload method yang direkomendasikan:

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

## `Scalev.checkout.estimateSummary(payload)`

Mengembalikan ringkasan checkout untuk halaman yang menampilkan breakdown biaya sebelum submit.

Method ini opsional. Kirim item, destination, payment, dan `shipping` yang sama seperti payload yang akan dikirim ke `Scalev.checkout.createOrder(payload)`.

Hasil method ini tidak menggantikan validasi pembuatan order. Scalev tetap memvalidasi dan menerapkan shipping final, discount, biaya tambahan, dan total saat order dibuat.

```js theme={null}
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);
```

Payload method yang direkomendasikan:

```json theme={null}
{
  "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"
  }
}
```

Contoh response:

```json theme={null}
{
  "productPrice": 268000,
  "shippingCost": 15300,
  "otherIncome": 0,
  "otherIncomeName": "Biaya Lainnya",
  "grossRevenue": 273300
}
```

Gunakan `estimateSummary` hanya saat UI checkout menampilkan total sebelum submit. Jangan hitung atau submit `otherIncome` atau `otherIncomeName`; Scalev menghitung biaya tambahan yang dikonfigurasi di store pada `estimateSummary` dan menerapkannya lagi saat `createOrder`.

## `Scalev.checkout.createOrder(payload)`

Membuat public order sungguhan dan memvalidasi payload. Panggil method ini hanya setelah pengunjung sengaja submit form.

```js theme={null}
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;
}
```

Field payload yang didukung:

* Object customer: `customer.name`, `customer.phone`, `customer.email`
* Object destination: `destination.address`, `destination.subdistrictId`, `destination.postalCode`
* Field payment: `paymentMethod`
* List item: `items[]` dengan `{ type: "product", variantUniqueId, quantity }` atau `{ type: "bundle", bundlePriceOptionUniqueId, quantity }`
* Field discount: `discountCode`

Jangan submit field product discount atau shipping discount di level atas. Harga produk dan penyesuaian item-level berada di `items[]` saat dibutuhkan. Gunakan `validateDiscount` untuk melihat estimasi discount code yang eligible, lalu kirim kode terpilih sebagai `discountCode` ke `createOrder`.

Untuk pilihan pengiriman, panggil `Scalev.checkout.shippingOptions(payload)`, biarkan pengunjung memilih satu option, lalu kirim seluruh option itu sebagai `shipping` ke summary dan order.

Payload method yang direkomendasikan:

```json theme={null}
{
  "customer": {
    "name": "Customer Name",
    "phone": "08123456789",
    "email": "customer@example.com"
  },
  "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"
}
```

Untuk payment selection, render `store.paymentMethodOptions[].display`, render gambar payment dari `store.paymentMethodOptions[].logoUrl`, dan submit hanya `value` option terpilih sebagai `paymentMethod`.

Contoh response:

```json theme={null}
{
  "orderId": "260601HPQSPBX",
  "status": "pending",
  "paymentStatus": "unpaid",
  "secretSlug": "orderSecretSlug",
  "publicOrderUrl": "https://brand.myscalev.com/order/orderSecretSlug",
  "paymentUrl": null,
  "productPrice": "90000.00",
  "productDiscount": "0.00",
  "shippingCost": "15300.00",
  "shippingDiscount": "0.00",
  "discountCodeDiscount": "10000.00",
  "otherIncome": "0.00",
  "otherIncomeName": "Biaya Lainnya",
  "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
}
```

### Error handling

Runtime method menolak `Promise` dengan JavaScript `Error`. Gunakan `error.message` untuk copy UI. Gunakan `error.status`, `error.data`, dan `Scalev.diagnostics.get()` hanya untuk debugging.

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

## `Scalev.analytics.track(provider, payload)`

Meneruskan event analytics yang dikonfigurasi melalui Scalev.

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

Nama provider yang didukung:

* `facebook`
* `tiktok`
* `kwai`

Contoh response:

```json theme={null}
null
```

Jika provider tidak dikirim, runtime melewati call tanpa menghubungi Scalev:

```json theme={null}
{
  "skipped": true,
  "reason": "missing_analytics_provider"
}
```

Jika request analytics gagal, checkout flow harus tetap berjalan dan runtime mengembalikan:

```json theme={null}
{
  "skipped": true,
  "reason": "analytics_request_failed"
}
```

## `Scalev.prefill.get()`

Membaca data prefill form yang aman untuk browser ini.

```js theme={null}
const prefill = await Scalev.prefill.get();
const customerName = prefill.customer?.name || "";
const customerPhone = prefill.customer?.phone || "";
const destinationAddress = prefill.destination?.address || "";
```

Contoh response:

```json theme={null}
{
  "customer": {
    "name": "Customer Name",
    "phone": "08123456789",
    "email": "customer@example.com"
  },
  "destination": {
    "address": "Jl. Contoh No. 1",
    "subdistrictId": 9104,
    "postalCode": "12250"
  }
}
```

## `Scalev.prefill.save(form, metadata)`

Menyimpan data prefill form yang aman di cookie terenkripsi `scv_fd`. Ini ditangani lokal oleh fe-public dan tidak memanggil Storefront atau page runtime API endpoint.

```js theme={null}
await Scalev.prefill.save(
  {
    customer: {
      name: "Customer Name",
      phone: "08123456789",
      email: "customer@example.com"
    },
    destination: {
      address: "Jl. Contoh No. 1",
      subdistrictId: 9104,
      postalCode: "12250"
    }
  },
  {
    source: "checkout_form"
  }
);
```

Contoh response:

```json theme={null}
{
  "saved": true
}
```

## `Scalev.diagnostics.get()`

Mengembalikan runtime diagnostics terbaru untuk debugging.

```js theme={null}
const diagnostics = Scalev.diagnostics.get();
```

Contoh response:

```json theme={null}
[
  {
    "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"
  }
]
```
