Skip to content

feat(receive): implement C12 end-to-end receive payment flow - #36

Merged
JoelVR17 merged 2 commits into
Ding-Payments:developfrom
davieslennox0:feat/c12-receive-payment-flow
Jul 20, 2026
Merged

feat(receive): implement C12 end-to-end receive payment flow#36
JoelVR17 merged 2 commits into
Ding-Payments:developfrom
davieslennox0:feat/c12-receive-payment-flow

Conversation

@davieslennox0

@davieslennox0 davieslennox0 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Closes #34

Description

Implements the complete C12 receive payment flow across all 12 atomic tasks (CLI-061–CLI-072). Full receiver journey from amount entry through NFC broadcast, waiting, and success/failure outcomes, including USDC trustline gating, session timer coordination, analytics instrumentation, and architecture documentation.

Atomic Tasks Delivered

  • CLI-061: ReceiveHomeView — amount input, asset selector, validation gating
  • CLI-062: receiveAmountSchema — Zod schema with USDC precision guard and localized errors
  • CLI-063: PaymentRequestBuilder — wraps createPaymentRequest with 5-min expiry default
  • CLI-064: ReceiveListeningView — NFC broadcast on mount, countdown, cancel
  • CLI-065: useReceivePayment — FSM orchestrator (idle→preparing→broadcasting→waiting→success/failed/cancelled)
  • CLI-066: WaitingForPaymentView — progress display with 60s timeout
  • CLI-067: ReceiveSuccessView — summary and reset/home actions
  • CLI-068: ReceiveFailedView — error-specific messages, retry and change-amount paths
  • CLI-069: ReceiveSessionManager — safe timer coordination with cancelAll()
  • CLI-070: TrustlineService — USDC trustline check via Horizon before listen
  • CLI-071: Analytics events — RECEIVE_STARTED/BROADCAST/WAITING/COMPLETED/FAILED/CANCELLED with sanitized buckets
  • CLI-072: docs/receive-flow.md — state diagram, file map, timeout model, error matrix, analytics mapping

How Has This Been Tested?

  • Unit tests: receiveAmountSchema, PaymentRequestBuilder, ReceiveSessionManager, TrustlineService
  • FSM orchestrator tested with mocked NFC, trustline, session, and analytics
  • All existing tests pass (69 total), typecheck, lint, and prettier checks green

Checklist

  • My code follows the coding conventions of this project
  • I have added/updated tests if needed
  • I have updated documentation
  • My changes generate no new warnings or errors

@davieslennox0

davieslennox0 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Two design decisions worth a reviewer's attention

Both go slightly beyond the literal task spec, made to keep the implementation correct against this repo's architecture and CI gates.

1. Shared orchestrator via React Context (not a per-screen hook)

The receive flow spans multiple Expo Router screens ((tabs)/receivereceive/listeningreceive/waitingreceive/success/failed). Expo Router unmounts/remounts screens on navigation, so calling useReceivePayment() independently in each view would spin up a fresh FSM instance per screen and silently reset state (payment request, current phase, timers) on every transition — breaking the flow.

To fix this, useReceivePayment is wrapped in a ReceivePaymentProvider mounted once in src/app/_layout.tsx, above all routes. Views consume shared state via useReceivePaymentContext() rather than calling the hook directly. This keeps a single orchestrator instance alive across the whole journey.

2. Derived waiting state instead of setState inside an effect

This repo's ESLint config enforces React Compiler rules (react-hooks/set-state-in-effect, react-hooks/refs), and npm run lint is a CI gate. My first pass synced the NFC-writer success → waiting transition through a useEffect + setState, and exposed values by reading ref.current during render — both are flagged by those rules.

The final version instead:

  • Derives waiting during render: phase === 'broadcasting' && nfcStatus === 'success' ? 'waiting' : phase, so no effect-driven state write is needed for the transition.
  • Exposes paymentRequest / error / txHash via useState rather than refs read at render time.
  • Keeps the single remaining effect side-effect-only (it emits the RECEIVE_WAITING analytics event exactly once per transition; it never calls a state setter).

Scope note

waiting → success is driven by the 60s wait-timeout plus an explicit confirmSuccess(txHash) call. Actual on-chain settlement detection (balance polling) is intentionally deferred to a future iteration, matching the MVP scope documented in WaitingForPaymentView and docs/receive-flow.md.

- CLI-061: ReceiveHomeView with amount input and asset selector
- CLI-062: receiveAmountSchema with Zod and USDC precision guard
- CLI-063: PaymentRequestBuilder wrapping createPaymentRequest
- CLI-064: ReceiveListeningView with NFC broadcast and countdown
- CLI-065: useReceivePayment FSM orchestrator hook
- CLI-066: WaitingForPaymentView with 60s timeout
- CLI-067: ReceiveSuccessView
- CLI-068: ReceiveFailedView with error-specific messages
- CLI-069: ReceiveSessionManager with safe timer coordination
- CLI-070: TrustlineService USDC trustline check via Horizon
- CLI-071: Receive analytics events added to AnalyticsEvents
- CLI-072: docs/receive-flow.md with state diagram and references
- Route files: /receive/listening, /waiting, /success, /failed
- README linked to receive-flow.md

Closes Ding-Payments#34
@davieslennox0
davieslennox0 force-pushed the feat/c12-receive-payment-flow branch from 6c72848 to 5591fbd Compare July 20, 2026 07:44
@JoelVR17
JoelVR17 merged commit 320b8f4 into Ding-Payments:develop Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[C12] Receive payment flow (end-to-end)

2 participants