@brainerce/mcp-server 3.12.1 → 3.12.3
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 +131 -2
- package/dist/bin/stdio.js +131 -2
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +131 -2
- package/dist/index.mjs +131 -2
- package/package.json +1 -1
package/dist/bin/http.js
CHANGED
|
@@ -1782,7 +1782,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1782
1782
|
|
|
1783
1783
|
### Restaurant features (advanced)
|
|
1784
1784
|
|
|
1785
|
-
|
|
1785
|
+
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.`;
|
|
1786
1786
|
}
|
|
1787
1787
|
function getProductReviewsSection() {
|
|
1788
1788
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3310,6 +3310,65 @@ so customers can track their shipment automatically.
|
|
|
3310
3310
|
|
|
3311
3311
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3312
3312
|
}
|
|
3313
|
+
function getLoyaltySection() {
|
|
3314
|
+
return `# Loyalty & Rewards
|
|
3315
|
+
|
|
3316
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3317
|
+
|
|
3318
|
+
> **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.
|
|
3319
|
+
|
|
3320
|
+
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**.
|
|
3321
|
+
|
|
3322
|
+
## Status
|
|
3323
|
+
|
|
3324
|
+
\`\`\`typescript
|
|
3325
|
+
// requires client.setCustomerToken(auth.token)
|
|
3326
|
+
const status = await client.getLoyaltyStatus();
|
|
3327
|
+
// {
|
|
3328
|
+
// enrolled, pointsBalance, lifetimeEarned,
|
|
3329
|
+
// program: { pointsName, currencyRatio, status } | null,
|
|
3330
|
+
// tier: { id, name, level, pointsMultiplier } | null,
|
|
3331
|
+
// nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
|
|
3332
|
+
// progressToNextTier, pointsToNextTier,
|
|
3333
|
+
// }
|
|
3334
|
+
\`\`\`
|
|
3335
|
+
|
|
3336
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3337
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3338
|
+
- \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
|
|
3339
|
+
|
|
3340
|
+
## Enroll (only if auto-enroll is off)
|
|
3341
|
+
|
|
3342
|
+
\`\`\`typescript
|
|
3343
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3344
|
+
\`\`\`
|
|
3345
|
+
|
|
3346
|
+
May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
|
|
3347
|
+
|
|
3348
|
+
## Rewards & redemption
|
|
3349
|
+
|
|
3350
|
+
\`\`\`typescript
|
|
3351
|
+
const rewards = await client.getAvailableRewards();
|
|
3352
|
+
// [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
|
|
3353
|
+
// Already filtered to rewards the customer's current tier qualifies for.
|
|
3354
|
+
|
|
3355
|
+
const { couponCode, discountType, discountValue, pointsBalance } =
|
|
3356
|
+
await client.redeemLoyaltyReward(rewardId);
|
|
3357
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3358
|
+
\`\`\`
|
|
3359
|
+
|
|
3360
|
+
- \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
|
|
3361
|
+
- \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3362
|
+
- Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
|
|
3363
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3364
|
+
|
|
3365
|
+
## Social share (optional)
|
|
3366
|
+
|
|
3367
|
+
\`\`\`typescript
|
|
3368
|
+
await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
|
|
3369
|
+
\`\`\`
|
|
3370
|
+
`;
|
|
3371
|
+
}
|
|
3313
3372
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3314
3373
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3315
3374
|
const cur = currency || "USD";
|
|
@@ -3336,6 +3395,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3336
3395
|
return getInventorySection();
|
|
3337
3396
|
case "discounts":
|
|
3338
3397
|
return getDiscountsSection();
|
|
3398
|
+
case "loyalty":
|
|
3399
|
+
return getLoyaltySection();
|
|
3339
3400
|
case "recommendations":
|
|
3340
3401
|
return getRecommendationsSection();
|
|
3341
3402
|
case "reviews":
|
|
@@ -3502,6 +3563,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3502
3563
|
"order-confirmation",
|
|
3503
3564
|
"inventory",
|
|
3504
3565
|
"discounts",
|
|
3566
|
+
"loyalty",
|
|
3505
3567
|
"recommendations",
|
|
3506
3568
|
"reviews",
|
|
3507
3569
|
"product-customization-fields",
|
|
@@ -4839,6 +4901,63 @@ interface TaxEstimateResponse {
|
|
|
4839
4901
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4840
4902
|
// full shipping address. The country comes from your edge runtime
|
|
4841
4903
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4904
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4905
|
+
interface LoyaltyTierSummary {
|
|
4906
|
+
id: string;
|
|
4907
|
+
name: string;
|
|
4908
|
+
level: number; // ordering; higher = better tier
|
|
4909
|
+
pointsMultiplier: number;
|
|
4910
|
+
}
|
|
4911
|
+
|
|
4912
|
+
interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
|
|
4913
|
+
qualificationType: 'SPEND' | 'POINTS';
|
|
4914
|
+
qualificationThreshold: number;
|
|
4915
|
+
}
|
|
4916
|
+
|
|
4917
|
+
interface LoyaltyStatus {
|
|
4918
|
+
enrolled: boolean;
|
|
4919
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4920
|
+
lifetimeEarned: number;
|
|
4921
|
+
program: {
|
|
4922
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4923
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4924
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4925
|
+
} | null; // null when the store has no loyalty program
|
|
4926
|
+
tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
|
|
4927
|
+
nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
|
|
4928
|
+
progressToNextTier: number; // 0..1
|
|
4929
|
+
pointsToNextTier: number | null;
|
|
4930
|
+
}
|
|
4931
|
+
|
|
4932
|
+
interface LoyaltyReward {
|
|
4933
|
+
id: string;
|
|
4934
|
+
name: string;
|
|
4935
|
+
description: string | null;
|
|
4936
|
+
pointsCost: number;
|
|
4937
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4938
|
+
discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
|
|
4939
|
+
minOrderAmount: number | null;
|
|
4940
|
+
maxUsesPerUser: number;
|
|
4941
|
+
isActive: boolean;
|
|
4942
|
+
minTierLevel: number | null; // minimum tier level required, or null for no restriction
|
|
4943
|
+
createdAt: string;
|
|
4944
|
+
updatedAt: string;
|
|
4945
|
+
}
|
|
4946
|
+
|
|
4947
|
+
interface RedeemRewardResult {
|
|
4948
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4949
|
+
discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4950
|
+
discountValue: number;
|
|
4951
|
+
pointsSpent: number;
|
|
4952
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4956
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4957
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4958
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4959
|
+
// client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
|
|
4960
|
+
`;
|
|
4842
4961
|
var TYPES_BY_DOMAIN = {
|
|
4843
4962
|
products: PRODUCTS_TYPES,
|
|
4844
4963
|
cart: CART_TYPES,
|
|
@@ -4851,7 +4970,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4851
4970
|
reviews: REVIEWS_TYPES,
|
|
4852
4971
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4853
4972
|
content: CONTENT_TYPES,
|
|
4854
|
-
regions: REGIONS_TYPES
|
|
4973
|
+
regions: REGIONS_TYPES,
|
|
4974
|
+
loyalty: LOYALTY_TYPES
|
|
4855
4975
|
};
|
|
4856
4976
|
function getTypesByDomain(domain) {
|
|
4857
4977
|
if (domain === "all") {
|
|
@@ -4885,6 +5005,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4885
5005
|
"modifier-groups",
|
|
4886
5006
|
"content",
|
|
4887
5007
|
"regions",
|
|
5008
|
+
"loyalty",
|
|
4888
5009
|
"all"
|
|
4889
5010
|
]).describe(
|
|
4890
5011
|
'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.'
|
|
@@ -6331,6 +6452,9 @@ function formatCapabilities(caps) {
|
|
|
6331
6452
|
lines.push(
|
|
6332
6453
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6333
6454
|
);
|
|
6455
|
+
lines.push(
|
|
6456
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6457
|
+
);
|
|
6334
6458
|
lines.push("");
|
|
6335
6459
|
lines.push("## Connection Settings");
|
|
6336
6460
|
lines.push(
|
|
@@ -6376,6 +6500,11 @@ function formatCapabilities(caps) {
|
|
|
6376
6500
|
if (caps.features.hasCoupons) {
|
|
6377
6501
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6378
6502
|
}
|
|
6503
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6504
|
+
suggestions.push(
|
|
6505
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
|
|
6506
|
+
);
|
|
6507
|
+
}
|
|
6379
6508
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6380
6509
|
suggestions.push(
|
|
6381
6510
|
`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").`
|
package/dist/bin/stdio.js
CHANGED
|
@@ -1779,7 +1779,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1779
1779
|
|
|
1780
1780
|
### Restaurant features (advanced)
|
|
1781
1781
|
|
|
1782
|
-
|
|
1782
|
+
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.`;
|
|
1783
1783
|
}
|
|
1784
1784
|
function getProductReviewsSection() {
|
|
1785
1785
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3307,6 +3307,65 @@ so customers can track their shipment automatically.
|
|
|
3307
3307
|
|
|
3308
3308
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3309
3309
|
}
|
|
3310
|
+
function getLoyaltySection() {
|
|
3311
|
+
return `# Loyalty & Rewards
|
|
3312
|
+
|
|
3313
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3314
|
+
|
|
3315
|
+
> **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.
|
|
3316
|
+
|
|
3317
|
+
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**.
|
|
3318
|
+
|
|
3319
|
+
## Status
|
|
3320
|
+
|
|
3321
|
+
\`\`\`typescript
|
|
3322
|
+
// requires client.setCustomerToken(auth.token)
|
|
3323
|
+
const status = await client.getLoyaltyStatus();
|
|
3324
|
+
// {
|
|
3325
|
+
// enrolled, pointsBalance, lifetimeEarned,
|
|
3326
|
+
// program: { pointsName, currencyRatio, status } | null,
|
|
3327
|
+
// tier: { id, name, level, pointsMultiplier } | null,
|
|
3328
|
+
// nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
|
|
3329
|
+
// progressToNextTier, pointsToNextTier,
|
|
3330
|
+
// }
|
|
3331
|
+
\`\`\`
|
|
3332
|
+
|
|
3333
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3334
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3335
|
+
- \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
|
|
3336
|
+
|
|
3337
|
+
## Enroll (only if auto-enroll is off)
|
|
3338
|
+
|
|
3339
|
+
\`\`\`typescript
|
|
3340
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3341
|
+
\`\`\`
|
|
3342
|
+
|
|
3343
|
+
May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
|
|
3344
|
+
|
|
3345
|
+
## Rewards & redemption
|
|
3346
|
+
|
|
3347
|
+
\`\`\`typescript
|
|
3348
|
+
const rewards = await client.getAvailableRewards();
|
|
3349
|
+
// [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
|
|
3350
|
+
// Already filtered to rewards the customer's current tier qualifies for.
|
|
3351
|
+
|
|
3352
|
+
const { couponCode, discountType, discountValue, pointsBalance } =
|
|
3353
|
+
await client.redeemLoyaltyReward(rewardId);
|
|
3354
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3355
|
+
\`\`\`
|
|
3356
|
+
|
|
3357
|
+
- \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
|
|
3358
|
+
- \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3359
|
+
- Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
|
|
3360
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3361
|
+
|
|
3362
|
+
## Social share (optional)
|
|
3363
|
+
|
|
3364
|
+
\`\`\`typescript
|
|
3365
|
+
await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
|
|
3366
|
+
\`\`\`
|
|
3367
|
+
`;
|
|
3368
|
+
}
|
|
3310
3369
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3311
3370
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3312
3371
|
const cur = currency || "USD";
|
|
@@ -3333,6 +3392,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3333
3392
|
return getInventorySection();
|
|
3334
3393
|
case "discounts":
|
|
3335
3394
|
return getDiscountsSection();
|
|
3395
|
+
case "loyalty":
|
|
3396
|
+
return getLoyaltySection();
|
|
3336
3397
|
case "recommendations":
|
|
3337
3398
|
return getRecommendationsSection();
|
|
3338
3399
|
case "reviews":
|
|
@@ -3499,6 +3560,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3499
3560
|
"order-confirmation",
|
|
3500
3561
|
"inventory",
|
|
3501
3562
|
"discounts",
|
|
3563
|
+
"loyalty",
|
|
3502
3564
|
"recommendations",
|
|
3503
3565
|
"reviews",
|
|
3504
3566
|
"product-customization-fields",
|
|
@@ -4836,6 +4898,63 @@ interface TaxEstimateResponse {
|
|
|
4836
4898
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4837
4899
|
// full shipping address. The country comes from your edge runtime
|
|
4838
4900
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4901
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4902
|
+
interface LoyaltyTierSummary {
|
|
4903
|
+
id: string;
|
|
4904
|
+
name: string;
|
|
4905
|
+
level: number; // ordering; higher = better tier
|
|
4906
|
+
pointsMultiplier: number;
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
|
|
4910
|
+
qualificationType: 'SPEND' | 'POINTS';
|
|
4911
|
+
qualificationThreshold: number;
|
|
4912
|
+
}
|
|
4913
|
+
|
|
4914
|
+
interface LoyaltyStatus {
|
|
4915
|
+
enrolled: boolean;
|
|
4916
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4917
|
+
lifetimeEarned: number;
|
|
4918
|
+
program: {
|
|
4919
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4920
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4921
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4922
|
+
} | null; // null when the store has no loyalty program
|
|
4923
|
+
tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
|
|
4924
|
+
nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
|
|
4925
|
+
progressToNextTier: number; // 0..1
|
|
4926
|
+
pointsToNextTier: number | null;
|
|
4927
|
+
}
|
|
4928
|
+
|
|
4929
|
+
interface LoyaltyReward {
|
|
4930
|
+
id: string;
|
|
4931
|
+
name: string;
|
|
4932
|
+
description: string | null;
|
|
4933
|
+
pointsCost: number;
|
|
4934
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4935
|
+
discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
|
|
4936
|
+
minOrderAmount: number | null;
|
|
4937
|
+
maxUsesPerUser: number;
|
|
4938
|
+
isActive: boolean;
|
|
4939
|
+
minTierLevel: number | null; // minimum tier level required, or null for no restriction
|
|
4940
|
+
createdAt: string;
|
|
4941
|
+
updatedAt: string;
|
|
4942
|
+
}
|
|
4943
|
+
|
|
4944
|
+
interface RedeemRewardResult {
|
|
4945
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4946
|
+
discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4947
|
+
discountValue: number;
|
|
4948
|
+
pointsSpent: number;
|
|
4949
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4950
|
+
}
|
|
4951
|
+
|
|
4952
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4953
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4954
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4955
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4956
|
+
// client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
|
|
4957
|
+
`;
|
|
4839
4958
|
var TYPES_BY_DOMAIN = {
|
|
4840
4959
|
products: PRODUCTS_TYPES,
|
|
4841
4960
|
cart: CART_TYPES,
|
|
@@ -4848,7 +4967,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4848
4967
|
reviews: REVIEWS_TYPES,
|
|
4849
4968
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4850
4969
|
content: CONTENT_TYPES,
|
|
4851
|
-
regions: REGIONS_TYPES
|
|
4970
|
+
regions: REGIONS_TYPES,
|
|
4971
|
+
loyalty: LOYALTY_TYPES
|
|
4852
4972
|
};
|
|
4853
4973
|
function getTypesByDomain(domain) {
|
|
4854
4974
|
if (domain === "all") {
|
|
@@ -4882,6 +5002,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4882
5002
|
"modifier-groups",
|
|
4883
5003
|
"content",
|
|
4884
5004
|
"regions",
|
|
5005
|
+
"loyalty",
|
|
4885
5006
|
"all"
|
|
4886
5007
|
]).describe(
|
|
4887
5008
|
'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.'
|
|
@@ -6328,6 +6449,9 @@ function formatCapabilities(caps) {
|
|
|
6328
6449
|
lines.push(
|
|
6329
6450
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6330
6451
|
);
|
|
6452
|
+
lines.push(
|
|
6453
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6454
|
+
);
|
|
6331
6455
|
lines.push("");
|
|
6332
6456
|
lines.push("## Connection Settings");
|
|
6333
6457
|
lines.push(
|
|
@@ -6373,6 +6497,11 @@ function formatCapabilities(caps) {
|
|
|
6373
6497
|
if (caps.features.hasCoupons) {
|
|
6374
6498
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6375
6499
|
}
|
|
6500
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6501
|
+
suggestions.push(
|
|
6502
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
|
|
6503
|
+
);
|
|
6504
|
+
}
|
|
6376
6505
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6377
6506
|
suggestions.push(
|
|
6378
6507
|
`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").`
|
package/dist/index.d.mts
CHANGED
|
@@ -75,6 +75,8 @@ interface StoreCapabilities {
|
|
|
75
75
|
hasCheckoutCustomFields: boolean;
|
|
76
76
|
/** True when at least one PUBLISHED Content row exists for the store. */
|
|
77
77
|
hasContent?: boolean;
|
|
78
|
+
/** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
|
|
79
|
+
hasLoyaltyProgram?: boolean;
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
|
package/dist/index.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ interface StoreCapabilities {
|
|
|
75
75
|
hasCheckoutCustomFields: boolean;
|
|
76
76
|
/** True when at least one PUBLISHED Content row exists for the store. */
|
|
77
77
|
hasContent?: boolean;
|
|
78
|
+
/** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
|
|
79
|
+
hasLoyaltyProgram?: boolean;
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
|
package/dist/index.js
CHANGED
|
@@ -1805,7 +1805,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1805
1805
|
|
|
1806
1806
|
### Restaurant features (advanced)
|
|
1807
1807
|
|
|
1808
|
-
|
|
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.`;
|
|
1809
1809
|
}
|
|
1810
1810
|
function getProductReviewsSection() {
|
|
1811
1811
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3333,6 +3333,65 @@ so customers can track their shipment automatically.
|
|
|
3333
3333
|
|
|
3334
3334
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3335
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
|
+
// {
|
|
3351
|
+
// enrolled, pointsBalance, lifetimeEarned,
|
|
3352
|
+
// program: { pointsName, currencyRatio, status } | null,
|
|
3353
|
+
// tier: { id, name, level, pointsMultiplier } | null,
|
|
3354
|
+
// nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
|
|
3355
|
+
// progressToNextTier, pointsToNextTier,
|
|
3356
|
+
// }
|
|
3357
|
+
\`\`\`
|
|
3358
|
+
|
|
3359
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3360
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3361
|
+
- \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
|
|
3362
|
+
|
|
3363
|
+
## Enroll (only if auto-enroll is off)
|
|
3364
|
+
|
|
3365
|
+
\`\`\`typescript
|
|
3366
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3367
|
+
\`\`\`
|
|
3368
|
+
|
|
3369
|
+
May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
|
|
3370
|
+
|
|
3371
|
+
## Rewards & redemption
|
|
3372
|
+
|
|
3373
|
+
\`\`\`typescript
|
|
3374
|
+
const rewards = await client.getAvailableRewards();
|
|
3375
|
+
// [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
|
|
3376
|
+
// Already filtered to rewards the customer's current tier qualifies for.
|
|
3377
|
+
|
|
3378
|
+
const { couponCode, discountType, discountValue, pointsBalance } =
|
|
3379
|
+
await client.redeemLoyaltyReward(rewardId);
|
|
3380
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3381
|
+
\`\`\`
|
|
3382
|
+
|
|
3383
|
+
- \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
|
|
3384
|
+
- \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3385
|
+
- Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
|
|
3386
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3387
|
+
|
|
3388
|
+
## Social share (optional)
|
|
3389
|
+
|
|
3390
|
+
\`\`\`typescript
|
|
3391
|
+
await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
|
|
3392
|
+
\`\`\`
|
|
3393
|
+
`;
|
|
3394
|
+
}
|
|
3336
3395
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3337
3396
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3338
3397
|
const cur = currency || "USD";
|
|
@@ -3359,6 +3418,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3359
3418
|
return getInventorySection();
|
|
3360
3419
|
case "discounts":
|
|
3361
3420
|
return getDiscountsSection();
|
|
3421
|
+
case "loyalty":
|
|
3422
|
+
return getLoyaltySection();
|
|
3362
3423
|
case "recommendations":
|
|
3363
3424
|
return getRecommendationsSection();
|
|
3364
3425
|
case "reviews":
|
|
@@ -3525,6 +3586,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3525
3586
|
"order-confirmation",
|
|
3526
3587
|
"inventory",
|
|
3527
3588
|
"discounts",
|
|
3589
|
+
"loyalty",
|
|
3528
3590
|
"recommendations",
|
|
3529
3591
|
"reviews",
|
|
3530
3592
|
"product-customization-fields",
|
|
@@ -4862,6 +4924,63 @@ interface TaxEstimateResponse {
|
|
|
4862
4924
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4863
4925
|
// full shipping address. The country comes from your edge runtime
|
|
4864
4926
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4927
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4928
|
+
interface LoyaltyTierSummary {
|
|
4929
|
+
id: string;
|
|
4930
|
+
name: string;
|
|
4931
|
+
level: number; // ordering; higher = better tier
|
|
4932
|
+
pointsMultiplier: number;
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4935
|
+
interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
|
|
4936
|
+
qualificationType: 'SPEND' | 'POINTS';
|
|
4937
|
+
qualificationThreshold: number;
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
interface LoyaltyStatus {
|
|
4941
|
+
enrolled: boolean;
|
|
4942
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4943
|
+
lifetimeEarned: number;
|
|
4944
|
+
program: {
|
|
4945
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4946
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4947
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4948
|
+
} | null; // null when the store has no loyalty program
|
|
4949
|
+
tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
|
|
4950
|
+
nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
|
|
4951
|
+
progressToNextTier: number; // 0..1
|
|
4952
|
+
pointsToNextTier: number | null;
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
interface LoyaltyReward {
|
|
4956
|
+
id: string;
|
|
4957
|
+
name: string;
|
|
4958
|
+
description: string | null;
|
|
4959
|
+
pointsCost: number;
|
|
4960
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4961
|
+
discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
|
|
4962
|
+
minOrderAmount: number | null;
|
|
4963
|
+
maxUsesPerUser: number;
|
|
4964
|
+
isActive: boolean;
|
|
4965
|
+
minTierLevel: number | null; // minimum tier level required, or null for no restriction
|
|
4966
|
+
createdAt: string;
|
|
4967
|
+
updatedAt: string;
|
|
4968
|
+
}
|
|
4969
|
+
|
|
4970
|
+
interface RedeemRewardResult {
|
|
4971
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4972
|
+
discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4973
|
+
discountValue: number;
|
|
4974
|
+
pointsSpent: number;
|
|
4975
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4979
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4980
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4981
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4982
|
+
// client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
|
|
4983
|
+
`;
|
|
4865
4984
|
var TYPES_BY_DOMAIN = {
|
|
4866
4985
|
products: PRODUCTS_TYPES,
|
|
4867
4986
|
cart: CART_TYPES,
|
|
@@ -4874,7 +4993,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4874
4993
|
reviews: REVIEWS_TYPES,
|
|
4875
4994
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4876
4995
|
content: CONTENT_TYPES,
|
|
4877
|
-
regions: REGIONS_TYPES
|
|
4996
|
+
regions: REGIONS_TYPES,
|
|
4997
|
+
loyalty: LOYALTY_TYPES
|
|
4878
4998
|
};
|
|
4879
4999
|
function getTypesByDomain(domain) {
|
|
4880
5000
|
if (domain === "all") {
|
|
@@ -4908,6 +5028,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4908
5028
|
"modifier-groups",
|
|
4909
5029
|
"content",
|
|
4910
5030
|
"regions",
|
|
5031
|
+
"loyalty",
|
|
4911
5032
|
"all"
|
|
4912
5033
|
]).describe(
|
|
4913
5034
|
'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.'
|
|
@@ -6354,6 +6475,9 @@ function formatCapabilities(caps) {
|
|
|
6354
6475
|
lines.push(
|
|
6355
6476
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6356
6477
|
);
|
|
6478
|
+
lines.push(
|
|
6479
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6480
|
+
);
|
|
6357
6481
|
lines.push("");
|
|
6358
6482
|
lines.push("## Connection Settings");
|
|
6359
6483
|
lines.push(
|
|
@@ -6399,6 +6523,11 @@ function formatCapabilities(caps) {
|
|
|
6399
6523
|
if (caps.features.hasCoupons) {
|
|
6400
6524
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6401
6525
|
}
|
|
6526
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6527
|
+
suggestions.push(
|
|
6528
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
|
|
6529
|
+
);
|
|
6530
|
+
}
|
|
6402
6531
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6403
6532
|
suggestions.push(
|
|
6404
6533
|
`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").`
|
package/dist/index.mjs
CHANGED
|
@@ -1773,7 +1773,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1773
1773
|
|
|
1774
1774
|
### Restaurant features (advanced)
|
|
1775
1775
|
|
|
1776
|
-
|
|
1776
|
+
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.`;
|
|
1777
1777
|
}
|
|
1778
1778
|
function getProductReviewsSection() {
|
|
1779
1779
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3301,6 +3301,65 @@ so customers can track their shipment automatically.
|
|
|
3301
3301
|
|
|
3302
3302
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3303
3303
|
}
|
|
3304
|
+
function getLoyaltySection() {
|
|
3305
|
+
return `# Loyalty & Rewards
|
|
3306
|
+
|
|
3307
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3308
|
+
|
|
3309
|
+
> **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.
|
|
3310
|
+
|
|
3311
|
+
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**.
|
|
3312
|
+
|
|
3313
|
+
## Status
|
|
3314
|
+
|
|
3315
|
+
\`\`\`typescript
|
|
3316
|
+
// requires client.setCustomerToken(auth.token)
|
|
3317
|
+
const status = await client.getLoyaltyStatus();
|
|
3318
|
+
// {
|
|
3319
|
+
// enrolled, pointsBalance, lifetimeEarned,
|
|
3320
|
+
// program: { pointsName, currencyRatio, status } | null,
|
|
3321
|
+
// tier: { id, name, level, pointsMultiplier } | null,
|
|
3322
|
+
// nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
|
|
3323
|
+
// progressToNextTier, pointsToNextTier,
|
|
3324
|
+
// }
|
|
3325
|
+
\`\`\`
|
|
3326
|
+
|
|
3327
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3328
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3329
|
+
- \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
|
|
3330
|
+
|
|
3331
|
+
## Enroll (only if auto-enroll is off)
|
|
3332
|
+
|
|
3333
|
+
\`\`\`typescript
|
|
3334
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3335
|
+
\`\`\`
|
|
3336
|
+
|
|
3337
|
+
May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
|
|
3338
|
+
|
|
3339
|
+
## Rewards & redemption
|
|
3340
|
+
|
|
3341
|
+
\`\`\`typescript
|
|
3342
|
+
const rewards = await client.getAvailableRewards();
|
|
3343
|
+
// [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
|
|
3344
|
+
// Already filtered to rewards the customer's current tier qualifies for.
|
|
3345
|
+
|
|
3346
|
+
const { couponCode, discountType, discountValue, pointsBalance } =
|
|
3347
|
+
await client.redeemLoyaltyReward(rewardId);
|
|
3348
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3349
|
+
\`\`\`
|
|
3350
|
+
|
|
3351
|
+
- \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
|
|
3352
|
+
- \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3353
|
+
- Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
|
|
3354
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3355
|
+
|
|
3356
|
+
## Social share (optional)
|
|
3357
|
+
|
|
3358
|
+
\`\`\`typescript
|
|
3359
|
+
await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
|
|
3360
|
+
\`\`\`
|
|
3361
|
+
`;
|
|
3362
|
+
}
|
|
3304
3363
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3305
3364
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3306
3365
|
const cur = currency || "USD";
|
|
@@ -3327,6 +3386,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3327
3386
|
return getInventorySection();
|
|
3328
3387
|
case "discounts":
|
|
3329
3388
|
return getDiscountsSection();
|
|
3389
|
+
case "loyalty":
|
|
3390
|
+
return getLoyaltySection();
|
|
3330
3391
|
case "recommendations":
|
|
3331
3392
|
return getRecommendationsSection();
|
|
3332
3393
|
case "reviews":
|
|
@@ -3493,6 +3554,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3493
3554
|
"order-confirmation",
|
|
3494
3555
|
"inventory",
|
|
3495
3556
|
"discounts",
|
|
3557
|
+
"loyalty",
|
|
3496
3558
|
"recommendations",
|
|
3497
3559
|
"reviews",
|
|
3498
3560
|
"product-customization-fields",
|
|
@@ -4830,6 +4892,63 @@ interface TaxEstimateResponse {
|
|
|
4830
4892
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4831
4893
|
// full shipping address. The country comes from your edge runtime
|
|
4832
4894
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4895
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4896
|
+
interface LoyaltyTierSummary {
|
|
4897
|
+
id: string;
|
|
4898
|
+
name: string;
|
|
4899
|
+
level: number; // ordering; higher = better tier
|
|
4900
|
+
pointsMultiplier: number;
|
|
4901
|
+
}
|
|
4902
|
+
|
|
4903
|
+
interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
|
|
4904
|
+
qualificationType: 'SPEND' | 'POINTS';
|
|
4905
|
+
qualificationThreshold: number;
|
|
4906
|
+
}
|
|
4907
|
+
|
|
4908
|
+
interface LoyaltyStatus {
|
|
4909
|
+
enrolled: boolean;
|
|
4910
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4911
|
+
lifetimeEarned: number;
|
|
4912
|
+
program: {
|
|
4913
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4914
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4915
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4916
|
+
} | null; // null when the store has no loyalty program
|
|
4917
|
+
tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
|
|
4918
|
+
nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
|
|
4919
|
+
progressToNextTier: number; // 0..1
|
|
4920
|
+
pointsToNextTier: number | null;
|
|
4921
|
+
}
|
|
4922
|
+
|
|
4923
|
+
interface LoyaltyReward {
|
|
4924
|
+
id: string;
|
|
4925
|
+
name: string;
|
|
4926
|
+
description: string | null;
|
|
4927
|
+
pointsCost: number;
|
|
4928
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4929
|
+
discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
|
|
4930
|
+
minOrderAmount: number | null;
|
|
4931
|
+
maxUsesPerUser: number;
|
|
4932
|
+
isActive: boolean;
|
|
4933
|
+
minTierLevel: number | null; // minimum tier level required, or null for no restriction
|
|
4934
|
+
createdAt: string;
|
|
4935
|
+
updatedAt: string;
|
|
4936
|
+
}
|
|
4937
|
+
|
|
4938
|
+
interface RedeemRewardResult {
|
|
4939
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4940
|
+
discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4941
|
+
discountValue: number;
|
|
4942
|
+
pointsSpent: number;
|
|
4943
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4947
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4948
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4949
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4950
|
+
// client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
|
|
4951
|
+
`;
|
|
4833
4952
|
var TYPES_BY_DOMAIN = {
|
|
4834
4953
|
products: PRODUCTS_TYPES,
|
|
4835
4954
|
cart: CART_TYPES,
|
|
@@ -4842,7 +4961,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4842
4961
|
reviews: REVIEWS_TYPES,
|
|
4843
4962
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4844
4963
|
content: CONTENT_TYPES,
|
|
4845
|
-
regions: REGIONS_TYPES
|
|
4964
|
+
regions: REGIONS_TYPES,
|
|
4965
|
+
loyalty: LOYALTY_TYPES
|
|
4846
4966
|
};
|
|
4847
4967
|
function getTypesByDomain(domain) {
|
|
4848
4968
|
if (domain === "all") {
|
|
@@ -4876,6 +4996,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4876
4996
|
"modifier-groups",
|
|
4877
4997
|
"content",
|
|
4878
4998
|
"regions",
|
|
4999
|
+
"loyalty",
|
|
4879
5000
|
"all"
|
|
4880
5001
|
]).describe(
|
|
4881
5002
|
'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.'
|
|
@@ -6322,6 +6443,9 @@ function formatCapabilities(caps) {
|
|
|
6322
6443
|
lines.push(
|
|
6323
6444
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6324
6445
|
);
|
|
6446
|
+
lines.push(
|
|
6447
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6448
|
+
);
|
|
6325
6449
|
lines.push("");
|
|
6326
6450
|
lines.push("## Connection Settings");
|
|
6327
6451
|
lines.push(
|
|
@@ -6367,6 +6491,11 @@ function formatCapabilities(caps) {
|
|
|
6367
6491
|
if (caps.features.hasCoupons) {
|
|
6368
6492
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6369
6493
|
}
|
|
6494
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6495
|
+
suggestions.push(
|
|
6496
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
|
|
6497
|
+
);
|
|
6498
|
+
}
|
|
6370
6499
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6371
6500
|
suggestions.push(
|
|
6372
6501
|
`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").`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainerce/mcp-server",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.3",
|
|
4
4
|
"packageManager": "pnpm@9.0.0",
|
|
5
5
|
"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.",
|
|
6
6
|
"bin": {
|