Skip to content

Commit c181ba0

Browse files
authored
Merge pull request #35 from YAustinXYZ/feature/c10-nfc-core
feature/c10-nfc-core
2 parents 4e1db99 + 6f4028f commit c181ba0

45 files changed

Lines changed: 1374 additions & 350 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 59 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,122 @@
1-
# Welcome to your Expo app 👋
2-
31
# Ding Payments — Mobile Client MVP
42

5-
This repository contains the core React Native application built with Expo Router and the Soroban Smart Contract SDK for the Ding Payments network.
3+
Peer-to-peer contactless (NFC) payments on Stellar with a self-custodial wallet and passkey authentication.
64

7-
## 🛠 Prerequisites
5+
## Prerequisites
86

9-
- **Node.js**: v18 or later
10-
- **Package Manager**: `npm`
11-
- **Development Target**: Physical iOS or Android device (required for NFC, passkeys, and SecureStore biometrics); simulator/emulator for UI-only work
7+
- **Node.js** v20+
8+
- **npm**
9+
- **Xcode** (iOS) or **Android Studio** (Android)
10+
- Physical NFC devices for end-to-end NFC validation
1211

13-
> ⚠️ Native Framework Limitation: This application leverages advanced hardware integrations including NFC capabilities and Passkey WebAuthn modules. These features cannot execute inside standard Expo Go. You must use an Expo Development Build (EAS Development Build) to validate NFC, passkeys, and SecureStore functionality on physical devices.
12+
> **Expo Go is not supported.** NFC, passkeys, and SecureStore require a **development build** (`npx expo run:ios` or `npx expo run:android`, or EAS dev build).
1413
15-
## 🚀 Local Development Setup
14+
## Setup
1615

17-
1. **Clone the Repository & Fetch Dependencies**
16+
1. Clone and install dependencies:
1817

1918
```bash
2019
npm install
20+
cp .env.example .env
2121
```
2222

23-
2. **Build requirements for native features**
24-
- NFC and passkey research require an Expo development build or custom native runtime.
23+
2. Build requirements for native features:
24+
2525
- Run `npx expo prebuild` and `npx expo run:android` / `npx expo run:ios` for device validation.
2626
- Use `npm run dev-client` to launch a dev-client session after native dependencies are installed.
2727

2828
## EAS Development Build
2929

30-
1. **Install and authenticate EAS CLI**
30+
1. Install and authenticate EAS CLI:
3131

3232
```bash
3333
npm install -g eas-cli
3434
eas login
3535
```
3636

37-
On first setup, link the project with `eas init`. Build profiles live in `eas.json` (`development`, `preview`, `production`).
38-
39-
2. **Create a development build**
37+
2. Create a development build:
4038

4139
```bash
4240
npm run dev:build:android
4341
# or
4442
npm run dev:build:ios
4543
```
4644

47-
Install the resulting build on a **physical device** (`.apk` on Android; iOS via internal distribution or TestFlight).
48-
49-
3. **Start the dev client**
45+
3. Start the dev client:
5046

5147
```bash
5248
npm run dev-client
5349
```
5450

55-
This runs `expo start --dev-client` and connects the installed development build to Metro.
56-
57-
4. **Native rebuild required**
58-
Rebuild and reinstall the development build after changes to:
59-
- `app.config.ts` plugins, permissions, or entitlements
60-
- native dependencies (for example `expo-dev-client`, `expo-secure-store`, `react-native-nfc-manager`, `react-native-passkey`)
61-
62-
JavaScript-only changes do not require a native rebuild.
63-
64-
5. **Expo Go limitations**
65-
Do not use Expo Go to validate NFC, passkeys, or SecureStore with biometric authentication. These flows require a development build with `expo-dev-client`.
51+
4. **Native rebuild required** after changes to `app.config.ts` plugins, permissions, or native dependencies (`expo-dev-client`, `expo-secure-store`, `react-native-nfc-manager`, `react-native-passkey`).
6652

67-
6. **Simulator vs physical device**
53+
## Quality checks (CI)
6854

69-
| Feature | Simulator / emulator | Physical device |
70-
| ------------------------ | -------------------- | --------------- |
71-
| General UI / routing | Yes | Yes |
72-
| NFC | No | Yes (required) |
73-
| Passkeys | Limited / unreliable | Yes (required) |
74-
| SecureStore + biometrics | Limited | Yes (required) |
75-
76-
Use a physical device for native capability smoke tests, including the `/c05` spike page.
77-
78-
## ✅ Quality checks (CI)
79-
80-
CI (`.github/workflows/ci-client.yml`) runs the exact same npm scripts you run locally, so a green local run means a green pipeline. Run all three before opening a PR:
55+
Run before opening a PR:
8156

8257
```bash
83-
npm run build # tsc --noEmit — type-checks the project
84-
npm run lint # expo lint (ESLint flat config + Prettier rules)
85-
npm run format # prettier --write . — auto-formats the repo
58+
npm run build
59+
npm run lint
60+
npm run test
61+
npm run format:check
8662
```
8763

88-
Helper scripts:
89-
90-
| Script | Purpose |
91-
| ------------------------------------- | ---------------------------------------------- |
92-
| `npm run build` / `npm run typecheck` | TypeScript type-check (`tsc --noEmit`) |
93-
| `npm run lint` | Report lint problems (`expo lint`) |
94-
| `npm run lint:fix` | Auto-fix lint problems |
95-
| `npm run format` | Format all files with Prettier |
96-
| `npm run format:check` | Verify formatting without writing (used by CI) |
97-
98-
Tooling config lives at the repo root: [`eslint.config.mjs`](eslint.config.mjs) (Expo flat config + Prettier) and [`.prettierrc`](.prettierrc) (`singleQuote`, `trailingComma: es5`). Editors with the ESLint and Prettier extensions pick these up automatically.
99-
10064
## C05 Spike documentation
10165

10266
- Passkey ADR: [`docs/adr-passkey-library.md`](docs/adr-passkey-library.md)
10367
- Stellar ADR: [`docs/adr-stellar-sdk.md`](docs/adr-stellar-sdk.md)
10468
- NFC ADR: [`docs/adr-nfc-library.md`](docs/adr-nfc-library.md)
10569
- Spike PoC page: open `/c05` in the app after starting the dev-client.
10670

107-
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
108-
109-
## Get started
71+
## NFC development (C10)
11072

111-
1. Install dependencies
73+
The NFC core stack lives under `src/features/nfc/`:
11274

113-
```bash
114-
npm install
115-
```
116-
117-
2. Start the app
118-
119-
```bash
120-
npx expo start
121-
```
75+
| Module | Purpose |
76+
| ---------------------------------------- | ----------------------------------------------------- |
77+
| `services/NfcService.*` | Native abstraction (support checks, sessions) |
78+
| `schemas/paymentRequest.ts` | Zod schema for `payment_request.v1` payloads |
79+
| `services/NfcPayloadCodec.ts` | Compact JSON encode/decode with size guard |
80+
| `services/NfcWriter.ts` / `NfcReader.ts` | Writer (receiver) and reader (payer) sessions |
81+
| `state/nfcSessionStore.ts` | Session state machine + `nfcActive` lock coordination |
82+
| `services/nfc-spike.ts` | Manual PoC helpers for device verification |
12283

123-
For NFC, passkeys, and SecureStore testing, use `npm run dev-client` (`expo start --dev-client`) with an installed development build—not Expo Go.
124-
125-
In the output, you'll find options to open the app in a
126-
127-
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
128-
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
129-
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
130-
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
131-
132-
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
133-
134-
## Get a fresh project
135-
136-
When you're ready, run:
84+
### Rebuild after native NFC changes
13785

13886
```bash
139-
npm run reset-project
87+
npx expo prebuild --clean
88+
npx expo run:ios
89+
# or
90+
npx expo run:android
14091
```
14192

142-
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
93+
### Smoke test on device
94+
95+
```typescript
96+
import { nfcSpikeCheckSupport } from '@/features/nfc/services/nfc-spike';
14397

144-
### Other setup steps
98+
const { supported, enabled } = await nfcSpikeCheckSupport();
99+
```
145100

146-
- To set up ESLint for linting, run `npx expo lint`, or follow our guide on ["Using ESLint and Prettier"](https://docs.expo.dev/guides/using-eslint/)
147-
- If you'd like to set up unit testing, follow our guide on ["Unit Testing with Jest"](https://docs.expo.dev/develop/unit-testing/)
148-
- Learn more about the TypeScript setup in this template in our guide on ["Using TypeScript"](https://docs.expo.dev/guides/typescript/)
101+
See [docs/adr-nfc-library.md](docs/adr-nfc-library.md) for platform constraints and payload limits (880 bytes max).
149102

150-
## Learn more
103+
## Scripts
151104

152-
To learn more about developing your project with Expo, look at the following resources:
105+
| Command | Description |
106+
| ------------------------------------- | -------------------------- |
107+
| `npm start` | Start Expo dev server |
108+
| `npm run dev-client` | Start Expo with dev-client |
109+
| `npm run build` / `npm run typecheck` | TypeScript check |
110+
| `npm test` | Run unit tests |
111+
| `npm run lint` | ESLint via Expo |
112+
| `npm run format:check` | Prettier check (CI) |
153113

154-
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
155-
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
114+
## Documentation
156115

157-
## Join the community
116+
- [Product flows & system definition](docs/ding-payments.md)
117+
- [Client MVP build plan](docs/build-plan-client-mvp.md)
118+
- [NFC library ADR](docs/adr-nfc-library.md)
158119

159-
Join our community of developers creating universal apps.
120+
## License
160121

161-
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
162-
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
122+
MIT

app.config.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
1919
infoPlist: {
2020
...(config.ios?.infoPlist ?? {}),
2121
NFCReaderUsageDescription:
22-
'Use NFC to read and write payment requests securely for Ding Payments.',
22+
'Ding Payments uses NFC to share and receive payment requests between devices.',
2323
NSFaceIDUsageDescription: 'Use Face ID to authenticate passkey operations safely.',
2424
},
2525
},
@@ -31,7 +31,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
3131
backgroundImage: './assets/images/android-icon-background.png',
3232
monochromeImage: './assets/images/android-icon-monochrome.png',
3333
},
34-
permissions: ['NFC'],
34+
permissions: ['android.permission.NFC'],
3535
predictiveBackGestureEnabled: false,
3636
},
3737
web: {
@@ -59,6 +59,14 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
5959
configureAndroidBackup: true,
6060
},
6161
],
62+
[
63+
'react-native-nfc-manager',
64+
{
65+
nfcPermission:
66+
'Ding Payments uses NFC to share and receive payment requests between devices.',
67+
includeNdefEntitlement: true,
68+
},
69+
],
6270
],
6371
experiments: {
6472
typedRoutes: true,

docs/adr-nfc-library.md

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,95 @@
1-
# ADR: NFC library selection for Expo mobile
1+
# ADR: NFC Library Selection — react-native-nfc-manager
22

3-
- Status: Accepted
4-
- Date: 2026-06-19
5-
- Related: C05, CLI-045
3+
- **Status:** Accepted
4+
- **Date:** 2026-06-19
5+
- **Related:** C05 (CLI-045), C10 (CLI-046–052)
66

77
## Context
88

9-
The app needs NFC NDEF read/write support for payment request payloads on physical devices. Expo Go cannot be used for this verification, so the runtime must be validated in a dev-client or custom native build.
9+
Ding Payments requires peer-to-peer NFC transport for payment-request payloads on iOS and Android. Expo Go does not expose native NFC APIs; a development build is mandatory.
1010

1111
## Decision
1212

13-
We will use `react-native-nfc-manager` for NFC integration.
13+
Use **react-native-nfc-manager** (v3.17+) with the official Expo config plugin.
1414

1515
### Why this library?
1616

17-
- It is the most mature React Native NFC library with both Android and iOS support.
18-
- It supports NDEF scanning and writing flows needed for payment request roundtrips.
19-
- It is compatible with Expo native builds and supports the required native permissions.
17+
- Mature NDEF read/write APIs on Android and Core NFC on iOS
18+
- Official Expo config plugin for permissions and entitlements
19+
- Active maintenance and broad community usage
20+
- Fits the abstraction layer (`NfcService`) without leaking native details to product code
2021

2122
### Alternatives considered
2223

23-
- `react-native-hce` or similar NFC libraries: considered weaker in NDEF support for both platforms.
24-
- Custom native Objective-C/Java modules: higher maintenance risk and slower validation.
25-
- NFC-only web alternatives: rejected because the client must validate native NFC behavior on devices.
24+
| Option | Rejected because |
25+
|--------|------------------|
26+
| `react-native-hce` or similar | Weaker NDEF support across both platforms |
27+
| Custom native modules | Higher maintenance; slower validation |
28+
| Expo Go only | No NFC access |
29+
| QR-only | Out of scope for C10; planned as fallback |
2630

2731
## Version pinning
2832

29-
- `react-native-nfc-manager@^3.4.0`
33+
- `react-native-nfc-manager@^3.17.2`
3034

31-
## Compatibility matrix
35+
## Platform constraints
3236

33-
- iOS: Core NFC supported on devices with NFC hardware and iOS 13+; iOS only supports NDEF tag discovery for this spike.
34-
- Android: NFC requires `android.permission.NFC`; supported devices must have NFC hardware enabled.
35-
- Expo Go: unsupported for NFC runtime validation.
37+
| Platform | Capability | Notes |
38+
|----------|------------|-------|
39+
| Android | NDEF push + tag reader mode | Primary P2P path via `setNdefPushMessage` |
40+
| iOS | Core NFC reader / NDEF write to tags | P2P limited; validate on physical hardware |
41+
| Web | Not supported | Stub returns `isSupported: false` |
42+
| Expo Go | Not supported | Requires dev build rebuild after native changes |
3643

37-
## Implementation notes
44+
## Payload limits
3845

39-
- App configuration is updated in `app.config.ts` to declare Android NFC permission and iOS NFC usage description.
40-
- The spike implementation is isolated at `src/features/nfc/services/nfc-spike.ts`.
41-
- The roundtrip payload for JSON NDEF should be capped to a safe practical limit, typically under 880 bytes.
42-
- The ADR is validated through the PoC page at `/c05` after running the Expo dev-client.
46+
- **Max NDEF payload:** 880 bytes (conservative; typical Type 2 tag usable ~888 bytes minus overhead)
47+
- **Encoding:** UTF-8 compact JSON (`application/json` MIME NDEF record)
48+
- **Schema:** `payment_request.v1` — see `src/features/nfc/schemas/paymentRequest.ts`
49+
50+
## Permissions
51+
52+
### iOS
53+
54+
- `NFCReaderUsageDescription` in Info.plist (via config plugin)
55+
- NDEF entitlement: `com.apple.developer.nfc.readersession.formats` (via `includeNdefEntitlement: true`)
56+
57+
### Android
58+
59+
- `android.permission.NFC` in AndroidManifest (via config plugin)
60+
- Minimum SDK enforced by plugin (API 31+)
61+
62+
## Session semantics (C10)
63+
64+
| Session | Timeout | Policy |
65+
|---------|---------|--------|
66+
| Writer (receiver) | 60s | Auto-cancel + resource cleanup |
67+
| Reader (payer) | 45s | Single-read per session; ignore duplicates |
4368

4469
## Validation matrix
4570

46-
- Android physical device: NFC initialization and NDEF write/read roundtrip works.
47-
- iOS physical device: NFC initialization and NDEF read/write behave as expected under Core NFC constraints.
48-
- Payload size validation: JSON payload remains below 880 bytes and roundtrip is successful on both test devices.
71+
- Android physical device: NFC initialization and NDEF write/read roundtrip works
72+
- iOS physical device: NFC read/write under Core NFC constraints
73+
- Payload size validation: JSON payload remains below 880 bytes
74+
- PoC page: `/c05` in dev-client for spike flows; C10 services in `src/features/nfc/`
75+
76+
## Rebuild requirement
4977

50-
## Manual validation note
78+
Any change to `app.config.ts` NFC plugin settings requires:
5179

52-
Use the `/c05` test page in the dev-client to execute NFC write/read flows and capture the exact read/write behavior in the ADR appendix.
80+
```bash
81+
npx expo prebuild --clean
82+
npx expo run:ios # or run:android
83+
```
5384

5485
## Rollback plan
5586

5687
If `react-native-nfc-manager` is incompatible with the Expo dev-client:
5788

58-
1. Re-evaluate with a custom `expo prebuild` workflow and explicit native module linking.
59-
2. If the library cannot be used, isolate NFC support behind a modular adapter and retain the ability to switch to a different NFC package or a pure native module.
89+
1. Re-evaluate with explicit native module linking via `expo prebuild`
90+
2. Isolate NFC behind `NfcService` adapter to swap library without UI changes
6091

61-
## Known limitations
92+
## References
6293

63-
- Payload size: JSON roundtrip payloads must be kept small to avoid tag write/read failures.
64-
- Platform differences: iOS and Android may behave differently, so the ADR must capture exact device compatibility notes.
65-
- Native build required: NFC validation is only reliable on an Expo dev-client or prebuilt binary.
66-
- iOS Core NFC only supports certain tag types and cannot run on simulator hardware.
94+
- [react-native-nfc-manager Expo wiki](https://github.com/revtel/react-native-nfc-manager/wiki/Expo-Go)
95+
- Product spec: `docs/ding-payments.md` — Proposed Payment Payload Structure

jest.setup.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
process.env.EXPO_PUBLIC_STELLAR_NETWORK = 'testnet';
2+
process.env.EXPO_PUBLIC_HORIZON_URL = 'https://horizon-testnet.stellar.org';
3+
process.env.EXPO_PUBLIC_RPC_URL = 'https://soroban-testnet.stellar.org';
4+
process.env.EXPO_PUBLIC_USDC_ISSUER = 'GBBD47IF6LWK7P7MUGHC2XLYUUXV6ZLW75PN7CHLIW2NSIW74UZEST66';

0 commit comments

Comments
 (0)