Generator Material Purchase Integration Guide
Framework-agnostic xTool material-purchase modal SDK. Drop it into any Vue / React / vanilla JS project via
<script src>orimport.
Table of contents
- Overview
- Install and import
- Quick start
- Public API
- Configuration reference
- Authentication
- Framework integration examples
- Internationalization (i18n)
- Styling and visual spec
- FAQ
- Build artifacts
- Versions and changelog
1. Overview
After calling GeneratorMaterialPurchase.init(...) followed by GeneratorMaterialPurchase.open(), the SDK inserts a modal under the current page's document.body:
position: fixed, slides in from the right edge- 320px wide, 100vh tall (full height)
- Semi-transparent backdrop (
rgba(0, 0, 0, 0.4)), click to dismiss - Contents: title bar / store switcher / product list (variant dropdown, quantity adjust, checkboxes) / total + Buy Now
Business capabilities:
- Picks a Shopify storefront by IP (US / CA / EU / UK / FR / DE / JP / AU), with manual override
- Uses
@xtool/shopify-sdkto create a cart → fetchescheckoutUrl→ defaults towindow.open(_, '_blank') - Calls the atomm backend for the accessory pack list / IP geolocation / distribution
trackId - Bundles en / zh strings
2. Install and import
2.1 Via CDN (recommended for non-Vue/React projects)
<!-- UMD: legacy-friendly, exposes window.GeneratorMaterialPurchase -->
<script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-material-purchase/index.umd.js"></script>Once loaded, window.GeneratorMaterialPurchase is your public API.
2.2 Via npm (recommended for Vue / React projects)
# Internal registry requires .npmrc → http://repository.makeblock.com/repository/npm-group/
pnpm add @atomm-developer/generator-material-purchase
# or
npm install @atomm-developer/generator-material-purchaseimport GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
// or named import
import { GeneratorMaterialPurchase } from '@atomm-developer/generator-material-purchase'The package ships both ESM (
index.es.js) and UMD (index.umd.js). Modern bundlers pick the right entry automatically.
3. Quick start
Minimal working example — two steps: init + open.
<!doctype html>
<html>
<head>
<script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-material-purchase/index.umd.js"></script>
</head>
<body>
<button id="buyBtn">Buy accessories</button>
<script>
// Step 1: initialize with required business params
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
})
// Step 2: open the modal on click
document.getElementById('buyBtn').addEventListener('click', () => {
GeneratorMaterialPurchase.open()
})
</script>
</body>
</html>4. Public API
Four methods are exposed:
interface GeneratorMaterialPurchaseApi {
init(options: PurchaseModalInitOptions): void
open(): Promise<void>
close(): void
destroy(): void
}| Method | Description |
|---|---|
init(options) | Must be called first. Sets business params, locale, and callbacks. Can be called multiple times to update config. |
open() | Mounts the modal DOM into document.body and kicks off the first product load (accessory pack + IP geo). Returns a Promise; init failures fire onError. |
close() | Plays exit animation, then unmounts the modal DOM. Also triggered by the user clicking the backdrop / close icon. |
destroy() | Force-unmounts and clears internal caches (e.g. product cache). To use the SDK again, call init first. |
open()can be called repeatedly. Each open resets UI state (checkboxes, variant selection, scroll position) but preservesinitconfig and the previous product cache (no duplicate request for the same store).
5. Configuration reference
interface PurchaseModalInitOptions {
/** Required: generator code, e.g. 'light-sign', 'flower-generator' */
generatorId: string
/** Required: accessory type. Currently 'default' is used across prod. */
accessoryType: string
/** Optional: default locale, 'en' | 'zh'. Defaults to 'en'. */
locale?: 'en' | 'zh' | string
/** Optional: modal z-index. Defaults to 9999. */
zIndex?: number
/** Optional: override the default prod apiBaseUrl. Defaults to https://xcs-api.xtool.com */
apiBaseUrl?: string
/** Optional: extend or override locale strings */
messages?: Partial<Record<string, Record<string, string>>>
/** Optional: checkout success callback. Defaults to window.open(checkoutUrl, '_blank') */
onCheckoutSuccess?: (checkoutUrl: string) => void
/** Optional: modal close callback */
onClose?: () => void
/** Optional: error callback (API failure / checkout failure / etc.) */
onError?: (err: unknown) => void
}Field semantics:
generatorId— Sent to the backend/generator-accessory-packendpoint to determine which generator's accessory list to load. Also used asrelatedObjectTypeon the distribution endpoint.accessoryType— Same endpoint's input. Currently'default'across prod.locale— Current i18n language. To switch dynamically, callinit({ locale: ... })thenopen()again.messages— Overrides individual strings or adds non-bundled languages, e.g.{ en: { buy_now: 'Checkout' }, ja: { ... } }.zIndex— Raise when the host page has higher-stacked elements (e.g. global toasts) that would otherwise overlay the modal.apiBaseUrl— Defaults to the prod atomm service. Point to other environments for local dev or staging.onCheckoutSuccess— Default behavior opens a new tab on the Shopify checkout page. To redirect the current page, implementlocation.href = url.onClose— Fires when the user dismisses the modal. Useful for analytics / business glue.onError— Fires on product-load failure, checkout failure, IP geolocation failure, etc. The SDK already shows a toast internally; you don't need extra UI.
6. Authentication
The SDK automatically injects two headers into every request to the atomm backend:
| Header | Source |
|---|---|
uToken | Reads document.cookie.utoken first, then falls back to localStorage.utoken |
lang | Reads localStorage.LANG_KEY, defaults to 'en' |
For cross-origin deployments:
- Make sure the host page has written
utokento cookie or localStorage (usually right after login).- Make sure
xcs-api.xtool.comwhitelists the caller domain in CORS and allows the customuToken / langheaders.
If you see 401 / 403, check whether uToken is present, expired, or stripped by cross-origin policy.
7. Framework integration examples
7.1 Vanilla JS (<script src>)
<script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-material-purchase/index.umd.js"></script>
<script>
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
locale: 'en',
onCheckoutSuccess: (url) => (location.href = url), // redirect current tab instead of new tab
onClose: () => console.log('modal closed'),
})
document.querySelector('#buyBtn').onclick = () => GeneratorMaterialPurchase.open()
</script>7.2 Vue 3 (Composition API)
<script setup lang="ts">
import { onMounted, onBeforeUnmount } from 'vue'
import GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
onMounted(() => {
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
})
})
onBeforeUnmount(() => {
GeneratorMaterialPurchase.destroy()
})
function handleClick() {
GeneratorMaterialPurchase.open()
}
</script>
<template>
<button @click="handleClick">Buy accessories</button>
</template>7.3 React (Hooks)
import { useEffect } from 'react'
import GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
export function BuyButton() {
useEffect(() => {
GeneratorMaterialPurchase.init({
generatorId: 'flower-generator',
accessoryType: 'default',
locale: 'en',
})
return () => GeneratorMaterialPurchase.destroy()
}, [])
return <button onClick={() => GeneratorMaterialPurchase.open()}>Buy accessories</button>
}7.4 Vue 2 (Options API)
<script>
import GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
export default {
mounted() {
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
})
},
beforeDestroy() {
GeneratorMaterialPurchase.destroy()
},
methods: {
openModal() {
GeneratorMaterialPurchase.open()
},
},
}
</script>
<template>
<button @click="openModal">Buy accessories</button>
</template>8. Internationalization (i18n)
8.1 Bundled languages
The SDK ships with full en and zh string sets, covering:
- Modal title / close button / country notice / loading / empty state / sold-out badge
- Total / discount notice / Buy Now
- Various error toasts (load failure, checkout failure, out of stock, etc.)
- Country names for all 8 storefronts (USA / Canada / Australia / EU / Germany / France / UK / Japan)
Switch via init({ locale: 'zh' }).
8.2 Extending or overriding strings
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
locale: 'en',
messages: {
en: {
buy_now: 'Checkout securely', // override one entry
},
ja: {
// add Japanese
shopify_supplies_kit: '消耗品キット',
buy_now: '今すぐ購入',
// ...missing entries fall back to en
},
},
})8.3 String key reference
| key | Meaning |
|---|---|
shopify_supplies_kit | Modal title |
close | Close button aria-label |
current_country_notice | Country notice, template includes {country} |
loading | Loading text |
no_items_in_cart | Empty product state |
sold_out | Sold-out badge |
total | Total label |
discount_notice | Discount code notice |
buy_now | Buy Now button |
failed_load_product_list | Load failure toast |
checkout_failed | Checkout failure toast |
selected_items_out_of_stock | Selected items out of stock toast |
no_valid_products_selected | No purchasable products toast |
store_us / store_ca / store_au / store_eu / store_de / store_fr / store_uk / store_jp | Storefront dropdown short names |
country_usa / country_canada / country_australia / country_eu / country_germany / country_france / country_uk / country_japan | Long country names |
9. Styling and visual spec
| Aspect | Spec |
|---|---|
| Mount | document.body.appendChild(host), host uses Shadow DOM for style isolation |
| Backdrop | position: fixed; inset: 0; background: rgba(0,0,0,0.4);, click to dismiss |
| Modal | position: fixed; top: 0; right: 0; width: 320px; height: 100vh; |
| Animation | Backdrop fades over 0.2s, modal slides over 0.25s translateX(100% → 0) |
| Font | Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif |
| Primary colors | Buy button #ff0035 / accent #070b10 / notice orange #ff7c23 |
| CSS naming | BEM style with unified xpm- prefix (Xtool Purchase Modal) |
Because the modal uses Shadow DOM, host-page CSS resets, Tailwind, or * { box-sizing: ... } won't bleed into the modal. Likewise, internal modal styles don't leak out to the host page.
10. FAQ
Q1: Calling open() throws "call init() before open()"
You called open() before init(). Call init once during mount (onMounted in Vue / useEffect in React).
Q2: Modal opens but product list spins forever / shows empty
Step through:
- In DevTools Network, verify
/generator-accessory-packreturns 200 withcode: 0. - On 401 / 403, confirm
uTokenis in cookie or localStorage. - On CORS errors, ask the backend to add your domain to the whitelist.
Q3: Checkout button is greyed out, what now?
When every checked item is detected as out-of-stock (outOfStock === true or availableForSale === false), the button greys out. Switch to a different variant or storefront.
Q4: How do I redirect the current tab instead of opening a new tab?
Pass onCheckoutSuccess:
GeneratorMaterialPurchase.init({
generatorId: '...',
accessoryType: 'default',
onCheckoutSuccess: (url) => (window.location.href = url),
})Q5: Locale switched but the modal didn't refresh
Re-call init({ locale: 'xx' }) after switching. If the modal is currently open, close() then open().
Q6: Can I open multiple modals at once?
No. The SDK is a singleton; a second open() waits for the first one's exit animation before mounting. Need concurrent modals? Let us know.
Q7: Does it pollute globals?
Only attaches window.GeneratorMaterialPurchase (UMD mode). Doesn't modify body styles; only appends a host element while the modal exists, removed after close/destroy.
Q8: DevTools is awkward inside Shadow DOM. Any workaround?
In Chrome DevTools → Settings → Preferences → Elements, enable "Show user agent shadow DOM" to expand it. Or call methods directly via window.GeneratorMaterialPurchase in the Console.
11. Build artifacts
Build command:
pnpm buildOutput in dist/:
| File | Use | Size (gzip) |
|---|---|---|
index.es.js | ESM entry, for npm / modern bundlers | ~15 KB |
index.umd.js | UMD, browser <script src> or Node require | ~12 KB |
index.d.ts | TypeScript declarations (rollup-merged single file) | — |
*.map | sourcemaps for runtime error tracing | — |
Published to npm with files:
dist/(artifacts)README.md
12. Versions and changelog
0.1.0 (initial release)
- First published version
- Exposes
init / open / close / destroy, attaches globalGeneratorMaterialPurchase - Ships 8 prod Shopify storefronts
- Modal layout: right-side 320px / full height / backdrop
- Bundled en + zh strings
- Auto-injects
uToken+langon every request - BEM CSS + Shadow DOM style isolation
Feedback
For bugs or feature requests, file an issue on the internal GitLab repo, or ping the maintainers in the team Feishu group.