Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bebe952
🔧 chore: add build:watch script to react-components
Jun 5, 2026
8d2b546
🔧 chore: add build:watch to core, hooks and root workspace
Jun 5, 2026
62113c3
🔒 fix(security): add overrides for high/critical CVEs on v5.0.0
Jun 8, 2026
21962c9
refactor: extract shared address form logic into reusable hooks
Jun 8, 2026
93eb485
fix: remove inline arrow wrappers in providerValues to prevent infini…
Jun 8, 2026
dd1164e
test: add real rapid-form integration test for BillingAddressForm val…
Jun 8, 2026
71116c3
fix: preserve all address fields when pre-filling from API and on use…
Jun 8, 2026
2978cc7
fix: dispatch input event in setValue and resetField so rapid-form ca…
Jun 8, 2026
a49883d
fix: call setValue when state is selected from AddressStateSelector d…
Jun 8, 2026
29d942a
fix: pre-fill state code when country is set for the first time in Ad…
Jun 8, 2026
a81faea
fix: read text input DOM value as fallback when country first detecte…
Jun 9, 2026
03f9adb
fix: expose address reducer values in BillingAddressFormContext/Shipp…
Jun 9, 2026
bed4be6
fix: resolve DTS build error TS4111 for billing/shipping address redu…
Jun 9, 2026
7d9e860
feat: add errorMode prop to BillingAddressForm and ShippingAddressForm
Jun 9, 2026
0cd9b39
test: add coverage tests for errorMode, AddressStateSelector shipping…
Jun 9, 2026
1b8fa9b
test: achieve 100% branch coverage on AddressStateSelector
Jun 9, 2026
5c0f4c9
fix: prevent infinite re-render loop in Shipments component
Jun 9, 2026
d24a1b8
fix: prevent infinite re-render loop in Shipment component
Jun 9, 2026
25a904d
fix: prevent infinite re-render loop in Shipment autoSelect flow
Jun 9, 2026
26ac678
fix: prevent infinite re-render loop in PaymentGateway
Jun 9, 2026
59f490a
fix: prevent infinite re-render loop in PaymentMethodsContainer
Jun 9, 2026
aad6827
fix: move fetchOrder call from useMemo to useEffect in useOrderState
Jun 9, 2026
3552944
feat: make PaymentMethod standalone, deprecate PaymentMethodsContainer
Jun 9, 2026
f3923c0
test: add PaymentMethodsContainer and PaymentMethod specs
Jun 9, 2026
2432677
test: add 100% coverage tests for PaymentMethod, PaymentMethodsContai…
Jun 9, 2026
b5cb599
chore: remove react-doctor workflow and fix pkg-pr-new build step
Jun 9, 2026
516f567
test: add 100% coverage for PlaceOrder, PrivacyAndTermsCheckbox, useP…
Jun 10, 2026
37a2f8a
fix: PlaceOrderButton stays disabled when errors clear but privacy/te…
Jun 10, 2026
5ee6a5b
fix: PlaceOrderButton stays disabled when no payment selected or erro…
Jun 10, 2026
9b150eb
fix: stabilize setPaymentRef and other callbacks in PaymentMethodsCon…
Jun 10, 2026
39ce2e6
fix: PlaceOrderButton must not enable when no payment method is selected
Jun 10, 2026
438079b
fix: AddressCountrySelector always shows pre-filled country
Jun 10, 2026
313f2fd
fix: revert unnecessary setValue empty call on country selector mount
Jun 10, 2026
8f61803
fix: normalize null value to empty string in BaseSelect
Jun 10, 2026
b333cd0
fix: preserve user selection in BaseSelect when parent oscillates bet…
Jun 10, 2026
4e1e1de
fix: keep BaseSelect uncontrolled with layout-effect sync
Jun 11, 2026
242e64a
fix: prevent duplicate payment source creation
Jul 1, 2026
eb3f472
chore: add typecheck script scoped to shipped src
Jul 2, 2026
2f87042
fix: add root build script so pre-commit hook resolves
Jul 2, 2026
0c8597e
fix: make core integration tests actually run against the test org
Jul 2, 2026
5b78b1a
refactor: continue standalone payment components work, drop examples app
Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pgk-pr-new.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Build elements 🛠
run: pnpm build
run: pnpm --filter @commercelayer/core --filter @commercelayer/hooks --filter @commercelayer/react-components build

- name: Publish 🚀 pkg.pr.new
run: |
Expand Down
36 changes: 36 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# React Components — Payment

This context covers the React components and state that let a storefront select a payment method and attach a payment source to an order during checkout.

## Language

**Payment Method**:
A selectable payment option on an order (a configured gateway option, e.g. "Stripe"). Backed by the `payment_method` resource.
_Avoid_: gateway (as a synonym), payment type

**Payment Source**:
The concrete payment instrument record attached to a single order (e.g. `stripe_payment`, `adyen_payment`, `wire_transfer`). Created per order from the selected Payment Method.
_Avoid_: payment, card, token

**Customer Payment Source**:
A Payment Source saved to a Customer for reuse across orders (a stored card). Selected via the order's `_customer_payment_source_id`.
_Avoid_: saved card (informal), wallet

**Payment Gateway**:
In this codebase, the React component that wires a specific gateway's UI/SDK and drives Payment Source creation for that gateway (e.g. `StripeGateway`, `AdyenGateway`).
_Avoid_: using "gateway" to mean the Payment Method

## Relationships

- An **Order** has at most one **Payment Method** and one **Payment Source**
- A **Payment Source** is created from the selected **Payment Method**
- A **Customer Payment Source** belongs to a **Customer**; selecting one sets the **Order**'s Payment Source

## Example dialogue

> **Dev:** "When the shopper picks Stripe, do we create the **Payment Source** in the `StripePayment` component?"
> **Domain expert:** "No — Stripe has no dedicated creation component. The `StripeGateway`/`PaymentGateway` effect creates the **Payment Source** once the **Payment Method** is selected. Adyen and Braintree, by contrast, create it inside their own components."

## Flagged ambiguities

- "set payment source" was used to mean both the async operation that creates/attaches a Payment Source *and* the reducer action that stores it in state — resolved: the operation is `setPaymentSource(...)`, the reducer action is `dispatch({ type: "setPaymentSource" })`.
19 changes: 19 additions & 0 deletions docs/adr/0001-coalesce-payment-source-requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Coalesce concurrent payment-source requests

## Context

`setPaymentSource` (in `PaymentMethodReducer.ts`) is a plain async function driven by React effects — notably the `PaymentGateway` effect, which fires it fire-and-forget from a 23-dependency effect. Before the first `create`/`update`/`updateOrder` request resolves, both `paymentSource` (context state) and `order.payment_source` are still null, so any dependency change re-runs the effect and fires a **second** request. On the Stripe / single-payment-method / new-source flow this produced two `stripe_payments.create` calls and two payment source records.

## Decision

De-duplicate genuinely-concurrent calls with a module-level `Map<string, Promise>` keyed by `` `${order.id}:${paymentResource}:${customerPaymentSourceId ?? paymentSourceId ?? 'create'}` ``. The first call stores its in-flight promise; concurrent calls with the same key return that same promise; the entry is deleted in a `finally` once it settles. This coalesces duplicates within a burst without permanently memoizing, so a later legitimate re-create still runs. It backs up the narrower in-flight `useRef` guard in the `PaymentGateway` effect and protects every gateway, not just Stripe.

## Considered options

- **Effect-only `useRef` guard** — kept as the first line of defense, but too narrow: only protects the `PaymentGateway` caller, not other gateways or future callers.
- **Idempotency check against `order.payment_source`** — unreliable: the order is stale until `getOrder` refreshes after the first request.
- **Hashing `attributes` into the key** — rejected as fragile; attributes are effectively constant for a given order + resource + path within a burst.

## Consequences

The reducer module now holds mutable global state, which reads as surprising for an otherwise-stateless function — this ADR is the "why." The key deliberately separates the create, update/retrieve, and saved-source (`updateOrder`) paths so coalescing never merges semantically different operations.
8 changes: 0 additions & 8 deletions examples/_app.tsx

This file was deleted.

226 changes: 0 additions & 226 deletions examples/cart.tsx

This file was deleted.

Loading
Loading