@brainerce/mcp-server 3.11.1 → 3.12.1

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/stdio.js CHANGED
@@ -718,17 +718,19 @@ function getPaymentProvidersSection() {
718
718
  return `## Payment Provider Detection (DO THIS FIRST on checkout page!)
719
719
 
720
720
  \`\`\`typescript
721
- const { hasPayments, providers } = await client.getPaymentProviders();
721
+ const { hasPayments, providers, defaultProvider } = await client.getPaymentProviders();
722
722
  if (!hasPayments) {
723
723
  // Show error: "Payment is not configured for this store"
724
724
  return;
725
725
  }
726
- const stripeProvider = providers.find(p => p.provider === 'stripe');
727
- const growProvider = providers.find(p => p.provider === 'grow');
728
- const paypalProvider = providers.find(p => p.provider === 'paypal');
726
+ // Primary vs additive (Shopify-parity): render wallets as express buttons ABOVE the card form.
727
+ const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal (WALLET)
728
+ const primary = defaultProvider && !defaultProvider.isAdditive ? defaultProvider : undefined;
729
729
  \`\`\`
730
730
 
731
- 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\`.
731
+ 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).
732
+
733
+ **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.)
732
734
 
733
735
  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**:
734
736
 
@@ -1116,11 +1118,22 @@ client-side overlay needed. Swatch colors are language-agnostic.
1116
1118
  function ProductDescription({ product }: { product: Product }) {
1117
1119
  const content = getDescriptionContent(product);
1118
1120
  if (!content) return null;
1119
- if ('html' in content) return <div dangerouslySetInnerHTML={{ __html: content.html }} />;
1121
+ // Descriptions are merchant HTML and may contain <video> + host-locked
1122
+ // YouTube/Vimeo <iframe> embeds. NEVER render unsanitized \u2014 sanitize and
1123
+ // allowlist video/source + iframe (restricted to youtube/vimeo hosts).
1124
+ if ('html' in content) {
1125
+ return <div dangerouslySetInnerHTML={{ __html: sanitizeProductHtml(content.html) }} />;
1126
+ }
1120
1127
  return <p>{content.text}</p>;
1121
1128
  }
1122
1129
  \`\`\`
1123
1130
 
1131
+ **Sanitize descriptions.** \`sanitizeProductHtml\` is the host-locked sanitizer the
1132
+ \`create-brainerce-store\` scaffold ships at \`src/lib/sanitize-html.ts\`. If you roll
1133
+ your own, allow \`video\`/\`source\` and \`iframe\` **restricted** to \`www.youtube.com\`,
1134
+ \`www.youtube-nocookie.com\`, \`player.vimeo.com\` (never arbitrary iframes), and add
1135
+ those hosts to your CSP \`frame-src\` or embeds render blank.
1136
+
1124
1137
  ### Metafields (Custom Product Fields) \u2014 display on product detail!
1125
1138
 
1126
1139
  **IMPORTANT: Render metafield values based on \`mf.type\`!** Don't just display \`mf.value\` as text for all types.
@@ -1203,7 +1216,28 @@ const { url } = await client.uploadCustomizationFile(file);
1203
1216
  // Then use the URL in metadata: metadata: { logo: url }
1204
1217
  \`\`\`
1205
1218
 
1206
- **Customization field properties:** \`key\`, \`name\` (display label), \`type\`, \`required\`, \`minLength\`, \`maxLength\`, \`minValue\`, \`maxValue\`, \`enumValues\`, \`defaultValue\`, \`position\`
1219
+ **Customization field properties:** \`key\`, \`name\` (display label), \`type\`, \`required\`, \`minLength\`, \`maxLength\`, \`minValue\`, \`maxValue\`, \`enumValues\` (array of \`{label, value, swatchColor?, swatchImageUrl?}\`), \`defaultValue\`, \`position\`
1220
+
1221
+ **SELECT / MULTI_SELECT \u2014 use \`option.value\` to submit, \`option.label\` to display:**
1222
+
1223
+ \`\`\`typescript
1224
+ for (const option of field.enumValues ?? []) {
1225
+ // option.value \u2192 what to submit in metadata
1226
+ // option.label \u2192 what to show the customer
1227
+ // option.swatchColor \u2192 optional hex color for color swatches
1228
+ }
1229
+ \`\`\`
1230
+
1231
+ **Display customizations in cart / checkout \u2014 no extra API call needed:**
1232
+
1233
+ CartItem and CheckoutLineItem both include a \`customizations\` object alongside \`metadata\`. Use \`customizations\` for display \u2014 it contains resolved labels, not raw keys.
1234
+
1235
+ \`\`\`typescript
1236
+ // Works for CartItem, CheckoutLineItem, and OrderItem \u2014 same shape
1237
+ const lines = Object.values(item.customizations ?? {})
1238
+ .map(c => \`\${c.label}: \${Array.isArray(c.value) ? c.value.join(', ') : c.value}\`);
1239
+ // e.g. ["Frame color: Gold", "Add-ons: Gift wrap"]
1240
+ \`\`\`
1207
1241
 
1208
1242
  ### Downloadable / Digital Products
1209
1243
 
@@ -2534,8 +2568,9 @@ await admin.createTaxRate({ name: 'VAT (Food)', rate: 0, country: 'GB', taxClass
2534
2568
  await admin.mergeTaxClasses(food.id, standardClassId);
2535
2569
  \`\`\`
2536
2570
 
2537
- Storefront (public, no apiKey \u2014 \`storeId\` mode): \`getStoreTaxClasses()\` lists
2538
- the store's classes (storefront-safe fields only) for a transparency badge.
2571
+ Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
2572
+ on the \`products:read\` scope every connection already has): \`getStoreTaxClasses()\`
2573
+ lists the store's classes (storefront-safe fields only) for a transparency badge.
2539
2574
 
2540
2575
  For a buyer-facing tax preview on PDP / PLP / cart, \`estimateTax({ country,
2541
2576
  subtotal })\` returns the tax portion at the Standard rate for the buyer's
@@ -2557,8 +2592,10 @@ A region binds countries \u2192 currency + tax-display mode + enabled payment
2557
2592
  providers. Manage them via the SDK (scopes \`regions:read\` / \`regions:write\`).
2558
2593
  \`detectRegion\` is a pure client-side helper to map a buyer's country to a
2559
2594
  region; pass the resolved \`regionId\` to \`createCheckout\` to associate the
2560
- checkout with a region (recorded for reporting + provider scoping; currency
2561
- follows the cart until FX price conversion lands).
2595
+ checkout with a region (recorded for reporting + provider scoping). FX-at-checkout:
2596
+ presentment-enabled regions (Stripe today) charge in the region currency and the
2597
+ checkout response carries a \`presentment\` overlay with the charged amounts;
2598
+ otherwise the checkout is charged in the store base currency.
2562
2599
 
2563
2600
  \`\`\`typescript
2564
2601
  const eu = await admin.createRegion({
@@ -2571,9 +2608,12 @@ const { data: regions } = await admin.getRegions();
2571
2608
  const region = admin.detectRegion('DE', regions); // \u2192 eu, else default, else null
2572
2609
  \`\`\`
2573
2610
 
2574
- Storefront (public, no apiKey \u2014 \`storeId\` mode): \`getStoreRegions()\` lists
2575
- active regions, \`getStoreRegion(id)\` returns one region + its payment providers.
2576
- Pair with \`detectRegion\` to pick the buyer's region for currency display.
2611
+ Storefront (public, no apiKey \u2014 \`storeId\` mode OR vibe-coded mode \`vc_*\`, gated
2612
+ on the \`products:read\` scope every connection already has): \`getStoreRegions()\`
2613
+ lists active regions, \`getStoreRegion(id)\` returns one region + its payment
2614
+ providers. \`getAutoRegion(country)\` and \`estimateTax({ country, subtotal })\` work
2615
+ in both modes too. Pair with \`detectRegion\` to pick the buyer's region for
2616
+ currency display.
2577
2617
 
2578
2618
  \`\`\`typescript
2579
2619
  const store = new BrainerceClient({ storeId: 'store_123' });
@@ -3197,6 +3237,76 @@ Rules:
3197
3237
  count updates when the bot adds items.
3198
3238
  `;
3199
3239
  }
3240
+ function getStorefrontAnalyticsSection(connectionId) {
3241
+ return `## Storefront Traffic Analytics (visits, countries, sources)
3242
+
3243
+ Brainerce can track cookieless storefront traffic \u2014 how many people visit the
3244
+ store, from which countries, and from which traffic sources \u2014 and surface it in
3245
+ the merchant dashboard alongside sales (so the merchant gets a REAL conversion
3246
+ rate: visits \u2192 orders).
3247
+
3248
+ Add ONE line to the storefront's root layout (\`<head>\` or end of \`<body>\`):
3249
+
3250
+ \`\`\`html
3251
+ <script defer src="https://api.brainerce.com/t.js" data-channel="${connectionId}"></script>
3252
+ \`\`\`
3253
+
3254
+ That's it \u2014 the pixel auto-sends a pageview beacon on load and on SPA route
3255
+ changes. Stores scaffolded with \`create-brainerce-store\` already include it.
3256
+
3257
+ Privacy: the pixel is cookieless and stores NO personal data \u2014 no cookie, no
3258
+ cross-site/cross-day tracking, and the visitor IP is resolved to a country
3259
+ server-side then discarded. No cookie-consent banner is required for it under
3260
+ GDPR/ePrivacy.
3261
+
3262
+ CSP: if the storefront sets a Content-Security-Policy, allow the script + beacon:
3263
+ - \`script-src\` \u2026 \`https://api.brainerce.com\`
3264
+ - \`connect-src\` \u2026 \`https://api.brainerce.com\`
3265
+
3266
+ The merchant can toggle tracking per sales channel in the dashboard. The data
3267
+ shows up under Dashboard \u2192 Analytics (visits over time, by country, by source,
3268
+ by device) and can also be asked of the AI assistant ("how many visits did I get
3269
+ this month and from where?").
3270
+ `;
3271
+ }
3272
+ function getShippingAppSection() {
3273
+ return `## App Store Shipping (Shippo)
3274
+
3275
+ Merchants install Shippo from the Brainerce App Store. After connecting their own Shippo account
3276
+ (OAuth), live carrier rates appear automatically at checkout alongside any manually configured
3277
+ zone rates. **Billing goes directly to the merchant's Shippo account \u2014 Brainerce never charges.**
3278
+
3279
+ ### Checkout \u2014 live carrier rates
3280
+
3281
+ No SDK changes needed. Once Shippo is installed and configured, the existing
3282
+ \`getShippingRates()\` call returns both manual zone rates and live Shippo quotes:
3283
+
3284
+ \`\`\`typescript
3285
+ const { rates } = await client.setShippingAddress(checkoutId, { ... });
3286
+ // rates may include:
3287
+ // { id: 'shippo:rate_8f123abc', name: 'USPS Priority Mail', price: '8.50', currency: 'USD', source: 'carrier' }
3288
+ \`\`\`
3289
+
3290
+ Rates prefixed \`shippo:\` are live Shippo quotes. Pass the full \`id\` as \`shippingRateId\`
3291
+ when calling \`selectShippingMethod()\`.
3292
+
3293
+ ### Admin \u2014 purchase a shipping label
3294
+
3295
+ After the order is placed, the merchant purchases a label by passing the Shippo rate object_id:
3296
+
3297
+ \`\`\`typescript
3298
+ const label = await admin.createShippingLabel(orderId, {
3299
+ shippoRateObjectId: 'rate_8f123456789abcdef',
3300
+ });
3301
+ console.log(label.labelUrl); // PDF ready to print
3302
+ console.log(label.trackingNumber); // auto-populated on the order
3303
+ \`\`\`
3304
+
3305
+ The \`trackingNumber\` is stored on the \`Order\` and returned in \`getMyOrders()\`
3306
+ so customers can track their shipment automatically.
3307
+
3308
+ **SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
3309
+ }
3200
3310
  function getSectionByTopic(topic, connectionId, currency) {
3201
3311
  const cid = connectionId || "vc_YOUR_CONNECTION_ID";
3202
3312
  const cur = currency || "USD";
@@ -3253,6 +3363,12 @@ function getSectionByTopic(topic, connectionId, currency) {
3253
3363
  return getBlogSection();
3254
3364
  case "storefront-bot":
3255
3365
  return getStorefrontBotSection(cid);
3366
+ case "analytics":
3367
+ case "traffic-analytics":
3368
+ return getStorefrontAnalyticsSection(cid);
3369
+ case "shipping-app":
3370
+ case "shippo":
3371
+ return getShippingAppSection();
3256
3372
  case "all":
3257
3373
  return [
3258
3374
  "# Brainerce SDK \u2014 full topic dump",
@@ -3321,6 +3437,10 @@ function getSectionByTopic(topic, connectionId, currency) {
3321
3437
  "",
3322
3438
  "---",
3323
3439
  "",
3440
+ getStorefrontAnalyticsSection(cid),
3441
+ "",
3442
+ "---",
3443
+ "",
3324
3444
  getProductCustomizationFieldsSection(),
3325
3445
  "",
3326
3446
  "---",
@@ -3390,6 +3510,7 @@ var GET_SDK_DOCS_SCHEMA = {
3390
3510
  "inquiries",
3391
3511
  "content",
3392
3512
  "storefront-bot",
3513
+ "analytics",
3393
3514
  "all"
3394
3515
  ]).describe("The SDK documentation topic to retrieve"),
3395
3516
  salesChannelId: import_zod.z.string().optional().describe("Sales channel ID (starts with vc_). Used to personalize setup code."),
@@ -3427,7 +3548,7 @@ interface Product {
3427
3548
  priceMin?: string | null; // Lowest variant price (VARIABLE products) \u2014 same as basePrice for VARIABLE, kept for back-compat and JSON-LD range display.
3428
3549
  priceMax?: string | null; // Highest variant price (VARIABLE products). Use with priceMin for "\u20AA49 \u2013 \u20AA199" range.
3429
3550
  priceVaries?: boolean; // true when variant prices differ \u2014 show "\u20AA49 \u2013 \u20AA199" range.
3430
- // FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive, display-only. The buyer is still charged in store currency at checkout; payment provider handles customer-side FX.
3551
+ // FX DISPLAY overlay (PRD \xA723). Set only when getProducts({ regionId }) was called AND region.currency !== store.currency. basePrice/salePrice above stay in store currency \u2014 these are additive. For presentment-enabled regions (Stripe) the displayPrice uses the same buffered rate the checkout charges (browsed == checkout); for display-only regions it is mid-market and the buyer is charged in store currency at checkout (payment provider handles customer-side FX).
3431
3552
  displayPrice?: string; // basePrice \xD7 daily FX rate, in displayCurrency.
3432
3553
  displaySalePrice?: string; // salePrice \xD7 rate (if salePrice present).
3433
3554
  displayPriceMin?: string; // priceMin \xD7 rate (VARIABLE products).
@@ -3530,11 +3651,18 @@ interface ProductCustomizationField {
3530
3651
  maxLength?: number | null;
3531
3652
  minValue?: number | null; // NUMBER bounds
3532
3653
  maxValue?: number | null;
3533
- enumValues?: string[]; // REQUIRED for SELECT / MULTI_SELECT
3654
+ enumValues?: CustomizationFieldOption[]; // REQUIRED for SELECT / MULTI_SELECT
3534
3655
  defaultValue?: string | null;
3535
3656
  position: number; // Render order
3536
3657
  }
3537
3658
 
3659
+ interface CustomizationFieldOption {
3660
+ label: string; // Display label (e.g., "Rose Gold")
3661
+ value: string; // Value stored in CartItem.metadata
3662
+ swatchColor?: string | null; // Optional hex color (e.g., "#B76E79")
3663
+ swatchImageUrl?: string | null; // Optional swatch image URL
3664
+ }
3665
+
3538
3666
  interface ProductQueryParams {
3539
3667
  page?: number;
3540
3668
  limit?: number;
@@ -3628,6 +3756,7 @@ interface CartItem {
3628
3756
  image?: ProductImage | string | null;
3629
3757
  } | null;
3630
3758
  metadata?: Record<string, unknown> | null; // Buyer-submitted customization values, keyed by ProductCustomizationField.key
3759
+ customizations?: Record<string, { label: string; value: string | string[]; type: string }>; // Resolved labels \u2014 use this to display "Color: Silver" without extra getProduct() call
3631
3760
  createdAt: string;
3632
3761
  updatedAt: string;
3633
3762
  }
@@ -3681,7 +3810,7 @@ interface Checkout {
3681
3810
  status: CheckoutStatus;
3682
3811
  email?: string | null;
3683
3812
  customerId?: string | null;
3684
- regionId?: string | null; // multi-region: recorded for reporting + provider scoping (currency follows the cart until FX lands)
3813
+ regionId?: string | null; // multi-region: recorded for reporting + provider scoping. FX-at-checkout: presentment-enabled regions (Stripe) charge in the region currency \u2014 see the presentment field below; else charged in store base currency
3685
3814
  shippingAddress?: CheckoutAddress | null;
3686
3815
  billingAddress?: CheckoutAddress | null;
3687
3816
  shippingRateId?: string | null;
@@ -3701,6 +3830,18 @@ interface Checkout {
3701
3830
  itemCount: number;
3702
3831
  availableShippingRates?: ShippingRate[];
3703
3832
  reservation?: ReservationInfo;
3833
+ // FX-at-checkout: present ONLY when the region charges in its own currency (presentment-enabled \u2014 Stripe today). The amounts the buyer is actually CHARGED, in presentment.currency; presentment.total equals the payment intent amount. Render these (not total/currency above) when present. Absent = charged in store base currency. The fields above stay the base reference.
3834
+ presentment?: {
3835
+ currency: string; // ISO-4217 charged currency, e.g. "EUR"
3836
+ subtotal: string;
3837
+ discountAmount: string;
3838
+ shippingAmount: string;
3839
+ taxAmount: string;
3840
+ surchargeAmount: string;
3841
+ total: string; // equals the charged amount, to the cent
3842
+ fxChargingRate: string; // base\u2192presentment rate (buffer included)
3843
+ fxBufferPercent: string | null;
3844
+ };
3704
3845
  createdAt: string;
3705
3846
  updatedAt: string;
3706
3847
  }
@@ -3715,6 +3856,7 @@ interface CheckoutLineItem {
3715
3856
  product: { id: string; name: string; sku: string; images?: ProductImage[]; };
3716
3857
  variant?: { id: string; name?: string | null; sku?: string | null; image?: ProductImage | string | null; } | null;
3717
3858
  metadata?: Record<string, unknown> | null; // Copied from CartItem.metadata \u2014 buyer customization values
3859
+ customizations?: Record<string, { label: string; value: string | string[]; type: string }>; // Resolved labels \u2014 same shape as OrderItem.customizations
3718
3860
  }
3719
3861
 
3720
3862
  interface CheckoutAddress {
@@ -3951,7 +4093,10 @@ interface PaymentProviderConfig {
3951
4093
  stripeAccountId?: string; // Stripe Connect only
3952
4094
  supportedMethods: string[];
3953
4095
  testMode: boolean;
3954
- isDefault: boolean;
4096
+ isDefault: boolean; // only a primary (CREDIT_CARD) is ever the default
4097
+ methodType: 'CREDIT_CARD' | 'WALLET' | 'OFFSITE' | 'LOCAL' | 'REDEEMABLE';
4098
+ presentation: 'card_form' | 'express_button' | 'redirect' | 'local_option' | 'redeemable_field';
4099
+ isAdditive: boolean; // wallet/alt method shown ALONGSIDE the primary (e.g. PayPal)
3955
4100
  }
3956
4101
  type PaymentProvider = PaymentProviderConfig;
3957
4102
 
@@ -4078,10 +4223,22 @@ interface CartAppliedDiscount {
4078
4223
  }
4079
4224
 
4080
4225
  // Recommendation types
4226
+ interface RecommendationVariant {
4227
+ id: string; name?: string; price?: string; salePrice?: string;
4228
+ attributes?: Record<string, string>; image?: ProductImage | string; inventory?: InventoryInfo;
4229
+ }
4081
4230
  interface ProductRecommendation {
4082
- id: string; name: string; slug: string; basePrice: string; salePrice?: string;
4231
+ id: string; name: string; slug: string;
4232
+ // For a VARIABLE target, basePrice is the pinned variant's price (when
4233
+ // targetVariantId is set) or the "from {min variant}" range \u2014 never 0.
4234
+ basePrice: string; salePrice?: string;
4083
4235
  images: ProductImage[]; type: 'SIMPLE' | 'VARIABLE'; inventory?: InventoryInfo;
4084
4236
  relationType: string;
4237
+ // Variant context (VARIABLE targets):
4238
+ targetVariantId?: string | null; // merchant-pinned variant, or null
4239
+ requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
4240
+ pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
4241
+ variants?: RecommendationVariant[]; // present when requiresVariantSelection
4085
4242
  }
4086
4243
  interface ProductRecommendationsResponse {
4087
4244
  crossSells: ProductRecommendation[];
@@ -4115,6 +4272,12 @@ interface CartBundleOfferOfferedProduct {
4115
4272
  salePrice: string | null;
4116
4273
  images: Array<{ url: string }>;
4117
4274
  type: string;
4275
+ // Variant context (VARIABLE offered products):
4276
+ variantId?: string | null; // merchant-pinned variant for this slot
4277
+ requiresVariantSelection?: boolean; // true \u2192 customer must pick from variants
4278
+ pinnedVariant?: { id: string; name?: string; attributes?: Record<string, string> } | null;
4279
+ variants?: RecommendationVariant[]; // present when requiresVariantSelection
4280
+ // originalPrice = pinned/cheapest variant price for VARIABLE (not a 0 parent base).
4118
4281
  originalPrice: string;
4119
4282
  discountedPrice: string;
4120
4283
  }
@@ -4598,10 +4761,12 @@ interface UpdateRegionDto extends Partial<CreateRegionDto> { isActive?: boolean
4598
4761
  // client.detectRegion(country, regions): Region | null \u2014 pure, no network.
4599
4762
  // \u2192 region whose countries includes the code, else the default region, else null.
4600
4763
  // Pass the resolved regionId to createCheckout to associate the checkout with a
4601
- // region (recorded for reporting + provider scoping; currency follows the cart
4602
- // until FX price conversion lands).
4764
+ // region (recorded for reporting + provider scoping). FX-at-checkout:
4765
+ // presentment-enabled regions (Stripe) charge in the region currency and the
4766
+ // response carries a presentment overlay; else charged in store base currency.
4603
4767
  //
4604
- // Storefront (public, no apiKey \u2014 storeId mode):
4768
+ // Storefront (public, no apiKey \u2014 storeId mode OR vibe-coded mode, salesChannelId 'vc_*';
4769
+ // the vc routes are gated on products:read, which every connection already has):
4605
4770
  interface PublicRegion {
4606
4771
  id: string; name: string; slug: string; currency: string;
4607
4772
  countries: string[]; taxInclusive: boolean; isDefault: boolean;
@@ -4653,7 +4818,8 @@ interface AssignTaxClassDto { productIds?: string[]; variantIds?: string[]; cate
4653
4818
  // deleteTaxClass(id), setDefaultTaxClass(id), assignTaxClass(id, dto),
4654
4819
  // mergeTaxClasses(id, targetId)
4655
4820
  //
4656
- // SDK methods (storefront, public \u2014 storeId mode, no apiKey):
4821
+ // SDK methods (storefront, public \u2014 storeId mode OR vibe-coded mode 'vc_*', no apiKey;
4822
+ // the vc routes are gated on products:read, which every connection already has):
4657
4823
  // getStoreRegions(), getStoreRegion(id), getAutoRegion(country)
4658
4824
  // getStoreTaxClasses(), estimateTax({ country?, subtotal })
4659
4825
  //
@@ -4978,7 +5144,15 @@ const checkout = await client.getCheckout(checkoutId);
4978
5144
  import { client } from './brainerce';
4979
5145
 
4980
5146
  const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
4981
- // each provider: { id, provider, name, publicKey, supportedMethods, testMode, isDefault, clientSdk? }
5147
+ // each provider: { id, provider, name, publicKey, supportedMethods, testMode,
5148
+ // isDefault, methodType, presentation, isAdditive, clientSdk? }
5149
+ //
5150
+ // methodType splits providers into ONE primary card processor (CREDIT_CARD, the
5151
+ // defaultProvider that settles the order) and additive methods (isAdditive:true,
5152
+ // e.g. PayPal as a WALLET). Render additive methods as express buttons ABOVE the
5153
+ // card form \u2014 they sit alongside the primary, never replace it. Call
5154
+ // createPaymentIntent({ providerId }) with the tapped provider's id. (Wallet-only
5155
+ // store: with no card processor, the wallet becomes defaultProvider and stands alone.)
4982
5156
  //
4983
5157
  // After createPaymentIntent, the response carries a clientSdk.renderType that
4984
5158
  // tells your UI how to render the payment step. The 5 possible values: