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