Cart and server checks
The cart line of a configurator, what your server checks before it charges, and which websites can show iJewel's prices.
The cart line says exactly which piece the shopper built. It comes from the browser, so your server checks it before you charge. This page shows the cart line, the checks on your server, what happens when the author publishes again, and which websites can show iJewel's prices.
SDK version
This page describes the configurator SDK 0.1.0-dev.2 (see Headless SDK). The server calls run in Node 18 or later, or in any server JavaScript that has fetch.
The cart line
session.configuration() returns the cart line. Store it with the order: it says exactly which ring the shopper built.
{
"schema": "ijewel.configuration/2.0",
"configurator": "cfgEngagement01",
"revision": 1,
"title": "Ring",
"state": "v2.r1.shape-round.carat-2-00-ct.centerStone-diamond.head-halo.shank-classic.shankMetal-14k-white-gold.headMetal-14k-white-gold",
"selection": { "shape": "round", "carat": "2-00-ct", "centerStone": "diamond", "head": "halo", "shank": "classic", "shankMetal": "14k-white-gold", "headMetal": "14k-white-gold" },
"derived": {},
"labels": [
{ "axis": "shape", "label": "Shape", "option": "round", "value": "Round" },
{ "axis": "carat", "label": "Carat", "option": "2-00-ct", "value": "2.00 ct" }
],
"tags": [],
"specs": {},
"parts": {
"head": { "variant": "round.2-00-ct.halo", "sha256": "b47214cb…" },
"shank": { "variant": "classic", "sha256": "d6ac24bd…" }
},
"sku": "ENG-RD-200-HL-CL-W14",
"price": { "amount": 3398200, "currency": "USD", "minorUnits": 2 }
}| Field | What it is |
|---|---|
configurator, revision | The folder id and the published revision. |
state | The key to open the same ring again. |
selection | Axis key → option key, for every visible axis. |
derived | Axes that the author derives from the others, for manufacturing. |
labels | The text of each choice, in the session's locale (open({ locale })). |
tags, specs | The author's public tags and specs of the picked options. |
parts | The 3D file of each part, pinned by its sha256: what the ring is made from. |
sku, price | Only when the session has a price answer for this configuration: call await session.price() first, or use await session.requestCart(), which prices and returns the line. |
What to check on your server
The cart line comes from the browser, so the shopper can change any field, the price too. Before you charge, check the line on your server with the same SDK (Node 18 or later, or any server JavaScript with fetch):
- Read the folder that you sell with
loadPublished({ drive, id }). Use your own Drive name and folder id, not the line's. - Check the line with
verifyConfiguration(folder, line). It refuses another configurator, a newer revision, and a state, selection, labels or parts that are not what the configurator gives. A line of an older revision passes when it is still the same ring (A line of an older revision). - Take the labels and parts from
check.configuration, not from the line. - Compute the price again for
check.configuration.state: your own price function, or iJewel's price service. The price service answers only anOriginheader from the allowed origins, and a server'sfetchsends none, so set one. That header is not a secret (Allowed origins for prices). - Charge the price that you computed. When it is not the line's
price, show the shopper the new price first.
All 5 steps are done.
import { loadPublished, verifyConfiguration, createSession, appPrice } from './configurator-sdk.mjs'
const folder = await loadPublished({ drive: 'drive', id: MY_FOLDER_ID })
const check = await verifyConfiguration(folder, line)
if (!check.ok) throw new Error(`cart line refused: ${check.problems.join(', ')}`)
const withOrigin = (url, init = {}) => fetch(url, { ...init, headers: { ...init.headers, Origin: 'https://shop.example' } })
const session = await createSession({ ...folder, state: check.configuration.state, providers: { price: appPrice(folder.priceUrl, folder.manifest, { fetch: withOrigin }) } })
await session.price() // asks the price service for check.configuration
const trusted = session.configuration() // the cart line of check.configuration, with its sku and pricesession.price() returns the price answer (total, currency, minorUnits, the lines). The cart line keeps the price as { amount, currency, minorUnits }, so take the line from session.configuration() after the price, not the answer itself.
check.problems names the fields that do not match, such as ['revision'] or ['selection', 'labels', 'parts'].
The price service does not sign its answers, and the SDK has no signed quotes. Your server asks for the price itself at checkout, as above.
A line of an older revision
The author can publish the configurator again while a ring sits in a shopper's cart. The line then names an older revision. Option keys never change their meaning from one revision to the next, so verifyConfiguration checks the line against the current revision:
- The same ring: every choice of the line is still there. The check passes, and
check.migratedFromis the line's revision.check.configurationis the ring in the current revision, with its current labels, files and state. Price it as above: the price can be new. - Another ring: the publish removed a choice of the line, added a choice, changed a rule that the ring follows, or made the ring impossible to build. The check fails with
revision. Open the line'sstatein a new session (open({ …, state })), which moves it to the current revision, and ask the shopper to confirm the new ring and its price.
A line of a newer revision than the folder that you read also fails with revision: read the folder again.
Allowed origins for prices
iJewel's price service answers only pages on the configurator's allowed origins:
- iJewel's own pages:
https://ijewel3d.comand its subdomains, where the shared links and the embeds open; - every domain that is registered and active in the author's Drive (Settings › Apps, embed and API › Domains). This part of the list is set when the author publishes the configurator in the Drive.
To show iJewel's prices on your shop:
- Register your shop domain in the Drive, for example
https://shop.example.com. - Publish the configurator again. A domain that you add later reaches the list only with the next publish.
The page's origin must match exactly: https://www.shop.example.com and https://shop.example.com are two origins. Only https origins go on the list, so a page on http://localhost gets no iJewel price (E_PRICE). Your own price function needs no list: it runs on your page and asks your server.
The list is a gate for web pages, not a sign-in. A browser always sends the page's true origin, so the list decides which websites can show iJewel's prices. A program outside a browser can send any Origin header, so the list keeps no price secret. It does not need to: a price is what every shopper of the configurator sees. Your server trusts a price because it asks iJewel's price service itself over HTTPS, not because of the header. The daily request limit (below) protects the price service.