@usethrottle/cart 3.0.1 → 3.2.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 CHANGED
@@ -1,3 +1,8 @@
1
+ <p align="left">
2
+ <img src="https://raw.githubusercontent.com/Epic-Design-Labs/app-throttle/main/packages/brand/assets/throttle-logo.png" alt="Throttle" height="56" />
3
+ </p>
4
+
5
+
1
6
  # @usethrottle/cart
2
7
 
3
8
  Typed Node.js REST client for the Throttle Cart API.
@@ -18,7 +23,11 @@ import { CartClient } from '@usethrottle/cart';
18
23
  const client = new CartClient({ apiKey: process.env.THROTTLE_API_KEY! });
19
24
 
20
25
  // 1. Create a cart
21
- const cart = await client.carts.create({ storeId: 'store_abc', currency: 'USD' });
26
+ const cart = await client.carts.create({
27
+ applicationId: '7f9d4c8a-5b2e-4f16-9a73-2d1e5c8b6f40',
28
+ currency: 'USD',
29
+ netN: 45,
30
+ });
22
31
 
23
32
  // 2. Add a line item
24
33
  await client.items.add(cart.id, {
@@ -48,8 +57,25 @@ const order = await client.carts.checkout(cart.id, { paymentMethod: 'card' });
48
57
  console.log(order.id, order.status);
49
58
  ```
50
59
 
60
+ Use applicationId for every public request body. Deprecated store identifiers
61
+ are rejected by the API.
62
+
51
63
  All monetary values are integers in the smallest currency unit (cents for USD).
52
64
 
65
+ ## Net-N invoice terms
66
+
67
+ Cart-level invoice terms are optional. Pass `netN` on cart create or update to
68
+ set the cart override, or `null` to clear it. The final invoice term is resolved
69
+ when the buyer completes checkout with Invoice Terms:
70
+
71
+ ```text
72
+ customer.netN ?? cart.netN ?? DEFAULT_NET_N
73
+ ```
74
+
75
+ `allowedMethods` only controls which payment methods can appear in checkout; it
76
+ does not change the Net-N day count. For hosted/embed checkout, create a cart
77
+ with `netN` and then create a checkout session for that cart.
78
+
53
79
  ## Shipping and tax
54
80
 
55
81
  Use the secret-key client on your backend to calculate and persist cart totals
@@ -74,7 +100,7 @@ External stores can also request read-only storefront quotes with a publishable
74
100
  import { StorefrontQuoteClient } from '@usethrottle/cart';
75
101
 
76
102
  const quotes = new StorefrontQuoteClient({
77
- applicationId: 'application_uuid',
103
+ applicationId: '7f9d4c8a-5b2e-4f16-9a73-2d1e5c8b6f40',
78
104
  quoteToken: 'pk_publishable_quote_token',
79
105
  });
80
106
 
@@ -102,7 +128,7 @@ when at least one axis is in `byo` mode:
102
128
 
103
129
  ```ts
104
130
  await client.shippingTax.pushExternalSnapshot({
105
- storeId: 'store_uuid',
131
+ applicationId: '7f9d4c8a-5b2e-4f16-9a73-2d1e5c8b6f40',
106
132
  cartId: cart.id,
107
133
  currency: 'USD',
108
134
  totals: {
package/dist/index.cjs CHANGED
@@ -41,6 +41,9 @@ var ThrottleApiError = class extends Error {
41
41
  };
42
42
 
43
43
  // src/client.ts
44
+ function segment(value) {
45
+ return encodeURIComponent(value);
46
+ }
44
47
  var CartClient = class {
45
48
  apiKey;
46
49
  baseUrl;
@@ -81,40 +84,44 @@ var CartClient = class {
81
84
  get carts() {
82
85
  return {
83
86
  create: (input) => this.request("POST", "/api/v1/carts", input),
84
- get: (id) => this.request("GET", `/api/v1/carts/${id}`),
85
- update: (id, input) => this.request("PATCH", `/api/v1/carts/${id}`, input),
86
- checkout: (id, input) => this.request("POST", `/api/v1/carts/${id}/checkout`, input ?? {}),
87
- merge: (id, customerId) => this.request("POST", `/api/v1/carts/${id}/merge`, { customerId })
87
+ get: (id) => this.request("GET", `/api/v1/carts/${segment(id)}`),
88
+ update: (id, input) => this.request("PATCH", `/api/v1/carts/${segment(id)}`, input),
89
+ checkout: (id, input) => this.request("POST", `/api/v1/carts/${segment(id)}/checkout`, input ?? {}),
90
+ merge: (id, customerId) => this.request("POST", `/api/v1/carts/${segment(id)}/merge`, { customerId })
88
91
  };
89
92
  }
90
93
  get items() {
91
94
  return {
92
- add: (cartId, input) => this.request("POST", `/api/v1/carts/${cartId}/items`, input),
93
- update: (cartId, itemId, input) => this.request("PATCH", `/api/v1/carts/${cartId}/items/${itemId}`, input),
94
- remove: (cartId, itemId) => this.request("DELETE", `/api/v1/carts/${cartId}/items/${itemId}`)
95
+ add: (cartId, input) => this.request("POST", `/api/v1/carts/${segment(cartId)}/items`, input),
96
+ update: (cartId, itemId, input) => this.request("PATCH", `/api/v1/carts/${segment(cartId)}/items/${segment(itemId)}`, input),
97
+ remove: (cartId, itemId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/items/${segment(itemId)}`)
95
98
  };
96
99
  }
97
100
  get shipping() {
98
101
  return {
99
- select: (cartId, input) => this.request("POST", `/api/v1/carts/${cartId}/shipping`, input),
100
- clear: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/shipping`)
102
+ select: (cartId, input) => this.request("POST", `/api/v1/carts/${segment(cartId)}/shipping`, input),
103
+ clear: (cartId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/shipping`)
101
104
  };
102
105
  }
103
106
  get discounts() {
104
107
  return {
105
- apply: (cartId, code) => this.request("POST", `/api/v1/carts/${cartId}/apply-discount`, { code }),
106
- remove: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/discount`)
108
+ apply: (cartId, code) => this.request("POST", `/api/v1/carts/${segment(cartId)}/apply-discount`, { code }),
109
+ remove: (cartId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/discount`)
107
110
  };
108
111
  }
109
112
  get taxLines() {
110
113
  return {
111
- set: (cartId, lines) => this.request("PUT", `/api/v1/carts/${cartId}/tax-lines`, { lines }),
112
- clear: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/tax-lines`)
114
+ set: (cartId, lines) => this.request("PUT", `/api/v1/carts/${segment(cartId)}/tax-lines`, { lines }),
115
+ clear: (cartId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/tax-lines`)
113
116
  };
114
117
  }
115
118
  get shippingTax() {
116
119
  return {
117
- calculateCart: (cartId, input = {}) => this.request("POST", `/api/v1/shipping-tax/carts/${cartId}/calculate`, input),
120
+ calculateCart: (cartId, input = {}) => this.request(
121
+ "POST",
122
+ `/api/v1/shipping-tax/carts/${segment(cartId)}/calculate`,
123
+ input
124
+ ),
118
125
  pushExternalSnapshot: (input) => this.request("POST", "/api/v1/shipping-tax/external-snapshots", input)
119
126
  };
120
127
  }
@@ -125,7 +132,7 @@ var CartClient = class {
125
132
  if (opts?.sinceSequence !== void 0) params.set("sinceSequence", String(opts.sinceSequence));
126
133
  if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
127
134
  const qs = params.toString();
128
- return this.request("GET", `/api/v1/carts/${cartId}/events${qs ? "?" + qs : ""}`);
135
+ return this.request("GET", `/api/v1/carts/${segment(cartId)}/events${qs ? "?" + qs : ""}`);
129
136
  }
130
137
  };
131
138
  }
@@ -140,12 +147,11 @@ var StorefrontQuoteClient = class {
140
147
  timeoutMs;
141
148
  origin;
142
149
  constructor(opts) {
143
- const applicationId = opts.applicationId ?? opts.storeId;
144
- if (!applicationId) throw new Error("StorefrontQuoteClient: applicationId is required");
150
+ if (!opts.applicationId) throw new Error("StorefrontQuoteClient: applicationId is required");
145
151
  if (!opts.quoteToken?.startsWith("pk_")) {
146
152
  throw new Error("StorefrontQuoteClient: quoteToken must be a publishable pk_ token");
147
153
  }
148
- this.applicationId = applicationId;
154
+ this.applicationId = opts.applicationId;
149
155
  this.quoteToken = opts.quoteToken;
150
156
  this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
151
157
  this.fetchImpl = opts.fetch ?? globalThis.fetch;
@@ -162,9 +168,9 @@ var StorefrontQuoteClient = class {
162
168
  method: "POST",
163
169
  headers,
164
170
  body: JSON.stringify({
171
+ ...input,
165
172
  applicationId: this.applicationId,
166
- quoteToken: this.quoteToken,
167
- ...input
173
+ quoteToken: this.quoteToken
168
174
  }),
169
175
  signal: ctrl.signal
170
176
  });
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  interface Cart {
2
2
  id: string;
3
- merchantId: string;
4
- storeId: string | null;
3
+ workspaceId: string;
4
+ applicationId: string;
5
5
  customerId: string | null;
6
6
  status: 'open' | 'checkout' | 'converted' | 'abandoned';
7
7
  currency: string;
@@ -10,6 +10,7 @@ interface Cart {
10
10
  discountTotal: number;
11
11
  shippingTotal: number;
12
12
  total: number;
13
+ netN: number | null;
13
14
  billingAddress: Record<string, unknown> | null;
14
15
  shippingAddress: Record<string, unknown> | null;
15
16
  notes: string | null;
@@ -87,9 +88,10 @@ interface CartEvent {
87
88
  createdAt: string;
88
89
  }
89
90
  interface CreateCartInput {
90
- storeId: string;
91
+ applicationId: string;
91
92
  customerId?: string;
92
93
  currency?: string;
94
+ netN?: number | null;
93
95
  metadata?: Record<string, unknown>;
94
96
  }
95
97
  interface UpdateCartInput {
@@ -97,6 +99,7 @@ interface UpdateCartInput {
97
99
  billingAddress?: Record<string, unknown>;
98
100
  shippingAddress?: Record<string, unknown>;
99
101
  notes?: string;
102
+ netN?: number | null;
100
103
  metadata?: Record<string, unknown>;
101
104
  }
102
105
  interface AddLineItemInput {
@@ -214,8 +217,8 @@ interface ShippingTaxCalculationResponse {
214
217
  kind: 'quote' | 'cart_estimate' | 'checkout_final' | 'external_snapshot';
215
218
  status: 'estimated' | 'final' | 'failed';
216
219
  source: 'off' | 'byo' | 'calculated' | 'app_based' | 'external_push';
217
- storeId: string;
218
- merchantId?: string;
220
+ applicationId: string;
221
+ workspaceId?: string;
219
222
  currency: string;
220
223
  configId?: string;
221
224
  configVersion?: number;
@@ -277,7 +280,7 @@ interface ShippingTaxCalculationResponse {
277
280
  }>;
278
281
  }
279
282
  interface ExternalShippingTaxSnapshotInput {
280
- storeId: string;
283
+ applicationId: string;
281
284
  cartId?: string | null;
282
285
  checkoutSessionId?: string | null;
283
286
  orderId?: string | null;
@@ -338,10 +341,7 @@ declare class CartClient {
338
341
  }
339
342
 
340
343
  interface StorefrontQuoteClientOptions {
341
- /** Current API contract: the application/store UUID that owns the quote config. */
342
- applicationId?: string;
343
- /** @deprecated Use applicationId. Kept as an alias for older storefronts. */
344
- storeId?: string;
344
+ applicationId: string;
345
345
  quoteToken: string;
346
346
  baseUrl?: string;
347
347
  fetch?: typeof globalThis.fetch;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  interface Cart {
2
2
  id: string;
3
- merchantId: string;
4
- storeId: string | null;
3
+ workspaceId: string;
4
+ applicationId: string;
5
5
  customerId: string | null;
6
6
  status: 'open' | 'checkout' | 'converted' | 'abandoned';
7
7
  currency: string;
@@ -10,6 +10,7 @@ interface Cart {
10
10
  discountTotal: number;
11
11
  shippingTotal: number;
12
12
  total: number;
13
+ netN: number | null;
13
14
  billingAddress: Record<string, unknown> | null;
14
15
  shippingAddress: Record<string, unknown> | null;
15
16
  notes: string | null;
@@ -87,9 +88,10 @@ interface CartEvent {
87
88
  createdAt: string;
88
89
  }
89
90
  interface CreateCartInput {
90
- storeId: string;
91
+ applicationId: string;
91
92
  customerId?: string;
92
93
  currency?: string;
94
+ netN?: number | null;
93
95
  metadata?: Record<string, unknown>;
94
96
  }
95
97
  interface UpdateCartInput {
@@ -97,6 +99,7 @@ interface UpdateCartInput {
97
99
  billingAddress?: Record<string, unknown>;
98
100
  shippingAddress?: Record<string, unknown>;
99
101
  notes?: string;
102
+ netN?: number | null;
100
103
  metadata?: Record<string, unknown>;
101
104
  }
102
105
  interface AddLineItemInput {
@@ -214,8 +217,8 @@ interface ShippingTaxCalculationResponse {
214
217
  kind: 'quote' | 'cart_estimate' | 'checkout_final' | 'external_snapshot';
215
218
  status: 'estimated' | 'final' | 'failed';
216
219
  source: 'off' | 'byo' | 'calculated' | 'app_based' | 'external_push';
217
- storeId: string;
218
- merchantId?: string;
220
+ applicationId: string;
221
+ workspaceId?: string;
219
222
  currency: string;
220
223
  configId?: string;
221
224
  configVersion?: number;
@@ -277,7 +280,7 @@ interface ShippingTaxCalculationResponse {
277
280
  }>;
278
281
  }
279
282
  interface ExternalShippingTaxSnapshotInput {
280
- storeId: string;
283
+ applicationId: string;
281
284
  cartId?: string | null;
282
285
  checkoutSessionId?: string | null;
283
286
  orderId?: string | null;
@@ -338,10 +341,7 @@ declare class CartClient {
338
341
  }
339
342
 
340
343
  interface StorefrontQuoteClientOptions {
341
- /** Current API contract: the application/store UUID that owns the quote config. */
342
- applicationId?: string;
343
- /** @deprecated Use applicationId. Kept as an alias for older storefronts. */
344
- storeId?: string;
344
+ applicationId: string;
345
345
  quoteToken: string;
346
346
  baseUrl?: string;
347
347
  fetch?: typeof globalThis.fetch;
package/dist/index.js CHANGED
@@ -13,6 +13,9 @@ var ThrottleApiError = class extends Error {
13
13
  };
14
14
 
15
15
  // src/client.ts
16
+ function segment(value) {
17
+ return encodeURIComponent(value);
18
+ }
16
19
  var CartClient = class {
17
20
  apiKey;
18
21
  baseUrl;
@@ -53,40 +56,44 @@ var CartClient = class {
53
56
  get carts() {
54
57
  return {
55
58
  create: (input) => this.request("POST", "/api/v1/carts", input),
56
- get: (id) => this.request("GET", `/api/v1/carts/${id}`),
57
- update: (id, input) => this.request("PATCH", `/api/v1/carts/${id}`, input),
58
- checkout: (id, input) => this.request("POST", `/api/v1/carts/${id}/checkout`, input ?? {}),
59
- merge: (id, customerId) => this.request("POST", `/api/v1/carts/${id}/merge`, { customerId })
59
+ get: (id) => this.request("GET", `/api/v1/carts/${segment(id)}`),
60
+ update: (id, input) => this.request("PATCH", `/api/v1/carts/${segment(id)}`, input),
61
+ checkout: (id, input) => this.request("POST", `/api/v1/carts/${segment(id)}/checkout`, input ?? {}),
62
+ merge: (id, customerId) => this.request("POST", `/api/v1/carts/${segment(id)}/merge`, { customerId })
60
63
  };
61
64
  }
62
65
  get items() {
63
66
  return {
64
- add: (cartId, input) => this.request("POST", `/api/v1/carts/${cartId}/items`, input),
65
- update: (cartId, itemId, input) => this.request("PATCH", `/api/v1/carts/${cartId}/items/${itemId}`, input),
66
- remove: (cartId, itemId) => this.request("DELETE", `/api/v1/carts/${cartId}/items/${itemId}`)
67
+ add: (cartId, input) => this.request("POST", `/api/v1/carts/${segment(cartId)}/items`, input),
68
+ update: (cartId, itemId, input) => this.request("PATCH", `/api/v1/carts/${segment(cartId)}/items/${segment(itemId)}`, input),
69
+ remove: (cartId, itemId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/items/${segment(itemId)}`)
67
70
  };
68
71
  }
69
72
  get shipping() {
70
73
  return {
71
- select: (cartId, input) => this.request("POST", `/api/v1/carts/${cartId}/shipping`, input),
72
- clear: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/shipping`)
74
+ select: (cartId, input) => this.request("POST", `/api/v1/carts/${segment(cartId)}/shipping`, input),
75
+ clear: (cartId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/shipping`)
73
76
  };
74
77
  }
75
78
  get discounts() {
76
79
  return {
77
- apply: (cartId, code) => this.request("POST", `/api/v1/carts/${cartId}/apply-discount`, { code }),
78
- remove: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/discount`)
80
+ apply: (cartId, code) => this.request("POST", `/api/v1/carts/${segment(cartId)}/apply-discount`, { code }),
81
+ remove: (cartId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/discount`)
79
82
  };
80
83
  }
81
84
  get taxLines() {
82
85
  return {
83
- set: (cartId, lines) => this.request("PUT", `/api/v1/carts/${cartId}/tax-lines`, { lines }),
84
- clear: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/tax-lines`)
86
+ set: (cartId, lines) => this.request("PUT", `/api/v1/carts/${segment(cartId)}/tax-lines`, { lines }),
87
+ clear: (cartId) => this.request("DELETE", `/api/v1/carts/${segment(cartId)}/tax-lines`)
85
88
  };
86
89
  }
87
90
  get shippingTax() {
88
91
  return {
89
- calculateCart: (cartId, input = {}) => this.request("POST", `/api/v1/shipping-tax/carts/${cartId}/calculate`, input),
92
+ calculateCart: (cartId, input = {}) => this.request(
93
+ "POST",
94
+ `/api/v1/shipping-tax/carts/${segment(cartId)}/calculate`,
95
+ input
96
+ ),
90
97
  pushExternalSnapshot: (input) => this.request("POST", "/api/v1/shipping-tax/external-snapshots", input)
91
98
  };
92
99
  }
@@ -97,7 +104,7 @@ var CartClient = class {
97
104
  if (opts?.sinceSequence !== void 0) params.set("sinceSequence", String(opts.sinceSequence));
98
105
  if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
99
106
  const qs = params.toString();
100
- return this.request("GET", `/api/v1/carts/${cartId}/events${qs ? "?" + qs : ""}`);
107
+ return this.request("GET", `/api/v1/carts/${segment(cartId)}/events${qs ? "?" + qs : ""}`);
101
108
  }
102
109
  };
103
110
  }
@@ -112,12 +119,11 @@ var StorefrontQuoteClient = class {
112
119
  timeoutMs;
113
120
  origin;
114
121
  constructor(opts) {
115
- const applicationId = opts.applicationId ?? opts.storeId;
116
- if (!applicationId) throw new Error("StorefrontQuoteClient: applicationId is required");
122
+ if (!opts.applicationId) throw new Error("StorefrontQuoteClient: applicationId is required");
117
123
  if (!opts.quoteToken?.startsWith("pk_")) {
118
124
  throw new Error("StorefrontQuoteClient: quoteToken must be a publishable pk_ token");
119
125
  }
120
- this.applicationId = applicationId;
126
+ this.applicationId = opts.applicationId;
121
127
  this.quoteToken = opts.quoteToken;
122
128
  this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
123
129
  this.fetchImpl = opts.fetch ?? globalThis.fetch;
@@ -134,9 +140,9 @@ var StorefrontQuoteClient = class {
134
140
  method: "POST",
135
141
  headers,
136
142
  body: JSON.stringify({
143
+ ...input,
137
144
  applicationId: this.applicationId,
138
- quoteToken: this.quoteToken,
139
- ...input
145
+ quoteToken: this.quoteToken
140
146
  }),
141
147
  signal: ctrl.signal
142
148
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethrottle/cart",
3
- "version": "3.0.1",
3
+ "version": "3.2.0",
4
4
  "description": "Typed REST client for the Throttle Cart API.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",