@brainerce/mcp-server 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/http.js +125 -19
- package/dist/bin/stdio.js +125 -19
- package/dist/index.js +125 -19
- package/dist/index.mjs +125 -19
- package/package.json +1 -1
package/dist/bin/http.js
CHANGED
|
@@ -163,7 +163,26 @@ import {
|
|
|
163
163
|
getDescriptionContent, isHtmlDescription,
|
|
164
164
|
getProductMetafieldValue, getProductCustomizationFields,
|
|
165
165
|
} from 'brainerce';
|
|
166
|
-
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
### Available features \u2014 request each topic for full docs
|
|
169
|
+
|
|
170
|
+
| Topic | What it covers |
|
|
171
|
+
|-------|----------------|
|
|
172
|
+
| \`products\` | Catalog listing, search, PDP, variants, swatches, stock, attributes |
|
|
173
|
+
| \`cart\` | Guest + logged-in cart, add/remove/update, reservations |
|
|
174
|
+
| \`checkout\` | Guest + customer checkout, shipping, address |
|
|
175
|
+
| \`payment\` | Payment providers, intents, status polling, confirmation |
|
|
176
|
+
| \`auth\` | Customer sign-up, login, OAuth, email verification |
|
|
177
|
+
| \`discounts\` | Coupons, banners, nudges |
|
|
178
|
+
| \`recommendations\` | Cross-sell / upsell grids |
|
|
179
|
+
| \`reviews\` | Product reviews & ratings |
|
|
180
|
+
| \`inventory\` | Stock badges, reservation countdown |
|
|
181
|
+
| \`inquiries\` | Contact / custom forms |
|
|
182
|
+
| \`i18n\` | Multi-language, RTL |
|
|
183
|
+
| \`admin\` | Server-to-server admin API (products, orders, customers) |
|
|
184
|
+
| \`storefront-bot\` | **AI chat widget** \u2014 add Brainerce's AI shopping assistant to any page with one script tag or \`BrainerceBot.mount()\`. Answers product questions, shows recommendation cards, handles cart adds, opens escalation form. |
|
|
185
|
+
| \`content\` | Blog, articles, static pages |`;
|
|
167
186
|
}
|
|
168
187
|
function getCheckoutFlowSection(_currency) {
|
|
169
188
|
return `## \u26A0\uFE0F CHECKOUT FLOW (CRITICAL \u2014 READ CAREFULLY!)
|
|
@@ -1885,8 +1904,8 @@ function getTaxDisplaySection(_currency) {
|
|
|
1885
1904
|
| Product Page | \u274C No | "Price excludes tax" (optional note) |
|
|
1886
1905
|
| Cart Page | \u26A0\uFE0F "Calculated at checkout" | No address yet |
|
|
1887
1906
|
| Checkout (before address) | \u26A0\uFE0F "Enter address to calculate" | Waiting for location |
|
|
1888
|
-
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` |
|
|
1889
|
-
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` |
|
|
1907
|
+
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` (0 in VAT mode \u2192 \`checkout.taxBreakdown.totalTax\`) |
|
|
1908
|
+
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` (0 in VAT mode \u2192 \`order.taxBreakdown.totalTax\`) |
|
|
1890
1909
|
|
|
1891
1910
|
### Cart Page \u2014 Partial Display
|
|
1892
1911
|
|
|
@@ -1938,7 +1957,13 @@ const totals = getCartTotals(cart);
|
|
|
1938
1957
|
<div className="flex justify-between text-green-600"><span>Discount</span><span>-{formatPrice(order.discountAmount, { currency: order.currency })}</span></div>
|
|
1939
1958
|
)}
|
|
1940
1959
|
<div className="flex justify-between"><span>Shipping</span><span>{formatPrice(order.shippingAmount, { currency: order.currency })}</span></div>
|
|
1941
|
-
|
|
1960
|
+
{/* Inclusive (VAT) stores keep taxAmount="0" \u2014 the real VAT is on taxBreakdown.totalTax. */}
|
|
1961
|
+
{(() => {
|
|
1962
|
+
const tax = parseFloat(order.taxAmount || '0') || order.taxBreakdown?.totalTax || 0;
|
|
1963
|
+
if (tax <= 0) return null;
|
|
1964
|
+
const incl = order.taxBreakdown?.pricesIncludeTax;
|
|
1965
|
+
return <div className="flex justify-between"><span>{incl ? 'Tax (incl.)' : 'Tax'}</span><span>{formatPrice(tax, { currency: order.currency })}</span></div>;
|
|
1966
|
+
})()}
|
|
1942
1967
|
<div className="flex justify-between font-bold text-lg border-t pt-2"><span>Total Paid</span><span>{formatPrice(order.totalAmount, { currency: order.currency })}</span></div>
|
|
1943
1968
|
</div>
|
|
1944
1969
|
\`\`\``;
|
|
@@ -2067,7 +2092,7 @@ A useful order card includes ALL of the following when the data is present. Each
|
|
|
2067
2092
|
| **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
|
|
2068
2093
|
| **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
|
|
2069
2094
|
| Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
|
|
2070
|
-
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. |
|
|
2095
|
+
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
|
|
2071
2096
|
|
|
2072
2097
|
#### Rendering \`order.items[i].customizations\` by type
|
|
2073
2098
|
|
|
@@ -2512,8 +2537,9 @@ await admin.createTaxRate({ name: 'VAT (Food)', rate: 0, country: 'GB', taxClass
|
|
|
2512
2537
|
await admin.mergeTaxClasses(food.id, standardClassId);
|
|
2513
2538
|
\`\`\`
|
|
2514
2539
|
|
|
2515
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2516
|
-
the
|
|
2540
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2541
|
+
on the \`products:read\` scope every connection already has): \`getStoreTaxClasses()\`
|
|
2542
|
+
lists the store's classes (storefront-safe fields only) for a transparency badge.
|
|
2517
2543
|
|
|
2518
2544
|
For a buyer-facing tax preview on PDP / PLP / cart, \`estimateTax({ country,
|
|
2519
2545
|
subtotal })\` returns the tax portion at the Standard rate for the buyer's
|
|
@@ -2535,8 +2561,10 @@ A region binds countries \u2192 currency + tax-display mode + enabled payment
|
|
|
2535
2561
|
providers. Manage them via the SDK (scopes \`regions:read\` / \`regions:write\`).
|
|
2536
2562
|
\`detectRegion\` is a pure client-side helper to map a buyer's country to a
|
|
2537
2563
|
region; pass the resolved \`regionId\` to \`createCheckout\` to associate the
|
|
2538
|
-
checkout with a region (recorded for reporting + provider scoping
|
|
2539
|
-
|
|
2564
|
+
checkout with a region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
2565
|
+
presentment-enabled regions (Stripe today) charge in the region currency and the
|
|
2566
|
+
checkout response carries a \`presentment\` overlay with the charged amounts;
|
|
2567
|
+
otherwise the checkout is charged in the store base currency.
|
|
2540
2568
|
|
|
2541
2569
|
\`\`\`typescript
|
|
2542
2570
|
const eu = await admin.createRegion({
|
|
@@ -2549,9 +2577,12 @@ const { data: regions } = await admin.getRegions();
|
|
|
2549
2577
|
const region = admin.detectRegion('DE', regions); // \u2192 eu, else default, else null
|
|
2550
2578
|
\`\`\`
|
|
2551
2579
|
|
|
2552
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2553
|
-
|
|
2554
|
-
|
|
2580
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2581
|
+
on the \`products:read\` scope every connection already has): \`getStoreRegions()\`
|
|
2582
|
+
lists active regions, \`getStoreRegion(id)\` returns one region + its payment
|
|
2583
|
+
providers. \`getAutoRegion(country)\` and \`estimateTax({ country, subtotal })\` work
|
|
2584
|
+
in both modes too. Pair with \`detectRegion\` to pick the buyer's region for
|
|
2585
|
+
currency display.
|
|
2555
2586
|
|
|
2556
2587
|
\`\`\`typescript
|
|
2557
2588
|
const store = new BrainerceClient({ storeId: 'store_123' });
|
|
@@ -3175,6 +3206,38 @@ Rules:
|
|
|
3175
3206
|
count updates when the bot adds items.
|
|
3176
3207
|
`;
|
|
3177
3208
|
}
|
|
3209
|
+
function getStorefrontAnalyticsSection(connectionId) {
|
|
3210
|
+
return `## Storefront Traffic Analytics (visits, countries, sources)
|
|
3211
|
+
|
|
3212
|
+
Brainerce can track cookieless storefront traffic \u2014 how many people visit the
|
|
3213
|
+
store, from which countries, and from which traffic sources \u2014 and surface it in
|
|
3214
|
+
the merchant dashboard alongside sales (so the merchant gets a REAL conversion
|
|
3215
|
+
rate: visits \u2192 orders).
|
|
3216
|
+
|
|
3217
|
+
Add ONE line to the storefront's root layout (\`<head>\` or end of \`<body>\`):
|
|
3218
|
+
|
|
3219
|
+
\`\`\`html
|
|
3220
|
+
<script defer src="https://api.brainerce.com/t.js" data-channel="${connectionId}"></script>
|
|
3221
|
+
\`\`\`
|
|
3222
|
+
|
|
3223
|
+
That's it \u2014 the pixel auto-sends a pageview beacon on load and on SPA route
|
|
3224
|
+
changes. Stores scaffolded with \`create-brainerce-store\` already include it.
|
|
3225
|
+
|
|
3226
|
+
Privacy: the pixel is cookieless and stores NO personal data \u2014 no cookie, no
|
|
3227
|
+
cross-site/cross-day tracking, and the visitor IP is resolved to a country
|
|
3228
|
+
server-side then discarded. No cookie-consent banner is required for it under
|
|
3229
|
+
GDPR/ePrivacy.
|
|
3230
|
+
|
|
3231
|
+
CSP: if the storefront sets a Content-Security-Policy, allow the script + beacon:
|
|
3232
|
+
- \`script-src\` \u2026 \`https://api.brainerce.com\`
|
|
3233
|
+
- \`connect-src\` \u2026 \`https://api.brainerce.com\`
|
|
3234
|
+
|
|
3235
|
+
The merchant can toggle tracking per sales channel in the dashboard. The data
|
|
3236
|
+
shows up under Dashboard \u2192 Analytics (visits over time, by country, by source,
|
|
3237
|
+
by device) and can also be asked of the AI assistant ("how many visits did I get
|
|
3238
|
+
this month and from where?").
|
|
3239
|
+
`;
|
|
3240
|
+
}
|
|
3178
3241
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3179
3242
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3180
3243
|
const cur = currency || "USD";
|
|
@@ -3231,6 +3294,9 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3231
3294
|
return getBlogSection();
|
|
3232
3295
|
case "storefront-bot":
|
|
3233
3296
|
return getStorefrontBotSection(cid);
|
|
3297
|
+
case "analytics":
|
|
3298
|
+
case "traffic-analytics":
|
|
3299
|
+
return getStorefrontAnalyticsSection(cid);
|
|
3234
3300
|
case "all":
|
|
3235
3301
|
return [
|
|
3236
3302
|
"# Brainerce SDK \u2014 full topic dump",
|
|
@@ -3299,6 +3365,10 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3299
3365
|
"",
|
|
3300
3366
|
"---",
|
|
3301
3367
|
"",
|
|
3368
|
+
getStorefrontAnalyticsSection(cid),
|
|
3369
|
+
"",
|
|
3370
|
+
"---",
|
|
3371
|
+
"",
|
|
3302
3372
|
getProductCustomizationFieldsSection(),
|
|
3303
3373
|
"",
|
|
3304
3374
|
"---",
|
|
@@ -3368,6 +3438,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3368
3438
|
"inquiries",
|
|
3369
3439
|
"content",
|
|
3370
3440
|
"storefront-bot",
|
|
3441
|
+
"analytics",
|
|
3371
3442
|
"all"
|
|
3372
3443
|
]).describe("The SDK documentation topic to retrieve"),
|
|
3373
3444
|
salesChannelId: import_zod.z.string().optional().describe("Sales channel ID (starts with vc_). Used to personalize setup code."),
|
|
@@ -3405,7 +3476,7 @@ interface Product {
|
|
|
3405
3476
|
priceMin?: string | null; // Lowest variant price (VARIABLE products) \u2014 same as basePrice for VARIABLE, kept for back-compat and JSON-LD range display.
|
|
3406
3477
|
priceMax?: string | null; // Highest variant price (VARIABLE products). Use with priceMin for "\u20AA49 \u2013 \u20AA199" range.
|
|
3407
3478
|
priceVaries?: boolean; // true when variant prices differ \u2014 show "\u20AA49 \u2013 \u20AA199" range.
|
|
3408
|
-
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive
|
|
3479
|
+
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive. For presentment-enabled regions (Stripe) the displayPrice uses the same buffered rate the checkout charges (browsed == checkout); for display-only regions it is mid-market and the buyer is charged in store currency at checkout (payment provider handles customer-side FX).
|
|
3409
3480
|
displayPrice?: string; // basePrice \xD7 daily FX rate, in displayCurrency.
|
|
3410
3481
|
displaySalePrice?: string; // salePrice \xD7 rate (if salePrice present).
|
|
3411
3482
|
displayPriceMin?: string; // priceMin \xD7 rate (VARIABLE products).
|
|
@@ -3659,7 +3730,7 @@ interface Checkout {
|
|
|
3659
3730
|
status: CheckoutStatus;
|
|
3660
3731
|
email?: string | null;
|
|
3661
3732
|
customerId?: string | null;
|
|
3662
|
-
regionId?: string | null; // multi-region: recorded for reporting + provider scoping (currency
|
|
3733
|
+
regionId?: string | null; // multi-region: recorded for reporting + provider scoping. FX-at-checkout: presentment-enabled regions (Stripe) charge in the region currency \u2014 see the presentment field below; else charged in store base currency
|
|
3663
3734
|
shippingAddress?: CheckoutAddress | null;
|
|
3664
3735
|
billingAddress?: CheckoutAddress | null;
|
|
3665
3736
|
shippingRateId?: string | null;
|
|
@@ -3679,6 +3750,18 @@ interface Checkout {
|
|
|
3679
3750
|
itemCount: number;
|
|
3680
3751
|
availableShippingRates?: ShippingRate[];
|
|
3681
3752
|
reservation?: ReservationInfo;
|
|
3753
|
+
// FX-at-checkout: present ONLY when the region charges in its own currency (presentment-enabled \u2014 Stripe today). The amounts the buyer is actually CHARGED, in presentment.currency; presentment.total equals the payment intent amount. Render these (not total/currency above) when present. Absent = charged in store base currency. The fields above stay the base reference.
|
|
3754
|
+
presentment?: {
|
|
3755
|
+
currency: string; // ISO-4217 charged currency, e.g. "EUR"
|
|
3756
|
+
subtotal: string;
|
|
3757
|
+
discountAmount: string;
|
|
3758
|
+
shippingAmount: string;
|
|
3759
|
+
taxAmount: string;
|
|
3760
|
+
surchargeAmount: string;
|
|
3761
|
+
total: string; // equals the charged amount, to the cent
|
|
3762
|
+
fxChargingRate: string; // base\u2192presentment rate (buffer included)
|
|
3763
|
+
fxBufferPercent: string | null;
|
|
3764
|
+
};
|
|
3682
3765
|
createdAt: string;
|
|
3683
3766
|
updatedAt: string;
|
|
3684
3767
|
}
|
|
@@ -3770,6 +3853,8 @@ interface Order {
|
|
|
3770
3853
|
totalAmount: string; // ALWAYS use this, not .total
|
|
3771
3854
|
total?: string; // optional alias
|
|
3772
3855
|
currency?: string;
|
|
3856
|
+
taxAmount?: string | null; // 0 in inclusive (VAT) mode \u2014 read taxBreakdown.totalTax
|
|
3857
|
+
taxBreakdown?: TaxBreakdown | null; // carries the included VAT when prices include tax
|
|
3773
3858
|
customer?: OrderCustomer | null;
|
|
3774
3859
|
items: OrderItem[];
|
|
3775
3860
|
itemCount?: number;
|
|
@@ -4054,10 +4139,22 @@ interface CartAppliedDiscount {
|
|
|
4054
4139
|
}
|
|
4055
4140
|
|
|
4056
4141
|
// Recommendation types
|
|
4142
|
+
interface RecommendationVariant {
|
|
4143
|
+
id: string; name?: string; price?: string; salePrice?: string;
|
|
4144
|
+
attributes?: Record<string, string>; image?: ProductImage | string; inventory?: InventoryInfo;
|
|
4145
|
+
}
|
|
4057
4146
|
interface ProductRecommendation {
|
|
4058
|
-
id: string; name: string; slug: string;
|
|
4147
|
+
id: string; name: string; slug: string;
|
|
4148
|
+
// For a VARIABLE target, basePrice is the pinned variant's price (when
|
|
4149
|
+
// targetVariantId is set) or the "from {min variant}" range \u2014 never 0.
|
|
4150
|
+
basePrice: string; salePrice?: string;
|
|
4059
4151
|
images: ProductImage[]; type: 'SIMPLE' | 'VARIABLE'; inventory?: InventoryInfo;
|
|
4060
4152
|
relationType: string;
|
|
4153
|
+
// Variant context (VARIABLE targets):
|
|
4154
|
+
targetVariantId?: string | null; // merchant-pinned variant, or null
|
|
4155
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4156
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4157
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4061
4158
|
}
|
|
4062
4159
|
interface ProductRecommendationsResponse {
|
|
4063
4160
|
crossSells: ProductRecommendation[];
|
|
@@ -4091,6 +4188,12 @@ interface CartBundleOfferOfferedProduct {
|
|
|
4091
4188
|
salePrice: string | null;
|
|
4092
4189
|
images: Array<{ url: string }>;
|
|
4093
4190
|
type: string;
|
|
4191
|
+
// Variant context (VARIABLE offered products):
|
|
4192
|
+
variantId?: string | null; // merchant-pinned variant for this slot
|
|
4193
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4194
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4195
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4196
|
+
// originalPrice = pinned/cheapest variant price for VARIABLE (not a 0 parent base).
|
|
4094
4197
|
originalPrice: string;
|
|
4095
4198
|
discountedPrice: string;
|
|
4096
4199
|
}
|
|
@@ -4574,10 +4677,12 @@ interface UpdateRegionDto extends Partial<CreateRegionDto> { isActive?: boolean
|
|
|
4574
4677
|
// client.detectRegion(country, regions): Region | null \u2014 pure, no network.
|
|
4575
4678
|
// \u2192 region whose countries includes the code, else the default region, else null.
|
|
4576
4679
|
// Pass the resolved regionId to createCheckout to associate the checkout with a
|
|
4577
|
-
// region (recorded for reporting + provider scoping
|
|
4578
|
-
//
|
|
4680
|
+
// region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
4681
|
+
// presentment-enabled regions (Stripe) charge in the region currency and the
|
|
4682
|
+
// response carries a presentment overlay; else charged in store base currency.
|
|
4579
4683
|
//
|
|
4580
|
-
// Storefront (public, no apiKey \u2014 storeId mode
|
|
4684
|
+
// Storefront (public, no apiKey \u2014 storeId mode OR vibe-coded mode, salesChannelId 'vc_*';
|
|
4685
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4581
4686
|
interface PublicRegion {
|
|
4582
4687
|
id: string; name: string; slug: string; currency: string;
|
|
4583
4688
|
countries: string[]; taxInclusive: boolean; isDefault: boolean;
|
|
@@ -4629,7 +4734,8 @@ interface AssignTaxClassDto { productIds?: string[]; variantIds?: string[]; cate
|
|
|
4629
4734
|
// deleteTaxClass(id), setDefaultTaxClass(id), assignTaxClass(id, dto),
|
|
4630
4735
|
// mergeTaxClasses(id, targetId)
|
|
4631
4736
|
//
|
|
4632
|
-
// SDK methods (storefront, public \u2014 storeId mode, no apiKey
|
|
4737
|
+
// SDK methods (storefront, public \u2014 storeId mode OR vibe-coded mode 'vc_*', no apiKey;
|
|
4738
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4633
4739
|
// getStoreRegions(), getStoreRegion(id), getAutoRegion(country)
|
|
4634
4740
|
// getStoreTaxClasses(), estimateTax({ country?, subtotal })
|
|
4635
4741
|
//
|
package/dist/bin/stdio.js
CHANGED
|
@@ -160,7 +160,26 @@ import {
|
|
|
160
160
|
getDescriptionContent, isHtmlDescription,
|
|
161
161
|
getProductMetafieldValue, getProductCustomizationFields,
|
|
162
162
|
} from 'brainerce';
|
|
163
|
-
|
|
163
|
+
\`\`\`
|
|
164
|
+
|
|
165
|
+
### Available features \u2014 request each topic for full docs
|
|
166
|
+
|
|
167
|
+
| Topic | What it covers |
|
|
168
|
+
|-------|----------------|
|
|
169
|
+
| \`products\` | Catalog listing, search, PDP, variants, swatches, stock, attributes |
|
|
170
|
+
| \`cart\` | Guest + logged-in cart, add/remove/update, reservations |
|
|
171
|
+
| \`checkout\` | Guest + customer checkout, shipping, address |
|
|
172
|
+
| \`payment\` | Payment providers, intents, status polling, confirmation |
|
|
173
|
+
| \`auth\` | Customer sign-up, login, OAuth, email verification |
|
|
174
|
+
| \`discounts\` | Coupons, banners, nudges |
|
|
175
|
+
| \`recommendations\` | Cross-sell / upsell grids |
|
|
176
|
+
| \`reviews\` | Product reviews & ratings |
|
|
177
|
+
| \`inventory\` | Stock badges, reservation countdown |
|
|
178
|
+
| \`inquiries\` | Contact / custom forms |
|
|
179
|
+
| \`i18n\` | Multi-language, RTL |
|
|
180
|
+
| \`admin\` | Server-to-server admin API (products, orders, customers) |
|
|
181
|
+
| \`storefront-bot\` | **AI chat widget** \u2014 add Brainerce's AI shopping assistant to any page with one script tag or \`BrainerceBot.mount()\`. Answers product questions, shows recommendation cards, handles cart adds, opens escalation form. |
|
|
182
|
+
| \`content\` | Blog, articles, static pages |`;
|
|
164
183
|
}
|
|
165
184
|
function getCheckoutFlowSection(_currency) {
|
|
166
185
|
return `## \u26A0\uFE0F CHECKOUT FLOW (CRITICAL \u2014 READ CAREFULLY!)
|
|
@@ -1882,8 +1901,8 @@ function getTaxDisplaySection(_currency) {
|
|
|
1882
1901
|
| Product Page | \u274C No | "Price excludes tax" (optional note) |
|
|
1883
1902
|
| Cart Page | \u26A0\uFE0F "Calculated at checkout" | No address yet |
|
|
1884
1903
|
| Checkout (before address) | \u26A0\uFE0F "Enter address to calculate" | Waiting for location |
|
|
1885
|
-
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` |
|
|
1886
|
-
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` |
|
|
1904
|
+
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` (0 in VAT mode \u2192 \`checkout.taxBreakdown.totalTax\`) |
|
|
1905
|
+
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` (0 in VAT mode \u2192 \`order.taxBreakdown.totalTax\`) |
|
|
1887
1906
|
|
|
1888
1907
|
### Cart Page \u2014 Partial Display
|
|
1889
1908
|
|
|
@@ -1935,7 +1954,13 @@ const totals = getCartTotals(cart);
|
|
|
1935
1954
|
<div className="flex justify-between text-green-600"><span>Discount</span><span>-{formatPrice(order.discountAmount, { currency: order.currency })}</span></div>
|
|
1936
1955
|
)}
|
|
1937
1956
|
<div className="flex justify-between"><span>Shipping</span><span>{formatPrice(order.shippingAmount, { currency: order.currency })}</span></div>
|
|
1938
|
-
|
|
1957
|
+
{/* Inclusive (VAT) stores keep taxAmount="0" \u2014 the real VAT is on taxBreakdown.totalTax. */}
|
|
1958
|
+
{(() => {
|
|
1959
|
+
const tax = parseFloat(order.taxAmount || '0') || order.taxBreakdown?.totalTax || 0;
|
|
1960
|
+
if (tax <= 0) return null;
|
|
1961
|
+
const incl = order.taxBreakdown?.pricesIncludeTax;
|
|
1962
|
+
return <div className="flex justify-between"><span>{incl ? 'Tax (incl.)' : 'Tax'}</span><span>{formatPrice(tax, { currency: order.currency })}</span></div>;
|
|
1963
|
+
})()}
|
|
1939
1964
|
<div className="flex justify-between font-bold text-lg border-t pt-2"><span>Total Paid</span><span>{formatPrice(order.totalAmount, { currency: order.currency })}</span></div>
|
|
1940
1965
|
</div>
|
|
1941
1966
|
\`\`\``;
|
|
@@ -2064,7 +2089,7 @@ A useful order card includes ALL of the following when the data is present. Each
|
|
|
2064
2089
|
| **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
|
|
2065
2090
|
| **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
|
|
2066
2091
|
| Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
|
|
2067
|
-
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. |
|
|
2092
|
+
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
|
|
2068
2093
|
|
|
2069
2094
|
#### Rendering \`order.items[i].customizations\` by type
|
|
2070
2095
|
|
|
@@ -2509,8 +2534,9 @@ await admin.createTaxRate({ name: 'VAT (Food)', rate: 0, country: 'GB', taxClass
|
|
|
2509
2534
|
await admin.mergeTaxClasses(food.id, standardClassId);
|
|
2510
2535
|
\`\`\`
|
|
2511
2536
|
|
|
2512
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2513
|
-
the
|
|
2537
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2538
|
+
on the \`products:read\` scope every connection already has): \`getStoreTaxClasses()\`
|
|
2539
|
+
lists the store's classes (storefront-safe fields only) for a transparency badge.
|
|
2514
2540
|
|
|
2515
2541
|
For a buyer-facing tax preview on PDP / PLP / cart, \`estimateTax({ country,
|
|
2516
2542
|
subtotal })\` returns the tax portion at the Standard rate for the buyer's
|
|
@@ -2532,8 +2558,10 @@ A region binds countries \u2192 currency + tax-display mode + enabled payment
|
|
|
2532
2558
|
providers. Manage them via the SDK (scopes \`regions:read\` / \`regions:write\`).
|
|
2533
2559
|
\`detectRegion\` is a pure client-side helper to map a buyer's country to a
|
|
2534
2560
|
region; pass the resolved \`regionId\` to \`createCheckout\` to associate the
|
|
2535
|
-
checkout with a region (recorded for reporting + provider scoping
|
|
2536
|
-
|
|
2561
|
+
checkout with a region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
2562
|
+
presentment-enabled regions (Stripe today) charge in the region currency and the
|
|
2563
|
+
checkout response carries a \`presentment\` overlay with the charged amounts;
|
|
2564
|
+
otherwise the checkout is charged in the store base currency.
|
|
2537
2565
|
|
|
2538
2566
|
\`\`\`typescript
|
|
2539
2567
|
const eu = await admin.createRegion({
|
|
@@ -2546,9 +2574,12 @@ const { data: regions } = await admin.getRegions();
|
|
|
2546
2574
|
const region = admin.detectRegion('DE', regions); // \u2192 eu, else default, else null
|
|
2547
2575
|
\`\`\`
|
|
2548
2576
|
|
|
2549
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2550
|
-
|
|
2551
|
-
|
|
2577
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2578
|
+
on the \`products:read\` scope every connection already has): \`getStoreRegions()\`
|
|
2579
|
+
lists active regions, \`getStoreRegion(id)\` returns one region + its payment
|
|
2580
|
+
providers. \`getAutoRegion(country)\` and \`estimateTax({ country, subtotal })\` work
|
|
2581
|
+
in both modes too. Pair with \`detectRegion\` to pick the buyer's region for
|
|
2582
|
+
currency display.
|
|
2552
2583
|
|
|
2553
2584
|
\`\`\`typescript
|
|
2554
2585
|
const store = new BrainerceClient({ storeId: 'store_123' });
|
|
@@ -3172,6 +3203,38 @@ Rules:
|
|
|
3172
3203
|
count updates when the bot adds items.
|
|
3173
3204
|
`;
|
|
3174
3205
|
}
|
|
3206
|
+
function getStorefrontAnalyticsSection(connectionId) {
|
|
3207
|
+
return `## Storefront Traffic Analytics (visits, countries, sources)
|
|
3208
|
+
|
|
3209
|
+
Brainerce can track cookieless storefront traffic \u2014 how many people visit the
|
|
3210
|
+
store, from which countries, and from which traffic sources \u2014 and surface it in
|
|
3211
|
+
the merchant dashboard alongside sales (so the merchant gets a REAL conversion
|
|
3212
|
+
rate: visits \u2192 orders).
|
|
3213
|
+
|
|
3214
|
+
Add ONE line to the storefront's root layout (\`<head>\` or end of \`<body>\`):
|
|
3215
|
+
|
|
3216
|
+
\`\`\`html
|
|
3217
|
+
<script defer src="https://api.brainerce.com/t.js" data-channel="${connectionId}"></script>
|
|
3218
|
+
\`\`\`
|
|
3219
|
+
|
|
3220
|
+
That's it \u2014 the pixel auto-sends a pageview beacon on load and on SPA route
|
|
3221
|
+
changes. Stores scaffolded with \`create-brainerce-store\` already include it.
|
|
3222
|
+
|
|
3223
|
+
Privacy: the pixel is cookieless and stores NO personal data \u2014 no cookie, no
|
|
3224
|
+
cross-site/cross-day tracking, and the visitor IP is resolved to a country
|
|
3225
|
+
server-side then discarded. No cookie-consent banner is required for it under
|
|
3226
|
+
GDPR/ePrivacy.
|
|
3227
|
+
|
|
3228
|
+
CSP: if the storefront sets a Content-Security-Policy, allow the script + beacon:
|
|
3229
|
+
- \`script-src\` \u2026 \`https://api.brainerce.com\`
|
|
3230
|
+
- \`connect-src\` \u2026 \`https://api.brainerce.com\`
|
|
3231
|
+
|
|
3232
|
+
The merchant can toggle tracking per sales channel in the dashboard. The data
|
|
3233
|
+
shows up under Dashboard \u2192 Analytics (visits over time, by country, by source,
|
|
3234
|
+
by device) and can also be asked of the AI assistant ("how many visits did I get
|
|
3235
|
+
this month and from where?").
|
|
3236
|
+
`;
|
|
3237
|
+
}
|
|
3175
3238
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3176
3239
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3177
3240
|
const cur = currency || "USD";
|
|
@@ -3228,6 +3291,9 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3228
3291
|
return getBlogSection();
|
|
3229
3292
|
case "storefront-bot":
|
|
3230
3293
|
return getStorefrontBotSection(cid);
|
|
3294
|
+
case "analytics":
|
|
3295
|
+
case "traffic-analytics":
|
|
3296
|
+
return getStorefrontAnalyticsSection(cid);
|
|
3231
3297
|
case "all":
|
|
3232
3298
|
return [
|
|
3233
3299
|
"# Brainerce SDK \u2014 full topic dump",
|
|
@@ -3296,6 +3362,10 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3296
3362
|
"",
|
|
3297
3363
|
"---",
|
|
3298
3364
|
"",
|
|
3365
|
+
getStorefrontAnalyticsSection(cid),
|
|
3366
|
+
"",
|
|
3367
|
+
"---",
|
|
3368
|
+
"",
|
|
3299
3369
|
getProductCustomizationFieldsSection(),
|
|
3300
3370
|
"",
|
|
3301
3371
|
"---",
|
|
@@ -3365,6 +3435,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3365
3435
|
"inquiries",
|
|
3366
3436
|
"content",
|
|
3367
3437
|
"storefront-bot",
|
|
3438
|
+
"analytics",
|
|
3368
3439
|
"all"
|
|
3369
3440
|
]).describe("The SDK documentation topic to retrieve"),
|
|
3370
3441
|
salesChannelId: import_zod.z.string().optional().describe("Sales channel ID (starts with vc_). Used to personalize setup code."),
|
|
@@ -3402,7 +3473,7 @@ interface Product {
|
|
|
3402
3473
|
priceMin?: string | null; // Lowest variant price (VARIABLE products) \u2014 same as basePrice for VARIABLE, kept for back-compat and JSON-LD range display.
|
|
3403
3474
|
priceMax?: string | null; // Highest variant price (VARIABLE products). Use with priceMin for "\u20AA49 \u2013 \u20AA199" range.
|
|
3404
3475
|
priceVaries?: boolean; // true when variant prices differ \u2014 show "\u20AA49 \u2013 \u20AA199" range.
|
|
3405
|
-
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive
|
|
3476
|
+
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive. For presentment-enabled regions (Stripe) the displayPrice uses the same buffered rate the checkout charges (browsed == checkout); for display-only regions it is mid-market and the buyer is charged in store currency at checkout (payment provider handles customer-side FX).
|
|
3406
3477
|
displayPrice?: string; // basePrice \xD7 daily FX rate, in displayCurrency.
|
|
3407
3478
|
displaySalePrice?: string; // salePrice \xD7 rate (if salePrice present).
|
|
3408
3479
|
displayPriceMin?: string; // priceMin \xD7 rate (VARIABLE products).
|
|
@@ -3656,7 +3727,7 @@ interface Checkout {
|
|
|
3656
3727
|
status: CheckoutStatus;
|
|
3657
3728
|
email?: string | null;
|
|
3658
3729
|
customerId?: string | null;
|
|
3659
|
-
regionId?: string | null; // multi-region: recorded for reporting + provider scoping (currency
|
|
3730
|
+
regionId?: string | null; // multi-region: recorded for reporting + provider scoping. FX-at-checkout: presentment-enabled regions (Stripe) charge in the region currency \u2014 see the presentment field below; else charged in store base currency
|
|
3660
3731
|
shippingAddress?: CheckoutAddress | null;
|
|
3661
3732
|
billingAddress?: CheckoutAddress | null;
|
|
3662
3733
|
shippingRateId?: string | null;
|
|
@@ -3676,6 +3747,18 @@ interface Checkout {
|
|
|
3676
3747
|
itemCount: number;
|
|
3677
3748
|
availableShippingRates?: ShippingRate[];
|
|
3678
3749
|
reservation?: ReservationInfo;
|
|
3750
|
+
// FX-at-checkout: present ONLY when the region charges in its own currency (presentment-enabled \u2014 Stripe today). The amounts the buyer is actually CHARGED, in presentment.currency; presentment.total equals the payment intent amount. Render these (not total/currency above) when present. Absent = charged in store base currency. The fields above stay the base reference.
|
|
3751
|
+
presentment?: {
|
|
3752
|
+
currency: string; // ISO-4217 charged currency, e.g. "EUR"
|
|
3753
|
+
subtotal: string;
|
|
3754
|
+
discountAmount: string;
|
|
3755
|
+
shippingAmount: string;
|
|
3756
|
+
taxAmount: string;
|
|
3757
|
+
surchargeAmount: string;
|
|
3758
|
+
total: string; // equals the charged amount, to the cent
|
|
3759
|
+
fxChargingRate: string; // base\u2192presentment rate (buffer included)
|
|
3760
|
+
fxBufferPercent: string | null;
|
|
3761
|
+
};
|
|
3679
3762
|
createdAt: string;
|
|
3680
3763
|
updatedAt: string;
|
|
3681
3764
|
}
|
|
@@ -3767,6 +3850,8 @@ interface Order {
|
|
|
3767
3850
|
totalAmount: string; // ALWAYS use this, not .total
|
|
3768
3851
|
total?: string; // optional alias
|
|
3769
3852
|
currency?: string;
|
|
3853
|
+
taxAmount?: string | null; // 0 in inclusive (VAT) mode \u2014 read taxBreakdown.totalTax
|
|
3854
|
+
taxBreakdown?: TaxBreakdown | null; // carries the included VAT when prices include tax
|
|
3770
3855
|
customer?: OrderCustomer | null;
|
|
3771
3856
|
items: OrderItem[];
|
|
3772
3857
|
itemCount?: number;
|
|
@@ -4051,10 +4136,22 @@ interface CartAppliedDiscount {
|
|
|
4051
4136
|
}
|
|
4052
4137
|
|
|
4053
4138
|
// Recommendation types
|
|
4139
|
+
interface RecommendationVariant {
|
|
4140
|
+
id: string; name?: string; price?: string; salePrice?: string;
|
|
4141
|
+
attributes?: Record<string, string>; image?: ProductImage | string; inventory?: InventoryInfo;
|
|
4142
|
+
}
|
|
4054
4143
|
interface ProductRecommendation {
|
|
4055
|
-
id: string; name: string; slug: string;
|
|
4144
|
+
id: string; name: string; slug: string;
|
|
4145
|
+
// For a VARIABLE target, basePrice is the pinned variant's price (when
|
|
4146
|
+
// targetVariantId is set) or the "from {min variant}" range \u2014 never 0.
|
|
4147
|
+
basePrice: string; salePrice?: string;
|
|
4056
4148
|
images: ProductImage[]; type: 'SIMPLE' | 'VARIABLE'; inventory?: InventoryInfo;
|
|
4057
4149
|
relationType: string;
|
|
4150
|
+
// Variant context (VARIABLE targets):
|
|
4151
|
+
targetVariantId?: string | null; // merchant-pinned variant, or null
|
|
4152
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4153
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4154
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4058
4155
|
}
|
|
4059
4156
|
interface ProductRecommendationsResponse {
|
|
4060
4157
|
crossSells: ProductRecommendation[];
|
|
@@ -4088,6 +4185,12 @@ interface CartBundleOfferOfferedProduct {
|
|
|
4088
4185
|
salePrice: string | null;
|
|
4089
4186
|
images: Array<{ url: string }>;
|
|
4090
4187
|
type: string;
|
|
4188
|
+
// Variant context (VARIABLE offered products):
|
|
4189
|
+
variantId?: string | null; // merchant-pinned variant for this slot
|
|
4190
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4191
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4192
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4193
|
+
// originalPrice = pinned/cheapest variant price for VARIABLE (not a 0 parent base).
|
|
4091
4194
|
originalPrice: string;
|
|
4092
4195
|
discountedPrice: string;
|
|
4093
4196
|
}
|
|
@@ -4571,10 +4674,12 @@ interface UpdateRegionDto extends Partial<CreateRegionDto> { isActive?: boolean
|
|
|
4571
4674
|
// client.detectRegion(country, regions): Region | null \u2014 pure, no network.
|
|
4572
4675
|
// \u2192 region whose countries includes the code, else the default region, else null.
|
|
4573
4676
|
// Pass the resolved regionId to createCheckout to associate the checkout with a
|
|
4574
|
-
// region (recorded for reporting + provider scoping
|
|
4575
|
-
//
|
|
4677
|
+
// region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
4678
|
+
// presentment-enabled regions (Stripe) charge in the region currency and the
|
|
4679
|
+
// response carries a presentment overlay; else charged in store base currency.
|
|
4576
4680
|
//
|
|
4577
|
-
// Storefront (public, no apiKey \u2014 storeId mode
|
|
4681
|
+
// Storefront (public, no apiKey \u2014 storeId mode OR vibe-coded mode, salesChannelId 'vc_*';
|
|
4682
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4578
4683
|
interface PublicRegion {
|
|
4579
4684
|
id: string; name: string; slug: string; currency: string;
|
|
4580
4685
|
countries: string[]; taxInclusive: boolean; isDefault: boolean;
|
|
@@ -4626,7 +4731,8 @@ interface AssignTaxClassDto { productIds?: string[]; variantIds?: string[]; cate
|
|
|
4626
4731
|
// deleteTaxClass(id), setDefaultTaxClass(id), assignTaxClass(id, dto),
|
|
4627
4732
|
// mergeTaxClasses(id, targetId)
|
|
4628
4733
|
//
|
|
4629
|
-
// SDK methods (storefront, public \u2014 storeId mode, no apiKey
|
|
4734
|
+
// SDK methods (storefront, public \u2014 storeId mode OR vibe-coded mode 'vc_*', no apiKey;
|
|
4735
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4630
4736
|
// getStoreRegions(), getStoreRegion(id), getAutoRegion(country)
|
|
4631
4737
|
// getStoreTaxClasses(), estimateTax({ country?, subtotal })
|
|
4632
4738
|
//
|
package/dist/index.js
CHANGED
|
@@ -186,7 +186,26 @@ import {
|
|
|
186
186
|
getDescriptionContent, isHtmlDescription,
|
|
187
187
|
getProductMetafieldValue, getProductCustomizationFields,
|
|
188
188
|
} from 'brainerce';
|
|
189
|
-
|
|
189
|
+
\`\`\`
|
|
190
|
+
|
|
191
|
+
### Available features \u2014 request each topic for full docs
|
|
192
|
+
|
|
193
|
+
| Topic | What it covers |
|
|
194
|
+
|-------|----------------|
|
|
195
|
+
| \`products\` | Catalog listing, search, PDP, variants, swatches, stock, attributes |
|
|
196
|
+
| \`cart\` | Guest + logged-in cart, add/remove/update, reservations |
|
|
197
|
+
| \`checkout\` | Guest + customer checkout, shipping, address |
|
|
198
|
+
| \`payment\` | Payment providers, intents, status polling, confirmation |
|
|
199
|
+
| \`auth\` | Customer sign-up, login, OAuth, email verification |
|
|
200
|
+
| \`discounts\` | Coupons, banners, nudges |
|
|
201
|
+
| \`recommendations\` | Cross-sell / upsell grids |
|
|
202
|
+
| \`reviews\` | Product reviews & ratings |
|
|
203
|
+
| \`inventory\` | Stock badges, reservation countdown |
|
|
204
|
+
| \`inquiries\` | Contact / custom forms |
|
|
205
|
+
| \`i18n\` | Multi-language, RTL |
|
|
206
|
+
| \`admin\` | Server-to-server admin API (products, orders, customers) |
|
|
207
|
+
| \`storefront-bot\` | **AI chat widget** \u2014 add Brainerce's AI shopping assistant to any page with one script tag or \`BrainerceBot.mount()\`. Answers product questions, shows recommendation cards, handles cart adds, opens escalation form. |
|
|
208
|
+
| \`content\` | Blog, articles, static pages |`;
|
|
190
209
|
}
|
|
191
210
|
function getCheckoutFlowSection(_currency) {
|
|
192
211
|
return `## \u26A0\uFE0F CHECKOUT FLOW (CRITICAL \u2014 READ CAREFULLY!)
|
|
@@ -1908,8 +1927,8 @@ function getTaxDisplaySection(_currency) {
|
|
|
1908
1927
|
| Product Page | \u274C No | "Price excludes tax" (optional note) |
|
|
1909
1928
|
| Cart Page | \u26A0\uFE0F "Calculated at checkout" | No address yet |
|
|
1910
1929
|
| Checkout (before address) | \u26A0\uFE0F "Enter address to calculate" | Waiting for location |
|
|
1911
|
-
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` |
|
|
1912
|
-
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` |
|
|
1930
|
+
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` (0 in VAT mode \u2192 \`checkout.taxBreakdown.totalTax\`) |
|
|
1931
|
+
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` (0 in VAT mode \u2192 \`order.taxBreakdown.totalTax\`) |
|
|
1913
1932
|
|
|
1914
1933
|
### Cart Page \u2014 Partial Display
|
|
1915
1934
|
|
|
@@ -1961,7 +1980,13 @@ const totals = getCartTotals(cart);
|
|
|
1961
1980
|
<div className="flex justify-between text-green-600"><span>Discount</span><span>-{formatPrice(order.discountAmount, { currency: order.currency })}</span></div>
|
|
1962
1981
|
)}
|
|
1963
1982
|
<div className="flex justify-between"><span>Shipping</span><span>{formatPrice(order.shippingAmount, { currency: order.currency })}</span></div>
|
|
1964
|
-
|
|
1983
|
+
{/* Inclusive (VAT) stores keep taxAmount="0" \u2014 the real VAT is on taxBreakdown.totalTax. */}
|
|
1984
|
+
{(() => {
|
|
1985
|
+
const tax = parseFloat(order.taxAmount || '0') || order.taxBreakdown?.totalTax || 0;
|
|
1986
|
+
if (tax <= 0) return null;
|
|
1987
|
+
const incl = order.taxBreakdown?.pricesIncludeTax;
|
|
1988
|
+
return <div className="flex justify-between"><span>{incl ? 'Tax (incl.)' : 'Tax'}</span><span>{formatPrice(tax, { currency: order.currency })}</span></div>;
|
|
1989
|
+
})()}
|
|
1965
1990
|
<div className="flex justify-between font-bold text-lg border-t pt-2"><span>Total Paid</span><span>{formatPrice(order.totalAmount, { currency: order.currency })}</span></div>
|
|
1966
1991
|
</div>
|
|
1967
1992
|
\`\`\``;
|
|
@@ -2090,7 +2115,7 @@ A useful order card includes ALL of the following when the data is present. Each
|
|
|
2090
2115
|
| **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
|
|
2091
2116
|
| **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
|
|
2092
2117
|
| Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
|
|
2093
|
-
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. |
|
|
2118
|
+
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
|
|
2094
2119
|
|
|
2095
2120
|
#### Rendering \`order.items[i].customizations\` by type
|
|
2096
2121
|
|
|
@@ -2535,8 +2560,9 @@ await admin.createTaxRate({ name: 'VAT (Food)', rate: 0, country: 'GB', taxClass
|
|
|
2535
2560
|
await admin.mergeTaxClasses(food.id, standardClassId);
|
|
2536
2561
|
\`\`\`
|
|
2537
2562
|
|
|
2538
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2539
|
-
the
|
|
2563
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2564
|
+
on the \`products:read\` scope every connection already has): \`getStoreTaxClasses()\`
|
|
2565
|
+
lists the store's classes (storefront-safe fields only) for a transparency badge.
|
|
2540
2566
|
|
|
2541
2567
|
For a buyer-facing tax preview on PDP / PLP / cart, \`estimateTax({ country,
|
|
2542
2568
|
subtotal })\` returns the tax portion at the Standard rate for the buyer's
|
|
@@ -2558,8 +2584,10 @@ A region binds countries \u2192 currency + tax-display mode + enabled payment
|
|
|
2558
2584
|
providers. Manage them via the SDK (scopes \`regions:read\` / \`regions:write\`).
|
|
2559
2585
|
\`detectRegion\` is a pure client-side helper to map a buyer's country to a
|
|
2560
2586
|
region; pass the resolved \`regionId\` to \`createCheckout\` to associate the
|
|
2561
|
-
checkout with a region (recorded for reporting + provider scoping
|
|
2562
|
-
|
|
2587
|
+
checkout with a region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
2588
|
+
presentment-enabled regions (Stripe today) charge in the region currency and the
|
|
2589
|
+
checkout response carries a \`presentment\` overlay with the charged amounts;
|
|
2590
|
+
otherwise the checkout is charged in the store base currency.
|
|
2563
2591
|
|
|
2564
2592
|
\`\`\`typescript
|
|
2565
2593
|
const eu = await admin.createRegion({
|
|
@@ -2572,9 +2600,12 @@ const { data: regions } = await admin.getRegions();
|
|
|
2572
2600
|
const region = admin.detectRegion('DE', regions); // \u2192 eu, else default, else null
|
|
2573
2601
|
\`\`\`
|
|
2574
2602
|
|
|
2575
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2576
|
-
|
|
2577
|
-
|
|
2603
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2604
|
+
on the \`products:read\` scope every connection already has): \`getStoreRegions()\`
|
|
2605
|
+
lists active regions, \`getStoreRegion(id)\` returns one region + its payment
|
|
2606
|
+
providers. \`getAutoRegion(country)\` and \`estimateTax({ country, subtotal })\` work
|
|
2607
|
+
in both modes too. Pair with \`detectRegion\` to pick the buyer's region for
|
|
2608
|
+
currency display.
|
|
2578
2609
|
|
|
2579
2610
|
\`\`\`typescript
|
|
2580
2611
|
const store = new BrainerceClient({ storeId: 'store_123' });
|
|
@@ -3198,6 +3229,38 @@ Rules:
|
|
|
3198
3229
|
count updates when the bot adds items.
|
|
3199
3230
|
`;
|
|
3200
3231
|
}
|
|
3232
|
+
function getStorefrontAnalyticsSection(connectionId) {
|
|
3233
|
+
return `## Storefront Traffic Analytics (visits, countries, sources)
|
|
3234
|
+
|
|
3235
|
+
Brainerce can track cookieless storefront traffic \u2014 how many people visit the
|
|
3236
|
+
store, from which countries, and from which traffic sources \u2014 and surface it in
|
|
3237
|
+
the merchant dashboard alongside sales (so the merchant gets a REAL conversion
|
|
3238
|
+
rate: visits \u2192 orders).
|
|
3239
|
+
|
|
3240
|
+
Add ONE line to the storefront's root layout (\`<head>\` or end of \`<body>\`):
|
|
3241
|
+
|
|
3242
|
+
\`\`\`html
|
|
3243
|
+
<script defer src="https://api.brainerce.com/t.js" data-channel="${connectionId}"></script>
|
|
3244
|
+
\`\`\`
|
|
3245
|
+
|
|
3246
|
+
That's it \u2014 the pixel auto-sends a pageview beacon on load and on SPA route
|
|
3247
|
+
changes. Stores scaffolded with \`create-brainerce-store\` already include it.
|
|
3248
|
+
|
|
3249
|
+
Privacy: the pixel is cookieless and stores NO personal data \u2014 no cookie, no
|
|
3250
|
+
cross-site/cross-day tracking, and the visitor IP is resolved to a country
|
|
3251
|
+
server-side then discarded. No cookie-consent banner is required for it under
|
|
3252
|
+
GDPR/ePrivacy.
|
|
3253
|
+
|
|
3254
|
+
CSP: if the storefront sets a Content-Security-Policy, allow the script + beacon:
|
|
3255
|
+
- \`script-src\` \u2026 \`https://api.brainerce.com\`
|
|
3256
|
+
- \`connect-src\` \u2026 \`https://api.brainerce.com\`
|
|
3257
|
+
|
|
3258
|
+
The merchant can toggle tracking per sales channel in the dashboard. The data
|
|
3259
|
+
shows up under Dashboard \u2192 Analytics (visits over time, by country, by source,
|
|
3260
|
+
by device) and can also be asked of the AI assistant ("how many visits did I get
|
|
3261
|
+
this month and from where?").
|
|
3262
|
+
`;
|
|
3263
|
+
}
|
|
3201
3264
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3202
3265
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3203
3266
|
const cur = currency || "USD";
|
|
@@ -3254,6 +3317,9 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3254
3317
|
return getBlogSection();
|
|
3255
3318
|
case "storefront-bot":
|
|
3256
3319
|
return getStorefrontBotSection(cid);
|
|
3320
|
+
case "analytics":
|
|
3321
|
+
case "traffic-analytics":
|
|
3322
|
+
return getStorefrontAnalyticsSection(cid);
|
|
3257
3323
|
case "all":
|
|
3258
3324
|
return [
|
|
3259
3325
|
"# Brainerce SDK \u2014 full topic dump",
|
|
@@ -3322,6 +3388,10 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3322
3388
|
"",
|
|
3323
3389
|
"---",
|
|
3324
3390
|
"",
|
|
3391
|
+
getStorefrontAnalyticsSection(cid),
|
|
3392
|
+
"",
|
|
3393
|
+
"---",
|
|
3394
|
+
"",
|
|
3325
3395
|
getProductCustomizationFieldsSection(),
|
|
3326
3396
|
"",
|
|
3327
3397
|
"---",
|
|
@@ -3391,6 +3461,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3391
3461
|
"inquiries",
|
|
3392
3462
|
"content",
|
|
3393
3463
|
"storefront-bot",
|
|
3464
|
+
"analytics",
|
|
3394
3465
|
"all"
|
|
3395
3466
|
]).describe("The SDK documentation topic to retrieve"),
|
|
3396
3467
|
salesChannelId: import_zod.z.string().optional().describe("Sales channel ID (starts with vc_). Used to personalize setup code."),
|
|
@@ -3428,7 +3499,7 @@ interface Product {
|
|
|
3428
3499
|
priceMin?: string | null; // Lowest variant price (VARIABLE products) \u2014 same as basePrice for VARIABLE, kept for back-compat and JSON-LD range display.
|
|
3429
3500
|
priceMax?: string | null; // Highest variant price (VARIABLE products). Use with priceMin for "\u20AA49 \u2013 \u20AA199" range.
|
|
3430
3501
|
priceVaries?: boolean; // true when variant prices differ \u2014 show "\u20AA49 \u2013 \u20AA199" range.
|
|
3431
|
-
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive
|
|
3502
|
+
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive. For presentment-enabled regions (Stripe) the displayPrice uses the same buffered rate the checkout charges (browsed == checkout); for display-only regions it is mid-market and the buyer is charged in store currency at checkout (payment provider handles customer-side FX).
|
|
3432
3503
|
displayPrice?: string; // basePrice \xD7 daily FX rate, in displayCurrency.
|
|
3433
3504
|
displaySalePrice?: string; // salePrice \xD7 rate (if salePrice present).
|
|
3434
3505
|
displayPriceMin?: string; // priceMin \xD7 rate (VARIABLE products).
|
|
@@ -3682,7 +3753,7 @@ interface Checkout {
|
|
|
3682
3753
|
status: CheckoutStatus;
|
|
3683
3754
|
email?: string | null;
|
|
3684
3755
|
customerId?: string | null;
|
|
3685
|
-
regionId?: string | null; // multi-region: recorded for reporting + provider scoping (currency
|
|
3756
|
+
regionId?: string | null; // multi-region: recorded for reporting + provider scoping. FX-at-checkout: presentment-enabled regions (Stripe) charge in the region currency \u2014 see the presentment field below; else charged in store base currency
|
|
3686
3757
|
shippingAddress?: CheckoutAddress | null;
|
|
3687
3758
|
billingAddress?: CheckoutAddress | null;
|
|
3688
3759
|
shippingRateId?: string | null;
|
|
@@ -3702,6 +3773,18 @@ interface Checkout {
|
|
|
3702
3773
|
itemCount: number;
|
|
3703
3774
|
availableShippingRates?: ShippingRate[];
|
|
3704
3775
|
reservation?: ReservationInfo;
|
|
3776
|
+
// FX-at-checkout: present ONLY when the region charges in its own currency (presentment-enabled \u2014 Stripe today). The amounts the buyer is actually CHARGED, in presentment.currency; presentment.total equals the payment intent amount. Render these (not total/currency above) when present. Absent = charged in store base currency. The fields above stay the base reference.
|
|
3777
|
+
presentment?: {
|
|
3778
|
+
currency: string; // ISO-4217 charged currency, e.g. "EUR"
|
|
3779
|
+
subtotal: string;
|
|
3780
|
+
discountAmount: string;
|
|
3781
|
+
shippingAmount: string;
|
|
3782
|
+
taxAmount: string;
|
|
3783
|
+
surchargeAmount: string;
|
|
3784
|
+
total: string; // equals the charged amount, to the cent
|
|
3785
|
+
fxChargingRate: string; // base\u2192presentment rate (buffer included)
|
|
3786
|
+
fxBufferPercent: string | null;
|
|
3787
|
+
};
|
|
3705
3788
|
createdAt: string;
|
|
3706
3789
|
updatedAt: string;
|
|
3707
3790
|
}
|
|
@@ -3793,6 +3876,8 @@ interface Order {
|
|
|
3793
3876
|
totalAmount: string; // ALWAYS use this, not .total
|
|
3794
3877
|
total?: string; // optional alias
|
|
3795
3878
|
currency?: string;
|
|
3879
|
+
taxAmount?: string | null; // 0 in inclusive (VAT) mode \u2014 read taxBreakdown.totalTax
|
|
3880
|
+
taxBreakdown?: TaxBreakdown | null; // carries the included VAT when prices include tax
|
|
3796
3881
|
customer?: OrderCustomer | null;
|
|
3797
3882
|
items: OrderItem[];
|
|
3798
3883
|
itemCount?: number;
|
|
@@ -4077,10 +4162,22 @@ interface CartAppliedDiscount {
|
|
|
4077
4162
|
}
|
|
4078
4163
|
|
|
4079
4164
|
// Recommendation types
|
|
4165
|
+
interface RecommendationVariant {
|
|
4166
|
+
id: string; name?: string; price?: string; salePrice?: string;
|
|
4167
|
+
attributes?: Record<string, string>; image?: ProductImage | string; inventory?: InventoryInfo;
|
|
4168
|
+
}
|
|
4080
4169
|
interface ProductRecommendation {
|
|
4081
|
-
id: string; name: string; slug: string;
|
|
4170
|
+
id: string; name: string; slug: string;
|
|
4171
|
+
// For a VARIABLE target, basePrice is the pinned variant's price (when
|
|
4172
|
+
// targetVariantId is set) or the "from {min variant}" range \u2014 never 0.
|
|
4173
|
+
basePrice: string; salePrice?: string;
|
|
4082
4174
|
images: ProductImage[]; type: 'SIMPLE' | 'VARIABLE'; inventory?: InventoryInfo;
|
|
4083
4175
|
relationType: string;
|
|
4176
|
+
// Variant context (VARIABLE targets):
|
|
4177
|
+
targetVariantId?: string | null; // merchant-pinned variant, or null
|
|
4178
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4179
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4180
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4084
4181
|
}
|
|
4085
4182
|
interface ProductRecommendationsResponse {
|
|
4086
4183
|
crossSells: ProductRecommendation[];
|
|
@@ -4114,6 +4211,12 @@ interface CartBundleOfferOfferedProduct {
|
|
|
4114
4211
|
salePrice: string | null;
|
|
4115
4212
|
images: Array<{ url: string }>;
|
|
4116
4213
|
type: string;
|
|
4214
|
+
// Variant context (VARIABLE offered products):
|
|
4215
|
+
variantId?: string | null; // merchant-pinned variant for this slot
|
|
4216
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4217
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4218
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4219
|
+
// originalPrice = pinned/cheapest variant price for VARIABLE (not a 0 parent base).
|
|
4117
4220
|
originalPrice: string;
|
|
4118
4221
|
discountedPrice: string;
|
|
4119
4222
|
}
|
|
@@ -4597,10 +4700,12 @@ interface UpdateRegionDto extends Partial<CreateRegionDto> { isActive?: boolean
|
|
|
4597
4700
|
// client.detectRegion(country, regions): Region | null \u2014 pure, no network.
|
|
4598
4701
|
// \u2192 region whose countries includes the code, else the default region, else null.
|
|
4599
4702
|
// Pass the resolved regionId to createCheckout to associate the checkout with a
|
|
4600
|
-
// region (recorded for reporting + provider scoping
|
|
4601
|
-
//
|
|
4703
|
+
// region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
4704
|
+
// presentment-enabled regions (Stripe) charge in the region currency and the
|
|
4705
|
+
// response carries a presentment overlay; else charged in store base currency.
|
|
4602
4706
|
//
|
|
4603
|
-
// Storefront (public, no apiKey \u2014 storeId mode
|
|
4707
|
+
// Storefront (public, no apiKey \u2014 storeId mode OR vibe-coded mode, salesChannelId 'vc_*';
|
|
4708
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4604
4709
|
interface PublicRegion {
|
|
4605
4710
|
id: string; name: string; slug: string; currency: string;
|
|
4606
4711
|
countries: string[]; taxInclusive: boolean; isDefault: boolean;
|
|
@@ -4652,7 +4757,8 @@ interface AssignTaxClassDto { productIds?: string[]; variantIds?: string[]; cate
|
|
|
4652
4757
|
// deleteTaxClass(id), setDefaultTaxClass(id), assignTaxClass(id, dto),
|
|
4653
4758
|
// mergeTaxClasses(id, targetId)
|
|
4654
4759
|
//
|
|
4655
|
-
// SDK methods (storefront, public \u2014 storeId mode, no apiKey
|
|
4760
|
+
// SDK methods (storefront, public \u2014 storeId mode OR vibe-coded mode 'vc_*', no apiKey;
|
|
4761
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4656
4762
|
// getStoreRegions(), getStoreRegion(id), getAutoRegion(country)
|
|
4657
4763
|
// getStoreTaxClasses(), estimateTax({ country?, subtotal })
|
|
4658
4764
|
//
|
package/dist/index.mjs
CHANGED
|
@@ -154,7 +154,26 @@ import {
|
|
|
154
154
|
getDescriptionContent, isHtmlDescription,
|
|
155
155
|
getProductMetafieldValue, getProductCustomizationFields,
|
|
156
156
|
} from 'brainerce';
|
|
157
|
-
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
### Available features \u2014 request each topic for full docs
|
|
160
|
+
|
|
161
|
+
| Topic | What it covers |
|
|
162
|
+
|-------|----------------|
|
|
163
|
+
| \`products\` | Catalog listing, search, PDP, variants, swatches, stock, attributes |
|
|
164
|
+
| \`cart\` | Guest + logged-in cart, add/remove/update, reservations |
|
|
165
|
+
| \`checkout\` | Guest + customer checkout, shipping, address |
|
|
166
|
+
| \`payment\` | Payment providers, intents, status polling, confirmation |
|
|
167
|
+
| \`auth\` | Customer sign-up, login, OAuth, email verification |
|
|
168
|
+
| \`discounts\` | Coupons, banners, nudges |
|
|
169
|
+
| \`recommendations\` | Cross-sell / upsell grids |
|
|
170
|
+
| \`reviews\` | Product reviews & ratings |
|
|
171
|
+
| \`inventory\` | Stock badges, reservation countdown |
|
|
172
|
+
| \`inquiries\` | Contact / custom forms |
|
|
173
|
+
| \`i18n\` | Multi-language, RTL |
|
|
174
|
+
| \`admin\` | Server-to-server admin API (products, orders, customers) |
|
|
175
|
+
| \`storefront-bot\` | **AI chat widget** \u2014 add Brainerce's AI shopping assistant to any page with one script tag or \`BrainerceBot.mount()\`. Answers product questions, shows recommendation cards, handles cart adds, opens escalation form. |
|
|
176
|
+
| \`content\` | Blog, articles, static pages |`;
|
|
158
177
|
}
|
|
159
178
|
function getCheckoutFlowSection(_currency) {
|
|
160
179
|
return `## \u26A0\uFE0F CHECKOUT FLOW (CRITICAL \u2014 READ CAREFULLY!)
|
|
@@ -1876,8 +1895,8 @@ function getTaxDisplaySection(_currency) {
|
|
|
1876
1895
|
| Product Page | \u274C No | "Price excludes tax" (optional note) |
|
|
1877
1896
|
| Cart Page | \u26A0\uFE0F "Calculated at checkout" | No address yet |
|
|
1878
1897
|
| Checkout (before address) | \u26A0\uFE0F "Enter address to calculate" | Waiting for location |
|
|
1879
|
-
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` |
|
|
1880
|
-
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` |
|
|
1898
|
+
| Checkout (after address) | \u2705 **REQUIRED** | \`checkout.taxAmount\` (0 in VAT mode \u2192 \`checkout.taxBreakdown.totalTax\`) |
|
|
1899
|
+
| Order Confirmation | \u2705 **REQUIRED** | \`order.taxAmount\` (0 in VAT mode \u2192 \`order.taxBreakdown.totalTax\`) |
|
|
1881
1900
|
|
|
1882
1901
|
### Cart Page \u2014 Partial Display
|
|
1883
1902
|
|
|
@@ -1929,7 +1948,13 @@ const totals = getCartTotals(cart);
|
|
|
1929
1948
|
<div className="flex justify-between text-green-600"><span>Discount</span><span>-{formatPrice(order.discountAmount, { currency: order.currency })}</span></div>
|
|
1930
1949
|
)}
|
|
1931
1950
|
<div className="flex justify-between"><span>Shipping</span><span>{formatPrice(order.shippingAmount, { currency: order.currency })}</span></div>
|
|
1932
|
-
|
|
1951
|
+
{/* Inclusive (VAT) stores keep taxAmount="0" \u2014 the real VAT is on taxBreakdown.totalTax. */}
|
|
1952
|
+
{(() => {
|
|
1953
|
+
const tax = parseFloat(order.taxAmount || '0') || order.taxBreakdown?.totalTax || 0;
|
|
1954
|
+
if (tax <= 0) return null;
|
|
1955
|
+
const incl = order.taxBreakdown?.pricesIncludeTax;
|
|
1956
|
+
return <div className="flex justify-between"><span>{incl ? 'Tax (incl.)' : 'Tax'}</span><span>{formatPrice(tax, { currency: order.currency })}</span></div>;
|
|
1957
|
+
})()}
|
|
1933
1958
|
<div className="flex justify-between font-bold text-lg border-t pt-2"><span>Total Paid</span><span>{formatPrice(order.totalAmount, { currency: order.currency })}</span></div>
|
|
1934
1959
|
</div>
|
|
1935
1960
|
\`\`\``;
|
|
@@ -2058,7 +2083,7 @@ A useful order card includes ALL of the following when the data is present. Each
|
|
|
2058
2083
|
| **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
|
|
2059
2084
|
| **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
|
|
2060
2085
|
| Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
|
|
2061
|
-
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. |
|
|
2086
|
+
| Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
|
|
2062
2087
|
|
|
2063
2088
|
#### Rendering \`order.items[i].customizations\` by type
|
|
2064
2089
|
|
|
@@ -2503,8 +2528,9 @@ await admin.createTaxRate({ name: 'VAT (Food)', rate: 0, country: 'GB', taxClass
|
|
|
2503
2528
|
await admin.mergeTaxClasses(food.id, standardClassId);
|
|
2504
2529
|
\`\`\`
|
|
2505
2530
|
|
|
2506
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2507
|
-
the
|
|
2531
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2532
|
+
on the \`products:read\` scope every connection already has): \`getStoreTaxClasses()\`
|
|
2533
|
+
lists the store's classes (storefront-safe fields only) for a transparency badge.
|
|
2508
2534
|
|
|
2509
2535
|
For a buyer-facing tax preview on PDP / PLP / cart, \`estimateTax({ country,
|
|
2510
2536
|
subtotal })\` returns the tax portion at the Standard rate for the buyer's
|
|
@@ -2526,8 +2552,10 @@ A region binds countries \u2192 currency + tax-display mode + enabled payment
|
|
|
2526
2552
|
providers. Manage them via the SDK (scopes \`regions:read\` / \`regions:write\`).
|
|
2527
2553
|
\`detectRegion\` is a pure client-side helper to map a buyer's country to a
|
|
2528
2554
|
region; pass the resolved \`regionId\` to \`createCheckout\` to associate the
|
|
2529
|
-
checkout with a region (recorded for reporting + provider scoping
|
|
2530
|
-
|
|
2555
|
+
checkout with a region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
2556
|
+
presentment-enabled regions (Stripe today) charge in the region currency and the
|
|
2557
|
+
checkout response carries a \`presentment\` overlay with the charged amounts;
|
|
2558
|
+
otherwise the checkout is charged in the store base currency.
|
|
2531
2559
|
|
|
2532
2560
|
\`\`\`typescript
|
|
2533
2561
|
const eu = await admin.createRegion({
|
|
@@ -2540,9 +2568,12 @@ const { data: regions } = await admin.getRegions();
|
|
|
2540
2568
|
const region = admin.detectRegion('DE', regions); // \u2192 eu, else default, else null
|
|
2541
2569
|
\`\`\`
|
|
2542
2570
|
|
|
2543
|
-
Storefront (public, no apiKey \u2014 \`storeId\` mode
|
|
2544
|
-
|
|
2545
|
-
|
|
2571
|
+
Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
|
|
2572
|
+
on the \`products:read\` scope every connection already has): \`getStoreRegions()\`
|
|
2573
|
+
lists active regions, \`getStoreRegion(id)\` returns one region + its payment
|
|
2574
|
+
providers. \`getAutoRegion(country)\` and \`estimateTax({ country, subtotal })\` work
|
|
2575
|
+
in both modes too. Pair with \`detectRegion\` to pick the buyer's region for
|
|
2576
|
+
currency display.
|
|
2546
2577
|
|
|
2547
2578
|
\`\`\`typescript
|
|
2548
2579
|
const store = new BrainerceClient({ storeId: 'store_123' });
|
|
@@ -3166,6 +3197,38 @@ Rules:
|
|
|
3166
3197
|
count updates when the bot adds items.
|
|
3167
3198
|
`;
|
|
3168
3199
|
}
|
|
3200
|
+
function getStorefrontAnalyticsSection(connectionId) {
|
|
3201
|
+
return `## Storefront Traffic Analytics (visits, countries, sources)
|
|
3202
|
+
|
|
3203
|
+
Brainerce can track cookieless storefront traffic \u2014 how many people visit the
|
|
3204
|
+
store, from which countries, and from which traffic sources \u2014 and surface it in
|
|
3205
|
+
the merchant dashboard alongside sales (so the merchant gets a REAL conversion
|
|
3206
|
+
rate: visits \u2192 orders).
|
|
3207
|
+
|
|
3208
|
+
Add ONE line to the storefront's root layout (\`<head>\` or end of \`<body>\`):
|
|
3209
|
+
|
|
3210
|
+
\`\`\`html
|
|
3211
|
+
<script defer src="https://api.brainerce.com/t.js" data-channel="${connectionId}"></script>
|
|
3212
|
+
\`\`\`
|
|
3213
|
+
|
|
3214
|
+
That's it \u2014 the pixel auto-sends a pageview beacon on load and on SPA route
|
|
3215
|
+
changes. Stores scaffolded with \`create-brainerce-store\` already include it.
|
|
3216
|
+
|
|
3217
|
+
Privacy: the pixel is cookieless and stores NO personal data \u2014 no cookie, no
|
|
3218
|
+
cross-site/cross-day tracking, and the visitor IP is resolved to a country
|
|
3219
|
+
server-side then discarded. No cookie-consent banner is required for it under
|
|
3220
|
+
GDPR/ePrivacy.
|
|
3221
|
+
|
|
3222
|
+
CSP: if the storefront sets a Content-Security-Policy, allow the script + beacon:
|
|
3223
|
+
- \`script-src\` \u2026 \`https://api.brainerce.com\`
|
|
3224
|
+
- \`connect-src\` \u2026 \`https://api.brainerce.com\`
|
|
3225
|
+
|
|
3226
|
+
The merchant can toggle tracking per sales channel in the dashboard. The data
|
|
3227
|
+
shows up under Dashboard \u2192 Analytics (visits over time, by country, by source,
|
|
3228
|
+
by device) and can also be asked of the AI assistant ("how many visits did I get
|
|
3229
|
+
this month and from where?").
|
|
3230
|
+
`;
|
|
3231
|
+
}
|
|
3169
3232
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3170
3233
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3171
3234
|
const cur = currency || "USD";
|
|
@@ -3222,6 +3285,9 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3222
3285
|
return getBlogSection();
|
|
3223
3286
|
case "storefront-bot":
|
|
3224
3287
|
return getStorefrontBotSection(cid);
|
|
3288
|
+
case "analytics":
|
|
3289
|
+
case "traffic-analytics":
|
|
3290
|
+
return getStorefrontAnalyticsSection(cid);
|
|
3225
3291
|
case "all":
|
|
3226
3292
|
return [
|
|
3227
3293
|
"# Brainerce SDK \u2014 full topic dump",
|
|
@@ -3290,6 +3356,10 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3290
3356
|
"",
|
|
3291
3357
|
"---",
|
|
3292
3358
|
"",
|
|
3359
|
+
getStorefrontAnalyticsSection(cid),
|
|
3360
|
+
"",
|
|
3361
|
+
"---",
|
|
3362
|
+
"",
|
|
3293
3363
|
getProductCustomizationFieldsSection(),
|
|
3294
3364
|
"",
|
|
3295
3365
|
"---",
|
|
@@ -3359,6 +3429,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3359
3429
|
"inquiries",
|
|
3360
3430
|
"content",
|
|
3361
3431
|
"storefront-bot",
|
|
3432
|
+
"analytics",
|
|
3362
3433
|
"all"
|
|
3363
3434
|
]).describe("The SDK documentation topic to retrieve"),
|
|
3364
3435
|
salesChannelId: z.string().optional().describe("Sales channel ID (starts with vc_). Used to personalize setup code."),
|
|
@@ -3396,7 +3467,7 @@ interface Product {
|
|
|
3396
3467
|
priceMin?: string | null; // Lowest variant price (VARIABLE products) \u2014 same as basePrice for VARIABLE, kept for back-compat and JSON-LD range display.
|
|
3397
3468
|
priceMax?: string | null; // Highest variant price (VARIABLE products). Use with priceMin for "\u20AA49 \u2013 \u20AA199" range.
|
|
3398
3469
|
priceVaries?: boolean; // true when variant prices differ \u2014 show "\u20AA49 \u2013 \u20AA199" range.
|
|
3399
|
-
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive
|
|
3470
|
+
// FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive. For presentment-enabled regions (Stripe) the displayPrice uses the same buffered rate the checkout charges (browsed == checkout); for display-only regions it is mid-market and the buyer is charged in store currency at checkout (payment provider handles customer-side FX).
|
|
3400
3471
|
displayPrice?: string; // basePrice \xD7 daily FX rate, in displayCurrency.
|
|
3401
3472
|
displaySalePrice?: string; // salePrice \xD7 rate (if salePrice present).
|
|
3402
3473
|
displayPriceMin?: string; // priceMin \xD7 rate (VARIABLE products).
|
|
@@ -3650,7 +3721,7 @@ interface Checkout {
|
|
|
3650
3721
|
status: CheckoutStatus;
|
|
3651
3722
|
email?: string | null;
|
|
3652
3723
|
customerId?: string | null;
|
|
3653
|
-
regionId?: string | null; // multi-region: recorded for reporting + provider scoping (currency
|
|
3724
|
+
regionId?: string | null; // multi-region: recorded for reporting + provider scoping. FX-at-checkout: presentment-enabled regions (Stripe) charge in the region currency \u2014 see the presentment field below; else charged in store base currency
|
|
3654
3725
|
shippingAddress?: CheckoutAddress | null;
|
|
3655
3726
|
billingAddress?: CheckoutAddress | null;
|
|
3656
3727
|
shippingRateId?: string | null;
|
|
@@ -3670,6 +3741,18 @@ interface Checkout {
|
|
|
3670
3741
|
itemCount: number;
|
|
3671
3742
|
availableShippingRates?: ShippingRate[];
|
|
3672
3743
|
reservation?: ReservationInfo;
|
|
3744
|
+
// FX-at-checkout: present ONLY when the region charges in its own currency (presentment-enabled \u2014 Stripe today). The amounts the buyer is actually CHARGED, in presentment.currency; presentment.total equals the payment intent amount. Render these (not total/currency above) when present. Absent = charged in store base currency. The fields above stay the base reference.
|
|
3745
|
+
presentment?: {
|
|
3746
|
+
currency: string; // ISO-4217 charged currency, e.g. "EUR"
|
|
3747
|
+
subtotal: string;
|
|
3748
|
+
discountAmount: string;
|
|
3749
|
+
shippingAmount: string;
|
|
3750
|
+
taxAmount: string;
|
|
3751
|
+
surchargeAmount: string;
|
|
3752
|
+
total: string; // equals the charged amount, to the cent
|
|
3753
|
+
fxChargingRate: string; // base\u2192presentment rate (buffer included)
|
|
3754
|
+
fxBufferPercent: string | null;
|
|
3755
|
+
};
|
|
3673
3756
|
createdAt: string;
|
|
3674
3757
|
updatedAt: string;
|
|
3675
3758
|
}
|
|
@@ -3761,6 +3844,8 @@ interface Order {
|
|
|
3761
3844
|
totalAmount: string; // ALWAYS use this, not .total
|
|
3762
3845
|
total?: string; // optional alias
|
|
3763
3846
|
currency?: string;
|
|
3847
|
+
taxAmount?: string | null; // 0 in inclusive (VAT) mode \u2014 read taxBreakdown.totalTax
|
|
3848
|
+
taxBreakdown?: TaxBreakdown | null; // carries the included VAT when prices include tax
|
|
3764
3849
|
customer?: OrderCustomer | null;
|
|
3765
3850
|
items: OrderItem[];
|
|
3766
3851
|
itemCount?: number;
|
|
@@ -4045,10 +4130,22 @@ interface CartAppliedDiscount {
|
|
|
4045
4130
|
}
|
|
4046
4131
|
|
|
4047
4132
|
// Recommendation types
|
|
4133
|
+
interface RecommendationVariant {
|
|
4134
|
+
id: string; name?: string; price?: string; salePrice?: string;
|
|
4135
|
+
attributes?: Record<string, string>; image?: ProductImage | string; inventory?: InventoryInfo;
|
|
4136
|
+
}
|
|
4048
4137
|
interface ProductRecommendation {
|
|
4049
|
-
id: string; name: string; slug: string;
|
|
4138
|
+
id: string; name: string; slug: string;
|
|
4139
|
+
// For a VARIABLE target, basePrice is the pinned variant's price (when
|
|
4140
|
+
// targetVariantId is set) or the "from {min variant}" range \u2014 never 0.
|
|
4141
|
+
basePrice: string; salePrice?: string;
|
|
4050
4142
|
images: ProductImage[]; type: 'SIMPLE' | 'VARIABLE'; inventory?: InventoryInfo;
|
|
4051
4143
|
relationType: string;
|
|
4144
|
+
// Variant context (VARIABLE targets):
|
|
4145
|
+
targetVariantId?: string | null; // merchant-pinned variant, or null
|
|
4146
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4147
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4148
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4052
4149
|
}
|
|
4053
4150
|
interface ProductRecommendationsResponse {
|
|
4054
4151
|
crossSells: ProductRecommendation[];
|
|
@@ -4082,6 +4179,12 @@ interface CartBundleOfferOfferedProduct {
|
|
|
4082
4179
|
salePrice: string | null;
|
|
4083
4180
|
images: Array<{ url: string }>;
|
|
4084
4181
|
type: string;
|
|
4182
|
+
// Variant context (VARIABLE offered products):
|
|
4183
|
+
variantId?: string | null; // merchant-pinned variant for this slot
|
|
4184
|
+
requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
|
|
4185
|
+
pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
|
|
4186
|
+
variants?: RecommendationVariant[]; // present when requiresVariantSelection
|
|
4187
|
+
// originalPrice = pinned/cheapest variant price for VARIABLE (not a 0 parent base).
|
|
4085
4188
|
originalPrice: string;
|
|
4086
4189
|
discountedPrice: string;
|
|
4087
4190
|
}
|
|
@@ -4565,10 +4668,12 @@ interface UpdateRegionDto extends Partial<CreateRegionDto> { isActive?: boolean
|
|
|
4565
4668
|
// client.detectRegion(country, regions): Region | null \u2014 pure, no network.
|
|
4566
4669
|
// \u2192 region whose countries includes the code, else the default region, else null.
|
|
4567
4670
|
// Pass the resolved regionId to createCheckout to associate the checkout with a
|
|
4568
|
-
// region (recorded for reporting + provider scoping
|
|
4569
|
-
//
|
|
4671
|
+
// region (recorded for reporting + provider scoping). FX-at-checkout:
|
|
4672
|
+
// presentment-enabled regions (Stripe) charge in the region currency and the
|
|
4673
|
+
// response carries a presentment overlay; else charged in store base currency.
|
|
4570
4674
|
//
|
|
4571
|
-
// Storefront (public, no apiKey \u2014 storeId mode
|
|
4675
|
+
// Storefront (public, no apiKey \u2014 storeId mode OR vibe-coded mode, salesChannelId 'vc_*';
|
|
4676
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4572
4677
|
interface PublicRegion {
|
|
4573
4678
|
id: string; name: string; slug: string; currency: string;
|
|
4574
4679
|
countries: string[]; taxInclusive: boolean; isDefault: boolean;
|
|
@@ -4620,7 +4725,8 @@ interface AssignTaxClassDto { productIds?: string[]; variantIds?: string[]; cate
|
|
|
4620
4725
|
// deleteTaxClass(id), setDefaultTaxClass(id), assignTaxClass(id, dto),
|
|
4621
4726
|
// mergeTaxClasses(id, targetId)
|
|
4622
4727
|
//
|
|
4623
|
-
// SDK methods (storefront, public \u2014 storeId mode, no apiKey
|
|
4728
|
+
// SDK methods (storefront, public \u2014 storeId mode OR vibe-coded mode 'vc_*', no apiKey;
|
|
4729
|
+
// the vc routes are gated on products:read, which every connection already has):
|
|
4624
4730
|
// getStoreRegions(), getStoreRegion(id), getAutoRegion(country)
|
|
4625
4731
|
// getStoreTaxClasses(), estimateTax({ country?, subtotal })
|
|
4626
4732
|
//
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainerce/mcp-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"description": "Framework-agnostic domain knowledge API for Brainerce. Provides SDK docs, types, business flows, critical rules, and store capabilities to AI tools like Lovable, Cursor, Bolt, v0, and Claude Code. Does NOT provide framework-specific boilerplate — clients build in whatever framework fits.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"brainerce-mcp": "dist/bin/stdio.js"
|