API 文档
Shoppi SDK、JSON API 和 TagCode:合作伙伴在 Shoppi 店铺上开发所需的一切。
工作原理
Shoppi Cloud 上的每家店铺都可以用 Shoppi SDK 扩展——这是平台在每个 Shoppi 托管域名上提供的 JavaScript 库;也可以用 TagCode——页面引擎在页面到达浏览器之前于服务器端解析的占位符。两者都调用同一个 JSON API,该 API 也可直接使用。
- 浏览器中无需 API 密钥。店铺通过请求来源的域名识别,因此 SDK 在店铺自己的域名上运行,代码中不含任何密钥。
- 支付通过 Stripe 完成。结账会话创建在商家自己的 Stripe 账户上(Stripe Connect);Shoppi 从不持有商家客户的资金。退款也全部通过 Stripe 完成。
- 两个数据库。商品目录从 Shoppi 数据库读取,即商家在应用中管理的数据库;每个店铺域名还有自己的数据库。由你决定每个 API 从哪个数据库读取(见商品目录)。客户账户始终保存在店铺域名自己的数据库中,与其他店铺分开。
- 当前 SDK 版本:2.9.0。所有响应格式统一:
{"result": "ok" | "ko", "feedback": …}。
安装
在页面的 <head> 中引入一次 SDK。加载后会创建全局对象 window.Shoppi。
<script>
window.ShoppiConfig = {
pageId: "%_ID%", // store id; the page engine fills it in (use %_ID% inside JS, not {id})
currency: "EUR", // default currency for cart and checkout
defaultLocale: "it", // language used when the URL has no /xx/ prefix
baseUrl: "", // leave empty: API calls go to the same origin
sources: "shoppi" // catalog database: "shoppi" (default) or "local"
};
</script>
<script src="/shoppi/shoppi-sdk.js?v=2.9.0"></script>
所有设置都是可选的:没有 ShoppiConfig 时,SDK 通过域名识别店铺并使用欧元。想让浏览器获取新版 SDK 时,请提高 ?v= 参数。
店铺与内容 — Shoppi.site
| 方法 | 返回 |
|---|---|
Shoppi.site.getInfo() |
店铺资料:名称、标志、封面、描述、类别、城市、国家、语言。 |
Shoppi.site.getPage(slug) |
按 slug 获取内容页面:标题、HTML 内容和关键词,或 null。 |
Shoppi.site.getPages() |
店铺的版块:内容页面和商品所属的类别。 |
Shoppi.site.getProducts(options) |
同 Shoppi.catalog.products()。 |
Shoppi.site.getProduct(idOrSlug) |
同 Shoppi.catalog.product()。 |
Shoppi.site.getCategories() |
同 Shoppi.catalog.categories()。 |
Shoppi.site.search(q) / getSuggestions(q) |
同 Shoppi.catalog.search() 和 suggestions()。 |
const info = await Shoppi.site.getInfo();
document.getElementById('store-name').textContent = info.title;
商品目录 — Shoppi.catalog
商品、类别、照片和搜索:商家在 Shoppi 应用中管理的数据,可直接用于搭建店面。无论类型如何,每个条目都以相同的结构返回:
{
id: 9610389, type: 'products', // products | services | files | offers
slug: 'curtirisorisobasmati2×500g',
title: 'Curtiriso Riso Basmati 2 × 500 g',
description: '', content: 'Basmati', // content may contain HTML
price: 4.79, currency: 'EUR', price_format: '4,79 €',
photo: 'https://storage.shoppi.app/…', photo_hires: '…', photos: ['…'],
quantity: 5, in_stock: true, ship_cost: 0, code: 'CX-DIS-000001',
category: 'Dispensa', keywords: ['dispensa'], link: '…', date: 1791312283
}
| 方法 | 返回 |
|---|---|
Shoppi.catalog.products(options) |
返回 { items, count, limit, skip, hasMore }。选项:type(products、services、files、offers、all)、limit(24)、skip、sort(newest、updated、popular、price_asc、price_desc、title)、category(id、slug 或标题)、q、ids、inStock。 |
Shoppi.catalog.product(idOrSlug, { type }) |
一个条目,或 null。绝不会返回其他店铺的条目。 |
Shoppi.catalog.categories({ type }) |
sections(默认:商品所归属的类别)、albums 或 tree。 |
Shoppi.catalog.category(ref) |
按 id、slug 或标题获取一个类别。 |
photos(), pages(), page(slug), news(), videos() |
店铺内容。 |
Shoppi.catalog.search(q, { type, limit, minPrice, maxPrice }) |
返回 { items, count, suggestions, did_you_mean }。 |
Shoppi.catalog.suggestions(q) |
用于自动补全的标题。 |
Shoppi.catalog.info() |
店铺资料。 |
const page = await Shoppi.catalog.products({ limit: 12 });
const cats = await Shoppi.catalog.categories(); // [{ id: 600, slug: 'dispensa', title: 'Dispensa' }, …]
const food = await Shoppi.catalog.products({ category: 'dispensa' });
const one = await Shoppi.catalog.product(9610389); // or by slug
const hits = await Shoppi.catalog.search('riso'); // { items, count, suggestions }
使用哪个数据库:按 API、按调用
'shoppi'(默认)读取 Shoppi 数据库,商家在应用中管理的商品就在这里。'local' 读取店铺域名自己的数据库,在你写入之前为空。可在 ShoppiConfig.sources 中按 API 选择,运行时用 Shoppi.catalog.use(),或单次调用时用 { source }。数据源也可以是其他主机的基础 URL,或返回你自有数据的函数。购物车、结账、客户账户和邮件始终在店铺自己的域名上运行。
window.ShoppiConfig = {
pageId: "%_ID%",
sources: { products: 'shoppi', categories: 'local' } // or one value: 'local'
};
Shoppi.catalog.use('search', 'local'); // at runtime, one API
await Shoppi.catalog.products({ limit: 12 }, { source: 'local' }); // for one call
渲染与事件
Shoppi.render(target, items, template) 用列表填充一个元素:模板可以是函数、Handlebars 字符串,或留空以使用带“加入购物车”按钮的内置卡片。带 data-shoppi-list 的元素会在页面加载时自动填充;其余 data-shoppi-* 属性就是对应方法的选项。document 上的事件:shoppi:cart:added、shoppi:cart:updated、shoppi:checkout:started、shoppi:rendered。
const r = await Shoppi.catalog.products({ limit: 12 });
Shoppi.render('grid', r); // built-in card with add-to-cart button
Shoppi.render('grid', r, p => `<h3>${p.title}</h3><p>${p.price_format}</p>`);
<div data-shoppi-list="products" data-shoppi-limit="12" data-shoppi-category="dispensa">
<template><div class="card"><img src="{{photo}}"><h3>{{title}}</h3>
<button data-shoppi-add="{{id}}" data-shoppi-price="{{price}}"
data-shoppi-title="{{title}}" data-shoppi-image="{{photo}}">Add</button></div></template>
</div>
document.addEventListener('shoppi:cart:added', e => console.log(e.detail.title));
购物车 — Shoppi.cart
购物车保存在浏览器中(localStorage),刷新后仍然保留。传入的价格和标题会一直带到结账,因此请始终设置。
| 调用 | 作用 |
|---|---|
Shoppi.cart.add(id, qty, { price, title, image }) |
添加商品(数量默认为 1)。 |
Shoppi.cart.updateQty(id, qty) |
设置数量;设为 0 即删除该行。 |
Shoppi.cart.remove(id) |
删除一行。 |
Shoppi.cart.clear() |
清空购物车。 |
Shoppi.cart.setCountry(code) |
设置配送国家(ISO 代码,例如 'IT')。 |
Shoppi.cart.data |
当前状态:{ items, count, total, country }。 |
无需 JavaScript,使用 HTML 属性:
<button data-shoppi-add="10034"
data-shoppi-price="29.90"
data-shoppi-title="Leather wallet"
data-shoppi-image="/img/wallet.jpg">Add to cart</button>
<span class="shoppi-cart-count"></span> <!-- item count, updated automatically -->
<span class="shoppi-cart-total"></span> <!-- total, formatted in the store currency -->
<select data-shoppi-country> <!-- shipping country -->
<option value="IT">Italia</option>
<option value="DE">Deutschland</option>
</select>
<button data-shoppi-checkout>Checkout</button>
结账 — Shoppi.checkout
| 方法 | 作用 |
|---|---|
Shoppi.checkout.start() |
在商家的 Stripe 账户上为当前购物车创建 Stripe Checkout 会话,并将客户跳转过去。 |
Shoppi.checkout.startDirect(productId, metadata) |
一键购买单个商品,无需购物车(服务、预订、下载)。 |
Shoppi.checkout.createOnboardingLink() |
返回店铺的 Stripe 开户链接,用于连接或完善其 Stripe 账户。 |
document.querySelector('#buy').addEventListener('click', () => Shoppi.checkout.start());
// Stripe onboarding for the merchant
const url = await Shoppi.checkout.createOnboardingLink();
window.location.href = url;
任何带有 data-shoppi-checkout 的按钮都与 Shoppi.checkout.start() 作用相同。支付费用取决于店铺套餐:Stripe 手续费加上每笔支付 0.4% + 1 个最小货币单位的 Shoppi Pay 应用费。退款(全额或部分、线上或 POS)同样通过 Stripe 完成。
客户账户 — Shoppi.user
店铺客户的账户,保存在店铺数据库中。登录后会话令牌保存在浏览器中,并随每个 SDK 请求通过 X-User-Token 请求头发送。账户与店铺域名绑定:两个店铺永远看不到彼此的客户。
| 方法 | 作用 |
|---|---|
Shoppi.user.register(email, password, firstName, lastName) |
创建账户并登录。 |
Shoppi.user.login(email, password) |
登录。 |
Shoppi.user.getProfile() |
已登录客户的资料(未登录时为 null)。 |
Shoppi.user.updateProfile(data) |
更新资料字段,例如 { phone }。 |
Shoppi.user.saveAddress(type, address) |
保存地址;type 例如 'shipping'。 |
Shoppi.user.logout() |
清除会话并刷新页面。 |
导航与翻译 — Shoppi.router, Shoppi.lang
URL 采用 /<language>/<page> 格式,例如 /it/contatti。Shoppi.router.navigate(slug, locale) 无需整页刷新即可加载页面并显示在 <main> 中;带 data-shoppi-link 的链接作用相同。
<a href="/it/contatti" data-shoppi-link="contatti">Contatti</a>
Shoppi.lang.load(locale) 加载店铺的翻译;Shoppi.lang.t(key, vars) 返回文本并替换写作 %_NAME% 的占位符。
本地目录 — Shoppi.local
| 方法 | 返回 |
|---|---|
Shoppi.local.getCities() |
本地目录中可用的城市。 |
Shoppi.local.getBusinesses(cityId, categoryId) |
某城市的商家,可按类别筛选。 |
邮件 — Shoppi.mailer
从店铺发送事务性邮件,例如联系表单。限制为每台设备每天 10 封。
await Shoppi.mailer.send({
to: '[email protected]',
subject: 'New request from the website',
body: '<p>Hello!</p>',
replyTo: '[email protected]' // optional
});
Shoppi.mailer.remaining(); // emails left today on this device
JSON API
每个 SDK 方法都是对 JSON API 的调用,你也可以用 Shoppi.call(endpoint, params, method) 或普通的 fetch 直接调用:格式为 /api/<module>/<method>/json,请求店铺自己的域名。
| 端点 | 用途 |
|---|---|
GET /api/website/info/json |
店铺资料 |
GET /api/website/section/json?clear_title=<slug> |
内容页 |
POST /api/checkout/createStripeSession/json |
根据购物车创建 Stripe Checkout 会话 |
POST /api/checkout/startDirect/json |
直接购买单个商品 |
GET /api/checkout/createOnboardingLink/json |
Stripe 开户链接 |
POST /api/frontend_user/register/json |
客户注册 |
POST /api/frontend_user/login/json |
客户登录 |
GET /api/local/getCities/json |
本地目录:城市 |
GET /api/website/products/json |
商品(另有 listServices、listFiles、listOffers) |
GET /api/website/product/json?id= |
单个商品,按 id 或 clear_title
|
GET /api/website/sections/json |
版块与类别 |
GET /api/website/listSells/json?parent= |
归属于某个版块的条目 |
GET /api/search/results/json?query= |
搜索 |
GET /api/search/suggestions/json?query= |
自动补全 |
const res = await fetch('/api/website/info/json', { credentials: 'include' });
const data = await res.json(); // { "result": "ok", ... } or { "result": "ko", "feedback": "reason" }
商品目录端点在店铺域名(其自有数据库)和 https://shoppi.app(Shoppi 数据库)上都可访问。对于 'shoppi' 数据源,SDK 会携带店铺 id 调用 shoppi.app:不属于调用域名的 id 会被拒绝。Shoppi.call(endpoint, params, method, { base }) 用于选择主机。
Shoppi.call() 返回 ok 响应中的有用部分;响应为 ko 时抛出带有 feedback 信息的 Error。
TagCode(服务器端)
TagCode 是店铺 HTML 模板中的占位符,在页面发送前于服务器端解析,因此内容存在于搜索引擎看到的 HTML 中。
| TagCode | 输出 |
|---|---|
{css} / {js}
|
页面及其插件的样式表和脚本 |
{page_title}, {page_description}
|
SEO 标题和描述 |
{page_name}, {page_logo}
|
店铺名称和标志 |
{locale}, {currency}, {country}
|
当前语言、货币、访客国家 |
{title}, {content}, {photo}, {price}, {link}
|
当前商品或文章的字段 |
{cfield:title}, {cfield:description}, {cfield:cover}
|
店铺资料字段 |
{cart:act=drawer} |
侧边购物车 |
{search:nout=true} |
搜索,初始化时不输出默认内容 |
{cookieconsent} |
Cookie 同意横幅 |
{offers:act=seo} |
商品的 JSON-LD 结构化数据 |
在 JavaScript 对象中请用 %_ID% 表示店铺 ID:花括号在那里会被当作 TagCode 解析。
文件与数据访问
每家店铺的文件都可以通过 WebDAV 访问(也可以用 Shoppi Go 桌面应用),数据始终可导出:标准 HTML 模板、店铺自己的数据库以及上面的 JSON API。访问凭据在店铺的账户面板中。