Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
265f86a
docs: server-only cart architecture specs + ralph loop setup
field123 Mar 9, 2026
5ed4b4d
feat(ep-commerce): Phase 0 — ShopperContext foundation + server utili…
field123 Mar 9, 2026
883f88b
feat(ep-commerce): Phase 1 — cart read hooks with SWR + checkout norm…
field123 Mar 9, 2026
8fb1efe
docs: update implementation plan — Phase 1 complete, Phase 2 next
field123 Mar 9, 2026
a5a90d4
feat(ep-commerce): Phase 2 — cart mutation hooks (add, remove, update)
field123 Mar 9, 2026
5d454d9
feat(ep-commerce): Phase 3 (P3-1..P3-3) — deprecations, serverCartMod…
field123 Mar 9, 2026
a1de031
docs: update implementation plan — P3-1 through P3-3 complete, P3-4 next
field123 Mar 9, 2026
8f1e6e8
feat(ep-commerce): Phase 3 complete (P3-4, P3-5) — audit + ServerCart…
field123 Mar 9, 2026
8794289
feat(ep-commerce): composable checkout — EPCheckoutProvider (CC-P0-1)
field123 Mar 9, 2026
818d853
feat(ep-commerce): composable checkout — StepIndicator, Button, Order…
field123 Mar 9, 2026
a6f593b
feat(ep-commerce): composable checkout — form field components (CC-P1…
field123 Mar 9, 2026
592a0dd
feat(ep-commerce): composable checkout — ShippingMethodSelector + Pay…
field123 Mar 9, 2026
3f2c77c
fix(ep-commerce): tsdx build compat — inline type syntax + logger arity
field123 Mar 9, 2026
92f2958
feat(ep-commerce): checkout session Phase A — server-authoritative se…
field123 Mar 10, 2026
97748ae
feat(ep-commerce): checkout session Phase B — Clover payment adapter …
field123 Mar 10, 2026
85dcd73
feat(ep-commerce): checkout session Phase C — Stripe payment adapter …
field123 Mar 10, 2026
9e29bb7
feat(ep-commerce): checkout session Phase D — production hardening
field123 Mar 10, 2026
98e7fea
feat(ep-commerce): checkout session Phase Consumer — server entry + r…
field123 Mar 10, 2026
d45c9c1
test(ep-commerce): add missing checkout session tests + fix jest config
field123 Mar 10, 2026
a870124
fix(ep-commerce): add @jest-environment jsdom to 7 test files for mon…
field123 Mar 10, 2026
bf35f4f
docs(ep-commerce): add checkout session specs + condense implementati…
field123 Mar 10, 2026
dfb337b
fix(ep-commerce): add EP order retry logic to /pay handler
field123 Mar 10, 2026
c59ebfd
feat(ep-commerce): composable checkout Phase 1 — EPCheckoutProvider, …
field123 Mar 10, 2026
40f19d0
feat(ep-commerce): composable checkout Phases 2+3 — form field update…
field123 Mar 10, 2026
ad94c12
feat(ep-commerce): wire shopperContextData pre-population for checkou…
field123 Mar 10, 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
3 changes: 3 additions & 0 deletions .ralph/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ cd packages/plasmic-mcp && npm run typecheck # TypeScript type checking (ts

- Monorepo: platform/ (apps), packages/ (SDK), plasmicpkgs/ (code components)
- **EP Commerce components:** `plasmicpkgs/commerce-providers/elastic-path/src/`
- **Server-cart architecture (current focus):** `elastic-path/src/shopper-context/` (new directory)
- **Singleton context pattern to follow:** `elastic-path/src/bundle/composable/BundleContext.tsx`, `elastic-path/src/cart-drawer/CartDrawerContext.tsx`
- **Existing cart hooks (being replaced):** `elastic-path/src/cart/use-cart.tsx`, `use-add-item.tsx`, `use-remove-item.tsx`, `use-update-item.tsx`
- **Composable component examples:** `elastic-path/src/bundle/composable/`, `elastic-path/src/cart-drawer/`, `elastic-path/src/variant-picker/`
- **Existing hooks:** `elastic-path/src/product/use-search.tsx`, `use-product.tsx`; `elastic-path/src/site/use-categories.tsx`
- **Data normalization:** `elastic-path/src/utils/normalize.ts`
Expand Down
380 changes: 151 additions & 229 deletions .ralph/IMPLEMENTATION_PLAN.md

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions .ralph/PROMPT_build.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
0a. Study `.ralph/specs/*` with up to 500 parallel Sonnet subagents to learn the application specifications.
0a. Study `.ralph/specs/server-cart-architecture.md` and `.ralph/specs/phase-0-shopper-context.md` through `.ralph/specs/phase-3-credential-removal.md` with up to 500 parallel Sonnet subagents to learn the server-cart architecture specifications.
0b. Study @.ralph/IMPLEMENTATION_PLAN.md.
0c. For reference, the application source code is in `packages/*/src/*`, `plasmicpkgs/*/src/*`, `plasmicpkgs-dev/*`, `platform/wab/src/*`.
0c. For reference, the application source code is in `plasmicpkgs/commerce-providers/elastic-path/src/*`. Study the existing singleton context pattern in `src/bundle/composable/BundleContext.tsx` and `src/cart-drawer/CartDrawerContext.tsx` — new ShopperContext must follow this pattern.

1. Your task is to implement functionality per the specifications using parallel subagents. Follow @.ralph/IMPLEMENTATION_PLAN.md and choose the most important item to address. Before making changes, search the codebase (don't assume not implemented) using Sonnet subagents. You may use up to 500 parallel Sonnet subagents for searches/reads and only 1 Sonnet subagent for build/tests. Use Opus subagents when complex reasoning is needed (debugging, architectural decisions).
2. After implementing functionality or resolving problems, run the tests for that unit of code that was improved. Use the test commands from @.ralph/AGENTS.md for the relevant package. If functionality is missing then it's your job to add it as per the application specifications. Ultrathink.
1. Your task is to implement functionality per the server-cart specifications. Follow @.ralph/IMPLEMENTATION_PLAN.md and choose the most important incomplete item (build in phase order: P0 → P1 → P2 → P3). Before making changes, search the codebase (don't assume not implemented) using Sonnet subagents. You may use up to 500 parallel Sonnet subagents for searches/reads and only 1 Sonnet subagent for build/tests. Use Opus subagents when complex reasoning is needed.
2. After implementing functionality, run the tests. Use the test commands from @.ralph/AGENTS.md for the relevant package. All new code goes in `plasmicpkgs/commerce-providers/elastic-path/src/shopper-context/`. Ultrathink.
3. When you discover issues, immediately update @.ralph/IMPLEMENTATION_PLAN.md with your findings using a subagent. When resolved, update and remove the item.
4. When the tests pass, update @.ralph/IMPLEMENTATION_PLAN.md, then stage changed files with explicit `git add <file1> <file2> ...` (never use `git add -A`, `git add .`, or `git add -u`), then `git commit` with a message describing the changes. After the commit, `git push`.

99999. Important: When authoring documentation, capture the why — tests and implementation importance.
999999. Important: Single sources of truth, no migrations/adapters. If tests unrelated to your work fail, resolve them as part of the increment.
9999999. You may add extra logging if required to debug issues.
99999999. Keep @.ralph/IMPLEMENTATION_PLAN.md current with learnings using a subagent — future work depends on this to avoid duplicating efforts. Update especially after finishing your turn.
999999999. When you learn something new about how to run the application, update @.ralph/AGENTS.md using a subagent but keep it brief. For example if you run commands multiple times before learning the correct command then that file should be updated.
9999999999. For any bugs you notice, resolve them or document them in @.ralph/IMPLEMENTATION_PLAN.md using a subagent even if it is unrelated to the current piece of work.
99999999999. Implement functionality completely. Placeholders and stubs waste efforts and time redoing the same work.
999999999999. When @.ralph/IMPLEMENTATION_PLAN.md becomes large periodically clean out the items that are completed from the file using a subagent.
9999999999999. If you find inconsistencies in the .ralph/specs/* then use an Opus subagent with 'ultrathink' requested to update the specs.
99999999999999. IMPORTANT: Keep @.ralph/AGENTS.md operational only — status updates and progress notes belong in `IMPLEMENTATION_PLAN.md`. A bloated AGENTS.md pollutes every future loop's context.
999999999999999. IMPORTANT: Always use explicit file paths with `git add` (e.g., `git add packages/plasmic-mcp/src/server.ts`). NEVER use `git add -A`, `git add .`, or `git add -u`.
999999999. When you learn something new about how to run the application, update @.ralph/AGENTS.md using a subagent but keep it brief.
9999999999. For any bugs you notice, resolve them or document them in @.ralph/IMPLEMENTATION_PLAN.md using a subagent.
99999999999. Implement functionality completely. Placeholders and stubs waste time.
999999999999. When @.ralph/IMPLEMENTATION_PLAN.md becomes large periodically clean out completed items.
9999999999999. If you find inconsistencies in the .ralph/specs/* then use an Opus subagent with 'ultrathink' to update the specs.
99999999999999. IMPORTANT: Keep @.ralph/AGENTS.md operational only — status updates belong in IMPLEMENTATION_PLAN.md.
999999999999999. IMPORTANT: Always use explicit file paths with `git add`. NEVER use `git add -A`, `git add .`, or `git add -u`.
17 changes: 11 additions & 6 deletions .ralph/PROMPT_plan.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
0a. Study `.ralph/specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
0a. Study `.ralph/specs/server-cart-architecture.md` and `.ralph/specs/phase-0-shopper-context.md` through `.ralph/specs/phase-3-credential-removal.md` with up to 250 parallel Sonnet subagents to learn the server-cart architecture specifications.
0b. Study @.ralph/IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0c. Study `plasmicpkgs/commerce-providers/elastic-path/src/*` with up to 250 parallel Sonnet subagents to understand existing composable component patterns (bundle, cart-drawer, variant-picker), hooks, and utilities.
0d. For reference, the application source code is in `plasmicpkgs/commerce-providers/elastic-path/src/*`, `packages/*/src/*`, `plasmicpkgs-dev/*`, `platform/wab/src/*`.
0c. Study `plasmicpkgs/commerce-providers/elastic-path/src/*` with up to 250 parallel Sonnet subagents to understand existing code patterns — especially `src/cart/`, `src/checkout/composable/`, `src/utils/cart-cookie.ts`, `src/registerCommerceProvider.tsx`, `src/const.ts`, and the singleton context pattern in `src/bundle/composable/BundleContext.tsx` and `src/cart-drawer/CartDrawerContext.tsx`.
0d. For reference, the application source code is in `plasmicpkgs/commerce-providers/elastic-path/src/*`, `packages/*/src/*`, `plasmicpkgs-dev/*`.

1. Study @.ralph/IMPLEMENTATION_PLAN.md (if present; it may be incorrect) and use up to 500 Sonnet subagents to study existing source code in `plasmicpkgs/commerce-providers/elastic-path/src/*` and compare it against `.ralph/specs/*`. Use an Opus subagent to analyze findings, prioritize tasks, and create/update @.ralph/IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented. Ultrathink. Consider searching for TODO, minimal implementations, placeholders, skipped/flaky tests, and inconsistent patterns. Study @.ralph/IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
1. Study @.ralph/IMPLEMENTATION_PLAN.md (if present; it may be incorrect) and use up to 500 Sonnet subagents to study existing source code in `plasmicpkgs/commerce-providers/elastic-path/src/*` and compare it against the server-cart specs. Specifically check:
- Does `src/shopper-context/` directory exist yet? What files are in it?
- What is the current state of `src/cart/use-cart.tsx` and other cart hooks?
- How does the Symbol.for singleton context pattern work in `BundleContext.tsx`?
- What does `src/checkout/composable/EPCheckoutCartSummary.tsx` accept as props?
Use an Opus subagent to analyze findings, prioritize tasks, and create/update @.ralph/IMPLEMENTATION_PLAN.md as a bullet point list sorted by phase (P0 → P1 → P2 → P3). Ultrathink. Consider searching for TODO, placeholders, skipped tests, and incomplete implementations.

IMPORTANT: Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `packages/` and `plasmicpkgs/` as the monorepo's shared libraries for SDK packages and code component packages. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
IMPORTANT: Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Build in phase order: Phase 0 must be complete before Phase 1, etc. The primary target directory is `src/shopper-context/` (new) within the EP commerce provider package. Follow the headless Provider → Hook pattern documented in @.ralph/AGENTS.md. Per upstream merge strategy, prefer new files over modifying existing ones.

ULTIMATE GOAL: Implement all specifications in `.ralph/specs/`. The primary source code is in `plasmicpkgs/commerce-providers/elastic-path/src/`. Follow the headless Provider → Repeater composable pattern documented in @.ralph/AGENTS.md. Per upstream merge strategy, prefer new files over modifying existing ones. Consider missing elements and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at .ralph/specs/FILENAME.md. If you create a new element then document the plan to implement it in @.ralph/IMPLEMENTATION_PLAN.md using a subagent.
ULTIMATE GOAL: Implement the server-only cart architecture per `.ralph/specs/phase-0-shopper-context.md` through `.ralph/specs/phase-3-credential-removal.md`. All new code goes in `src/shopper-context/` within `plasmicpkgs/commerce-providers/elastic-path/`. Existing cart hooks in `src/cart/` are NOT modified until Phase 3 (deprecation only). If you find inconsistencies in the specs, use an Opus subagent with 'ultrathink' to update the specs.
111 changes: 111 additions & 0 deletions .ralph/specs/checkout-session-clover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Checkout Session — Clover Payment Components

> Phase B. Clover payment adapter (server-side) and 5 Plasmic components: EPCloverPayment + 4 individual card field components. Includes full 3DS2 support.

## Jobs to Be Done

- As a **Plasmic designer**, I want to drop individual Clover card field components (number, expiry, CVV, postal code) so I have full layout control over payment fields
- As a **storefront developer**, I want a Clover payment adapter that handles tokenization, charging, and 3DS flows through the session model
- As a **shopper**, I want 3D Secure authentication to work seamlessly without leaving the checkout page

## Acceptance Criteria

### Clover Payment Adapter (Server-Side)

- [ ] `cloverAdapter` implements `PaymentAdapter` interface
- [ ] `initializePayment()`: charges Clover with token + idempotency key, inspects `threeDsData.status`
- No 3DS → returns `status: "ready"` with `chargeId`
- `METHOD_FLOW` → returns `status: "requires_action"` with `actionData.type: "3ds_method"` + method data
- `CHALLENGE` → returns `status: "requires_action"` with `actionData.type: "3ds_challenge"` + challenge data
- [ ] `confirmPayment()`: calls `finalizeCloverPayment(chargeId, flowStatus)`
- Success → returns `status: "succeeded"` with `gatewayOrderId`
- `CHALLENGE` escalation → returns `status: "requires_action"` with challenge data
- `AUTHENTICATION_FAILED` → returns `status: "failed"` with error message
- [ ] Idempotency key derived from EP order ID: `clover-charge-${orderId}`
- [ ] Card declined (402 from Clover) surfaced as `status: "failed"` with "Your card was declined"
- [ ] Retry with same idempotency key on network/timeout errors (one retry)

### EPCloverPayment Component

- [ ] Props: `children` (slot), `pakmsKey`, `merchantId?`, `environment?` ("sandbox" | "production"), `className?`, `previewState?`
- [ ] DataProvider `"cloverPaymentData"`: `{ isReady, isProcessing, error, isTokenizing, is3DSActive }`
- [ ] Registers gateway name `"clover"` with EPCheckoutSessionProvider via payment registration context
- [ ] Registers `confirm` handler that tokenizes card and returns `{ token }` for `/pay`
- [ ] Internal 3DS state machine:
- On `session.payment.status === "requires_action"`, reads `actionData`
- Lazy-loads `clover3DS-sdk.js` via singleton promise
- `3ds_method`: calls `perform3DSFingerPrinting()`, waits for `executePatch` CustomEvent, calls `confirmPayment({ stage: "method", flowStatus })`
- `3ds_challenge`: calls `perform3DSChallenge()`, waits for `executePatch` CustomEvent, calls `confirmPayment({ stage: "challenge", flowStatus })`
- [ ] Creates Clover SDK elements instance from `pakmsKey`
- [ ] Provides Clover elements context to child field components
- [ ] PreviewStates: auto, ready, processing, error — with static mock field rendering
- [ ] Registration metadata with props, DataProvider, slot

### EPCloverCardNumber, EPCloverCardExpiry, EPCloverCardCVV, EPCloverCardPostalCode

- [ ] Each component renders a Clover iframe field via the Clover SDK elements instance
- [ ] Props (shared across all 4): `className?`, `placeholder?`, `inputFontFamily?`, `inputFontSize?`, `inputColor?`, `inputPadding?`, `fieldHeight?`, `fieldBorderColor?`, `fieldBorderRadius?`, `errorColor?`
- [ ] Each reads the Clover elements instance from the parent EPCloverPayment context
- [ ] Style props passed to the iframe at mount time
- [ ] In-editor preview: static div mimicking an input field (not a real iframe)
- [ ] Registration metadata for each with style props exposed

### Integration

- [ ] `EPCloverPayment` + 4 field components registered in `src/index.tsx` via `registerAll()`
- [ ] Clover adapter registered in the adapter registry
- [ ] `clover-singleton.ts` pattern for SDK lazy-loading (no duplicate script tags)
- [ ] 3DS SDK loaded only when `requires_action` is received (not at mount time)

## Happy Path

1. Designer drops `EPCloverPayment` inside `EPCheckoutSessionProvider`, adds 4 card field components as children
2. Clover SDK initializes from `pakmsKey`, card field iframes render
3. Shopper fills in card fields (data stays in Clover iframes — PCI SAQ-A)
4. Shopper clicks "Place Order" → `placeOrder()` fires
5. EPCloverPayment tokenizes card via Clover SDK → receives `cloverToken`
6. Provider sends `{ gateway: "clover", token: cloverToken }` to `/pay`
7. Server: EP checkout → EP authorize → Clover charge with token
8. If no 3DS: charge succeeds, `/pay` returns `status: "processing"`, provider calls `/confirm`, server captures EP transaction
9. If 3DS method: session returns `requires_action`, EPCloverPayment runs fingerprinting, calls `/confirm` with flow status
10. If 3DS challenge (direct or escalated): EPCloverPayment shows challenge, calls `/confirm`
11. Server captures EP transaction, session → "complete"

## Edge Cases

| Scenario | Expected Behaviour |
|----------|-------------------|
| Clover SDK fails to load | `cloverPaymentData.error` set, field components show error state |
| Card tokenization fails | Error surfaced in `cloverPaymentData.error`, Place Order re-enabled |
| 3DS method → challenge escalation | EPCloverPayment handles both stages automatically |
| 3DS AUTHENTICATION_FAILED | Session payment status → "failed", error displayed |
| 3DS SDK fails to load | Error surfaced, payment fails gracefully |
| `executePatch` event never fires (timeout) | 30-second timeout, fail with "Authentication timed out" |
| EPCloverPayment placed outside EPCheckoutSessionProvider | Console warning, component renders children but registration no-ops |
| Clover card field placed outside EPCloverPayment | Console warning, renders placeholder |
| Network error during Clover charge | Retry once with same idempotency key, then fail |
| Card declined | "Your card was declined" error, session remains "open" for retry |

## File Targets

| File | Type | Purpose |
|------|------|---------|
| `src/checkout/session/adapters/clover-adapter.ts` | New | PaymentAdapter implementation for Clover |
| `src/checkout/session/EPCloverPayment.tsx` | New | Parent component — SDK init, tokenization, 3DS |
| `src/checkout/session/EPCloverCardNumber.tsx` | New | Card number iframe field |
| `src/checkout/session/EPCloverCardExpiry.tsx` | New | Expiry iframe field |
| `src/checkout/session/EPCloverCardCVV.tsx` | New | CVV iframe field |
| `src/checkout/session/EPCloverCardPostalCode.tsx` | New | Postal code iframe field |
| `src/checkout/session/clover-context.ts` | New | Internal React context for Clover elements instance |
| `src/checkout/session/clover-3ds-sdk.ts` | New | 3DS SDK lazy-loader (singleton) |
| `src/checkout/session/__tests__/clover-adapter.test.ts` | New | Adapter unit tests |
| `src/checkout/session/__tests__/EPCloverPayment.test.tsx` | New | Component tests |
| `src/checkout/session/__tests__/EPCloverCardNumber.test.tsx` | New | Field component tests |

## Out of Scope

- Clover webhook handling
- Clover refund/void flows
- Clover saved cards / customer vault
- Clover tip/gratuity support
- Multi-tender (split payment across gateways)
Loading