@brainerce/mcp-server 3.12.0 → 3.12.2
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 +198 -12
- package/dist/bin/stdio.js +198 -12
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +198 -12
- package/dist/index.mjs +198 -12
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -744,17 +744,19 @@ function getPaymentProvidersSection() {
|
|
|
744
744
|
return `## Payment Provider Detection (DO THIS FIRST on checkout page!)
|
|
745
745
|
|
|
746
746
|
\`\`\`typescript
|
|
747
|
-
const { hasPayments, providers } = await client.getPaymentProviders();
|
|
747
|
+
const { hasPayments, providers, defaultProvider } = await client.getPaymentProviders();
|
|
748
748
|
if (!hasPayments) {
|
|
749
749
|
// Show error: "Payment is not configured for this store"
|
|
750
750
|
return;
|
|
751
751
|
}
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
const
|
|
752
|
+
// Primary vs additive (Shopify-parity): render wallets as express buttons ABOVE the card form.
|
|
753
|
+
const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal (WALLET)
|
|
754
|
+
const primary = defaultProvider && !defaultProvider.isAdditive ? defaultProvider : undefined;
|
|
755
755
|
\`\`\`
|
|
756
756
|
|
|
757
|
-
Each provider has: \`id\`, \`provider\` (flexible string \u2014 \`'stripe'\`, \`'grow'\`, \`'paypal'\`, \`'cardcom'\`, \`'morning'\`, \`'takbull'\`, \`'sandbox'\`, and future providers), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault
|
|
757
|
+
Each provider has: \`id\`, \`provider\` (flexible string \u2014 \`'stripe'\`, \`'grow'\`, \`'paypal'\`, \`'cardcom'\`, \`'morning'\`, \`'takbull'\`, \`'sandbox'\`, and future providers), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`, plus the taxonomy fields \`methodType\` (\`'CREDIT_CARD'\` = the single primary card processor that settles the order; \`'WALLET'\`/other = additive), \`isAdditive\`, and \`presentation\` (\`'card_form'\` | \`'express_button'\` | \u2026).
|
|
758
|
+
|
|
759
|
+
**Primary vs. additive:** there is one primary card processor (the \`defaultProvider\`, \`isAdditive: false\`) plus any additive methods (\`isAdditive: true\`, e.g. PayPal). Render additive methods as accelerated-checkout **express buttons above** the card form \u2014 they sit alongside the primary, never replace it. Create the intent with the tapped provider's \`id\`. (Exception: a wallet-only store has no card processor, so its wallet becomes the \`defaultProvider\` and stands alone.)
|
|
758
760
|
|
|
759
761
|
The payment intent returned from \`createPaymentIntent()\` includes a \`clientSdk\` object whose \`renderType\` tells you exactly how to render \u2014 **branch on THAT, not on the provider name**:
|
|
760
762
|
|
|
@@ -1142,11 +1144,22 @@ client-side overlay needed. Swatch colors are language-agnostic.
|
|
|
1142
1144
|
function ProductDescription({ product }: { product: Product }) {
|
|
1143
1145
|
const content = getDescriptionContent(product);
|
|
1144
1146
|
if (!content) return null;
|
|
1145
|
-
|
|
1147
|
+
// Descriptions are merchant HTML and may contain <video> + host-locked
|
|
1148
|
+
// YouTube/Vimeo <iframe> embeds. NEVER render unsanitized \u2014 sanitize and
|
|
1149
|
+
// allowlist video/source + iframe (restricted to youtube/vimeo hosts).
|
|
1150
|
+
if ('html' in content) {
|
|
1151
|
+
return <div dangerouslySetInnerHTML={{ __html: sanitizeProductHtml(content.html) }} />;
|
|
1152
|
+
}
|
|
1146
1153
|
return <p>{content.text}</p>;
|
|
1147
1154
|
}
|
|
1148
1155
|
\`\`\`
|
|
1149
1156
|
|
|
1157
|
+
**Sanitize descriptions.** \`sanitizeProductHtml\` is the host-locked sanitizer the
|
|
1158
|
+
\`create-brainerce-store\` scaffold ships at \`src/lib/sanitize-html.ts\`. If you roll
|
|
1159
|
+
your own, allow \`video\`/\`source\` and \`iframe\` **restricted** to \`www.youtube.com\`,
|
|
1160
|
+
\`www.youtube-nocookie.com\`, \`player.vimeo.com\` (never arbitrary iframes), and add
|
|
1161
|
+
those hosts to your CSP \`frame-src\` or embeds render blank.
|
|
1162
|
+
|
|
1150
1163
|
### Metafields (Custom Product Fields) \u2014 display on product detail!
|
|
1151
1164
|
|
|
1152
1165
|
**IMPORTANT: Render metafield values based on \`mf.type\`!** Don't just display \`mf.value\` as text for all types.
|
|
@@ -1229,7 +1242,28 @@ const { url } = await client.uploadCustomizationFile(file);
|
|
|
1229
1242
|
// Then use the URL in metadata: metadata: { logo: url }
|
|
1230
1243
|
\`\`\`
|
|
1231
1244
|
|
|
1232
|
-
**Customization field properties:** \`key\`, \`name\` (display label), \`type\`, \`required\`, \`minLength\`, \`maxLength\`, \`minValue\`, \`maxValue\`, \`enumValues
|
|
1245
|
+
**Customization field properties:** \`key\`, \`name\` (display label), \`type\`, \`required\`, \`minLength\`, \`maxLength\`, \`minValue\`, \`maxValue\`, \`enumValues\` (array of \`{label, value, swatchColor?, swatchImageUrl?}\`), \`defaultValue\`, \`position\`
|
|
1246
|
+
|
|
1247
|
+
**SELECT / MULTI_SELECT \u2014 use \`option.value\` to submit, \`option.label\` to display:**
|
|
1248
|
+
|
|
1249
|
+
\`\`\`typescript
|
|
1250
|
+
for (const option of field.enumValues ?? []) {
|
|
1251
|
+
// option.value \u2192 what to submit in metadata
|
|
1252
|
+
// option.label \u2192 what to show the customer
|
|
1253
|
+
// option.swatchColor \u2192 optional hex color for color swatches
|
|
1254
|
+
}
|
|
1255
|
+
\`\`\`
|
|
1256
|
+
|
|
1257
|
+
**Display customizations in cart / checkout \u2014 no extra API call needed:**
|
|
1258
|
+
|
|
1259
|
+
CartItem and CheckoutLineItem both include a \`customizations\` object alongside \`metadata\`. Use \`customizations\` for display \u2014 it contains resolved labels, not raw keys.
|
|
1260
|
+
|
|
1261
|
+
\`\`\`typescript
|
|
1262
|
+
// Works for CartItem, CheckoutLineItem, and OrderItem \u2014 same shape
|
|
1263
|
+
const lines = Object.values(item.customizations ?? {})
|
|
1264
|
+
.map(c => \`\${c.label}: \${Array.isArray(c.value) ? c.value.join(', ') : c.value}\`);
|
|
1265
|
+
// e.g. ["Frame color: Gold", "Add-ons: Gift wrap"]
|
|
1266
|
+
\`\`\`
|
|
1233
1267
|
|
|
1234
1268
|
### Downloadable / Digital Products
|
|
1235
1269
|
|
|
@@ -1771,7 +1805,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1771
1805
|
|
|
1772
1806
|
### Restaurant features (advanced)
|
|
1773
1807
|
|
|
1774
|
-
|
|
1808
|
+
Scheduled availability windows, nested combos (depth \u2264 3 via \`nestedByModifierId\`), and downsell modifiers (negative \`priceDelta\`) are all built on the same data model. See INTEGRATION-OPTIONAL.md "Restaurant / build-your-own products" for those flows.`;
|
|
1775
1809
|
}
|
|
1776
1810
|
function getProductReviewsSection() {
|
|
1777
1811
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3261,6 +3295,85 @@ by device) and can also be asked of the AI assistant ("how many visits did I get
|
|
|
3261
3295
|
this month and from where?").
|
|
3262
3296
|
`;
|
|
3263
3297
|
}
|
|
3298
|
+
function getShippingAppSection() {
|
|
3299
|
+
return `## App Store Shipping (Shippo)
|
|
3300
|
+
|
|
3301
|
+
Merchants install Shippo from the Brainerce App Store. After connecting their own Shippo account
|
|
3302
|
+
(OAuth), live carrier rates appear automatically at checkout alongside any manually configured
|
|
3303
|
+
zone rates. **Billing goes directly to the merchant's Shippo account \u2014 Brainerce never charges.**
|
|
3304
|
+
|
|
3305
|
+
### Checkout \u2014 live carrier rates
|
|
3306
|
+
|
|
3307
|
+
No SDK changes needed. Once Shippo is installed and configured, the existing
|
|
3308
|
+
\`getShippingRates()\` call returns both manual zone rates and live Shippo quotes:
|
|
3309
|
+
|
|
3310
|
+
\`\`\`typescript
|
|
3311
|
+
const { rates } = await client.setShippingAddress(checkoutId, { ... });
|
|
3312
|
+
// rates may include:
|
|
3313
|
+
// { id: 'shippo:rate_8f123abc', name: 'USPS Priority Mail', price: '8.50', currency: 'USD', source: 'carrier' }
|
|
3314
|
+
\`\`\`
|
|
3315
|
+
|
|
3316
|
+
Rates prefixed \`shippo:\` are live Shippo quotes. Pass the full \`id\` as \`shippingRateId\`
|
|
3317
|
+
when calling \`selectShippingMethod()\`.
|
|
3318
|
+
|
|
3319
|
+
### Admin \u2014 purchase a shipping label
|
|
3320
|
+
|
|
3321
|
+
After the order is placed, the merchant purchases a label by passing the Shippo rate object_id:
|
|
3322
|
+
|
|
3323
|
+
\`\`\`typescript
|
|
3324
|
+
const label = await admin.createShippingLabel(orderId, {
|
|
3325
|
+
shippoRateObjectId: 'rate_8f123456789abcdef',
|
|
3326
|
+
});
|
|
3327
|
+
console.log(label.labelUrl); // PDF ready to print
|
|
3328
|
+
console.log(label.trackingNumber); // auto-populated on the order
|
|
3329
|
+
\`\`\`
|
|
3330
|
+
|
|
3331
|
+
The \`trackingNumber\` is stored on the \`Order\` and returned in \`getMyOrders()\`
|
|
3332
|
+
so customers can track their shipment automatically.
|
|
3333
|
+
|
|
3334
|
+
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3335
|
+
}
|
|
3336
|
+
function getLoyaltySection() {
|
|
3337
|
+
return `# Loyalty & Rewards
|
|
3338
|
+
|
|
3339
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3340
|
+
|
|
3341
|
+
> **Storefront mode only.** These methods work only with \`new BrainerceClient({ storeId })\` + a logged-in \`customerToken\`. There is **no vibe-coded route** \u2014 in \`salesChannelId: 'vc_*'\` mode they throw. Skip loyalty if you connect vibe-coded.
|
|
3342
|
+
|
|
3343
|
+
Points are earned automatically by the platform when an order is paid \u2014 there is no earn call from the storefront. Your job is to **show** balance/rewards and to **redeem**.
|
|
3344
|
+
|
|
3345
|
+
## Status
|
|
3346
|
+
|
|
3347
|
+
\`\`\`typescript
|
|
3348
|
+
// requires client.setCustomerToken(auth.token)
|
|
3349
|
+
const status = await client.getLoyaltyStatus();
|
|
3350
|
+
// { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
|
|
3351
|
+
\`\`\`
|
|
3352
|
+
|
|
3353
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3354
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3355
|
+
|
|
3356
|
+
## Enroll (only if auto-enroll is off)
|
|
3357
|
+
|
|
3358
|
+
\`\`\`typescript
|
|
3359
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3360
|
+
\`\`\`
|
|
3361
|
+
|
|
3362
|
+
## Rewards & redemption
|
|
3363
|
+
|
|
3364
|
+
\`\`\`typescript
|
|
3365
|
+
const rewards = await client.getAvailableRewards();
|
|
3366
|
+
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3367
|
+
|
|
3368
|
+
const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
|
|
3369
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3370
|
+
\`\`\`
|
|
3371
|
+
|
|
3372
|
+
- \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3373
|
+
- Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
|
|
3374
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3375
|
+
`;
|
|
3376
|
+
}
|
|
3264
3377
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3265
3378
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3266
3379
|
const cur = currency || "USD";
|
|
@@ -3287,6 +3400,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3287
3400
|
return getInventorySection();
|
|
3288
3401
|
case "discounts":
|
|
3289
3402
|
return getDiscountsSection();
|
|
3403
|
+
case "loyalty":
|
|
3404
|
+
return getLoyaltySection();
|
|
3290
3405
|
case "recommendations":
|
|
3291
3406
|
return getRecommendationsSection();
|
|
3292
3407
|
case "reviews":
|
|
@@ -3320,6 +3435,9 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3320
3435
|
case "analytics":
|
|
3321
3436
|
case "traffic-analytics":
|
|
3322
3437
|
return getStorefrontAnalyticsSection(cid);
|
|
3438
|
+
case "shipping-app":
|
|
3439
|
+
case "shippo":
|
|
3440
|
+
return getShippingAppSection();
|
|
3323
3441
|
case "all":
|
|
3324
3442
|
return [
|
|
3325
3443
|
"# Brainerce SDK \u2014 full topic dump",
|
|
@@ -3450,6 +3568,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3450
3568
|
"order-confirmation",
|
|
3451
3569
|
"inventory",
|
|
3452
3570
|
"discounts",
|
|
3571
|
+
"loyalty",
|
|
3453
3572
|
"recommendations",
|
|
3454
3573
|
"reviews",
|
|
3455
3574
|
"product-customization-fields",
|
|
@@ -3602,11 +3721,18 @@ interface ProductCustomizationField {
|
|
|
3602
3721
|
maxLength?: number | null;
|
|
3603
3722
|
minValue?: number | null; // NUMBER bounds
|
|
3604
3723
|
maxValue?: number | null;
|
|
3605
|
-
enumValues?:
|
|
3724
|
+
enumValues?: CustomizationFieldOption[]; // REQUIRED for SELECT / MULTI_SELECT
|
|
3606
3725
|
defaultValue?: string | null;
|
|
3607
3726
|
position: number; // Render order
|
|
3608
3727
|
}
|
|
3609
3728
|
|
|
3729
|
+
interface CustomizationFieldOption {
|
|
3730
|
+
label: string; // Display label (e.g., "Rose Gold")
|
|
3731
|
+
value: string; // Value stored in CartItem.metadata
|
|
3732
|
+
swatchColor?: string | null; // Optional hex color (e.g., "#B76E79")
|
|
3733
|
+
swatchImageUrl?: string | null; // Optional swatch image URL
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3610
3736
|
interface ProductQueryParams {
|
|
3611
3737
|
page?: number;
|
|
3612
3738
|
limit?: number;
|
|
@@ -3700,6 +3826,7 @@ interface CartItem {
|
|
|
3700
3826
|
image?: ProductImage | string | null;
|
|
3701
3827
|
} | null;
|
|
3702
3828
|
metadata?: Record<string, unknown> | null; // Buyer-submitted customization values, keyed by ProductCustomizationField.key
|
|
3829
|
+
customizations?: Record<string, { label: string; value: string | string[]; type: string }>; // Resolved labels \u2014 use this to display "Color: Silver" without extra getProduct() call
|
|
3703
3830
|
createdAt: string;
|
|
3704
3831
|
updatedAt: string;
|
|
3705
3832
|
}
|
|
@@ -3799,6 +3926,7 @@ interface CheckoutLineItem {
|
|
|
3799
3926
|
product: { id: string; name: string; sku: string; images?: ProductImage[]; };
|
|
3800
3927
|
variant?: { id: string; name?: string | null; sku?: string | null; image?: ProductImage | string | null; } | null;
|
|
3801
3928
|
metadata?: Record<string, unknown> | null; // Copied from CartItem.metadata \u2014 buyer customization values
|
|
3929
|
+
customizations?: Record<string, { label: string; value: string | string[]; type: string }>; // Resolved labels \u2014 same shape as OrderItem.customizations
|
|
3802
3930
|
}
|
|
3803
3931
|
|
|
3804
3932
|
interface CheckoutAddress {
|
|
@@ -4035,7 +4163,10 @@ interface PaymentProviderConfig {
|
|
|
4035
4163
|
stripeAccountId?: string; // Stripe Connect only
|
|
4036
4164
|
supportedMethods: string[];
|
|
4037
4165
|
testMode: boolean;
|
|
4038
|
-
isDefault: boolean;
|
|
4166
|
+
isDefault: boolean; // only a primary (CREDIT_CARD) is ever the default
|
|
4167
|
+
methodType: 'CREDIT_CARD' | 'WALLET' | 'OFFSITE' | 'LOCAL' | 'REDEEMABLE';
|
|
4168
|
+
presentation: 'card_form' | 'express_button' | 'redirect' | 'local_option' | 'redeemable_field';
|
|
4169
|
+
isAdditive: boolean; // wallet/alt method shown ALONGSIDE the primary (e.g. PayPal)
|
|
4039
4170
|
}
|
|
4040
4171
|
type PaymentProvider = PaymentProviderConfig;
|
|
4041
4172
|
|
|
@@ -4775,6 +4906,43 @@ interface TaxEstimateResponse {
|
|
|
4775
4906
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4776
4907
|
// full shipping address. The country comes from your edge runtime
|
|
4777
4908
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4909
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4910
|
+
interface LoyaltyStatus {
|
|
4911
|
+
enrolled: boolean;
|
|
4912
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4913
|
+
lifetimeEarned: number;
|
|
4914
|
+
program: {
|
|
4915
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4916
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4917
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4918
|
+
} | null; // null when the store has no loyalty program
|
|
4919
|
+
}
|
|
4920
|
+
|
|
4921
|
+
interface LoyaltyReward {
|
|
4922
|
+
id: string;
|
|
4923
|
+
name: string;
|
|
4924
|
+
description: string | null;
|
|
4925
|
+
pointsCost: number;
|
|
4926
|
+
discountValue: number; // fixed amount off, store currency
|
|
4927
|
+
minOrderAmount: number | null;
|
|
4928
|
+
maxUsesPerUser: number;
|
|
4929
|
+
isActive: boolean;
|
|
4930
|
+
createdAt: string;
|
|
4931
|
+
updatedAt: string;
|
|
4932
|
+
}
|
|
4933
|
+
|
|
4934
|
+
interface RedeemRewardResult {
|
|
4935
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4936
|
+
discountValue: number;
|
|
4937
|
+
pointsSpent: number;
|
|
4938
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4939
|
+
}
|
|
4940
|
+
|
|
4941
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4942
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4943
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4944
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4945
|
+
`;
|
|
4778
4946
|
var TYPES_BY_DOMAIN = {
|
|
4779
4947
|
products: PRODUCTS_TYPES,
|
|
4780
4948
|
cart: CART_TYPES,
|
|
@@ -4787,7 +4955,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4787
4955
|
reviews: REVIEWS_TYPES,
|
|
4788
4956
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4789
4957
|
content: CONTENT_TYPES,
|
|
4790
|
-
regions: REGIONS_TYPES
|
|
4958
|
+
regions: REGIONS_TYPES,
|
|
4959
|
+
loyalty: LOYALTY_TYPES
|
|
4791
4960
|
};
|
|
4792
4961
|
function getTypesByDomain(domain) {
|
|
4793
4962
|
if (domain === "all") {
|
|
@@ -4821,6 +4990,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4821
4990
|
"modifier-groups",
|
|
4822
4991
|
"content",
|
|
4823
4992
|
"regions",
|
|
4993
|
+
"loyalty",
|
|
4824
4994
|
"all"
|
|
4825
4995
|
]).describe(
|
|
4826
4996
|
'The domain of types to retrieve. Use "helpers" for helper function signatures and common types like StoreInfo, PaginatedResponse. Use "content" for FAQ / Footer / Header / Announcement / RichText / Page. Use "regions" for Region / TaxClass admin types.'
|
|
@@ -5083,7 +5253,15 @@ const checkout = await client.getCheckout(checkoutId);
|
|
|
5083
5253
|
import { client } from './brainerce';
|
|
5084
5254
|
|
|
5085
5255
|
const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
|
|
5086
|
-
// each provider: { id, provider, name, publicKey, supportedMethods, testMode,
|
|
5256
|
+
// each provider: { id, provider, name, publicKey, supportedMethods, testMode,
|
|
5257
|
+
// isDefault, methodType, presentation, isAdditive, clientSdk? }
|
|
5258
|
+
//
|
|
5259
|
+
// methodType splits providers into ONE primary card processor (CREDIT_CARD, the
|
|
5260
|
+
// defaultProvider that settles the order) and additive methods (isAdditive:true,
|
|
5261
|
+
// e.g. PayPal as a WALLET). Render additive methods as express buttons ABOVE the
|
|
5262
|
+
// card form \u2014 they sit alongside the primary, never replace it. Call
|
|
5263
|
+
// createPaymentIntent({ providerId }) with the tapped provider's id. (Wallet-only
|
|
5264
|
+
// store: with no card processor, the wallet becomes defaultProvider and stands alone.)
|
|
5087
5265
|
//
|
|
5088
5266
|
// After createPaymentIntent, the response carries a clientSdk.renderType that
|
|
5089
5267
|
// tells your UI how to render the payment step. The 5 possible values:
|
|
@@ -6259,6 +6437,9 @@ function formatCapabilities(caps) {
|
|
|
6259
6437
|
lines.push(
|
|
6260
6438
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6261
6439
|
);
|
|
6440
|
+
lines.push(
|
|
6441
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6442
|
+
);
|
|
6262
6443
|
lines.push("");
|
|
6263
6444
|
lines.push("## Connection Settings");
|
|
6264
6445
|
lines.push(
|
|
@@ -6304,6 +6485,11 @@ function formatCapabilities(caps) {
|
|
|
6304
6485
|
if (caps.features.hasCoupons) {
|
|
6305
6486
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6306
6487
|
}
|
|
6488
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6489
|
+
suggestions.push(
|
|
6490
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
|
|
6491
|
+
);
|
|
6492
|
+
}
|
|
6307
6493
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6308
6494
|
suggestions.push(
|
|
6309
6495
|
`Checkout custom fields are configured. Build a step between shipping/pickup and payment using getCheckoutCustomFields() + setCheckoutCustomFields(). Render checkout.appliedSurcharges in the order summary so customers see what they're paying for. Use get-code-example("checkout-custom-fields").`
|