brainerce 1.38.0 → 1.41.0
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/README.md +70 -6
- package/dist/index.d.mts +193 -27
- package/dist/index.d.ts +193 -27
- package/dist/index.js +83 -25
- package/dist/index.mjs +83 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -400,7 +400,7 @@ if (!page) notFound();
|
|
|
400
400
|
|
|
401
401
|
All `get` / `getBySlug` return `null` on 404 — render a hard-coded fallback so the page never crashes when the merchant hasn't seeded yet.
|
|
402
402
|
|
|
403
|
-
**Security**: `FAQ.items[i].answer`, `RICH_TEXT.html`,
|
|
403
|
+
**Security**: `FAQ.items[i].answer`, `RICH_TEXT.html`, `PAGE.html`, and `Product.description` are **merchant-authored HTML**. The server does NOT pre-sanitize FAQ/RICH_TEXT/PAGE (merchants may embed iframes); `Product.description` is server-sanitized on write but you still sanitize on render. `Product.description` may contain `<video>` and host-locked YouTube/Vimeo `<iframe>` embeds — allow those tags (iframe restricted to `www.youtube.com` / `www.youtube-nocookie.com` / `player.vimeo.com`) and add those hosts to your CSP `frame-src`. ALWAYS sanitize before injecting:
|
|
404
404
|
|
|
405
405
|
```typescript
|
|
406
406
|
import DOMPurify from 'isomorphic-dompurify';
|
|
@@ -1526,10 +1526,19 @@ fields.forEach((field) => {
|
|
|
1526
1526
|
// field.required: whether customer must fill this in
|
|
1527
1527
|
// field.minLength, field.maxLength: validation for text fields
|
|
1528
1528
|
// field.minValue, field.maxValue: validation for number fields
|
|
1529
|
-
// field.enumValues:
|
|
1529
|
+
// field.enumValues: CustomizationFieldOption[] for SELECT/MULTI_SELECT
|
|
1530
|
+
// each option: { label: string, value: string, swatchColor?: string, swatchImageUrl?: string }
|
|
1531
|
+
// use option.value for metadata submission, option.label for display
|
|
1530
1532
|
// field.defaultValue: default value to pre-fill
|
|
1531
1533
|
});
|
|
1532
1534
|
|
|
1535
|
+
// Render SELECT options using label/value (not string[] anymore):
|
|
1536
|
+
for (const option of field.enumValues ?? []) {
|
|
1537
|
+
// option.label → show to user (e.g. "Gold")
|
|
1538
|
+
// option.value → submit in metadata (e.g. "gold")
|
|
1539
|
+
// option.swatchColor → hex color for color swatch UI, if set
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1533
1542
|
// Customer fills in values, then add to cart with metadata
|
|
1534
1543
|
await client.addToCart(cartId, {
|
|
1535
1544
|
productId: product.id,
|
|
@@ -1537,10 +1546,23 @@ await client.addToCart(cartId, {
|
|
|
1537
1546
|
metadata: {
|
|
1538
1547
|
cake_text: 'Happy Birthday!', // TEXT field
|
|
1539
1548
|
font_color: '#FF0000', // COLOR field
|
|
1549
|
+
frame_color: 'gold', // SELECT — use option.value
|
|
1540
1550
|
},
|
|
1541
1551
|
});
|
|
1542
1552
|
```
|
|
1543
1553
|
|
|
1554
|
+
**Display customizations in cart/checkout — no extra API call needed:**
|
|
1555
|
+
|
|
1556
|
+
`CartItem` and `CheckoutLineItem` both include a `customizations` object with resolved labels. Use it directly — no need to call `getProduct()` per item.
|
|
1557
|
+
|
|
1558
|
+
```typescript
|
|
1559
|
+
// Works for CartItem, CheckoutLineItem, and OrderItem — same shape
|
|
1560
|
+
const summary = Object.values(item.customizations ?? {})
|
|
1561
|
+
.map(c => `${c.label}: ${Array.isArray(c.value) ? c.value.join(', ') : c.value}`)
|
|
1562
|
+
.join(' | ');
|
|
1563
|
+
// e.g. "Frame color: Gold | Add-ons: Gift wrap, Engraving"
|
|
1564
|
+
```
|
|
1565
|
+
|
|
1544
1566
|
**For IMAGE/GALLERY fields**, upload the file first:
|
|
1545
1567
|
|
|
1546
1568
|
```typescript
|
|
@@ -2353,7 +2375,16 @@ const checkoutId = checkout.id;
|
|
|
2353
2375
|
|
|
2354
2376
|
#### Get All Payment Providers (Recommended)
|
|
2355
2377
|
|
|
2356
|
-
Use this method to get ALL enabled payment providers and build dynamic UI
|
|
2378
|
+
Use this method to get ALL enabled payment providers and build dynamic UI.
|
|
2379
|
+
|
|
2380
|
+
**Primary vs. additive methods (Shopify-parity).** Each provider carries a
|
|
2381
|
+
`methodType`. A `CREDIT_CARD` provider is the *primary* card processor — the
|
|
2382
|
+
single method that settles the order (`defaultProvider`, `isAdditive: false`,
|
|
2383
|
+
`presentation: 'card_form'`). Everything else is *additive* (`isAdditive: true`),
|
|
2384
|
+
e.g. PayPal is a `'WALLET'` with `presentation: 'express_button'`. Render additive
|
|
2385
|
+
methods as accelerated-checkout **express buttons above the card form** — they sit
|
|
2386
|
+
*alongside* the primary, never replace it. (Exception: a wallet-only store has no
|
|
2387
|
+
card processor, so its wallet becomes the `defaultProvider` and stands alone.)
|
|
2357
2388
|
|
|
2358
2389
|
```typescript
|
|
2359
2390
|
const { hasPayments, providers, defaultProvider } = await client.getPaymentProviders();
|
|
@@ -2369,7 +2400,10 @@ const { hasPayments, providers, defaultProvider } = await client.getPaymentProvi
|
|
|
2369
2400
|
// publicKey: 'pk_live_xxx...',
|
|
2370
2401
|
// supportedMethods: ['card', 'ideal'],
|
|
2371
2402
|
// testMode: false,
|
|
2372
|
-
// isDefault: true
|
|
2403
|
+
// isDefault: true,
|
|
2404
|
+
// methodType: 'CREDIT_CARD', // primary card processor
|
|
2405
|
+
// presentation: 'card_form',
|
|
2406
|
+
// isAdditive: false
|
|
2373
2407
|
// },
|
|
2374
2408
|
// {
|
|
2375
2409
|
// id: 'provider_yyy',
|
|
@@ -2378,12 +2412,20 @@ const { hasPayments, providers, defaultProvider } = await client.getPaymentProvi
|
|
|
2378
2412
|
// publicKey: 'client_id_xxx...',
|
|
2379
2413
|
// supportedMethods: ['paypal'],
|
|
2380
2414
|
// testMode: false,
|
|
2381
|
-
// isDefault: false
|
|
2415
|
+
// isDefault: false,
|
|
2416
|
+
// methodType: 'WALLET', // additive — render as an express button
|
|
2417
|
+
// presentation: 'express_button',
|
|
2418
|
+
// isAdditive: true
|
|
2382
2419
|
// }
|
|
2383
2420
|
// ],
|
|
2384
|
-
// defaultProvider: { ... } // The
|
|
2421
|
+
// defaultProvider: { ... } // The settling primary (never an additive wallet
|
|
2422
|
+
// // while a card processor is installed)
|
|
2385
2423
|
// }
|
|
2386
2424
|
|
|
2425
|
+
// Recommended pattern: express buttons (additive) above the primary card form.
|
|
2426
|
+
const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal
|
|
2427
|
+
const primary = defaultProvider && !defaultProvider.isAdditive ? defaultProvider : undefined;
|
|
2428
|
+
|
|
2387
2429
|
// Build dynamic UI based on available providers
|
|
2388
2430
|
if (!hasPayments) {
|
|
2389
2431
|
return <div>Payment not configured for this store</div>;
|
|
@@ -3402,6 +3444,8 @@ client.trackEvent({ eventType: 'engagement', path: '/products/shoes', engagedMs:
|
|
|
3402
3444
|
|
|
3403
3445
|
> `trackEvent()` is fire-and-forget — errors are silently swallowed so a failed beacon never breaks your storefront. Available in `salesChannelId` and `storeId` modes.
|
|
3404
3446
|
|
|
3447
|
+
> **Analytics always goes directly to the Brainerce API** (`keepalive` beacon), even when you route the rest of the SDK through a same-origin BFF proxy (`proxyMode` / `baseUrl: '/api/store'`). This is deliberate: a beacon relayed through your server would arrive from the server's datacenter IP and mis-geolocate **every** visitor to that region (e.g. a Vercel US function → all visitors show as US). When `proxyMode` is on — or `baseUrl` shares the page origin — the SDK auto-targets `https://api.brainerce.com` for beacons; override with `analyticsBaseUrl` only if your API is self-hosted.
|
|
3448
|
+
|
|
3405
3449
|
**Privacy:** cookieless, no PII stored. The visitor IP is resolved to a country server-side and then discarded. No cookie-consent banner required under GDPR/ePrivacy.
|
|
3406
3450
|
|
|
3407
3451
|
---
|
|
@@ -3550,6 +3594,26 @@ await client.createZoneShippingRate('zone_id', {
|
|
|
3550
3594
|
});
|
|
3551
3595
|
```
|
|
3552
3596
|
|
|
3597
|
+
### App Store Shipping (Shippo)
|
|
3598
|
+
|
|
3599
|
+
Once a merchant installs the Shippo app from the Brainerce App Store and connects their own
|
|
3600
|
+
Shippo account, live carrier rates appear automatically at checkout. Billing goes directly
|
|
3601
|
+
to the merchant's Shippo account.
|
|
3602
|
+
|
|
3603
|
+
```typescript
|
|
3604
|
+
// After an order is placed, purchase a shipping label:
|
|
3605
|
+
const label = await admin.createShippingLabel(orderId, {
|
|
3606
|
+
shippoRateObjectId: 'rate_8f123456789abcdef', // Shippo rate.object_id from checkout rates
|
|
3607
|
+
});
|
|
3608
|
+
|
|
3609
|
+
console.log(label.labelUrl); // PDF label URL for printing
|
|
3610
|
+
console.log(label.trackingNumber); // Auto-populated on the order
|
|
3611
|
+
console.log(label.carrier); // e.g. 'USPS', 'UPS', 'FedEx'
|
|
3612
|
+
```
|
|
3613
|
+
|
|
3614
|
+
The `trackingNumber` is stored on the `Order` automatically — customers see it in their
|
|
3615
|
+
order history without any extra integration work.
|
|
3616
|
+
|
|
3553
3617
|
### Tax Configuration
|
|
3554
3618
|
|
|
3555
3619
|
`rate` is a **whole percentage** (`7.25` = 7.25%, not `0.0725`). A rate may target
|
package/dist/index.d.mts
CHANGED
|
@@ -112,6 +112,32 @@ interface BrainerceClientOptions {
|
|
|
112
112
|
* ```
|
|
113
113
|
*/
|
|
114
114
|
proxyMode?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Base URL used for analytics beacons (`trackEvent`) ONLY — everything else
|
|
117
|
+
* keeps using `baseUrl`.
|
|
118
|
+
*
|
|
119
|
+
* Analytics must reach the Brainerce API **directly from the browser** so the
|
|
120
|
+
* visitor's real IP — and therefore country — is preserved. If a beacon is routed
|
|
121
|
+
* through a same-origin BFF proxy (see `proxyMode`), it arrives at the backend from
|
|
122
|
+
* the proxy server's datacenter IP instead (e.g. a Vercel US region), and every
|
|
123
|
+
* visitor is mis-geolocated to that datacenter.
|
|
124
|
+
*
|
|
125
|
+
* You normally do NOT need to set this: when `proxyMode` is on (or `baseUrl` shares
|
|
126
|
+
* the current page's origin), the SDK automatically sends analytics to
|
|
127
|
+
* `https://api.brainerce.com`. Set it explicitly only when your Brainerce API lives
|
|
128
|
+
* at a custom / self-hosted origin.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const client = new BrainerceClient({
|
|
133
|
+
* connectionId: 'vc_abc123...',
|
|
134
|
+
* baseUrl: '/api/store', // products / cart / auth go through the BFF proxy
|
|
135
|
+
* proxyMode: true,
|
|
136
|
+
* // analytics auto-routes direct to https://api.brainerce.com — no config needed
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
analyticsBaseUrl?: string;
|
|
115
141
|
}
|
|
116
142
|
interface StoreInfo {
|
|
117
143
|
id: string;
|
|
@@ -256,13 +282,20 @@ interface Product {
|
|
|
256
282
|
*/
|
|
257
283
|
localeSlugs?: Record<string, string> | null;
|
|
258
284
|
/**
|
|
259
|
-
* Product description as HTML.
|
|
285
|
+
* Product description as HTML. May contain self-hosted `<video>` and
|
|
286
|
+
* host-locked YouTube/Vimeo `<iframe>` embeds. **Always sanitize before
|
|
287
|
+
* rendering**, e.g.:
|
|
260
288
|
*
|
|
261
289
|
* import { sanitizeHTML } from '@brainerce/utils';
|
|
262
290
|
* <div dangerouslySetInnerHTML={{ __html: sanitizeHTML(product.description) }} />
|
|
263
291
|
*
|
|
264
|
-
* The backend strips dangerous tags/attributes on write, but storefronts
|
|
265
|
-
* still treat
|
|
292
|
+
* The backend strips dangerous tags/attributes on write, but storefronts
|
|
293
|
+
* should still treat this as untrusted HTML and sanitize on render. Your
|
|
294
|
+
* sanitizer must allow `video`/`source` and — for embeds — `iframe` restricted
|
|
295
|
+
* to the hosts `www.youtube.com`, `www.youtube-nocookie.com`,
|
|
296
|
+
* `player.vimeo.com` (never arbitrary iframes). Sizing is on the `width`/
|
|
297
|
+
* `height` attributes (inline `style` is stripped). If you set a CSP, add
|
|
298
|
+
* those three hosts to `frame-src` or embeds render blank.
|
|
266
299
|
*/
|
|
267
300
|
description?: string | null;
|
|
268
301
|
descriptionFormat?: 'text' | 'html' | 'markdown' | null;
|
|
@@ -1064,7 +1097,14 @@ interface CreateProductDto {
|
|
|
1064
1097
|
status?: 'active' | 'draft';
|
|
1065
1098
|
type?: 'SIMPLE' | 'VARIABLE';
|
|
1066
1099
|
isDownloadable?: boolean;
|
|
1100
|
+
/** Existing category IDs to assign. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `categoryNames`. */
|
|
1067
1101
|
categories?: string[];
|
|
1102
|
+
/** Category names to assign — matched case-insensitively and auto-created if none exists. Merged with any IDs in `categories`. */
|
|
1103
|
+
categoryNames?: string[];
|
|
1104
|
+
/** Existing brand IDs to assign. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `brandNames`. */
|
|
1105
|
+
brands?: string[];
|
|
1106
|
+
/** Brand names to assign — matched case-insensitively and auto-created if none exists. Merged with any IDs in `brands`. */
|
|
1107
|
+
brandNames?: string[];
|
|
1068
1108
|
tags?: string[];
|
|
1069
1109
|
images?: ProductImage[];
|
|
1070
1110
|
}
|
|
@@ -1078,7 +1118,14 @@ interface UpdateProductDto {
|
|
|
1078
1118
|
costPrice?: number | null;
|
|
1079
1119
|
status?: 'active' | 'draft';
|
|
1080
1120
|
isDownloadable?: boolean;
|
|
1121
|
+
/** Existing category IDs to assign (replaces the current set). Pass `[]` to clear. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `categoryNames`. */
|
|
1081
1122
|
categories?: string[];
|
|
1123
|
+
/** Category names to assign — matched case-insensitively and auto-created if missing. Additive: added to `categories` (or to the product's current categories when `categories` is omitted). */
|
|
1124
|
+
categoryNames?: string[];
|
|
1125
|
+
/** Existing brand IDs to assign (replaces the current set). Pass `[]` to clear. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `brandNames`. */
|
|
1126
|
+
brands?: string[];
|
|
1127
|
+
/** Brand names to assign — matched case-insensitively and auto-created if missing. Additive: added to `brands` (or to the product's current brands when `brands` is omitted). */
|
|
1128
|
+
brandNames?: string[];
|
|
1082
1129
|
tags?: string[];
|
|
1083
1130
|
images?: ProductImage[];
|
|
1084
1131
|
}
|
|
@@ -1806,6 +1853,20 @@ interface CartItem {
|
|
|
1806
1853
|
notes?: string | null;
|
|
1807
1854
|
/** Optional metadata for this line item */
|
|
1808
1855
|
metadata?: Record<string, unknown> | null;
|
|
1856
|
+
/**
|
|
1857
|
+
* Resolved customer input field values with merchant-defined labels.
|
|
1858
|
+
* Mirrors `OrderItem.customizations` — same shape, live (not order-frozen).
|
|
1859
|
+
* Present only when the item has at least one customization value set.
|
|
1860
|
+
*
|
|
1861
|
+
* @example
|
|
1862
|
+
* // Display "Color: Silver / Size: 45" without an extra getProduct() call:
|
|
1863
|
+
* Object.entries(item.customizations ?? {}).map(([, c]) => `${c.label}: ${c.value}`)
|
|
1864
|
+
*/
|
|
1865
|
+
customizations?: Record<string, {
|
|
1866
|
+
label: string;
|
|
1867
|
+
value: string | string[];
|
|
1868
|
+
type: string;
|
|
1869
|
+
}>;
|
|
1809
1870
|
/**
|
|
1810
1871
|
* Per-line modifier breakdown — present when the line was added with
|
|
1811
1872
|
* modifier `selections`. Snapshot taken at add-time; immutable to catalog
|
|
@@ -2326,6 +2387,20 @@ interface CheckoutLineItem {
|
|
|
2326
2387
|
notes?: string | null;
|
|
2327
2388
|
/** Custom metadata including customer input field values */
|
|
2328
2389
|
metadata?: Record<string, unknown> | null;
|
|
2390
|
+
/**
|
|
2391
|
+
* Resolved customer input field values with merchant-defined labels.
|
|
2392
|
+
* Mirrors `OrderItem.customizations` — same shape, live (not order-frozen).
|
|
2393
|
+
* Present only when the item has at least one customization value set.
|
|
2394
|
+
*
|
|
2395
|
+
* @example
|
|
2396
|
+
* // Render "Color: Silver / Ring size: 45" in your checkout summary:
|
|
2397
|
+
* Object.entries(item.customizations ?? {}).map(([, c]) => `${c.label}: ${c.value}`)
|
|
2398
|
+
*/
|
|
2399
|
+
customizations?: Record<string, {
|
|
2400
|
+
label: string;
|
|
2401
|
+
value: string | string[];
|
|
2402
|
+
type: string;
|
|
2403
|
+
}>;
|
|
2329
2404
|
}
|
|
2330
2405
|
/**
|
|
2331
2406
|
* Represents an available shipping rate/method for checkout.
|
|
@@ -3070,6 +3145,18 @@ interface PaymentClientSdk {
|
|
|
3070
3145
|
*
|
|
3071
3146
|
* **Alias:** `PaymentProvider` is the same type (use either name).
|
|
3072
3147
|
*/
|
|
3148
|
+
/**
|
|
3149
|
+
* Payment-method taxonomy (Shopify-parity). `CREDIT_CARD` is a *primary* card
|
|
3150
|
+
* processor — the single method that settles the order; everything else is an
|
|
3151
|
+
* *additive* method (wallet/alt/local/redeemable) that coexists alongside it.
|
|
3152
|
+
*/
|
|
3153
|
+
type PaymentMethodType = 'CREDIT_CARD' | 'WALLET' | 'OFFSITE' | 'LOCAL' | 'REDEEMABLE';
|
|
3154
|
+
/**
|
|
3155
|
+
* How a storefront should render a payment method. `card_form` = the main card
|
|
3156
|
+
* fields; `express_button` = an accelerated-checkout button (render it ABOVE the
|
|
3157
|
+
* card form, like PayPal / Apple Pay); `redirect` = send the buyer offsite.
|
|
3158
|
+
*/
|
|
3159
|
+
type PaymentPresentation = 'card_form' | 'express_button' | 'redirect' | 'local_option' | 'redeemable_field';
|
|
3073
3160
|
interface PaymentProviderConfig {
|
|
3074
3161
|
/** Provider ID (use this when creating payment intents) */
|
|
3075
3162
|
id: string;
|
|
@@ -3085,8 +3172,26 @@ interface PaymentProviderConfig {
|
|
|
3085
3172
|
supportedMethods: string[];
|
|
3086
3173
|
/** Whether this is test/sandbox mode */
|
|
3087
3174
|
testMode: boolean;
|
|
3088
|
-
/**
|
|
3175
|
+
/**
|
|
3176
|
+
* Whether this is the store's default *settling* provider. Only a primary
|
|
3177
|
+
* (`methodType: 'CREDIT_CARD'`) provider is ever the default — an additive
|
|
3178
|
+
* wallet never becomes the default while a card processor is installed.
|
|
3179
|
+
*/
|
|
3089
3180
|
isDefault: boolean;
|
|
3181
|
+
/**
|
|
3182
|
+
* Payment-method taxonomy — primary card processor vs additive wallet/alt.
|
|
3183
|
+
* Use it to place the method: primary → the main card form; additive → an
|
|
3184
|
+
* express button. See `presentation`.
|
|
3185
|
+
*/
|
|
3186
|
+
methodType: PaymentMethodType;
|
|
3187
|
+
/** Rendering hint derived from `methodType` (card_form | express_button | …). */
|
|
3188
|
+
presentation: PaymentPresentation;
|
|
3189
|
+
/**
|
|
3190
|
+
* True for wallet/alternative methods (e.g. PayPal) that are shown *alongside*
|
|
3191
|
+
* the primary card processor rather than replacing it. Render these as
|
|
3192
|
+
* accelerated-checkout express buttons above the card form.
|
|
3193
|
+
*/
|
|
3194
|
+
isAdditive: boolean;
|
|
3090
3195
|
/** Client-side SDK configuration for providers that render a widget */
|
|
3091
3196
|
clientSdk?: PaymentClientSdk;
|
|
3092
3197
|
}
|
|
@@ -3983,7 +4088,8 @@ interface MetafieldDefinition {
|
|
|
3983
4088
|
maxLength?: number | null;
|
|
3984
4089
|
minValue?: number | null;
|
|
3985
4090
|
maxValue?: number | null;
|
|
3986
|
-
|
|
4091
|
+
/** Allowed values for SELECT / MULTI_SELECT fields, with optional swatch metadata. */
|
|
4092
|
+
enumValues?: CustomizationFieldOption[] | null;
|
|
3987
4093
|
defaultValue?: string | null;
|
|
3988
4094
|
position: number;
|
|
3989
4095
|
isActive: boolean;
|
|
@@ -4067,7 +4173,8 @@ interface PublicMetafieldDefinition {
|
|
|
4067
4173
|
* Pair with `getProducts({ metafields: { [key]: [...] } })`.
|
|
4068
4174
|
*/
|
|
4069
4175
|
filterable?: boolean;
|
|
4070
|
-
|
|
4176
|
+
/** Allowed values for SELECT / MULTI_SELECT fields, with optional swatch metadata. */
|
|
4177
|
+
enumValues?: CustomizationFieldOption[];
|
|
4071
4178
|
minLength?: number | null;
|
|
4072
4179
|
maxLength?: number | null;
|
|
4073
4180
|
minValue?: number | null;
|
|
@@ -4075,6 +4182,20 @@ interface PublicMetafieldDefinition {
|
|
|
4075
4182
|
defaultValue?: string | null;
|
|
4076
4183
|
position: number;
|
|
4077
4184
|
}
|
|
4185
|
+
/**
|
|
4186
|
+
* A single selectable option for a SELECT or MULTI_SELECT customization field.
|
|
4187
|
+
* Legacy string-only `enumValues` arrays are automatically promoted to this shape.
|
|
4188
|
+
*/
|
|
4189
|
+
interface CustomizationFieldOption {
|
|
4190
|
+
/** Human-readable label shown in the storefront UI (e.g., "Rose Gold"). */
|
|
4191
|
+
label: string;
|
|
4192
|
+
/** Value stored in `CartItem.metadata` and carried through to `OrderItem.customizations`. */
|
|
4193
|
+
value: string;
|
|
4194
|
+
/** Optional CSS hex color for rendering a color swatch (e.g., "#B76E79"). */
|
|
4195
|
+
swatchColor?: string | null;
|
|
4196
|
+
/** Optional image URL for rendering a texture/image swatch. */
|
|
4197
|
+
swatchImageUrl?: string | null;
|
|
4198
|
+
}
|
|
4078
4199
|
/**
|
|
4079
4200
|
* Customer-facing input field definition assigned to a specific product.
|
|
4080
4201
|
* Returned in product responses to tell the storefront which input fields to render.
|
|
@@ -4090,7 +4211,11 @@ interface ProductCustomizationField {
|
|
|
4090
4211
|
maxLength?: number | null;
|
|
4091
4212
|
minValue?: number | null;
|
|
4092
4213
|
maxValue?: number | null;
|
|
4093
|
-
|
|
4214
|
+
/**
|
|
4215
|
+
* Allowed values for SELECT / MULTI_SELECT fields.
|
|
4216
|
+
* Each option includes a label, value, and optional swatch color/image.
|
|
4217
|
+
*/
|
|
4218
|
+
enumValues?: CustomizationFieldOption[];
|
|
4094
4219
|
defaultValue?: string | null;
|
|
4095
4220
|
position: number;
|
|
4096
4221
|
}
|
|
@@ -5379,6 +5504,8 @@ declare class BrainerceClient {
|
|
|
5379
5504
|
private readonly storeId?;
|
|
5380
5505
|
private readonly connectionId?;
|
|
5381
5506
|
private readonly baseUrl;
|
|
5507
|
+
/** Direct API base for analytics beacons — bypasses any same-origin BFF proxy so the visitor's real IP (hence country) reaches the backend. */
|
|
5508
|
+
private readonly analyticsBaseUrl;
|
|
5382
5509
|
private readonly timeout;
|
|
5383
5510
|
private customerToken;
|
|
5384
5511
|
private customerCartId;
|
|
@@ -5548,6 +5675,20 @@ declare class BrainerceClient {
|
|
|
5548
5675
|
* ```
|
|
5549
5676
|
*/
|
|
5550
5677
|
trackEvent(payload?: TrackEventPayload): Promise<void>;
|
|
5678
|
+
/**
|
|
5679
|
+
* Resolve the base URL for analytics beacons. Analytics must go DIRECTLY to the
|
|
5680
|
+
* Brainerce API (never through a same-origin BFF proxy) so the visitor's real IP —
|
|
5681
|
+
* hence country — reaches the backend. Explicit `analyticsBaseUrl` wins; otherwise
|
|
5682
|
+
* use the direct API when `baseUrl` is a same-origin proxy, else the (already-direct)
|
|
5683
|
+
* `baseUrl` (which may be a custom / self-hosted Brainerce API).
|
|
5684
|
+
*/
|
|
5685
|
+
private resolveAnalyticsBaseUrl;
|
|
5686
|
+
/**
|
|
5687
|
+
* Fire-and-forget analytics beacon sent directly to `analyticsBaseUrl`. Uses
|
|
5688
|
+
* `keepalive` so it survives page unload (engagement events fired on pagehide),
|
|
5689
|
+
* omits credentials (cookieless), and never throws.
|
|
5690
|
+
*/
|
|
5691
|
+
private sendAnalyticsBeacon;
|
|
5551
5692
|
/**
|
|
5552
5693
|
* Get a list of products with pagination and filtering
|
|
5553
5694
|
* Works in vibe-coded, storefront (public), and admin mode
|
|
@@ -5972,6 +6113,29 @@ declare class BrainerceClient {
|
|
|
5972
6113
|
* ```
|
|
5973
6114
|
*/
|
|
5974
6115
|
updateOrderShipping(orderId: string, data: UpdateOrderShippingDto): Promise<Order>;
|
|
6116
|
+
/**
|
|
6117
|
+
* Create a shipping label for an order via the installed App Store shipping app.
|
|
6118
|
+
* Pass the rate ID returned from checkout rate shopping. Billing goes directly
|
|
6119
|
+
* to the merchant's carrier account — Brainerce is not a billing intermediary.
|
|
6120
|
+
*
|
|
6121
|
+
* @example
|
|
6122
|
+
* ```typescript
|
|
6123
|
+
* const label = await client.createShippingLabel('order_abc', {
|
|
6124
|
+
* rateId: 'rate_8f123456789abcdef',
|
|
6125
|
+
* });
|
|
6126
|
+
* console.log('Label URL:', label.labelUrl);
|
|
6127
|
+
* console.log('Tracking:', label.trackingNumber);
|
|
6128
|
+
* ```
|
|
6129
|
+
*/
|
|
6130
|
+
createShippingLabel(orderId: string, data: {
|
|
6131
|
+
rateId: string;
|
|
6132
|
+
parcel?: Record<string, string>;
|
|
6133
|
+
}): Promise<{
|
|
6134
|
+
shipmentId: string;
|
|
6135
|
+
labelUrl: string;
|
|
6136
|
+
trackingNumber: string;
|
|
6137
|
+
carrier: string;
|
|
6138
|
+
}>;
|
|
5975
6139
|
/**
|
|
5976
6140
|
* Cancel an order
|
|
5977
6141
|
* Works for Shopify and WooCommerce orders that haven't been fulfilled
|
|
@@ -7859,6 +8023,14 @@ declare class BrainerceClient {
|
|
|
7859
8023
|
*
|
|
7860
8024
|
* @returns Payment providers config with hasPayments flag and provider list
|
|
7861
8025
|
*
|
|
8026
|
+
* Each provider carries a `methodType` (Shopify-parity taxonomy) and a derived
|
|
8027
|
+
* `isAdditive` / `presentation`. There is at most ONE primary card processor
|
|
8028
|
+
* (`methodType: 'CREDIT_CARD'`, the `defaultProvider`) that settles the order;
|
|
8029
|
+
* additive methods (`isAdditive: true`, e.g. PayPal as a `'WALLET'`) are shown
|
|
8030
|
+
* *alongside* it as accelerated-checkout express buttons — they never replace
|
|
8031
|
+
* the card form. A wallet-only store is the one exception: with no card
|
|
8032
|
+
* processor installed, the wallet becomes the `defaultProvider` and stands alone.
|
|
8033
|
+
*
|
|
7862
8034
|
* @example
|
|
7863
8035
|
* ```typescript
|
|
7864
8036
|
* const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
|
|
@@ -7868,27 +8040,21 @@ declare class BrainerceClient {
|
|
|
7868
8040
|
* return;
|
|
7869
8041
|
* }
|
|
7870
8042
|
*
|
|
7871
|
-
* //
|
|
7872
|
-
* const
|
|
7873
|
-
*
|
|
7874
|
-
*
|
|
7875
|
-
*
|
|
7876
|
-
* if (stripeProvider) {
|
|
7877
|
-
* const stripe = await loadStripe(stripeProvider.publicKey, {
|
|
7878
|
-
* stripeAccount: stripeProvider.stripeAccountId, // For Stripe Connect
|
|
7879
|
-
* });
|
|
7880
|
-
* // Show Stripe payment form
|
|
7881
|
-
* }
|
|
7882
|
-
*
|
|
7883
|
-
* // Initialize PayPal if available
|
|
7884
|
-
* if (paypalProvider) {
|
|
7885
|
-
* // Load PayPal SDK with paypalProvider.publicKey as client-id
|
|
7886
|
-
* // Show PayPal buttons
|
|
8043
|
+
* // Render additive methods (wallets) as express buttons ABOVE the card form.
|
|
8044
|
+
* const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal
|
|
8045
|
+
* for (const m of expressMethods) {
|
|
8046
|
+
* // m.presentation === 'express_button' — render an express/accelerated button
|
|
8047
|
+
* // and call createPaymentIntent({ providerId: m.id }) when the buyer taps it.
|
|
7887
8048
|
* }
|
|
7888
8049
|
*
|
|
7889
|
-
* //
|
|
7890
|
-
* if (defaultProvider) {
|
|
7891
|
-
*
|
|
8050
|
+
* // Render the primary card processor (the settling default) as the main form.
|
|
8051
|
+
* if (defaultProvider && !defaultProvider.isAdditive) {
|
|
8052
|
+
* if (defaultProvider.provider === 'stripe') {
|
|
8053
|
+
* const stripe = await loadStripe(defaultProvider.publicKey, {
|
|
8054
|
+
* stripeAccount: defaultProvider.stripeAccountId, // For Stripe Connect
|
|
8055
|
+
* });
|
|
8056
|
+
* // Show the Stripe card form
|
|
8057
|
+
* }
|
|
7892
8058
|
* }
|
|
7893
8059
|
* ```
|
|
7894
8060
|
*/
|
|
@@ -9543,7 +9709,7 @@ declare class BrainerceError extends Error {
|
|
|
9543
9709
|
constructor(message: string, statusCode: number, details?: unknown);
|
|
9544
9710
|
}
|
|
9545
9711
|
|
|
9546
|
-
declare const SDK_VERSION = "1.
|
|
9712
|
+
declare const SDK_VERSION = "1.41.0";
|
|
9547
9713
|
|
|
9548
9714
|
/**
|
|
9549
9715
|
* Verify a webhook signature from Brainerce
|
package/dist/index.d.ts
CHANGED
|
@@ -112,6 +112,32 @@ interface BrainerceClientOptions {
|
|
|
112
112
|
* ```
|
|
113
113
|
*/
|
|
114
114
|
proxyMode?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Base URL used for analytics beacons (`trackEvent`) ONLY — everything else
|
|
117
|
+
* keeps using `baseUrl`.
|
|
118
|
+
*
|
|
119
|
+
* Analytics must reach the Brainerce API **directly from the browser** so the
|
|
120
|
+
* visitor's real IP — and therefore country — is preserved. If a beacon is routed
|
|
121
|
+
* through a same-origin BFF proxy (see `proxyMode`), it arrives at the backend from
|
|
122
|
+
* the proxy server's datacenter IP instead (e.g. a Vercel US region), and every
|
|
123
|
+
* visitor is mis-geolocated to that datacenter.
|
|
124
|
+
*
|
|
125
|
+
* You normally do NOT need to set this: when `proxyMode` is on (or `baseUrl` shares
|
|
126
|
+
* the current page's origin), the SDK automatically sends analytics to
|
|
127
|
+
* `https://api.brainerce.com`. Set it explicitly only when your Brainerce API lives
|
|
128
|
+
* at a custom / self-hosted origin.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const client = new BrainerceClient({
|
|
133
|
+
* connectionId: 'vc_abc123...',
|
|
134
|
+
* baseUrl: '/api/store', // products / cart / auth go through the BFF proxy
|
|
135
|
+
* proxyMode: true,
|
|
136
|
+
* // analytics auto-routes direct to https://api.brainerce.com — no config needed
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
analyticsBaseUrl?: string;
|
|
115
141
|
}
|
|
116
142
|
interface StoreInfo {
|
|
117
143
|
id: string;
|
|
@@ -256,13 +282,20 @@ interface Product {
|
|
|
256
282
|
*/
|
|
257
283
|
localeSlugs?: Record<string, string> | null;
|
|
258
284
|
/**
|
|
259
|
-
* Product description as HTML.
|
|
285
|
+
* Product description as HTML. May contain self-hosted `<video>` and
|
|
286
|
+
* host-locked YouTube/Vimeo `<iframe>` embeds. **Always sanitize before
|
|
287
|
+
* rendering**, e.g.:
|
|
260
288
|
*
|
|
261
289
|
* import { sanitizeHTML } from '@brainerce/utils';
|
|
262
290
|
* <div dangerouslySetInnerHTML={{ __html: sanitizeHTML(product.description) }} />
|
|
263
291
|
*
|
|
264
|
-
* The backend strips dangerous tags/attributes on write, but storefronts
|
|
265
|
-
* still treat
|
|
292
|
+
* The backend strips dangerous tags/attributes on write, but storefronts
|
|
293
|
+
* should still treat this as untrusted HTML and sanitize on render. Your
|
|
294
|
+
* sanitizer must allow `video`/`source` and — for embeds — `iframe` restricted
|
|
295
|
+
* to the hosts `www.youtube.com`, `www.youtube-nocookie.com`,
|
|
296
|
+
* `player.vimeo.com` (never arbitrary iframes). Sizing is on the `width`/
|
|
297
|
+
* `height` attributes (inline `style` is stripped). If you set a CSP, add
|
|
298
|
+
* those three hosts to `frame-src` or embeds render blank.
|
|
266
299
|
*/
|
|
267
300
|
description?: string | null;
|
|
268
301
|
descriptionFormat?: 'text' | 'html' | 'markdown' | null;
|
|
@@ -1064,7 +1097,14 @@ interface CreateProductDto {
|
|
|
1064
1097
|
status?: 'active' | 'draft';
|
|
1065
1098
|
type?: 'SIMPLE' | 'VARIABLE';
|
|
1066
1099
|
isDownloadable?: boolean;
|
|
1100
|
+
/** Existing category IDs to assign. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `categoryNames`. */
|
|
1067
1101
|
categories?: string[];
|
|
1102
|
+
/** Category names to assign — matched case-insensitively and auto-created if none exists. Merged with any IDs in `categories`. */
|
|
1103
|
+
categoryNames?: string[];
|
|
1104
|
+
/** Existing brand IDs to assign. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `brandNames`. */
|
|
1105
|
+
brands?: string[];
|
|
1106
|
+
/** Brand names to assign — matched case-insensitively and auto-created if none exists. Merged with any IDs in `brands`. */
|
|
1107
|
+
brandNames?: string[];
|
|
1068
1108
|
tags?: string[];
|
|
1069
1109
|
images?: ProductImage[];
|
|
1070
1110
|
}
|
|
@@ -1078,7 +1118,14 @@ interface UpdateProductDto {
|
|
|
1078
1118
|
costPrice?: number | null;
|
|
1079
1119
|
status?: 'active' | 'draft';
|
|
1080
1120
|
isDownloadable?: boolean;
|
|
1121
|
+
/** Existing category IDs to assign (replaces the current set). Pass `[]` to clear. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `categoryNames`. */
|
|
1081
1122
|
categories?: string[];
|
|
1123
|
+
/** Category names to assign — matched case-insensitively and auto-created if missing. Additive: added to `categories` (or to the product's current categories when `categories` is omitted). */
|
|
1124
|
+
categoryNames?: string[];
|
|
1125
|
+
/** Existing brand IDs to assign (replaces the current set). Pass `[]` to clear. Unknown/cross-store IDs are rejected with 400. To assign by name (auto-creating if missing), use `brandNames`. */
|
|
1126
|
+
brands?: string[];
|
|
1127
|
+
/** Brand names to assign — matched case-insensitively and auto-created if missing. Additive: added to `brands` (or to the product's current brands when `brands` is omitted). */
|
|
1128
|
+
brandNames?: string[];
|
|
1082
1129
|
tags?: string[];
|
|
1083
1130
|
images?: ProductImage[];
|
|
1084
1131
|
}
|
|
@@ -1806,6 +1853,20 @@ interface CartItem {
|
|
|
1806
1853
|
notes?: string | null;
|
|
1807
1854
|
/** Optional metadata for this line item */
|
|
1808
1855
|
metadata?: Record<string, unknown> | null;
|
|
1856
|
+
/**
|
|
1857
|
+
* Resolved customer input field values with merchant-defined labels.
|
|
1858
|
+
* Mirrors `OrderItem.customizations` — same shape, live (not order-frozen).
|
|
1859
|
+
* Present only when the item has at least one customization value set.
|
|
1860
|
+
*
|
|
1861
|
+
* @example
|
|
1862
|
+
* // Display "Color: Silver / Size: 45" without an extra getProduct() call:
|
|
1863
|
+
* Object.entries(item.customizations ?? {}).map(([, c]) => `${c.label}: ${c.value}`)
|
|
1864
|
+
*/
|
|
1865
|
+
customizations?: Record<string, {
|
|
1866
|
+
label: string;
|
|
1867
|
+
value: string | string[];
|
|
1868
|
+
type: string;
|
|
1869
|
+
}>;
|
|
1809
1870
|
/**
|
|
1810
1871
|
* Per-line modifier breakdown — present when the line was added with
|
|
1811
1872
|
* modifier `selections`. Snapshot taken at add-time; immutable to catalog
|
|
@@ -2326,6 +2387,20 @@ interface CheckoutLineItem {
|
|
|
2326
2387
|
notes?: string | null;
|
|
2327
2388
|
/** Custom metadata including customer input field values */
|
|
2328
2389
|
metadata?: Record<string, unknown> | null;
|
|
2390
|
+
/**
|
|
2391
|
+
* Resolved customer input field values with merchant-defined labels.
|
|
2392
|
+
* Mirrors `OrderItem.customizations` — same shape, live (not order-frozen).
|
|
2393
|
+
* Present only when the item has at least one customization value set.
|
|
2394
|
+
*
|
|
2395
|
+
* @example
|
|
2396
|
+
* // Render "Color: Silver / Ring size: 45" in your checkout summary:
|
|
2397
|
+
* Object.entries(item.customizations ?? {}).map(([, c]) => `${c.label}: ${c.value}`)
|
|
2398
|
+
*/
|
|
2399
|
+
customizations?: Record<string, {
|
|
2400
|
+
label: string;
|
|
2401
|
+
value: string | string[];
|
|
2402
|
+
type: string;
|
|
2403
|
+
}>;
|
|
2329
2404
|
}
|
|
2330
2405
|
/**
|
|
2331
2406
|
* Represents an available shipping rate/method for checkout.
|
|
@@ -3070,6 +3145,18 @@ interface PaymentClientSdk {
|
|
|
3070
3145
|
*
|
|
3071
3146
|
* **Alias:** `PaymentProvider` is the same type (use either name).
|
|
3072
3147
|
*/
|
|
3148
|
+
/**
|
|
3149
|
+
* Payment-method taxonomy (Shopify-parity). `CREDIT_CARD` is a *primary* card
|
|
3150
|
+
* processor — the single method that settles the order; everything else is an
|
|
3151
|
+
* *additive* method (wallet/alt/local/redeemable) that coexists alongside it.
|
|
3152
|
+
*/
|
|
3153
|
+
type PaymentMethodType = 'CREDIT_CARD' | 'WALLET' | 'OFFSITE' | 'LOCAL' | 'REDEEMABLE';
|
|
3154
|
+
/**
|
|
3155
|
+
* How a storefront should render a payment method. `card_form` = the main card
|
|
3156
|
+
* fields; `express_button` = an accelerated-checkout button (render it ABOVE the
|
|
3157
|
+
* card form, like PayPal / Apple Pay); `redirect` = send the buyer offsite.
|
|
3158
|
+
*/
|
|
3159
|
+
type PaymentPresentation = 'card_form' | 'express_button' | 'redirect' | 'local_option' | 'redeemable_field';
|
|
3073
3160
|
interface PaymentProviderConfig {
|
|
3074
3161
|
/** Provider ID (use this when creating payment intents) */
|
|
3075
3162
|
id: string;
|
|
@@ -3085,8 +3172,26 @@ interface PaymentProviderConfig {
|
|
|
3085
3172
|
supportedMethods: string[];
|
|
3086
3173
|
/** Whether this is test/sandbox mode */
|
|
3087
3174
|
testMode: boolean;
|
|
3088
|
-
/**
|
|
3175
|
+
/**
|
|
3176
|
+
* Whether this is the store's default *settling* provider. Only a primary
|
|
3177
|
+
* (`methodType: 'CREDIT_CARD'`) provider is ever the default — an additive
|
|
3178
|
+
* wallet never becomes the default while a card processor is installed.
|
|
3179
|
+
*/
|
|
3089
3180
|
isDefault: boolean;
|
|
3181
|
+
/**
|
|
3182
|
+
* Payment-method taxonomy — primary card processor vs additive wallet/alt.
|
|
3183
|
+
* Use it to place the method: primary → the main card form; additive → an
|
|
3184
|
+
* express button. See `presentation`.
|
|
3185
|
+
*/
|
|
3186
|
+
methodType: PaymentMethodType;
|
|
3187
|
+
/** Rendering hint derived from `methodType` (card_form | express_button | …). */
|
|
3188
|
+
presentation: PaymentPresentation;
|
|
3189
|
+
/**
|
|
3190
|
+
* True for wallet/alternative methods (e.g. PayPal) that are shown *alongside*
|
|
3191
|
+
* the primary card processor rather than replacing it. Render these as
|
|
3192
|
+
* accelerated-checkout express buttons above the card form.
|
|
3193
|
+
*/
|
|
3194
|
+
isAdditive: boolean;
|
|
3090
3195
|
/** Client-side SDK configuration for providers that render a widget */
|
|
3091
3196
|
clientSdk?: PaymentClientSdk;
|
|
3092
3197
|
}
|
|
@@ -3983,7 +4088,8 @@ interface MetafieldDefinition {
|
|
|
3983
4088
|
maxLength?: number | null;
|
|
3984
4089
|
minValue?: number | null;
|
|
3985
4090
|
maxValue?: number | null;
|
|
3986
|
-
|
|
4091
|
+
/** Allowed values for SELECT / MULTI_SELECT fields, with optional swatch metadata. */
|
|
4092
|
+
enumValues?: CustomizationFieldOption[] | null;
|
|
3987
4093
|
defaultValue?: string | null;
|
|
3988
4094
|
position: number;
|
|
3989
4095
|
isActive: boolean;
|
|
@@ -4067,7 +4173,8 @@ interface PublicMetafieldDefinition {
|
|
|
4067
4173
|
* Pair with `getProducts({ metafields: { [key]: [...] } })`.
|
|
4068
4174
|
*/
|
|
4069
4175
|
filterable?: boolean;
|
|
4070
|
-
|
|
4176
|
+
/** Allowed values for SELECT / MULTI_SELECT fields, with optional swatch metadata. */
|
|
4177
|
+
enumValues?: CustomizationFieldOption[];
|
|
4071
4178
|
minLength?: number | null;
|
|
4072
4179
|
maxLength?: number | null;
|
|
4073
4180
|
minValue?: number | null;
|
|
@@ -4075,6 +4182,20 @@ interface PublicMetafieldDefinition {
|
|
|
4075
4182
|
defaultValue?: string | null;
|
|
4076
4183
|
position: number;
|
|
4077
4184
|
}
|
|
4185
|
+
/**
|
|
4186
|
+
* A single selectable option for a SELECT or MULTI_SELECT customization field.
|
|
4187
|
+
* Legacy string-only `enumValues` arrays are automatically promoted to this shape.
|
|
4188
|
+
*/
|
|
4189
|
+
interface CustomizationFieldOption {
|
|
4190
|
+
/** Human-readable label shown in the storefront UI (e.g., "Rose Gold"). */
|
|
4191
|
+
label: string;
|
|
4192
|
+
/** Value stored in `CartItem.metadata` and carried through to `OrderItem.customizations`. */
|
|
4193
|
+
value: string;
|
|
4194
|
+
/** Optional CSS hex color for rendering a color swatch (e.g., "#B76E79"). */
|
|
4195
|
+
swatchColor?: string | null;
|
|
4196
|
+
/** Optional image URL for rendering a texture/image swatch. */
|
|
4197
|
+
swatchImageUrl?: string | null;
|
|
4198
|
+
}
|
|
4078
4199
|
/**
|
|
4079
4200
|
* Customer-facing input field definition assigned to a specific product.
|
|
4080
4201
|
* Returned in product responses to tell the storefront which input fields to render.
|
|
@@ -4090,7 +4211,11 @@ interface ProductCustomizationField {
|
|
|
4090
4211
|
maxLength?: number | null;
|
|
4091
4212
|
minValue?: number | null;
|
|
4092
4213
|
maxValue?: number | null;
|
|
4093
|
-
|
|
4214
|
+
/**
|
|
4215
|
+
* Allowed values for SELECT / MULTI_SELECT fields.
|
|
4216
|
+
* Each option includes a label, value, and optional swatch color/image.
|
|
4217
|
+
*/
|
|
4218
|
+
enumValues?: CustomizationFieldOption[];
|
|
4094
4219
|
defaultValue?: string | null;
|
|
4095
4220
|
position: number;
|
|
4096
4221
|
}
|
|
@@ -5379,6 +5504,8 @@ declare class BrainerceClient {
|
|
|
5379
5504
|
private readonly storeId?;
|
|
5380
5505
|
private readonly connectionId?;
|
|
5381
5506
|
private readonly baseUrl;
|
|
5507
|
+
/** Direct API base for analytics beacons — bypasses any same-origin BFF proxy so the visitor's real IP (hence country) reaches the backend. */
|
|
5508
|
+
private readonly analyticsBaseUrl;
|
|
5382
5509
|
private readonly timeout;
|
|
5383
5510
|
private customerToken;
|
|
5384
5511
|
private customerCartId;
|
|
@@ -5548,6 +5675,20 @@ declare class BrainerceClient {
|
|
|
5548
5675
|
* ```
|
|
5549
5676
|
*/
|
|
5550
5677
|
trackEvent(payload?: TrackEventPayload): Promise<void>;
|
|
5678
|
+
/**
|
|
5679
|
+
* Resolve the base URL for analytics beacons. Analytics must go DIRECTLY to the
|
|
5680
|
+
* Brainerce API (never through a same-origin BFF proxy) so the visitor's real IP —
|
|
5681
|
+
* hence country — reaches the backend. Explicit `analyticsBaseUrl` wins; otherwise
|
|
5682
|
+
* use the direct API when `baseUrl` is a same-origin proxy, else the (already-direct)
|
|
5683
|
+
* `baseUrl` (which may be a custom / self-hosted Brainerce API).
|
|
5684
|
+
*/
|
|
5685
|
+
private resolveAnalyticsBaseUrl;
|
|
5686
|
+
/**
|
|
5687
|
+
* Fire-and-forget analytics beacon sent directly to `analyticsBaseUrl`. Uses
|
|
5688
|
+
* `keepalive` so it survives page unload (engagement events fired on pagehide),
|
|
5689
|
+
* omits credentials (cookieless), and never throws.
|
|
5690
|
+
*/
|
|
5691
|
+
private sendAnalyticsBeacon;
|
|
5551
5692
|
/**
|
|
5552
5693
|
* Get a list of products with pagination and filtering
|
|
5553
5694
|
* Works in vibe-coded, storefront (public), and admin mode
|
|
@@ -5972,6 +6113,29 @@ declare class BrainerceClient {
|
|
|
5972
6113
|
* ```
|
|
5973
6114
|
*/
|
|
5974
6115
|
updateOrderShipping(orderId: string, data: UpdateOrderShippingDto): Promise<Order>;
|
|
6116
|
+
/**
|
|
6117
|
+
* Create a shipping label for an order via the installed App Store shipping app.
|
|
6118
|
+
* Pass the rate ID returned from checkout rate shopping. Billing goes directly
|
|
6119
|
+
* to the merchant's carrier account — Brainerce is not a billing intermediary.
|
|
6120
|
+
*
|
|
6121
|
+
* @example
|
|
6122
|
+
* ```typescript
|
|
6123
|
+
* const label = await client.createShippingLabel('order_abc', {
|
|
6124
|
+
* rateId: 'rate_8f123456789abcdef',
|
|
6125
|
+
* });
|
|
6126
|
+
* console.log('Label URL:', label.labelUrl);
|
|
6127
|
+
* console.log('Tracking:', label.trackingNumber);
|
|
6128
|
+
* ```
|
|
6129
|
+
*/
|
|
6130
|
+
createShippingLabel(orderId: string, data: {
|
|
6131
|
+
rateId: string;
|
|
6132
|
+
parcel?: Record<string, string>;
|
|
6133
|
+
}): Promise<{
|
|
6134
|
+
shipmentId: string;
|
|
6135
|
+
labelUrl: string;
|
|
6136
|
+
trackingNumber: string;
|
|
6137
|
+
carrier: string;
|
|
6138
|
+
}>;
|
|
5975
6139
|
/**
|
|
5976
6140
|
* Cancel an order
|
|
5977
6141
|
* Works for Shopify and WooCommerce orders that haven't been fulfilled
|
|
@@ -7859,6 +8023,14 @@ declare class BrainerceClient {
|
|
|
7859
8023
|
*
|
|
7860
8024
|
* @returns Payment providers config with hasPayments flag and provider list
|
|
7861
8025
|
*
|
|
8026
|
+
* Each provider carries a `methodType` (Shopify-parity taxonomy) and a derived
|
|
8027
|
+
* `isAdditive` / `presentation`. There is at most ONE primary card processor
|
|
8028
|
+
* (`methodType: 'CREDIT_CARD'`, the `defaultProvider`) that settles the order;
|
|
8029
|
+
* additive methods (`isAdditive: true`, e.g. PayPal as a `'WALLET'`) are shown
|
|
8030
|
+
* *alongside* it as accelerated-checkout express buttons — they never replace
|
|
8031
|
+
* the card form. A wallet-only store is the one exception: with no card
|
|
8032
|
+
* processor installed, the wallet becomes the `defaultProvider` and stands alone.
|
|
8033
|
+
*
|
|
7862
8034
|
* @example
|
|
7863
8035
|
* ```typescript
|
|
7864
8036
|
* const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
|
|
@@ -7868,27 +8040,21 @@ declare class BrainerceClient {
|
|
|
7868
8040
|
* return;
|
|
7869
8041
|
* }
|
|
7870
8042
|
*
|
|
7871
|
-
* //
|
|
7872
|
-
* const
|
|
7873
|
-
*
|
|
7874
|
-
*
|
|
7875
|
-
*
|
|
7876
|
-
* if (stripeProvider) {
|
|
7877
|
-
* const stripe = await loadStripe(stripeProvider.publicKey, {
|
|
7878
|
-
* stripeAccount: stripeProvider.stripeAccountId, // For Stripe Connect
|
|
7879
|
-
* });
|
|
7880
|
-
* // Show Stripe payment form
|
|
7881
|
-
* }
|
|
7882
|
-
*
|
|
7883
|
-
* // Initialize PayPal if available
|
|
7884
|
-
* if (paypalProvider) {
|
|
7885
|
-
* // Load PayPal SDK with paypalProvider.publicKey as client-id
|
|
7886
|
-
* // Show PayPal buttons
|
|
8043
|
+
* // Render additive methods (wallets) as express buttons ABOVE the card form.
|
|
8044
|
+
* const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal
|
|
8045
|
+
* for (const m of expressMethods) {
|
|
8046
|
+
* // m.presentation === 'express_button' — render an express/accelerated button
|
|
8047
|
+
* // and call createPaymentIntent({ providerId: m.id }) when the buyer taps it.
|
|
7887
8048
|
* }
|
|
7888
8049
|
*
|
|
7889
|
-
* //
|
|
7890
|
-
* if (defaultProvider) {
|
|
7891
|
-
*
|
|
8050
|
+
* // Render the primary card processor (the settling default) as the main form.
|
|
8051
|
+
* if (defaultProvider && !defaultProvider.isAdditive) {
|
|
8052
|
+
* if (defaultProvider.provider === 'stripe') {
|
|
8053
|
+
* const stripe = await loadStripe(defaultProvider.publicKey, {
|
|
8054
|
+
* stripeAccount: defaultProvider.stripeAccountId, // For Stripe Connect
|
|
8055
|
+
* });
|
|
8056
|
+
* // Show the Stripe card form
|
|
8057
|
+
* }
|
|
7892
8058
|
* }
|
|
7893
8059
|
* ```
|
|
7894
8060
|
*/
|
|
@@ -9543,7 +9709,7 @@ declare class BrainerceError extends Error {
|
|
|
9543
9709
|
constructor(message: string, statusCode: number, details?: unknown);
|
|
9544
9710
|
}
|
|
9545
9711
|
|
|
9546
|
-
declare const SDK_VERSION = "1.
|
|
9712
|
+
declare const SDK_VERSION = "1.41.0";
|
|
9547
9713
|
|
|
9548
9714
|
/**
|
|
9549
9715
|
* Verify a webhook signature from Brainerce
|
package/dist/index.js
CHANGED
|
@@ -185,7 +185,7 @@ function isDevGuardsEnabled() {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// src/version.ts
|
|
188
|
-
var SDK_VERSION = "1.
|
|
188
|
+
var SDK_VERSION = "1.41.0";
|
|
189
189
|
|
|
190
190
|
// src/client.ts
|
|
191
191
|
var DEFAULT_BASE_URL = "https://api.brainerce.com";
|
|
@@ -653,6 +653,7 @@ var BrainerceClient = class {
|
|
|
653
653
|
}
|
|
654
654
|
}
|
|
655
655
|
this.proxyMode = options.proxyMode || false;
|
|
656
|
+
this.analyticsBaseUrl = this.resolveAnalyticsBaseUrl(options.analyticsBaseUrl, resolvedBase);
|
|
656
657
|
this.onAuthError = options.onAuthError;
|
|
657
658
|
this.hydrateSessionCart();
|
|
658
659
|
this.detectRecoverCartFromUrl();
|
|
@@ -1106,12 +1107,46 @@ var BrainerceClient = class {
|
|
|
1106
1107
|
* ```
|
|
1107
1108
|
*/
|
|
1108
1109
|
async trackEvent(payload = {}) {
|
|
1110
|
+
if (this.isVibeCodedMode() && this.connectionId) {
|
|
1111
|
+
this.sendAnalyticsBeacon(`/api/vc/${encodePathSegment(this.connectionId)}/events`, payload);
|
|
1112
|
+
} else if (this.isStorefrontMode() && this.storeId) {
|
|
1113
|
+
this.sendAnalyticsBeacon(`/api/stores/${encodePathSegment(this.storeId)}/events`, payload);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Resolve the base URL for analytics beacons. Analytics must go DIRECTLY to the
|
|
1118
|
+
* Brainerce API (never through a same-origin BFF proxy) so the visitor's real IP —
|
|
1119
|
+
* hence country — reaches the backend. Explicit `analyticsBaseUrl` wins; otherwise
|
|
1120
|
+
* use the direct API when `baseUrl` is a same-origin proxy, else the (already-direct)
|
|
1121
|
+
* `baseUrl` (which may be a custom / self-hosted Brainerce API).
|
|
1122
|
+
*/
|
|
1123
|
+
resolveAnalyticsBaseUrl(explicit, resolvedBase) {
|
|
1124
|
+
if (explicit) return explicit.replace(/\/$/, "");
|
|
1125
|
+
const sameOriginProxy = this.proxyMode || typeof window !== "undefined" && !!window.location?.origin && resolvedBase.startsWith(window.location.origin);
|
|
1126
|
+
return sameOriginProxy ? DEFAULT_BASE_URL : resolvedBase;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Fire-and-forget analytics beacon sent directly to `analyticsBaseUrl`. Uses
|
|
1130
|
+
* `keepalive` so it survives page unload (engagement events fired on pagehide),
|
|
1131
|
+
* omits credentials (cookieless), and never throws.
|
|
1132
|
+
*/
|
|
1133
|
+
sendAnalyticsBeacon(eventsPath, payload) {
|
|
1109
1134
|
try {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
}
|
|
1135
|
+
const headers = {
|
|
1136
|
+
"Content-Type": "application/json",
|
|
1137
|
+
"X-SDK-Version": SDK_VERSION,
|
|
1138
|
+
"ngrok-skip-browser-warning": "true"
|
|
1139
|
+
};
|
|
1140
|
+
if (this.origin) headers["Origin"] = this.origin;
|
|
1141
|
+
void fetch(`${this.analyticsBaseUrl}${eventsPath}`, {
|
|
1142
|
+
method: "POST",
|
|
1143
|
+
headers,
|
|
1144
|
+
body: JSON.stringify(payload),
|
|
1145
|
+
keepalive: true,
|
|
1146
|
+
credentials: "omit",
|
|
1147
|
+
mode: "cors"
|
|
1148
|
+
}).catch(() => {
|
|
1149
|
+
});
|
|
1115
1150
|
} catch {
|
|
1116
1151
|
}
|
|
1117
1152
|
}
|
|
@@ -1805,6 +1840,27 @@ var BrainerceClient = class {
|
|
|
1805
1840
|
data
|
|
1806
1841
|
);
|
|
1807
1842
|
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Create a shipping label for an order via the installed App Store shipping app.
|
|
1845
|
+
* Pass the rate ID returned from checkout rate shopping. Billing goes directly
|
|
1846
|
+
* to the merchant's carrier account — Brainerce is not a billing intermediary.
|
|
1847
|
+
*
|
|
1848
|
+
* @example
|
|
1849
|
+
* ```typescript
|
|
1850
|
+
* const label = await client.createShippingLabel('order_abc', {
|
|
1851
|
+
* rateId: 'rate_8f123456789abcdef',
|
|
1852
|
+
* });
|
|
1853
|
+
* console.log('Label URL:', label.labelUrl);
|
|
1854
|
+
* console.log('Tracking:', label.trackingNumber);
|
|
1855
|
+
* ```
|
|
1856
|
+
*/
|
|
1857
|
+
async createShippingLabel(orderId, data) {
|
|
1858
|
+
return this.request(
|
|
1859
|
+
"POST",
|
|
1860
|
+
`/api/v1/orders/${encodePathSegment(orderId)}/shipments/app-label`,
|
|
1861
|
+
data
|
|
1862
|
+
);
|
|
1863
|
+
}
|
|
1808
1864
|
/**
|
|
1809
1865
|
* Cancel an order
|
|
1810
1866
|
* Works for Shopify and WooCommerce orders that haven't been fulfilled
|
|
@@ -5208,6 +5264,14 @@ var BrainerceClient = class {
|
|
|
5208
5264
|
*
|
|
5209
5265
|
* @returns Payment providers config with hasPayments flag and provider list
|
|
5210
5266
|
*
|
|
5267
|
+
* Each provider carries a `methodType` (Shopify-parity taxonomy) and a derived
|
|
5268
|
+
* `isAdditive` / `presentation`. There is at most ONE primary card processor
|
|
5269
|
+
* (`methodType: 'CREDIT_CARD'`, the `defaultProvider`) that settles the order;
|
|
5270
|
+
* additive methods (`isAdditive: true`, e.g. PayPal as a `'WALLET'`) are shown
|
|
5271
|
+
* *alongside* it as accelerated-checkout express buttons — they never replace
|
|
5272
|
+
* the card form. A wallet-only store is the one exception: with no card
|
|
5273
|
+
* processor installed, the wallet becomes the `defaultProvider` and stands alone.
|
|
5274
|
+
*
|
|
5211
5275
|
* @example
|
|
5212
5276
|
* ```typescript
|
|
5213
5277
|
* const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
|
|
@@ -5217,27 +5281,21 @@ var BrainerceClient = class {
|
|
|
5217
5281
|
* return;
|
|
5218
5282
|
* }
|
|
5219
5283
|
*
|
|
5220
|
-
* //
|
|
5221
|
-
* const
|
|
5222
|
-
*
|
|
5223
|
-
*
|
|
5224
|
-
*
|
|
5225
|
-
* if (stripeProvider) {
|
|
5226
|
-
* const stripe = await loadStripe(stripeProvider.publicKey, {
|
|
5227
|
-
* stripeAccount: stripeProvider.stripeAccountId, // For Stripe Connect
|
|
5228
|
-
* });
|
|
5229
|
-
* // Show Stripe payment form
|
|
5230
|
-
* }
|
|
5231
|
-
*
|
|
5232
|
-
* // Initialize PayPal if available
|
|
5233
|
-
* if (paypalProvider) {
|
|
5234
|
-
* // Load PayPal SDK with paypalProvider.publicKey as client-id
|
|
5235
|
-
* // Show PayPal buttons
|
|
5284
|
+
* // Render additive methods (wallets) as express buttons ABOVE the card form.
|
|
5285
|
+
* const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal
|
|
5286
|
+
* for (const m of expressMethods) {
|
|
5287
|
+
* // m.presentation === 'express_button' — render an express/accelerated button
|
|
5288
|
+
* // and call createPaymentIntent({ providerId: m.id }) when the buyer taps it.
|
|
5236
5289
|
* }
|
|
5237
5290
|
*
|
|
5238
|
-
* //
|
|
5239
|
-
* if (defaultProvider) {
|
|
5240
|
-
*
|
|
5291
|
+
* // Render the primary card processor (the settling default) as the main form.
|
|
5292
|
+
* if (defaultProvider && !defaultProvider.isAdditive) {
|
|
5293
|
+
* if (defaultProvider.provider === 'stripe') {
|
|
5294
|
+
* const stripe = await loadStripe(defaultProvider.publicKey, {
|
|
5295
|
+
* stripeAccount: defaultProvider.stripeAccountId, // For Stripe Connect
|
|
5296
|
+
* });
|
|
5297
|
+
* // Show the Stripe card form
|
|
5298
|
+
* }
|
|
5241
5299
|
* }
|
|
5242
5300
|
* ```
|
|
5243
5301
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -115,7 +115,7 @@ function isDevGuardsEnabled() {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// src/version.ts
|
|
118
|
-
var SDK_VERSION = "1.
|
|
118
|
+
var SDK_VERSION = "1.41.0";
|
|
119
119
|
|
|
120
120
|
// src/client.ts
|
|
121
121
|
var DEFAULT_BASE_URL = "https://api.brainerce.com";
|
|
@@ -583,6 +583,7 @@ var BrainerceClient = class {
|
|
|
583
583
|
}
|
|
584
584
|
}
|
|
585
585
|
this.proxyMode = options.proxyMode || false;
|
|
586
|
+
this.analyticsBaseUrl = this.resolveAnalyticsBaseUrl(options.analyticsBaseUrl, resolvedBase);
|
|
586
587
|
this.onAuthError = options.onAuthError;
|
|
587
588
|
this.hydrateSessionCart();
|
|
588
589
|
this.detectRecoverCartFromUrl();
|
|
@@ -1036,12 +1037,46 @@ var BrainerceClient = class {
|
|
|
1036
1037
|
* ```
|
|
1037
1038
|
*/
|
|
1038
1039
|
async trackEvent(payload = {}) {
|
|
1040
|
+
if (this.isVibeCodedMode() && this.connectionId) {
|
|
1041
|
+
this.sendAnalyticsBeacon(`/api/vc/${encodePathSegment(this.connectionId)}/events`, payload);
|
|
1042
|
+
} else if (this.isStorefrontMode() && this.storeId) {
|
|
1043
|
+
this.sendAnalyticsBeacon(`/api/stores/${encodePathSegment(this.storeId)}/events`, payload);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Resolve the base URL for analytics beacons. Analytics must go DIRECTLY to the
|
|
1048
|
+
* Brainerce API (never through a same-origin BFF proxy) so the visitor's real IP —
|
|
1049
|
+
* hence country — reaches the backend. Explicit `analyticsBaseUrl` wins; otherwise
|
|
1050
|
+
* use the direct API when `baseUrl` is a same-origin proxy, else the (already-direct)
|
|
1051
|
+
* `baseUrl` (which may be a custom / self-hosted Brainerce API).
|
|
1052
|
+
*/
|
|
1053
|
+
resolveAnalyticsBaseUrl(explicit, resolvedBase) {
|
|
1054
|
+
if (explicit) return explicit.replace(/\/$/, "");
|
|
1055
|
+
const sameOriginProxy = this.proxyMode || typeof window !== "undefined" && !!window.location?.origin && resolvedBase.startsWith(window.location.origin);
|
|
1056
|
+
return sameOriginProxy ? DEFAULT_BASE_URL : resolvedBase;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Fire-and-forget analytics beacon sent directly to `analyticsBaseUrl`. Uses
|
|
1060
|
+
* `keepalive` so it survives page unload (engagement events fired on pagehide),
|
|
1061
|
+
* omits credentials (cookieless), and never throws.
|
|
1062
|
+
*/
|
|
1063
|
+
sendAnalyticsBeacon(eventsPath, payload) {
|
|
1039
1064
|
try {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}
|
|
1065
|
+
const headers = {
|
|
1066
|
+
"Content-Type": "application/json",
|
|
1067
|
+
"X-SDK-Version": SDK_VERSION,
|
|
1068
|
+
"ngrok-skip-browser-warning": "true"
|
|
1069
|
+
};
|
|
1070
|
+
if (this.origin) headers["Origin"] = this.origin;
|
|
1071
|
+
void fetch(`${this.analyticsBaseUrl}${eventsPath}`, {
|
|
1072
|
+
method: "POST",
|
|
1073
|
+
headers,
|
|
1074
|
+
body: JSON.stringify(payload),
|
|
1075
|
+
keepalive: true,
|
|
1076
|
+
credentials: "omit",
|
|
1077
|
+
mode: "cors"
|
|
1078
|
+
}).catch(() => {
|
|
1079
|
+
});
|
|
1045
1080
|
} catch {
|
|
1046
1081
|
}
|
|
1047
1082
|
}
|
|
@@ -1735,6 +1770,27 @@ var BrainerceClient = class {
|
|
|
1735
1770
|
data
|
|
1736
1771
|
);
|
|
1737
1772
|
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Create a shipping label for an order via the installed App Store shipping app.
|
|
1775
|
+
* Pass the rate ID returned from checkout rate shopping. Billing goes directly
|
|
1776
|
+
* to the merchant's carrier account — Brainerce is not a billing intermediary.
|
|
1777
|
+
*
|
|
1778
|
+
* @example
|
|
1779
|
+
* ```typescript
|
|
1780
|
+
* const label = await client.createShippingLabel('order_abc', {
|
|
1781
|
+
* rateId: 'rate_8f123456789abcdef',
|
|
1782
|
+
* });
|
|
1783
|
+
* console.log('Label URL:', label.labelUrl);
|
|
1784
|
+
* console.log('Tracking:', label.trackingNumber);
|
|
1785
|
+
* ```
|
|
1786
|
+
*/
|
|
1787
|
+
async createShippingLabel(orderId, data) {
|
|
1788
|
+
return this.request(
|
|
1789
|
+
"POST",
|
|
1790
|
+
`/api/v1/orders/${encodePathSegment(orderId)}/shipments/app-label`,
|
|
1791
|
+
data
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1738
1794
|
/**
|
|
1739
1795
|
* Cancel an order
|
|
1740
1796
|
* Works for Shopify and WooCommerce orders that haven't been fulfilled
|
|
@@ -5138,6 +5194,14 @@ var BrainerceClient = class {
|
|
|
5138
5194
|
*
|
|
5139
5195
|
* @returns Payment providers config with hasPayments flag and provider list
|
|
5140
5196
|
*
|
|
5197
|
+
* Each provider carries a `methodType` (Shopify-parity taxonomy) and a derived
|
|
5198
|
+
* `isAdditive` / `presentation`. There is at most ONE primary card processor
|
|
5199
|
+
* (`methodType: 'CREDIT_CARD'`, the `defaultProvider`) that settles the order;
|
|
5200
|
+
* additive methods (`isAdditive: true`, e.g. PayPal as a `'WALLET'`) are shown
|
|
5201
|
+
* *alongside* it as accelerated-checkout express buttons — they never replace
|
|
5202
|
+
* the card form. A wallet-only store is the one exception: with no card
|
|
5203
|
+
* processor installed, the wallet becomes the `defaultProvider` and stands alone.
|
|
5204
|
+
*
|
|
5141
5205
|
* @example
|
|
5142
5206
|
* ```typescript
|
|
5143
5207
|
* const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
|
|
@@ -5147,27 +5211,21 @@ var BrainerceClient = class {
|
|
|
5147
5211
|
* return;
|
|
5148
5212
|
* }
|
|
5149
5213
|
*
|
|
5150
|
-
* //
|
|
5151
|
-
* const
|
|
5152
|
-
*
|
|
5153
|
-
*
|
|
5154
|
-
*
|
|
5155
|
-
* if (stripeProvider) {
|
|
5156
|
-
* const stripe = await loadStripe(stripeProvider.publicKey, {
|
|
5157
|
-
* stripeAccount: stripeProvider.stripeAccountId, // For Stripe Connect
|
|
5158
|
-
* });
|
|
5159
|
-
* // Show Stripe payment form
|
|
5160
|
-
* }
|
|
5161
|
-
*
|
|
5162
|
-
* // Initialize PayPal if available
|
|
5163
|
-
* if (paypalProvider) {
|
|
5164
|
-
* // Load PayPal SDK with paypalProvider.publicKey as client-id
|
|
5165
|
-
* // Show PayPal buttons
|
|
5214
|
+
* // Render additive methods (wallets) as express buttons ABOVE the card form.
|
|
5215
|
+
* const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal
|
|
5216
|
+
* for (const m of expressMethods) {
|
|
5217
|
+
* // m.presentation === 'express_button' — render an express/accelerated button
|
|
5218
|
+
* // and call createPaymentIntent({ providerId: m.id }) when the buyer taps it.
|
|
5166
5219
|
* }
|
|
5167
5220
|
*
|
|
5168
|
-
* //
|
|
5169
|
-
* if (defaultProvider) {
|
|
5170
|
-
*
|
|
5221
|
+
* // Render the primary card processor (the settling default) as the main form.
|
|
5222
|
+
* if (defaultProvider && !defaultProvider.isAdditive) {
|
|
5223
|
+
* if (defaultProvider.provider === 'stripe') {
|
|
5224
|
+
* const stripe = await loadStripe(defaultProvider.publicKey, {
|
|
5225
|
+
* stripeAccount: defaultProvider.stripeAccountId, // For Stripe Connect
|
|
5226
|
+
* });
|
|
5227
|
+
* // Show the Stripe card form
|
|
5228
|
+
* }
|
|
5171
5229
|
* }
|
|
5172
5230
|
* ```
|
|
5173
5231
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainerce",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.41.0",
|
|
4
4
|
"description": "Official SDK for building e-commerce storefronts with Brainerce Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|