@cartbase/storefront 0.2.0 → 0.4.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.4.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()` →
@@ -47,11 +47,10 @@ export interface StoreShippingOption {
47
47
  name: string
48
48
  provider_id: string | null
49
49
  service_zone_id: string | null
50
- shipping_option_type_id: string | null
51
50
  shipping_profile_id: string | null
52
51
  data: Record<string, unknown> | null
53
- /** Embedded `shipping_option_types` row (label/description/code) or null. */
54
- type: Record<string, unknown> | null
52
+ /** The option's display label (e.g. "Express") as `{ label }`, or null. */
53
+ type: { label: string } | null
55
54
  /** Flat price in the cart currency; null without `cart_id` or when no price row matches. */
56
55
  amount: MajorUnitAmount | null
57
56
  /** Always "flat" today — calculated-rate carriers are not wired yet. */
@@ -137,28 +136,54 @@ export interface ListPaymentProvidersQuery {
137
136
  cart_id?: string
138
137
  }
139
138
 
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. */
139
+ /** A connected PROCESSOR entry of GET /api/store/payment-providers. */
140
+ export interface StorePaymentProcessorEntry {
141
+ /** e.g. `pp_stripe`. `pp_giftcard` is internal and never listed. */
143
142
  id: string
144
143
  is_enabled: boolean
145
144
  created_at?: string
146
145
  }
147
146
 
147
+ /** A merchant PAYMENT METHOD entry (pp_* kill, 20260811250000): methods
148
+ * stand alone — no provider id anywhere. Initiate sessions with
149
+ * `payment_method_id`; `kind === "cod"` marks the pre-created
150
+ * collection-on-delivery method (courier cash collection); `instructions`
151
+ * is the merchant's checkout note, rendered under the method. */
152
+ export interface StorePaymentMethodEntry {
153
+ payment_method_id: string
154
+ name: string
155
+ kind: "manual" | "cod"
156
+ instructions: string | null
157
+ /** The method's fee (any method may carry one) — predict
158
+ * `payment_method_fee_total` from it before a session exists. */
159
+ fee_amount: number | null
160
+ fee_label: string | null
161
+ }
162
+
163
+ /** Row of GET /api/store/payment-providers — a processor or a method. */
164
+ export type StorePaymentEntry = StorePaymentProcessorEntry | StorePaymentMethodEntry
165
+
166
+ /** True when the entry is a merchant payment method (vs a processor). */
167
+ export const isMethodEntry = (
168
+ entry: StorePaymentEntry
169
+ ): entry is StorePaymentMethodEntry => "payment_method_id" in entry
170
+
148
171
  export interface PaymentProviderListResponse extends ListEnvelope {
149
- payment_providers: StorePaymentProvider[]
172
+ payment_providers: StorePaymentEntry[]
150
173
  }
151
174
 
152
175
  /**
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.
176
+ * List everything payable (rule-filtered, display-ordered): connected
177
+ * processors + every ENABLED merchant method. → 200 list envelope;
178
+ * `count`/`limit` = full filtered list (no pagination). An empty list is
179
+ * honest: a fresh store cannot take money until the merchant connects a
180
+ * processor, flips the COD switch, or creates a manual method
181
+ * (no-payment-method-by-default).
158
182
  * 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`.
183
+ * Settings: processor enablement (admin integrations), the Payments
184
+ * settings methods manager, checkout rules (`target_type=payment_method`,
185
+ * methods participate under their `payment_method_id`) +
186
+ * `checkout_method_order`.
162
187
  */
163
188
  export async function listPaymentProviders(
164
189
  client: StorefrontClient,
@@ -213,12 +238,18 @@ export async function createPaymentCollection(
213
238
  }
214
239
 
215
240
  export interface InitiatePaymentSessionInput {
216
- /** `pp_stripe` | `pp_cod` | `pp_manual` — NEVER `pp_giftcard` (→ 400 `invalid_provider`). */
217
- provider_id: string
241
+ /** A connected processor (`pp_stripe`) — NEVER `pp_giftcard` (→ 400
242
+ * `invalid_provider`). Exactly ONE of `provider_id` /
243
+ * `payment_method_id` (→ 400 `invalid_data` otherwise). */
244
+ provider_id?: string
245
+ /** A merchant payment method (`payment_method_id` from the listing's
246
+ * method entries) — the session lands with `provider_id` NULL and the
247
+ * method snapshot (`payment_method_id`/`_name`/`_kind`) in `data`. */
248
+ payment_method_id?: string
218
249
  /**
219
- * Merged into the session's `data` blob. Provider-state keys
250
+ * Merged into the session's `data` blob. Server-owned keys
220
251
  * (`payment_intent_id`, `client_secret`, `status`, `stripe_customer_id`,
221
- * `setup_future_usage`) are server-owned and stripped.
252
+ * `setup_future_usage`, and the method snapshot keys) are stripped.
222
253
  */
223
254
  data?: Record<string, unknown>
224
255
  /**
@@ -233,16 +264,19 @@ export interface InitiatePaymentSessionInput {
233
264
 
234
265
  export interface StorePaymentSession {
235
266
  id: string
236
- provider_id: string
237
- /** `collection.amount gift_card_total` — the remainder this provider charges. */
267
+ /** The processor, or NULL for a merchant-method session (the method
268
+ * snapshot lives in `data`). */
269
+ provider_id: string | null
270
+ /** `collection.amount − gift_card_total` — the remainder this tender charges. */
238
271
  amount: MajorUnitAmount
239
272
  currency_code: string
240
273
  /** `pending` until authorized at complete. */
241
274
  status: string
242
275
  authorized_at: string | null
243
276
  /**
244
- * Provider blob. For Stripe: `payment_intent_id`, `client_secret`
245
- * (mount Stripe Elements with it), `status`. Plain row for pp_cod/pp_manual.
277
+ * Tender blob. For Stripe: `payment_intent_id`, `client_secret`
278
+ * (mount Stripe Elements with it), `status`. For methods: the snapshot
279
+ * `payment_method_id` / `payment_method_name` / `payment_method_kind`.
246
280
  */
247
281
  data: Record<string, unknown> | null
248
282
  [key: string]: unknown
@@ -260,10 +294,11 @@ export interface InitiatePaymentSessionResponse {
260
294
  * in place; terminal PIs self-heal by rotation.
261
295
  * Auth: anon `x-client-id`.
262
296
  * Errors: 404 `payment_collection_not_found`, 400 `invalid_provider`
263
- * (pp_giftcard) | `stripe_not_configured` | `validation_failed`.
297
+ * (pp_giftcard / unknown processor) | `invalid_data` (tender XOR violated,
298
+ * unknown/disabled method) | `stripe_not_configured` | `validation_failed`.
264
299
  * 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.
300
+ * shrinks the session amount; the COD fee appears on the cart the moment a
301
+ * live COD-method session exists.
267
302
  */
268
303
  export async function initiatePaymentSession(
269
304
  client: StorefrontClient,
@@ -312,28 +347,37 @@ export interface PrepareCheckoutInput {
312
347
  * switching carriers never leaks stale fields into the order.
313
348
  */
314
349
  carrier_metadata?: Record<string, unknown>
315
- /** `pp_stripe` | `pp_cod` | `pp_manual` — never `pp_giftcard` (→ 400 `invalid_provider`). */
316
- payment_provider: string
350
+ /** A connected processor (`pp_stripe`) — never `pp_giftcard`. Exactly ONE
351
+ * of `payment_provider` / `payment_method_id`. */
352
+ payment_provider?: string
353
+ /** A merchant payment method — the session lands provider-less with the
354
+ * method snapshot (pp_* kill, 20260811250000). */
355
+ payment_method_id?: string
317
356
  }
318
357
 
319
358
  /** Response of prepare-checkout (verbatim src/lib/checkout-orchestration/prepare.ts). */
320
359
  export interface PrepareCheckoutResult {
321
360
  cart_id: string
322
361
  payment_collection_id: string | null
323
- /** Stripe Elements secret; null for pp_cod/pp_manual and zero-remainder carts. */
362
+ /** Stripe Elements secret; null for method sessions and zero-remainder carts. */
324
363
  client_secret: string | null
325
- /** The provider whose session was minted; null when zero-remainder skipped the session. */
364
+ /** The processor whose session was minted; null for method sessions and
365
+ * when zero-remainder skipped the session. */
326
366
  provider_id: string | null
367
+ /** The method whose session was minted; null for processor sessions and
368
+ * when zero-remainder skipped the session. */
369
+ payment_method_id: string | null
327
370
  }
328
371
 
329
372
  /**
330
373
  * Atomic Buy-click step 1 of 2. ONE call writes address (+billing mirror) +
331
374
  * carrier metadata → shipping method (priced against the new destination) →
332
375
  * 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.
376
+ * amount. For the COD method the native fee applies once the session
377
+ * exists, so amounts are re-synced after it. Zero-remainder carts (gift
378
+ * cards cover the total) skip the tender session — `client_secret`,
379
+ * `provider_id` and `payment_method_id` come back null and the cart
380
+ * completes on the gift session.
337
381
  *
338
382
  * Fully compensated (wire-pattern): any failure rolls back session →
339
383
  * collection → shipping method → addresses/metadata to the pre-call
@@ -341,13 +385,14 @@ export interface PrepareCheckoutResult {
341
385
  * `checkout_error_logs` (step `prepare-checkout`).
342
386
  *
343
387
  * After prepare: Stripe → `stripe.confirmPayment(client_secret)` →
344
- * `completeCart()`; COD/manual → `completeCart()` directly.
388
+ * `completeCart()`; merchant methods → `completeCart()` directly.
345
389
  *
346
390
  * Auth: anon `x-client-id`.
347
391
  * Errors: 404 `cart_not_found` | `shipping_option_not_found`, 409
348
392
  * `cart_completed`, 400 `validation_failed` | `invalid_provider` |
349
- * `shipping_price_missing` | `stripe_not_configured`.
350
- * Settings: Stripe credentials, COD integration fee, gift-card tender,
393
+ * `invalid_data` (tender XOR violated) | `shipping_price_missing` |
394
+ * `stripe_not_configured`.
395
+ * Settings: Stripe credentials, the COD method's fee, gift-card tender,
351
396
  * checkout rules (enforced at listings + complete, not here).
352
397
  */
353
398
  export async function prepareCheckout(
@@ -358,10 +403,14 @@ export async function prepareCheckout(
358
403
  return client.post(`/api/store/carts/${cartId}/prepare-checkout`, input)
359
404
  }
360
405
 
361
- /** Body of POST /api/store/carts/:id/sync-payment-amount (`.strict()`). */
406
+ /** Body of POST /api/store/carts/:id/sync-payment-amount (`.strict()`).
407
+ * At most ONE of the two — the requested tender; omit both to sync
408
+ * whatever session is pending. */
362
409
  export interface SyncPaymentAmountInput {
363
- /** Rotate to this provider when it differs from the pending session's. */
410
+ /** Rotate to this processor when the pending session's tender differs. */
364
411
  provider_id?: string
412
+ /** Rotate to this merchant method when the pending session's tender differs. */
413
+ payment_method_id?: string
365
414
  }
366
415
 
367
416
  /** Response matrix (verbatim src/lib/checkout-orchestration/sync.ts). */
@@ -371,6 +420,7 @@ export interface SyncPaymentAmountResult {
371
420
  rotated?: boolean
372
421
  client_secret?: string | null
373
422
  provider_id?: string | null
423
+ payment_method_id?: string | null
374
424
  /** No-op reasons: `cart-completed` | `no_payment_collection` | `no_pending_session`. */
375
425
  reason?: string
376
426
  }
@@ -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
+ }