create-cartbase 0.1.25 → 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.25",
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
@@ -529,9 +533,13 @@ code-first through the error-copy maps, never raw API strings.
529
533
 
530
534
  ### `<CheckoutShippingMethodList />` — `checkout/shipping-method-list`
531
535
 
532
- - **Purpose** — radio list of shipping options with inline carrier-picker
533
- expansion, free-shipping label, optional per-carrier logos, optional
534
- 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.
535
543
  - **SDK calls** — renders `checkout.listShippingOptions(client, {cart_id})`
536
544
  rows AS SERVED (rule-filtering + `checkout_method_order` are server-side).
537
545
  - **Props contract** — hook state + `logoByFulfillmentOptionId?` as an
@@ -561,8 +569,11 @@ code-first through the error-copy maps, never raw API strings.
561
569
  - **Purpose** — THE pickup picker, every carrier, mounted by
562
570
  `CheckoutClient` when the option's adapter says its destination is an
563
571
  office or locker. Nearest three by haversine distance to the typed address, each
564
- with its distance, and a door to search the whole catalogue. The shape
565
- 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.
566
577
  - **SDK calls** — `integrations.listPickupPoints(client, provider, {mode,
567
578
  catalogue: true, locale})`, one door for all carriers. The complete eligible
568
579
  catalogue is cached per SDK client, carrier, mode and language for ten
@@ -720,7 +731,9 @@ code-first through the error-copy maps, never raw API strings.
720
731
  `<MobileOrderSummaryBody />`: the items with their quantity pills, the
721
732
  code and gift-card doors, then subtotal, shipping, the method fee,
722
733
  discount, VAT and the gift-card tender pair. The grand total is not
723
- 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.
724
737
  - **Mount rules** — `CheckoutClient` mounts both (the bottom one through
725
738
  `beforePaymentButton`, with `hideDiscount`, beside an always-open
726
739
  `DiscountSection`, because a code field two taps deep inside a closed bar
@@ -738,10 +751,11 @@ code-first through the error-copy maps, never raw API strings.
738
751
 
739
752
  ### `<DiscountSection />` — `checkout/discount-section`
740
753
 
741
- - **Purpose** — promo-code input + applied-promotion list (percentage or
742
- fixed amount display). Collapsed behind its own door by default;
743
- `alwaysOpen` renders the field itself, which is what the checkout passes
744
- 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.
745
759
  - **SDK calls** — `POST /api/store/carts/:id/promotions {promo_codes}`
746
760
  via the client transport (additive apply; the carts SDK module ships no
747
761
  wrapper for this route yet — see [carts.md](carts.md) for the cart
@@ -755,9 +769,10 @@ code-first through the error-copy maps, never raw API strings.
755
769
 
756
770
  ### `<GiftCardSection />` — `checkout/gift-card-section`
757
771
 
758
- - **Purpose** — gift-card code input + applied-cards chips (masked
759
- `••••last4`, per-card live coverage, remove). Cartbase-new — no legacy
760
- 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.
761
776
  - **SDK calls** — `giftCards.applyGiftCard` / `giftCards.removeGiftCard`
762
777
  ([gift-cards.md](gift-cards.md)). Renders `cart.gift_cards[]` /
763
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.24.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
  }