@brainerce/mcp-server 3.12.2 → 3.12.4
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 +172 -68
- package/dist/bin/stdio.js +136 -10
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +136 -10
- package/dist/index.mjs +136 -10
- package/package.json +1 -1
package/dist/bin/stdio.js
CHANGED
|
@@ -217,7 +217,7 @@ async function startCheckout() {
|
|
|
217
217
|
2. Customer optionally applies coupon on cart page \u2192 \`applyCoupon(cartId, code)\`
|
|
218
218
|
3. Detect payment providers \u2192 \`getPaymentProviders()\`
|
|
219
219
|
4. Start checkout session (\`startGuestCheckout()\` or \`createCheckout()\`)
|
|
220
|
-
5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\`
|
|
220
|
+
5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\` \u2014 also pass \`notes\` from the **"Order notes" textarea that every checkout page should include by default** (optional field, max 2000 chars; lands on the order for the merchant)
|
|
221
221
|
6. Select shipping method \u2192 \`selectShippingMethod()\`
|
|
222
222
|
6b. (Optional) Set checkout custom fields \u2192 \`setCheckoutCustomFields()\` \u2014 surcharges auto-calculated
|
|
223
223
|
7. Create payment intent \u2192 \`createPaymentIntent()\` \u2192 returns \`{ clientSecret, provider }\`
|
|
@@ -239,6 +239,8 @@ function CheckoutPage() {
|
|
|
239
239
|
const [checkout, setCheckout] = useState<Checkout | null>(null);
|
|
240
240
|
const [error, setError] = useState<string | null>(null);
|
|
241
241
|
const [loading, setLoading] = useState(true);
|
|
242
|
+
// Optional "Order notes" textarea \u2014 render it on the checkout page by default
|
|
243
|
+
const [orderNotes, setOrderNotes] = useState('');
|
|
242
244
|
|
|
243
245
|
useEffect(() => { startCheckout(); }, []);
|
|
244
246
|
|
|
@@ -273,6 +275,7 @@ function CheckoutPage() {
|
|
|
273
275
|
postalCode: cart.shippingAddress.postalCode,
|
|
274
276
|
country: cart.shippingAddress.country,
|
|
275
277
|
phone: cart.shippingAddress.phone,
|
|
278
|
+
notes: orderNotes || undefined, // from the "Order notes" textarea \u2014 include one by default
|
|
276
279
|
});
|
|
277
280
|
setCheckout(checkoutData);
|
|
278
281
|
|
|
@@ -2123,6 +2126,7 @@ A useful order card includes ALL of the following when the data is present. Each
|
|
|
2123
2126
|
| **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
|
|
2124
2127
|
| **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
|
|
2125
2128
|
| Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
|
|
2129
|
+
| **Order note** | \`order.notes\` | The shopper's own checkout note, echoed back read-only. Render when present ("Your order note"). |
|
|
2126
2130
|
| 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.)". |
|
|
2127
2131
|
|
|
2128
2132
|
#### Rendering \`order.items[i].customizations\` by type
|
|
@@ -2148,7 +2152,6 @@ The storefront scaffolded by \`create-brainerce-store\` already implements this.
|
|
|
2148
2152
|
These fields are NOT returned to buyers by \`/customers/me/orders\`. If you see them in older examples, ignore them \u2014 they are for merchant/admin views only.
|
|
2149
2153
|
|
|
2150
2154
|
- \`order.accountId\`, \`order.storeId\`, \`order.customerId\` (already known on the request)
|
|
2151
|
-
- \`order.notes\` (internal merchant notes)
|
|
2152
2155
|
- \`order.customFieldValues\` (order-level checkout fields \u2014 merchant concept; buyers see \`items[i].customizations\`)
|
|
2153
2156
|
- \`order.appliedSurcharges\`, \`order.surchargeAmount\`, \`order.appliedRuleIds\`, \`order.downloadMeta\`, \`order.pickupLocationData\``;
|
|
2154
2157
|
}
|
|
@@ -2196,7 +2199,22 @@ function OrderConfirmation() {
|
|
|
2196
2199
|
|
|
2197
2200
|
**Common mistake:** Don't just check URL params and show success immediately!
|
|
2198
2201
|
You MUST call \`waitForOrder(checkoutId)\` to verify the order was created.
|
|
2199
|
-
Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart
|
|
2202
|
+
Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.
|
|
2203
|
+
|
|
2204
|
+
### Optional: render full order details on the confirmation page
|
|
2205
|
+
|
|
2206
|
+
Want a richer thank-you page (items, shipping address, totals, the shopper's
|
|
2207
|
+
order note)? After \`waitForOrder\` succeeds, fetch the full buyer-safe order \u2014
|
|
2208
|
+
works for guests too (possession of the checkout id is the credential):
|
|
2209
|
+
|
|
2210
|
+
\`\`\`typescript
|
|
2211
|
+
const order = await client.getOrderByCheckout(checkoutId);
|
|
2212
|
+
// order.items, order.shippingAddress, order.notes, order.subtotal,
|
|
2213
|
+
// order.shippingAmount, order.taxAmount / order.taxBreakdown, order.totalAmount
|
|
2214
|
+
\`\`\`
|
|
2215
|
+
|
|
2216
|
+
Render whatever subset fits your design \u2014 same field shapes as the buyer
|
|
2217
|
+
order-history view (see "Buyer order view" section).`;
|
|
2200
2218
|
}
|
|
2201
2219
|
function getI18nSection() {
|
|
2202
2220
|
return `## Internationalization (i18n)
|
|
@@ -2526,6 +2544,8 @@ dashboard or the admin SDK).
|
|
|
2526
2544
|
|
|
2527
2545
|
\`\`\`typescript
|
|
2528
2546
|
// Publish to a specific vibe-coded site (admin mode):
|
|
2547
|
+
await admin.publishProductToSalesChannel('prod_id', 'vc_conn_id');
|
|
2548
|
+
await admin.publishCouponToSalesChannel('coupon_id', 'vc_conn_id');
|
|
2529
2549
|
await admin.publishCategoryToVibeCodedSite('cat_id', 'conn_id');
|
|
2530
2550
|
await admin.publishTagToVibeCodedSite('tag_id', 'conn_id');
|
|
2531
2551
|
await admin.publishBrandToVibeCodedSite('brand_id', 'conn_id');
|
|
@@ -3321,11 +3341,18 @@ Points are earned automatically by the platform when an order is paid \u2014 the
|
|
|
3321
3341
|
\`\`\`typescript
|
|
3322
3342
|
// requires client.setCustomerToken(auth.token)
|
|
3323
3343
|
const status = await client.getLoyaltyStatus();
|
|
3324
|
-
// {
|
|
3344
|
+
// {
|
|
3345
|
+
// enrolled, pointsBalance, lifetimeEarned,
|
|
3346
|
+
// program: { pointsName, currencyRatio, status } | null,
|
|
3347
|
+
// tier: { id, name, level, pointsMultiplier } | null,
|
|
3348
|
+
// nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
|
|
3349
|
+
// progressToNextTier, pointsToNextTier,
|
|
3350
|
+
// }
|
|
3325
3351
|
\`\`\`
|
|
3326
3352
|
|
|
3327
3353
|
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3328
3354
|
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3355
|
+
- \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
|
|
3329
3356
|
|
|
3330
3357
|
## Enroll (only if auto-enroll is off)
|
|
3331
3358
|
|
|
@@ -3333,19 +3360,67 @@ const status = await client.getLoyaltyStatus();
|
|
|
3333
3360
|
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3334
3361
|
\`\`\`
|
|
3335
3362
|
|
|
3363
|
+
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).
|
|
3364
|
+
|
|
3336
3365
|
## Rewards & redemption
|
|
3337
3366
|
|
|
3338
3367
|
\`\`\`typescript
|
|
3339
3368
|
const rewards = await client.getAvailableRewards();
|
|
3340
|
-
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3369
|
+
// [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
|
|
3370
|
+
// Already filtered to rewards the customer's current tier qualifies for.
|
|
3341
3371
|
|
|
3342
|
-
const { couponCode, discountValue, pointsBalance } =
|
|
3372
|
+
const { couponCode, discountType, discountValue, pointsBalance } =
|
|
3373
|
+
await client.redeemLoyaltyReward(rewardId);
|
|
3343
3374
|
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3344
3375
|
\`\`\`
|
|
3345
3376
|
|
|
3346
|
-
- \`
|
|
3347
|
-
-
|
|
3377
|
+
- \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
|
|
3378
|
+
- \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3379
|
+
- Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
|
|
3348
3380
|
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3381
|
+
|
|
3382
|
+
## Social share (optional)
|
|
3383
|
+
|
|
3384
|
+
\`\`\`typescript
|
|
3385
|
+
await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
|
|
3386
|
+
\`\`\`
|
|
3387
|
+
|
|
3388
|
+
## Referrals (when the store enables them)
|
|
3389
|
+
|
|
3390
|
+
\`getLoyaltyStatus()\` also returns \`referralCode\` (the member's share code, null when referrals are disabled or the customer isn't enrolled) and \`referralWelcomeCoupon\` (the still-unused welcome coupon this customer got for signing up via a referral link).
|
|
3391
|
+
|
|
3392
|
+
\`\`\`typescript
|
|
3393
|
+
// 1. Referrer: build a share link around their code.
|
|
3394
|
+
const { referralCode } = await client.getLoyaltyStatus();
|
|
3395
|
+
const shareUrl = \`https://your-store.com/?ref=\${referralCode}\`;
|
|
3396
|
+
|
|
3397
|
+
// 2. Landing page: PUBLIC lookup \u2014 no customerToken needed.
|
|
3398
|
+
const info = await client.getReferralInfo(refFromQuery);
|
|
3399
|
+
// { valid, referrerFirstName, reward: { name, type, value, minOrderAmount } | null }
|
|
3400
|
+
if (info.valid) {
|
|
3401
|
+
// "Jane sent you a gift: 10% off your first order"
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// 3. Registration: pass the code \u2014 invalid codes never fail registration
|
|
3405
|
+
// (validated asynchronously server-side with fraud checks).
|
|
3406
|
+
await client.registerCustomer({ email, password, referralCode: refFromQuery });
|
|
3407
|
+
|
|
3408
|
+
// 4. Show the referee their welcome coupon after login:
|
|
3409
|
+
const { referralWelcomeCoupon } = await client.getLoyaltyStatus();
|
|
3410
|
+
if (referralWelcomeCoupon) {
|
|
3411
|
+
await client.applyCoupon(cartId, referralWelcomeCoupon.code);
|
|
3412
|
+
}
|
|
3413
|
+
\`\`\`
|
|
3414
|
+
|
|
3415
|
+
The referrer earns a points bonus after the referee's first qualifying order (held through the program's pending window, like order points). Everything server-side \u2014 no storefront earn calls.
|
|
3416
|
+
|
|
3417
|
+
## Birthday gifts (when the store enables them)
|
|
3418
|
+
|
|
3419
|
+
No dedicated calls \u2014 collect \`birthMonth\`/\`birthDay\` (1-12 / 1-31, **no year**, must be sent together) via \`updateMyProfile()\` and the platform emails a one-time gift coupon ahead of the customer's birthday.
|
|
3420
|
+
|
|
3421
|
+
\`\`\`typescript
|
|
3422
|
+
await client.updateMyProfile({ birthMonth: 4, birthDay: 17 });
|
|
3423
|
+
\`\`\`
|
|
3349
3424
|
`;
|
|
3350
3425
|
}
|
|
3351
3426
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
@@ -3870,6 +3945,7 @@ interface Checkout {
|
|
|
3870
3945
|
appliedSurcharges?: Array<{ key: string; name: string; value: unknown; amount: string }> | null;
|
|
3871
3946
|
customFieldValues?: Record<string, unknown> | null;
|
|
3872
3947
|
couponCode?: string | null;
|
|
3948
|
+
notes?: string | null; // Order-level shopper note \u2014 copied onto the order at completion
|
|
3873
3949
|
lineItems: CheckoutLineItem[]; // Use THIS for order summary, NOT cart.items!
|
|
3874
3950
|
itemCount: number;
|
|
3875
3951
|
availableShippingRates?: ShippingRate[];
|
|
@@ -3938,6 +4014,7 @@ interface SetShippingAddressDto {
|
|
|
3938
4014
|
postalCode: string;
|
|
3939
4015
|
country: string;
|
|
3940
4016
|
phone?: string;
|
|
4017
|
+
notes?: string; // Order notes textarea \u2014 include one on checkout by default! Max 2000 chars, lands on the order
|
|
3941
4018
|
}
|
|
3942
4019
|
|
|
3943
4020
|
interface CreateCheckoutDto {
|
|
@@ -4881,6 +4958,18 @@ interface TaxEstimateResponse {
|
|
|
4881
4958
|
// full shipping address. The country comes from your edge runtime
|
|
4882
4959
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4883
4960
|
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4961
|
+
interface LoyaltyTierSummary {
|
|
4962
|
+
id: string;
|
|
4963
|
+
name: string;
|
|
4964
|
+
level: number; // ordering; higher = better tier
|
|
4965
|
+
pointsMultiplier: number;
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4968
|
+
interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
|
|
4969
|
+
qualificationType: 'SPEND' | 'POINTS';
|
|
4970
|
+
qualificationThreshold: number;
|
|
4971
|
+
}
|
|
4972
|
+
|
|
4884
4973
|
interface LoyaltyStatus {
|
|
4885
4974
|
enrolled: boolean;
|
|
4886
4975
|
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
@@ -4890,6 +4979,28 @@ interface LoyaltyStatus {
|
|
|
4890
4979
|
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4891
4980
|
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4892
4981
|
} | null; // null when the store has no loyalty program
|
|
4982
|
+
tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
|
|
4983
|
+
nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
|
|
4984
|
+
progressToNextTier: number; // 0..1
|
|
4985
|
+
pointsToNextTier: number | null;
|
|
4986
|
+
referralCode?: string | null; // member's share code; null when referrals disabled / not enrolled
|
|
4987
|
+
referralWelcomeCoupon?: { // unused welcome coupon from signing up via a referral link
|
|
4988
|
+
code: string;
|
|
4989
|
+
type: string; // 'PERCENTAGE' | 'FIXED_AMOUNT'
|
|
4990
|
+
value: number;
|
|
4991
|
+
} | null;
|
|
4992
|
+
}
|
|
4993
|
+
|
|
4994
|
+
// Public referral-link lookup \u2014 NO customerToken needed (safe pre-signup)
|
|
4995
|
+
interface ReferralInfo {
|
|
4996
|
+
valid: boolean; // false for unknown/disabled codes
|
|
4997
|
+
referrerFirstName: string | null; // for "Jane sent you a gift" copy \u2014 no other PII
|
|
4998
|
+
reward: {
|
|
4999
|
+
name: string;
|
|
5000
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
5001
|
+
value: number;
|
|
5002
|
+
minOrderAmount: number | null;
|
|
5003
|
+
} | null;
|
|
4893
5004
|
}
|
|
4894
5005
|
|
|
4895
5006
|
interface LoyaltyReward {
|
|
@@ -4897,16 +5008,19 @@ interface LoyaltyReward {
|
|
|
4897
5008
|
name: string;
|
|
4898
5009
|
description: string | null;
|
|
4899
5010
|
pointsCost: number;
|
|
4900
|
-
|
|
5011
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
5012
|
+
discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
|
|
4901
5013
|
minOrderAmount: number | null;
|
|
4902
5014
|
maxUsesPerUser: number;
|
|
4903
5015
|
isActive: boolean;
|
|
5016
|
+
minTierLevel: number | null; // minimum tier level required, or null for no restriction
|
|
4904
5017
|
createdAt: string;
|
|
4905
5018
|
updatedAt: string;
|
|
4906
5019
|
}
|
|
4907
5020
|
|
|
4908
5021
|
interface RedeemRewardResult {
|
|
4909
5022
|
couponCode: string; // one-time coupon to apply at checkout
|
|
5023
|
+
discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4910
5024
|
discountValue: number;
|
|
4911
5025
|
pointsSpent: number;
|
|
4912
5026
|
pointsBalance: number; // remaining balance after redemption
|
|
@@ -4916,6 +5030,10 @@ interface RedeemRewardResult {
|
|
|
4916
5030
|
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4917
5031
|
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4918
5032
|
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
5033
|
+
// client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
|
|
5034
|
+
// client.getReferralInfo(code: string): Promise<ReferralInfo> // PUBLIC \u2014 no customerToken
|
|
5035
|
+
// Referral signup: client.registerCustomer({ ..., referralCode })
|
|
5036
|
+
// Birthday gift data: client.updateMyProfile({ birthMonth, birthDay }) // 1-12 / 1-31, no year, both together
|
|
4919
5037
|
`;
|
|
4920
5038
|
var TYPES_BY_DOMAIN = {
|
|
4921
5039
|
products: PRODUCTS_TYPES,
|
|
@@ -6414,6 +6532,14 @@ function formatCapabilities(caps) {
|
|
|
6414
6532
|
lines.push(
|
|
6415
6533
|
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6416
6534
|
);
|
|
6535
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6536
|
+
lines.push(
|
|
6537
|
+
caps.features.hasReferralProgram ? "\u2705 Referral program enabled (share codes + welcome reward \u2014 storefront-mode only)" : "\u274C Referral program not enabled"
|
|
6538
|
+
);
|
|
6539
|
+
lines.push(
|
|
6540
|
+
caps.features.hasBirthdayRewards ? "\u2705 Birthday gifts enabled (set birthMonth/birthDay via updateMyProfile)" : "\u274C Birthday gifts not enabled"
|
|
6541
|
+
);
|
|
6542
|
+
}
|
|
6417
6543
|
lines.push("");
|
|
6418
6544
|
lines.push("## Connection Settings");
|
|
6419
6545
|
lines.push(
|
|
@@ -6461,7 +6587,7 @@ function formatCapabilities(caps) {
|
|
|
6461
6587
|
}
|
|
6462
6588
|
if (caps.features.hasLoyaltyProgram) {
|
|
6463
6589
|
suggestions.push(
|
|
6464
|
-
"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(),
|
|
6590
|
+
"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."
|
|
6465
6591
|
);
|
|
6466
6592
|
}
|
|
6467
6593
|
if (caps.features.hasCheckoutCustomFields) {
|
package/dist/index.d.mts
CHANGED
|
@@ -77,6 +77,10 @@ interface StoreCapabilities {
|
|
|
77
77
|
hasContent?: boolean;
|
|
78
78
|
/** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
|
|
79
79
|
hasLoyaltyProgram?: boolean;
|
|
80
|
+
/** True when the loyalty program has the referral program enabled. Storefront-mode only. */
|
|
81
|
+
hasReferralProgram?: boolean;
|
|
82
|
+
/** True when the loyalty program has birthday gifts enabled. */
|
|
83
|
+
hasBirthdayRewards?: boolean;
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
|
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,10 @@ interface StoreCapabilities {
|
|
|
77
77
|
hasContent?: boolean;
|
|
78
78
|
/** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
|
|
79
79
|
hasLoyaltyProgram?: boolean;
|
|
80
|
+
/** True when the loyalty program has the referral program enabled. Storefront-mode only. */
|
|
81
|
+
hasReferralProgram?: boolean;
|
|
82
|
+
/** True when the loyalty program has birthday gifts enabled. */
|
|
83
|
+
hasBirthdayRewards?: boolean;
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
|
package/dist/index.js
CHANGED
|
@@ -243,7 +243,7 @@ async function startCheckout() {
|
|
|
243
243
|
2. Customer optionally applies coupon on cart page \u2192 \`applyCoupon(cartId, code)\`
|
|
244
244
|
3. Detect payment providers \u2192 \`getPaymentProviders()\`
|
|
245
245
|
4. Start checkout session (\`startGuestCheckout()\` or \`createCheckout()\`)
|
|
246
|
-
5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\`
|
|
246
|
+
5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\` \u2014 also pass \`notes\` from the **"Order notes" textarea that every checkout page should include by default** (optional field, max 2000 chars; lands on the order for the merchant)
|
|
247
247
|
6. Select shipping method \u2192 \`selectShippingMethod()\`
|
|
248
248
|
6b. (Optional) Set checkout custom fields \u2192 \`setCheckoutCustomFields()\` \u2014 surcharges auto-calculated
|
|
249
249
|
7. Create payment intent \u2192 \`createPaymentIntent()\` \u2192 returns \`{ clientSecret, provider }\`
|
|
@@ -265,6 +265,8 @@ function CheckoutPage() {
|
|
|
265
265
|
const [checkout, setCheckout] = useState<Checkout | null>(null);
|
|
266
266
|
const [error, setError] = useState<string | null>(null);
|
|
267
267
|
const [loading, setLoading] = useState(true);
|
|
268
|
+
// Optional "Order notes" textarea \u2014 render it on the checkout page by default
|
|
269
|
+
const [orderNotes, setOrderNotes] = useState('');
|
|
268
270
|
|
|
269
271
|
useEffect(() => { startCheckout(); }, []);
|
|
270
272
|
|
|
@@ -299,6 +301,7 @@ function CheckoutPage() {
|
|
|
299
301
|
postalCode: cart.shippingAddress.postalCode,
|
|
300
302
|
country: cart.shippingAddress.country,
|
|
301
303
|
phone: cart.shippingAddress.phone,
|
|
304
|
+
notes: orderNotes || undefined, // from the "Order notes" textarea \u2014 include one by default
|
|
302
305
|
});
|
|
303
306
|
setCheckout(checkoutData);
|
|
304
307
|
|
|
@@ -2149,6 +2152,7 @@ A useful order card includes ALL of the following when the data is present. Each
|
|
|
2149
2152
|
| **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
|
|
2150
2153
|
| **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
|
|
2151
2154
|
| Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
|
|
2155
|
+
| **Order note** | \`order.notes\` | The shopper's own checkout note, echoed back read-only. Render when present ("Your order note"). |
|
|
2152
2156
|
| 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.)". |
|
|
2153
2157
|
|
|
2154
2158
|
#### Rendering \`order.items[i].customizations\` by type
|
|
@@ -2174,7 +2178,6 @@ The storefront scaffolded by \`create-brainerce-store\` already implements this.
|
|
|
2174
2178
|
These fields are NOT returned to buyers by \`/customers/me/orders\`. If you see them in older examples, ignore them \u2014 they are for merchant/admin views only.
|
|
2175
2179
|
|
|
2176
2180
|
- \`order.accountId\`, \`order.storeId\`, \`order.customerId\` (already known on the request)
|
|
2177
|
-
- \`order.notes\` (internal merchant notes)
|
|
2178
2181
|
- \`order.customFieldValues\` (order-level checkout fields \u2014 merchant concept; buyers see \`items[i].customizations\`)
|
|
2179
2182
|
- \`order.appliedSurcharges\`, \`order.surchargeAmount\`, \`order.appliedRuleIds\`, \`order.downloadMeta\`, \`order.pickupLocationData\``;
|
|
2180
2183
|
}
|
|
@@ -2222,7 +2225,22 @@ function OrderConfirmation() {
|
|
|
2222
2225
|
|
|
2223
2226
|
**Common mistake:** Don't just check URL params and show success immediately!
|
|
2224
2227
|
You MUST call \`waitForOrder(checkoutId)\` to verify the order was created.
|
|
2225
|
-
Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart
|
|
2228
|
+
Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.
|
|
2229
|
+
|
|
2230
|
+
### Optional: render full order details on the confirmation page
|
|
2231
|
+
|
|
2232
|
+
Want a richer thank-you page (items, shipping address, totals, the shopper's
|
|
2233
|
+
order note)? After \`waitForOrder\` succeeds, fetch the full buyer-safe order \u2014
|
|
2234
|
+
works for guests too (possession of the checkout id is the credential):
|
|
2235
|
+
|
|
2236
|
+
\`\`\`typescript
|
|
2237
|
+
const order = await client.getOrderByCheckout(checkoutId);
|
|
2238
|
+
// order.items, order.shippingAddress, order.notes, order.subtotal,
|
|
2239
|
+
// order.shippingAmount, order.taxAmount / order.taxBreakdown, order.totalAmount
|
|
2240
|
+
\`\`\`
|
|
2241
|
+
|
|
2242
|
+
Render whatever subset fits your design \u2014 same field shapes as the buyer
|
|
2243
|
+
order-history view (see "Buyer order view" section).`;
|
|
2226
2244
|
}
|
|
2227
2245
|
function getI18nSection() {
|
|
2228
2246
|
return `## Internationalization (i18n)
|
|
@@ -2552,6 +2570,8 @@ dashboard or the admin SDK).
|
|
|
2552
2570
|
|
|
2553
2571
|
\`\`\`typescript
|
|
2554
2572
|
// Publish to a specific vibe-coded site (admin mode):
|
|
2573
|
+
await admin.publishProductToSalesChannel('prod_id', 'vc_conn_id');
|
|
2574
|
+
await admin.publishCouponToSalesChannel('coupon_id', 'vc_conn_id');
|
|
2555
2575
|
await admin.publishCategoryToVibeCodedSite('cat_id', 'conn_id');
|
|
2556
2576
|
await admin.publishTagToVibeCodedSite('tag_id', 'conn_id');
|
|
2557
2577
|
await admin.publishBrandToVibeCodedSite('brand_id', 'conn_id');
|
|
@@ -3347,11 +3367,18 @@ Points are earned automatically by the platform when an order is paid \u2014 the
|
|
|
3347
3367
|
\`\`\`typescript
|
|
3348
3368
|
// requires client.setCustomerToken(auth.token)
|
|
3349
3369
|
const status = await client.getLoyaltyStatus();
|
|
3350
|
-
// {
|
|
3370
|
+
// {
|
|
3371
|
+
// enrolled, pointsBalance, lifetimeEarned,
|
|
3372
|
+
// program: { pointsName, currencyRatio, status } | null,
|
|
3373
|
+
// tier: { id, name, level, pointsMultiplier } | null,
|
|
3374
|
+
// nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
|
|
3375
|
+
// progressToNextTier, pointsToNextTier,
|
|
3376
|
+
// }
|
|
3351
3377
|
\`\`\`
|
|
3352
3378
|
|
|
3353
3379
|
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3354
3380
|
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3381
|
+
- \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
|
|
3355
3382
|
|
|
3356
3383
|
## Enroll (only if auto-enroll is off)
|
|
3357
3384
|
|
|
@@ -3359,19 +3386,67 @@ const status = await client.getLoyaltyStatus();
|
|
|
3359
3386
|
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3360
3387
|
\`\`\`
|
|
3361
3388
|
|
|
3389
|
+
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).
|
|
3390
|
+
|
|
3362
3391
|
## Rewards & redemption
|
|
3363
3392
|
|
|
3364
3393
|
\`\`\`typescript
|
|
3365
3394
|
const rewards = await client.getAvailableRewards();
|
|
3366
|
-
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3395
|
+
// [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
|
|
3396
|
+
// Already filtered to rewards the customer's current tier qualifies for.
|
|
3367
3397
|
|
|
3368
|
-
const { couponCode, discountValue, pointsBalance } =
|
|
3398
|
+
const { couponCode, discountType, discountValue, pointsBalance } =
|
|
3399
|
+
await client.redeemLoyaltyReward(rewardId);
|
|
3369
3400
|
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3370
3401
|
\`\`\`
|
|
3371
3402
|
|
|
3372
|
-
- \`
|
|
3373
|
-
-
|
|
3403
|
+
- \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
|
|
3404
|
+
- \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3405
|
+
- Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
|
|
3374
3406
|
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3407
|
+
|
|
3408
|
+
## Social share (optional)
|
|
3409
|
+
|
|
3410
|
+
\`\`\`typescript
|
|
3411
|
+
await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
|
|
3412
|
+
\`\`\`
|
|
3413
|
+
|
|
3414
|
+
## Referrals (when the store enables them)
|
|
3415
|
+
|
|
3416
|
+
\`getLoyaltyStatus()\` also returns \`referralCode\` (the member's share code, null when referrals are disabled or the customer isn't enrolled) and \`referralWelcomeCoupon\` (the still-unused welcome coupon this customer got for signing up via a referral link).
|
|
3417
|
+
|
|
3418
|
+
\`\`\`typescript
|
|
3419
|
+
// 1. Referrer: build a share link around their code.
|
|
3420
|
+
const { referralCode } = await client.getLoyaltyStatus();
|
|
3421
|
+
const shareUrl = \`https://your-store.com/?ref=\${referralCode}\`;
|
|
3422
|
+
|
|
3423
|
+
// 2. Landing page: PUBLIC lookup \u2014 no customerToken needed.
|
|
3424
|
+
const info = await client.getReferralInfo(refFromQuery);
|
|
3425
|
+
// { valid, referrerFirstName, reward: { name, type, value, minOrderAmount } | null }
|
|
3426
|
+
if (info.valid) {
|
|
3427
|
+
// "Jane sent you a gift: 10% off your first order"
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
// 3. Registration: pass the code \u2014 invalid codes never fail registration
|
|
3431
|
+
// (validated asynchronously server-side with fraud checks).
|
|
3432
|
+
await client.registerCustomer({ email, password, referralCode: refFromQuery });
|
|
3433
|
+
|
|
3434
|
+
// 4. Show the referee their welcome coupon after login:
|
|
3435
|
+
const { referralWelcomeCoupon } = await client.getLoyaltyStatus();
|
|
3436
|
+
if (referralWelcomeCoupon) {
|
|
3437
|
+
await client.applyCoupon(cartId, referralWelcomeCoupon.code);
|
|
3438
|
+
}
|
|
3439
|
+
\`\`\`
|
|
3440
|
+
|
|
3441
|
+
The referrer earns a points bonus after the referee's first qualifying order (held through the program's pending window, like order points). Everything server-side \u2014 no storefront earn calls.
|
|
3442
|
+
|
|
3443
|
+
## Birthday gifts (when the store enables them)
|
|
3444
|
+
|
|
3445
|
+
No dedicated calls \u2014 collect \`birthMonth\`/\`birthDay\` (1-12 / 1-31, **no year**, must be sent together) via \`updateMyProfile()\` and the platform emails a one-time gift coupon ahead of the customer's birthday.
|
|
3446
|
+
|
|
3447
|
+
\`\`\`typescript
|
|
3448
|
+
await client.updateMyProfile({ birthMonth: 4, birthDay: 17 });
|
|
3449
|
+
\`\`\`
|
|
3375
3450
|
`;
|
|
3376
3451
|
}
|
|
3377
3452
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
@@ -3896,6 +3971,7 @@ interface Checkout {
|
|
|
3896
3971
|
appliedSurcharges?: Array<{ key: string; name: string; value: unknown; amount: string }> | null;
|
|
3897
3972
|
customFieldValues?: Record<string, unknown> | null;
|
|
3898
3973
|
couponCode?: string | null;
|
|
3974
|
+
notes?: string | null; // Order-level shopper note \u2014 copied onto the order at completion
|
|
3899
3975
|
lineItems: CheckoutLineItem[]; // Use THIS for order summary, NOT cart.items!
|
|
3900
3976
|
itemCount: number;
|
|
3901
3977
|
availableShippingRates?: ShippingRate[];
|
|
@@ -3964,6 +4040,7 @@ interface SetShippingAddressDto {
|
|
|
3964
4040
|
postalCode: string;
|
|
3965
4041
|
country: string;
|
|
3966
4042
|
phone?: string;
|
|
4043
|
+
notes?: string; // Order notes textarea \u2014 include one on checkout by default! Max 2000 chars, lands on the order
|
|
3967
4044
|
}
|
|
3968
4045
|
|
|
3969
4046
|
interface CreateCheckoutDto {
|
|
@@ -4907,6 +4984,18 @@ interface TaxEstimateResponse {
|
|
|
4907
4984
|
// full shipping address. The country comes from your edge runtime
|
|
4908
4985
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4909
4986
|
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4987
|
+
interface LoyaltyTierSummary {
|
|
4988
|
+
id: string;
|
|
4989
|
+
name: string;
|
|
4990
|
+
level: number; // ordering; higher = better tier
|
|
4991
|
+
pointsMultiplier: number;
|
|
4992
|
+
}
|
|
4993
|
+
|
|
4994
|
+
interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
|
|
4995
|
+
qualificationType: 'SPEND' | 'POINTS';
|
|
4996
|
+
qualificationThreshold: number;
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4910
4999
|
interface LoyaltyStatus {
|
|
4911
5000
|
enrolled: boolean;
|
|
4912
5001
|
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
@@ -4916,6 +5005,28 @@ interface LoyaltyStatus {
|
|
|
4916
5005
|
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4917
5006
|
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4918
5007
|
} | null; // null when the store has no loyalty program
|
|
5008
|
+
tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
|
|
5009
|
+
nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
|
|
5010
|
+
progressToNextTier: number; // 0..1
|
|
5011
|
+
pointsToNextTier: number | null;
|
|
5012
|
+
referralCode?: string | null; // member's share code; null when referrals disabled / not enrolled
|
|
5013
|
+
referralWelcomeCoupon?: { // unused welcome coupon from signing up via a referral link
|
|
5014
|
+
code: string;
|
|
5015
|
+
type: string; // 'PERCENTAGE' | 'FIXED_AMOUNT'
|
|
5016
|
+
value: number;
|
|
5017
|
+
} | null;
|
|
5018
|
+
}
|
|
5019
|
+
|
|
5020
|
+
// Public referral-link lookup \u2014 NO customerToken needed (safe pre-signup)
|
|
5021
|
+
interface ReferralInfo {
|
|
5022
|
+
valid: boolean; // false for unknown/disabled codes
|
|
5023
|
+
referrerFirstName: string | null; // for "Jane sent you a gift" copy \u2014 no other PII
|
|
5024
|
+
reward: {
|
|
5025
|
+
name: string;
|
|
5026
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
5027
|
+
value: number;
|
|
5028
|
+
minOrderAmount: number | null;
|
|
5029
|
+
} | null;
|
|
4919
5030
|
}
|
|
4920
5031
|
|
|
4921
5032
|
interface LoyaltyReward {
|
|
@@ -4923,16 +5034,19 @@ interface LoyaltyReward {
|
|
|
4923
5034
|
name: string;
|
|
4924
5035
|
description: string | null;
|
|
4925
5036
|
pointsCost: number;
|
|
4926
|
-
|
|
5037
|
+
type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
5038
|
+
discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
|
|
4927
5039
|
minOrderAmount: number | null;
|
|
4928
5040
|
maxUsesPerUser: number;
|
|
4929
5041
|
isActive: boolean;
|
|
5042
|
+
minTierLevel: number | null; // minimum tier level required, or null for no restriction
|
|
4930
5043
|
createdAt: string;
|
|
4931
5044
|
updatedAt: string;
|
|
4932
5045
|
}
|
|
4933
5046
|
|
|
4934
5047
|
interface RedeemRewardResult {
|
|
4935
5048
|
couponCode: string; // one-time coupon to apply at checkout
|
|
5049
|
+
discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
|
|
4936
5050
|
discountValue: number;
|
|
4937
5051
|
pointsSpent: number;
|
|
4938
5052
|
pointsBalance: number; // remaining balance after redemption
|
|
@@ -4942,6 +5056,10 @@ interface RedeemRewardResult {
|
|
|
4942
5056
|
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4943
5057
|
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4944
5058
|
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
5059
|
+
// client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
|
|
5060
|
+
// client.getReferralInfo(code: string): Promise<ReferralInfo> // PUBLIC \u2014 no customerToken
|
|
5061
|
+
// Referral signup: client.registerCustomer({ ..., referralCode })
|
|
5062
|
+
// Birthday gift data: client.updateMyProfile({ birthMonth, birthDay }) // 1-12 / 1-31, no year, both together
|
|
4945
5063
|
`;
|
|
4946
5064
|
var TYPES_BY_DOMAIN = {
|
|
4947
5065
|
products: PRODUCTS_TYPES,
|
|
@@ -6440,6 +6558,14 @@ function formatCapabilities(caps) {
|
|
|
6440
6558
|
lines.push(
|
|
6441
6559
|
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6442
6560
|
);
|
|
6561
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6562
|
+
lines.push(
|
|
6563
|
+
caps.features.hasReferralProgram ? "\u2705 Referral program enabled (share codes + welcome reward \u2014 storefront-mode only)" : "\u274C Referral program not enabled"
|
|
6564
|
+
);
|
|
6565
|
+
lines.push(
|
|
6566
|
+
caps.features.hasBirthdayRewards ? "\u2705 Birthday gifts enabled (set birthMonth/birthDay via updateMyProfile)" : "\u274C Birthday gifts not enabled"
|
|
6567
|
+
);
|
|
6568
|
+
}
|
|
6443
6569
|
lines.push("");
|
|
6444
6570
|
lines.push("## Connection Settings");
|
|
6445
6571
|
lines.push(
|
|
@@ -6487,7 +6613,7 @@ function formatCapabilities(caps) {
|
|
|
6487
6613
|
}
|
|
6488
6614
|
if (caps.features.hasLoyaltyProgram) {
|
|
6489
6615
|
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(),
|
|
6616
|
+
"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."
|
|
6491
6617
|
);
|
|
6492
6618
|
}
|
|
6493
6619
|
if (caps.features.hasCheckoutCustomFields) {
|