@cartbase/storefront 0.2.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartbase/storefront",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Storefront SDK + UI component library for Cartbase stores: typed API client, checkout orchestration, cart drawer, product/catalog components, tracking. Source-shipped TypeScript — add it to transpilePackages.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/api/carts.ts CHANGED
@@ -200,9 +200,11 @@ export interface Cart {
200
200
  credit_line_total: MajorUnitAmount
201
201
  credit_line_subtotal: MajorUnitAmount
202
202
  credit_line_tax_total: MajorUnitAmount
203
- /** Native COD fee — non-zero only when a live pp_cod session exists AND the cod integration is enabled. Folded into `total`. */
204
- cod_fee_total: MajorUnitAmount
205
- cod_fee_label: string | null
203
+ /** The payment method fee — the chosen method's fee_amount (any method
204
+ * may carry one); non-zero only while a live method session is selected.
205
+ * Folded into `total`; its own totals row, never a line item. */
206
+ payment_method_fee_total: MajorUnitAmount
207
+ payment_method_fee_label: string | null
206
208
  // — gift-card tender decoration (totals NEVER move; see gift-cards.ts) —
207
209
  gift_cards: AppliedGiftCard[]
208
210
  /** Σ of applied-card coverage (= the pp_giftcard session amount). */
@@ -6,10 +6,10 @@
6
6
  * 1. **Orchestrated (recommended)** — `prepareCheckout()` is the atomic
7
7
  * Buy-click: ONE call writes address (+billing mirror), shipping method
8
8
  * (+ carrier metadata), payment collection and the payment session at
9
- * the FINAL amount (real Stripe PaymentIntent, or a plain row for
10
- * pp_cod/pp_manual). Then `completeCart()` (see `./carts`). Amount
11
- * drift/repair between the two: `syncPaymentAmount()` and
12
- * `refreshPaymentIfTerminal()`.
9
+ * the FINAL amount (real Stripe PaymentIntent for a processor, or a
10
+ * provider-less row for a merchant method). Then `completeCart()`
11
+ * (see `./carts`). Amount drift/repair between the two:
12
+ * `syncPaymentAmount()` and `refreshPaymentIfTerminal()`.
13
13
  *
14
14
  * 2. **Manual (Medusa-style)** — `listShippingOptions()` →
15
15
  * `addShippingMethod()` → `createPaymentCollection()` →
@@ -137,28 +137,54 @@ export interface ListPaymentProvidersQuery {
137
137
  cart_id?: string
138
138
  }
139
139
 
140
- /** Row of GET /api/store/payment-providers. */
141
- export interface StorePaymentProvider {
142
- /** e.g. `pp_stripe`, `pp_cod`, `pp_manual`. `pp_giftcard` is internal and never listed. */
140
+ /** A connected PROCESSOR entry of GET /api/store/payment-providers. */
141
+ export interface StorePaymentProcessorEntry {
142
+ /** e.g. `pp_stripe`. `pp_giftcard` is internal and never listed. */
143
143
  id: string
144
144
  is_enabled: boolean
145
145
  created_at?: string
146
146
  }
147
147
 
148
+ /** A merchant PAYMENT METHOD entry (pp_* kill, 20260811250000): methods
149
+ * stand alone — no provider id anywhere. Initiate sessions with
150
+ * `payment_method_id`; `kind === "cod"` marks the pre-created
151
+ * collection-on-delivery method (courier cash collection); `instructions`
152
+ * is the merchant's checkout note, rendered under the method. */
153
+ export interface StorePaymentMethodEntry {
154
+ payment_method_id: string
155
+ name: string
156
+ kind: "manual" | "cod"
157
+ instructions: string | null
158
+ /** The method's fee (any method may carry one) — predict
159
+ * `payment_method_fee_total` from it before a session exists. */
160
+ fee_amount: number | null
161
+ fee_label: string | null
162
+ }
163
+
164
+ /** Row of GET /api/store/payment-providers — a processor or a method. */
165
+ export type StorePaymentEntry = StorePaymentProcessorEntry | StorePaymentMethodEntry
166
+
167
+ /** True when the entry is a merchant payment method (vs a processor). */
168
+ export const isMethodEntry = (
169
+ entry: StorePaymentEntry
170
+ ): entry is StorePaymentMethodEntry => "payment_method_id" in entry
171
+
148
172
  export interface PaymentProviderListResponse extends ListEnvelope {
149
- payment_providers: StorePaymentProvider[]
173
+ payment_providers: StorePaymentEntry[]
150
174
  }
151
175
 
152
176
  /**
153
- * List enabled payment providers (rule-filtered, display-ordered). → 200
154
- * list envelope; `count`/`limit` = full filtered list (no pagination).
155
- * Without `region_id`: the tenant's enabled providers
156
- * (`tenant_payment_providers` enabled catalog rows). With `region_id`:
157
- * providers linked to that region.
177
+ * List everything payable (rule-filtered, display-ordered): connected
178
+ * processors + every ENABLED merchant method. → 200 list envelope;
179
+ * `count`/`limit` = full filtered list (no pagination). An empty list is
180
+ * honest: a fresh store cannot take money until the merchant connects a
181
+ * processor, flips the COD switch, or creates a manual method
182
+ * (no-payment-method-by-default).
158
183
  * Auth: anon `x-client-id`.
159
- * Settings: provider enablement (admin integrations — enabling the COD or
160
- * Stripe integration provisions `pp_cod`/`pp_stripe`), checkout rules
161
- * (`target_type=payment_method`) + `checkout_method_order`.
184
+ * Settings: processor enablement (admin integrations), the Payments
185
+ * settings methods manager, checkout rules (`target_type=payment_method`,
186
+ * methods participate under their `payment_method_id`) +
187
+ * `checkout_method_order`.
162
188
  */
163
189
  export async function listPaymentProviders(
164
190
  client: StorefrontClient,
@@ -213,12 +239,18 @@ export async function createPaymentCollection(
213
239
  }
214
240
 
215
241
  export interface InitiatePaymentSessionInput {
216
- /** `pp_stripe` | `pp_cod` | `pp_manual` — NEVER `pp_giftcard` (→ 400 `invalid_provider`). */
217
- provider_id: string
242
+ /** A connected processor (`pp_stripe`) — NEVER `pp_giftcard` (→ 400
243
+ * `invalid_provider`). Exactly ONE of `provider_id` /
244
+ * `payment_method_id` (→ 400 `invalid_data` otherwise). */
245
+ provider_id?: string
246
+ /** A merchant payment method (`payment_method_id` from the listing's
247
+ * method entries) — the session lands with `provider_id` NULL and the
248
+ * method snapshot (`payment_method_id`/`_name`/`_kind`) in `data`. */
249
+ payment_method_id?: string
218
250
  /**
219
- * Merged into the session's `data` blob. Provider-state keys
251
+ * Merged into the session's `data` blob. Server-owned keys
220
252
  * (`payment_intent_id`, `client_secret`, `status`, `stripe_customer_id`,
221
- * `setup_future_usage`) are server-owned and stripped.
253
+ * `setup_future_usage`, and the method snapshot keys) are stripped.
222
254
  */
223
255
  data?: Record<string, unknown>
224
256
  /**
@@ -233,16 +265,19 @@ export interface InitiatePaymentSessionInput {
233
265
 
234
266
  export interface StorePaymentSession {
235
267
  id: string
236
- provider_id: string
237
- /** `collection.amount gift_card_total` — the remainder this provider charges. */
268
+ /** The processor, or NULL for a merchant-method session (the method
269
+ * snapshot lives in `data`). */
270
+ provider_id: string | null
271
+ /** `collection.amount − gift_card_total` — the remainder this tender charges. */
238
272
  amount: MajorUnitAmount
239
273
  currency_code: string
240
274
  /** `pending` until authorized at complete. */
241
275
  status: string
242
276
  authorized_at: string | null
243
277
  /**
244
- * Provider blob. For Stripe: `payment_intent_id`, `client_secret`
245
- * (mount Stripe Elements with it), `status`. Plain row for pp_cod/pp_manual.
278
+ * Tender blob. For Stripe: `payment_intent_id`, `client_secret`
279
+ * (mount Stripe Elements with it), `status`. For methods: the snapshot
280
+ * `payment_method_id` / `payment_method_name` / `payment_method_kind`.
246
281
  */
247
282
  data: Record<string, unknown> | null
248
283
  [key: string]: unknown
@@ -260,10 +295,11 @@ export interface InitiatePaymentSessionResponse {
260
295
  * in place; terminal PIs self-heal by rotation.
261
296
  * Auth: anon `x-client-id`.
262
297
  * Errors: 404 `payment_collection_not_found`, 400 `invalid_provider`
263
- * (pp_giftcard) | `stripe_not_configured` | `validation_failed`.
298
+ * (pp_giftcard / unknown processor) | `invalid_data` (tender XOR violated,
299
+ * unknown/disabled method) | `stripe_not_configured` | `validation_failed`.
264
300
  * Settings: Stripe credentials (admin integrations); gift-card tender
265
- * shrinks the session amount; COD fee appears on the cart the moment a live
266
- * pp_cod session exists.
301
+ * shrinks the session amount; the COD fee appears on the cart the moment a
302
+ * live COD-method session exists.
267
303
  */
268
304
  export async function initiatePaymentSession(
269
305
  client: StorefrontClient,
@@ -312,28 +348,37 @@ export interface PrepareCheckoutInput {
312
348
  * switching carriers never leaks stale fields into the order.
313
349
  */
314
350
  carrier_metadata?: Record<string, unknown>
315
- /** `pp_stripe` | `pp_cod` | `pp_manual` — never `pp_giftcard` (→ 400 `invalid_provider`). */
316
- payment_provider: string
351
+ /** A connected processor (`pp_stripe`) — never `pp_giftcard`. Exactly ONE
352
+ * of `payment_provider` / `payment_method_id`. */
353
+ payment_provider?: string
354
+ /** A merchant payment method — the session lands provider-less with the
355
+ * method snapshot (pp_* kill, 20260811250000). */
356
+ payment_method_id?: string
317
357
  }
318
358
 
319
359
  /** Response of prepare-checkout (verbatim src/lib/checkout-orchestration/prepare.ts). */
320
360
  export interface PrepareCheckoutResult {
321
361
  cart_id: string
322
362
  payment_collection_id: string | null
323
- /** Stripe Elements secret; null for pp_cod/pp_manual and zero-remainder carts. */
363
+ /** Stripe Elements secret; null for method sessions and zero-remainder carts. */
324
364
  client_secret: string | null
325
- /** The provider whose session was minted; null when zero-remainder skipped the session. */
365
+ /** The processor whose session was minted; null for method sessions and
366
+ * when zero-remainder skipped the session. */
326
367
  provider_id: string | null
368
+ /** The method whose session was minted; null for processor sessions and
369
+ * when zero-remainder skipped the session. */
370
+ payment_method_id: string | null
327
371
  }
328
372
 
329
373
  /**
330
374
  * Atomic Buy-click step 1 of 2. ONE call writes address (+billing mirror) +
331
375
  * carrier metadata → shipping method (priced against the new destination) →
332
376
  * payment collection at the shipped total → payment session at the FINAL
333
- * amount. For pp_cod the native fee applies once the session exists, so
334
- * amounts are re-synced after it. Zero-remainder carts (gift cards cover
335
- * the total) skip the provider session — `client_secret`/`provider_id`
336
- * come back null and the cart completes on the gift session.
377
+ * amount. For the COD method the native fee applies once the session
378
+ * exists, so amounts are re-synced after it. Zero-remainder carts (gift
379
+ * cards cover the total) skip the tender session — `client_secret`,
380
+ * `provider_id` and `payment_method_id` come back null and the cart
381
+ * completes on the gift session.
337
382
  *
338
383
  * Fully compensated (wire-pattern): any failure rolls back session →
339
384
  * collection → shipping method → addresses/metadata to the pre-call
@@ -341,13 +386,14 @@ export interface PrepareCheckoutResult {
341
386
  * `checkout_error_logs` (step `prepare-checkout`).
342
387
  *
343
388
  * After prepare: Stripe → `stripe.confirmPayment(client_secret)` →
344
- * `completeCart()`; COD/manual → `completeCart()` directly.
389
+ * `completeCart()`; merchant methods → `completeCart()` directly.
345
390
  *
346
391
  * Auth: anon `x-client-id`.
347
392
  * Errors: 404 `cart_not_found` | `shipping_option_not_found`, 409
348
393
  * `cart_completed`, 400 `validation_failed` | `invalid_provider` |
349
- * `shipping_price_missing` | `stripe_not_configured`.
350
- * Settings: Stripe credentials, COD integration fee, gift-card tender,
394
+ * `invalid_data` (tender XOR violated) | `shipping_price_missing` |
395
+ * `stripe_not_configured`.
396
+ * Settings: Stripe credentials, the COD method's fee, gift-card tender,
351
397
  * checkout rules (enforced at listings + complete, not here).
352
398
  */
353
399
  export async function prepareCheckout(
@@ -358,10 +404,14 @@ export async function prepareCheckout(
358
404
  return client.post(`/api/store/carts/${cartId}/prepare-checkout`, input)
359
405
  }
360
406
 
361
- /** Body of POST /api/store/carts/:id/sync-payment-amount (`.strict()`). */
407
+ /** Body of POST /api/store/carts/:id/sync-payment-amount (`.strict()`).
408
+ * At most ONE of the two — the requested tender; omit both to sync
409
+ * whatever session is pending. */
362
410
  export interface SyncPaymentAmountInput {
363
- /** Rotate to this provider when it differs from the pending session's. */
411
+ /** Rotate to this processor when the pending session's tender differs. */
364
412
  provider_id?: string
413
+ /** Rotate to this merchant method when the pending session's tender differs. */
414
+ payment_method_id?: string
365
415
  }
366
416
 
367
417
  /** Response matrix (verbatim src/lib/checkout-orchestration/sync.ts). */
@@ -371,6 +421,7 @@ export interface SyncPaymentAmountResult {
371
421
  rotated?: boolean
372
422
  client_secret?: string | null
373
423
  provider_id?: string | null
424
+ payment_method_id?: string | null
374
425
  /** No-op reasons: `cart-completed` | `no_payment_collection` | `no_pending_session`. */
375
426
  reason?: string
376
427
  }
@@ -7,9 +7,10 @@
7
7
  * src/lib/tracking/store-config-block.ts.
8
8
  *
9
9
  * The config payload is COMPOSED from an ordered block registry — each
10
- * block owns distinct top-level keys (`carriers`, `cod`, `tracking` today;
11
- * future blocks append). NOT wrapped in an envelope: the blocks ARE the
12
- * top-level keys.
10
+ * block owns distinct top-level keys (`carriers`, `tracking` today; future
11
+ * blocks append). NOT wrapped in an envelope: the blocks ARE the top-level
12
+ * keys. (The `cod` block died with the 'cod' integration, 2026-08-11
13
+ * method fees now ride the payment listing entries themselves.)
13
14
  *
14
15
  * SECURITY LAW: every block is an explicit allowlist — credentials
15
16
  * (carrier API keys, CAPI access_token, GA4 api_secret, Klaviyo
@@ -33,19 +34,6 @@ export interface PublicCarrierConfig {
33
34
  lockers_url?: string
34
35
  }
35
36
 
36
- /** COD checkout block — null unless enabled AND fee_amount > 0 (the exact
37
- * gate the totals engine applies, so the optimistic fee row never disagrees
38
- * with the charged total). */
39
- export interface PublicCodConfig {
40
- enabled: true
41
- /** EUR major units. */
42
- fee_amount: number
43
- fee_currency: "eur"
44
- fee_label: string
45
- /** Admin's checkout note, or null. */
46
- description: string | null
47
- }
48
-
49
37
  /** Public tag config — only ENABLED providers with a public id appear.
50
38
  * Secrets can never appear here (explicit allowlist). */
51
39
  export interface StoreTrackingBlock {
@@ -64,7 +52,6 @@ export interface StoreIntegrationsConfig {
64
52
  /** Keyed by provider slug (e.g. `boxnow`, `econt`). Disabled carriers are
65
53
  * ABSENT, never `enabled: false`. */
66
54
  carriers: Record<string, PublicCarrierConfig>
67
- cod: PublicCodConfig | null
68
55
  tracking: StoreTrackingBlock
69
56
  }
70
57
 
@@ -62,7 +62,7 @@ export const bulgarianCartDrawerLabels: CartDrawerLabels = {
62
62
 
63
63
  discount: "Отстъпка",
64
64
  tax: "ДДС",
65
- codFee: "Такса наложен платеж",
65
+ paymentMethodFee: "Такса за плащане",
66
66
  giftCard: "Подаръчна карта",
67
67
  remainingToPay: "Остава за плащане",
68
68
 
@@ -1,119 +1,119 @@
1
- /**
2
- * Cart drawer labels — default English copy.
3
- *
4
- * Every user-facing string in the cart drawer comes from a `CartDrawerLabels`
5
- * object. Stores override strings by passing a partial labels object to
6
- * `CartDrawerProvider`. English is the baseline; stores wanting Bulgarian,
7
- * German, etc. pass their own translations.
8
- *
9
- * Ported from `@1click/ui/src/cart-drawer/labels.ts` (v2.3.1). Cartbase
10
- * additions: the summary-breakdown row labels (`discount`, `tax`, `codFee`,
11
- * `giftCard`, `remainingToPay`) — the decorated cart carries
12
- * discount/tax/COD-fee/gift-card tender fields that the breakdown renders,
13
- * so the copy for those rows lives here too.
14
- */
15
-
16
- export type CartDrawerLabels = {
17
- // Header
18
- yourCart: string
19
- item: string
20
- items: string
21
- closeCart: string
22
-
23
- // Empty state
24
- emptyCartTitle: string
25
- emptyCartMessage: string
26
- browseProducts: string
27
-
28
- // Tiered progress
29
- awayFromFreeShipping: string
30
- allRewardsUnlocked: string
31
-
32
- // Gift wrap / notes
33
- addGiftWrap: string
34
- giftNote: string
35
- orderNote: string
36
- giftNotePlaceholder: string
37
- orderNotePlaceholder: string
38
-
39
- // Rewards
40
- estimatedRewardPoints: string
41
-
42
- // Cross-sell
43
- pairsWellWith: string
44
- youllLoveThis: string
45
- youMightAlsoLike: string
46
- addToCart: string
47
-
48
- // Summary / footer
49
- subtotal: string
50
- taxAndShipping: string
51
- shipping: string
52
- free: string
53
- calculatedAtCheckout: string
54
- total: string
55
- secureCheckout: string
56
- shippingAndTaxCalculatedAtCheckout: string
57
-
58
- // Summary breakdown rows (Cartbase decorated-cart fields)
59
- discount: string
60
- tax: string
61
- /** Fallback only — the server's `cod_fee_label` wins when present. */
62
- codFee: string
63
- /** Prefix for an applied gift-card row; rendered as `<giftCard> ••1234`. */
64
- giftCard: string
65
- /** Label for `gift_card_remainder` — what the remainder provider charges. */
66
- remainingToPay: string
67
-
68
- // Continue shopping
69
- continueShopping: string
70
-
71
- // A11y / buttons
72
- remove: string
73
- }
74
-
75
- export const defaultCartDrawerLabels: CartDrawerLabels = {
76
- yourCart: "Your cart",
77
- item: "item",
78
- items: "items",
79
- closeCart: "Close cart",
80
-
81
- emptyCartTitle: "Your cart is empty",
82
- emptyCartMessage: "You haven't added any products yet.",
83
- browseProducts: "Browse products",
84
-
85
- awayFromFreeShipping: "away from free shipping",
86
- allRewardsUnlocked: "All rewards unlocked",
87
-
88
- addGiftWrap: "Add gift wrapping",
89
- giftNote: "Gift note",
90
- orderNote: "Order note",
91
- giftNotePlaceholder: "Add a personal message...",
92
- orderNotePlaceholder: "Special instructions for this order...",
93
-
94
- estimatedRewardPoints: "Estimated reward points",
95
-
96
- pairsWellWith: "Pairs well with",
97
- youllLoveThis: "You'll love this",
98
- youMightAlsoLike: "You might also like",
99
- addToCart: "Add",
100
-
101
- subtotal: "Subtotal",
102
- taxAndShipping: "VAT included. Shipping calculated at checkout.",
103
- shipping: "Shipping",
104
- free: "FREE",
105
- calculatedAtCheckout: "Calculated at checkout",
106
- total: "Total",
107
- secureCheckout: "Checkout",
108
- shippingAndTaxCalculatedAtCheckout: "Shipping and tax calculated at checkout",
109
-
110
- discount: "Discount",
111
- tax: "VAT",
112
- codFee: "Cash on delivery fee",
113
- giftCard: "Gift card",
114
- remainingToPay: "Remaining to pay",
115
-
116
- continueShopping: "Continue shopping",
117
-
118
- remove: "Remove",
119
- }
1
+ /**
2
+ * Cart drawer labels — default English copy.
3
+ *
4
+ * Every user-facing string in the cart drawer comes from a `CartDrawerLabels`
5
+ * object. Stores override strings by passing a partial labels object to
6
+ * `CartDrawerProvider`. English is the baseline; stores wanting Bulgarian,
7
+ * German, etc. pass their own translations.
8
+ *
9
+ * Ported from `@1click/ui/src/cart-drawer/labels.ts` (v2.3.1). Cartbase
10
+ * additions: the summary-breakdown row labels (`discount`, `tax`, `paymentMethodFee`,
11
+ * `giftCard`, `remainingToPay`) — the decorated cart carries
12
+ * discount/tax/COD-fee/gift-card tender fields that the breakdown renders,
13
+ * so the copy for those rows lives here too.
14
+ */
15
+
16
+ export type CartDrawerLabels = {
17
+ // Header
18
+ yourCart: string
19
+ item: string
20
+ items: string
21
+ closeCart: string
22
+
23
+ // Empty state
24
+ emptyCartTitle: string
25
+ emptyCartMessage: string
26
+ browseProducts: string
27
+
28
+ // Tiered progress
29
+ awayFromFreeShipping: string
30
+ allRewardsUnlocked: string
31
+
32
+ // Gift wrap / notes
33
+ addGiftWrap: string
34
+ giftNote: string
35
+ orderNote: string
36
+ giftNotePlaceholder: string
37
+ orderNotePlaceholder: string
38
+
39
+ // Rewards
40
+ estimatedRewardPoints: string
41
+
42
+ // Cross-sell
43
+ pairsWellWith: string
44
+ youllLoveThis: string
45
+ youMightAlsoLike: string
46
+ addToCart: string
47
+
48
+ // Summary / footer
49
+ subtotal: string
50
+ taxAndShipping: string
51
+ shipping: string
52
+ free: string
53
+ calculatedAtCheckout: string
54
+ total: string
55
+ secureCheckout: string
56
+ shippingAndTaxCalculatedAtCheckout: string
57
+
58
+ // Summary breakdown rows (Cartbase decorated-cart fields)
59
+ discount: string
60
+ tax: string
61
+ /** Fallback only — the server's `payment_method_fee_label` wins when present. */
62
+ paymentMethodFee: string
63
+ /** Prefix for an applied gift-card row; rendered as `<giftCard> ••1234`. */
64
+ giftCard: string
65
+ /** Label for `gift_card_remainder` — what the remainder provider charges. */
66
+ remainingToPay: string
67
+
68
+ // Continue shopping
69
+ continueShopping: string
70
+
71
+ // A11y / buttons
72
+ remove: string
73
+ }
74
+
75
+ export const defaultCartDrawerLabels: CartDrawerLabels = {
76
+ yourCart: "Your cart",
77
+ item: "item",
78
+ items: "items",
79
+ closeCart: "Close cart",
80
+
81
+ emptyCartTitle: "Your cart is empty",
82
+ emptyCartMessage: "You haven't added any products yet.",
83
+ browseProducts: "Browse products",
84
+
85
+ awayFromFreeShipping: "away from free shipping",
86
+ allRewardsUnlocked: "All rewards unlocked",
87
+
88
+ addGiftWrap: "Add gift wrapping",
89
+ giftNote: "Gift note",
90
+ orderNote: "Order note",
91
+ giftNotePlaceholder: "Add a personal message...",
92
+ orderNotePlaceholder: "Special instructions for this order...",
93
+
94
+ estimatedRewardPoints: "Estimated reward points",
95
+
96
+ pairsWellWith: "Pairs well with",
97
+ youllLoveThis: "You'll love this",
98
+ youMightAlsoLike: "You might also like",
99
+ addToCart: "Add",
100
+
101
+ subtotal: "Subtotal",
102
+ taxAndShipping: "VAT included. Shipping calculated at checkout.",
103
+ shipping: "Shipping",
104
+ free: "FREE",
105
+ calculatedAtCheckout: "Calculated at checkout",
106
+ total: "Total",
107
+ secureCheckout: "Checkout",
108
+ shippingAndTaxCalculatedAtCheckout: "Shipping and tax calculated at checkout",
109
+
110
+ discount: "Discount",
111
+ tax: "VAT",
112
+ paymentMethodFee: "Payment method fee",
113
+ giftCard: "Gift card",
114
+ remainingToPay: "Remaining to pay",
115
+
116
+ continueShopping: "Continue shopping",
117
+
118
+ remove: "Remove",
119
+ }