create-cartbase 0.1.24 → 0.1.26

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": "create-cartbase",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Scaffold a Cartbase storefront: npm create cartbase my-store",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -147,10 +147,12 @@ curl -sf -X POST "$BASE/api/store/shipping-options/$SO_ID/calculate" \
147
147
 
148
148
  - **Purpose** — render the payment-method picker.
149
149
  - **Auth** — anon `x-client-id`.
150
- - **Request** — `GET ?region_id=…&cart_id=…`, both optional. Without
150
+ - **Request** — `GET ?region_id=…&cart_id=…&shipping_option_id=…`, all optional. Without
151
151
  `region_id`: the tenant's enabled providers (global catalog ∩ tenant
152
152
  enablement). With it: providers linked to that region. `cart_id` feeds
153
153
  the rules engine — storefronts SHOULD pass it during checkout.
154
+ `shipping_option_id` evaluates a prospective delivery click immediately,
155
+ before its separate cart-preference write finishes.
154
156
  - **Response** — list envelope, full filtered list. Two entry shapes share
155
157
  the array: connected PROCESSORS (`{id}` — pp_stripe) and merchant
156
158
  payment METHODS (`{payment_method_id, name, kind, instructions,
@@ -174,6 +176,13 @@ curl -sf -X POST "$BASE/api/store/shipping-options/$SO_ID/calculate" \
174
176
  "fee_amount": 4.99, "fee_label": "COD fee" }
175
177
  // pp_giftcard is INTERNAL tender and is never listed
176
178
  ],
179
+ "payment_providers_by_shipping_option": {
180
+ "so_boxnow": [{ "id": "pp_stripe", "is_enabled": true }],
181
+ "so_speedy": [
182
+ { "id": "pp_stripe", "is_enabled": true },
183
+ { "payment_method_id": "pm_cod", "name": "Cash on delivery", "kind": "cod" }
184
+ ]
185
+ },
177
186
  "count": 3, "offset": 0, "limit": 3
178
187
  }
179
188
  ```
@@ -191,7 +200,16 @@ curl -sf -X POST "$BASE/api/store/shipping-options/$SO_ID/calculate" \
191
200
  - **Errors** — none beyond the standard envelope (empty list when nothing
192
201
  is enabled); initiate 400s on unknown/disabled/foreign method ids and
193
202
  `payment_method_not_in_region` for an unclaimed region.
194
- - **SDK** — `checkout.listPaymentProviders(client, {region_id, cart_id})`.
203
+ - **SDK** — `checkout.listPaymentProviders(client, {region_id, cart_id,
204
+ shipping_option_id})`.
205
+ - **Delivery-dependent payment rules** — selecting a delivery option is
206
+ remembered on the cart before the atomic Buy call. The initial listing's
207
+ `payment_providers_by_shipping_option` map is evaluated by the server from
208
+ the same cart context and live rules. `CheckoutClient` selects the mapped
209
+ list synchronously on every carrier click; older fallback responses are
210
+ discarded, and remembered choices are serialized with latest-intent wins.
211
+ The completion guard repeats the decision against the attached shipping
212
+ method.
195
213
  - **Components** — payment picker.
196
214
  - **Settings** — Stripe connects in Settings, Payments (provisions
197
215
  `pp_stripe` + credentials); payment methods live in the same screen
@@ -433,11 +433,15 @@ code-first through the error-copy maps, never raw API strings.
433
433
  `customers.updateMe` (best-effort profile sync incl. Cartbase's first-class
434
434
  `company_name`/`company_eik`), `checkout.calculateShippingOption`
435
435
  (calculated-rate forward-compat), `checkout.prepareCheckout`,
436
- `checkout.syncPaymentAmount` (exposed + fired on payment-tab switch with
437
- the new `provider_id`), `checkout.refreshPaymentIfTerminal` (exposed —
438
- call REACTIVELY from Elements `loaderror` only), `carts.completeCart`.
439
- - **Props contract** `{client, cart, customer, availableShippingMethods,
440
- availablePaymentMethods, countryCode?, countries?, paymentMethodFilter?,
436
+ `checkout.syncPaymentAmount` (exposed + fired on payment-tab switch with
437
+ the new `provider_id`), `checkout.refreshPaymentIfTerminal` (exposed —
438
+ call REACTIVELY from Elements `loaderror` only),
439
+ the server-evaluated payment availability map from
440
+ `checkout.listPaymentProviders` so delivery-option payment rules update
441
+ synchronously; a prospective-option request is the fallback for older hosts,
442
+ `carts.completeCart`.
443
+ - **Props contract** — `{client, cart, customer, availableShippingMethods,
444
+ availablePaymentMethods, paymentMethodsByShippingOption?, countryCode?, countries?, paymentMethodFilter?,
441
445
  orderConfirmedPath?, onOrderPlaced?,
442
446
  resolveTrackingMetadata?, logError?}`. `countries` is OPTIONAL and is an
443
447
  override: omit it and the hook calls `GET /api/store/countries` itself, so
@@ -471,7 +475,8 @@ code-first through the error-copy maps, never raw API strings.
471
475
  fetches `listShippingOptions`/`listPaymentProviders` (+ customer, +
472
476
  `getIntegrationsConfig().cod`) and passes them down.
473
477
  - **Props contract** — hook options + `showGiftCards?`,
474
- `logoByFulfillmentOptionId?`, Stripe `appearance`/`fonts`, `onCartChange?`
478
+ `showSecureCheckout?`, `paymentMarks?`, `termsContent?`,
479
+ `logoByFulfillmentOptionId?`, Stripe `appearance`/`fonts`, `onCartChange?`
475
480
  (receives every decorated cart from summary mutations; the layout also
476
481
  re-runs `syncPaymentAmount` on those).
477
482
  - **Mount rules** — `"use client"`; inside `CheckoutProvider`; redirect
@@ -528,9 +533,13 @@ code-first through the error-copy maps, never raw API strings.
528
533
 
529
534
  ### `<CheckoutShippingMethodList />` — `checkout/shipping-method-list`
530
535
 
531
- - **Purpose** — radio list of shipping options with inline carrier-picker
532
- expansion, free-shipping label, optional per-carrier logos, optional
533
- read-only price preview pre-address (`previewWhenAddressNotReady`).
536
+ - **Purpose** — radio list of shipping options with inline carrier-picker
537
+ expansion, free-shipping label, optional per-carrier logos, optional
538
+ read-only price preview pre-address (`previewWhenAddressNotReady`).
539
+ - **Presentation** — visible shipping-option titles are uppercase so merchant
540
+ and carrier naming conventions do not create mixed capitalization in one
541
+ list. An unselected option keeps its card background and uses the primary
542
+ border color on hover.
534
543
  - **SDK calls** — renders `checkout.listShippingOptions(client, {cart_id})`
535
544
  rows AS SERVED (rule-filtering + `checkout_method_order` are server-side).
536
545
  - **Props contract** — hook state + `logoByFulfillmentOptionId?` as an
@@ -560,8 +569,11 @@ code-first through the error-copy maps, never raw API strings.
560
569
  - **Purpose** — THE pickup picker, every carrier, mounted by
561
570
  `CheckoutClient` when the option's adapter says its destination is an
562
571
  office or locker. Nearest three by haversine distance to the typed address, each
563
- with its distance, and a door to search the whole catalogue. The shape
564
- Alenika's checkout has carried since it was built.
572
+ with its distance, and a door to search the whole catalogue. The shape
573
+ Alenika's checkout has carried since it was built. Primary office and locker
574
+ names render uppercase across carriers; secondary addresses retain their
575
+ source casing. Point rows stay on the card surface and show only a primary
576
+ border on hover.
565
577
  - **SDK calls** — `integrations.listPickupPoints(client, provider, {mode,
566
578
  catalogue: true, locale})`, one door for all carriers. The complete eligible
567
579
  catalogue is cached per SDK client, carrier, mode and language for ten
@@ -630,7 +642,12 @@ code-first through the error-copy maps, never raw API strings.
630
642
  is on screen from arrival and the Buy button carries the gate, naming the
631
643
  missing step; pass true for the older dimmed-until-delivery flow),
632
644
  `beforePaymentButton?` slot, `total` (pass `optimisticTotal`),
633
- `nothingToPay?` (pass the hook's), `logError?`.
645
+ `nothingToPay?` (pass the hook's), `paymentMarks?` (the ordered result of
646
+ `getPaymentMarks`), `termsContent?` (the store's own linked policy notice),
647
+ `logError?`. Payment tabs follow the server's checkout-method order. The
648
+ offline tab prints the configured method name and instructions; the
649
+ connected processor tab shows up to three enabled payment marks and a
650
+ `+N` remainder.
634
651
  - **An order that asks for no money** (2026-09-18) — with `nothingToPay` the
635
652
  rail is replaced by one line saying the order is covered and the button
636
653
  becomes **Complete order**, carrying no amount and waiting on no card
@@ -663,7 +680,7 @@ code-first through the error-copy maps, never raw API strings.
663
680
 
664
681
  - **Purpose** — items (flat rows, READ-ONLY), promo + gift-card widgets,
665
682
  totals breakdown (subtotal / shipping / COD fee / discount / total +
666
- gift-card tender rows UNDER the unchanged total), secure badge.
683
+ gift-card tender rows UNDER the unchanged total), optional secure badge.
667
684
  `LineItemCard` is the standalone card variant.
668
685
  - **A CHECKOUT IS A CHECKOUT** (2026-09-18) — the rows carry no quantity
669
686
  control and call no cart endpoint. The order is stated here and changed in
@@ -686,7 +703,8 @@ code-first through the error-copy maps, never raw API strings.
686
703
  `tax_total`, `total`, `gift_card_total`, `gift_card_remainder`.
687
704
  - **Props contract** — `{client, cart, optimisticShippingCost,
688
705
  onOptimisticShippingClear?, optimisticMethodFee?,
689
- onOptimisticMethodFeeClear?, methodFeeLabel?, showGiftCards?,
706
+ onOptimisticMethodFeeClear?, methodFeeLabel?, showGiftCards?,
707
+ showSecureCheckout?,
690
708
  onCartChange?}`. Optimistic values clear automatically once the server
691
709
  cart catches up.
692
710
  - **The numbers** — every figure comes from `checkoutTotals`
@@ -713,7 +731,9 @@ code-first through the error-copy maps, never raw API strings.
713
731
  `<MobileOrderSummaryBody />`: the items with their quantity pills, the
714
732
  code and gift-card doors, then subtotal, shipping, the method fee,
715
733
  discount, VAT and the gift-card tender pair. The grand total is not
716
- repeated inside, because the bar's own row carries it open or closed.
734
+ repeated inside, because the bar's own row carries it open or closed.
735
+ The top bar and its open body use the normal card surface, preserving the
736
+ contrast of item copy, widgets and totals on small screens.
717
737
  - **Mount rules** — `CheckoutClient` mounts both (the bottom one through
718
738
  `beforePaymentButton`, with `hideDiscount`, beside an always-open
719
739
  `DiscountSection`, because a code field two taps deep inside a closed bar
@@ -731,10 +751,11 @@ code-first through the error-copy maps, never raw API strings.
731
751
 
732
752
  ### `<DiscountSection />` — `checkout/discount-section`
733
753
 
734
- - **Purpose** — promo-code input + applied-promotion list (percentage or
735
- fixed amount display). Collapsed behind its own door by default;
736
- `alwaysOpen` renders the field itself, which is what the checkout passes
737
- on a phone.
754
+ - **Purpose** — promo-code input + applied-promotion list (percentage or
755
+ fixed amount display). Collapsed behind its own door by default;
756
+ `alwaysOpen` renders the field itself, which is what the checkout passes
757
+ on a phone. The door and open field use bounded grids: the action never
758
+ wraps or escapes the card, and the supporting line is hidden below 380px.
738
759
  - **SDK calls** — `POST /api/store/carts/:id/promotions {promo_codes}`
739
760
  via the client transport (additive apply; the carts SDK module ships no
740
761
  wrapper for this route yet — see [carts.md](carts.md) for the cart
@@ -748,9 +769,10 @@ code-first through the error-copy maps, never raw API strings.
748
769
 
749
770
  ### `<GiftCardSection />` — `checkout/gift-card-section`
750
771
 
751
- - **Purpose** — gift-card code input + applied-cards chips (masked
752
- `••••last4`, per-card live coverage, remove). Cartbase-new — no legacy
753
- equivalent.
772
+ - **Purpose** — gift-card code input + applied-cards chips (masked
773
+ `••••last4`, per-card live coverage, remove). Cartbase-new — no legacy
774
+ equivalent. Its narrow-screen door follows the same bounded grid as the
775
+ promo door, so the label cannot turn the action into a four-line row.
754
776
  - **SDK calls** — `giftCards.applyGiftCard` / `giftCards.removeGiftCard`
755
777
  ([gift-cards.md](gift-cards.md)). Renders `cart.gift_cards[]` /
756
778
  `gift_card_total` / `gift_card_remainder` — server truth, no client
@@ -9,7 +9,7 @@
9
9
  "typecheck": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@cartbase/storefront": "^0.23.0",
12
+ "@cartbase/storefront": "^0.24.1",
13
13
  "next": "16.2.4",
14
14
  "react": "19.2.4",
15
15
  "react-dom": "19.2.4"
@@ -17,10 +17,12 @@ export function CheckoutPageClient({
17
17
  cart,
18
18
  shippingOptions,
19
19
  paymentProviders,
20
+ paymentMethodsByShippingOption,
20
21
  }: {
21
22
  cart: Cart
22
23
  shippingOptions: StoreShippingOption[]
23
24
  paymentProviders: StorePaymentEntry[]
25
+ paymentMethodsByShippingOption?: Record<string, StorePaymentEntry[]>
24
26
  }) {
25
27
  const router = useRouter()
26
28
 
@@ -32,6 +34,7 @@ export function CheckoutPageClient({
32
34
  customer={null}
33
35
  availableShippingMethods={shippingOptions}
34
36
  availablePaymentMethods={paymentProviders}
37
+ paymentMethodsByShippingOption={paymentMethodsByShippingOption}
35
38
  // NO COUNTRY LIST HERE, on purpose (2026-09-13). This file is the
36
39
  // seed for every scaffolded store, and it used to pass
37
40
  // `countries={[{iso_2:"bg", display_name:"Bulgaria"}]}` with
@@ -61,8 +61,11 @@ async function Checkout() {
61
61
  return (
62
62
  <CheckoutPageClient
63
63
  cart={cart}
64
- shippingOptions={shippingOptions.shipping_options}
65
- paymentProviders={paymentProviders.payment_providers}
64
+ shippingOptions={shippingOptions.shipping_options}
65
+ paymentProviders={paymentProviders.payment_providers}
66
+ paymentMethodsByShippingOption={
67
+ paymentProviders.payment_providers_by_shipping_option
68
+ }
66
69
  />
67
70
  )
68
71
  }