@nokinc-flur/sdk 1.1.4 → 2.0.0

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/README.md CHANGED
@@ -1,229 +1,229 @@
1
- # flur-sdk (Sprint 1 / Week 1)
2
-
3
- Typed client scaffold aligned to backend OpenAPI.
4
-
5
- ## Install (local dev)
6
-
7
- ```bash
8
- pnpm install --frozen-lockfile
9
- pnpm test
10
- pnpm build
11
- ```
12
-
13
- ## Generate types (optional)
14
-
15
- This repo includes a minimal OpenAPI file at `openapi/flur.openapi.json`.
16
-
17
- ```bash
18
- pnpm gen
19
- ```
20
-
21
- ## E2E test against a running backend (optional)
22
-
23
- 1. Start backend locally (see backend README)
24
- 2. Run (E2E executes when RUN_E2E=1 or FLUR_BASE_URL is set):
25
-
26
- ```bash
27
- FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 pnpm test
28
- ```
29
-
30
- Contract-focused E2E only:
31
-
32
- ```bash
33
- FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1 pnpm test:contract
34
- ```
35
-
36
- Optional envs for full send-money E2E coverage:
37
-
38
- - `FLUR_E2E_SESSION_TOKEN` (required for account/transactions/recipient)
39
- - `FLUR_E2E_RECIPIENT_IDENTIFIER` (required for recipient resolve / transfer)
40
- - `FLUR_E2E_SEND_AUTH_TOKEN` (required for transfer create)
41
- - `FLUR_E2E_DEVICE_ID` (required for transfer create)
42
- - `FLUR_E2E_AMOUNT_MINOR` (optional, default `100`)
43
- - `FLUR_E2E_CURRENCY` (optional, default `NGN`)
44
-
45
- Generate real E2E auth inputs against deployed backend:
46
-
47
- ```bash
48
- # Step 1: start onboarding and get requestId
49
- pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx
50
-
51
- # Step 2: rerun with OTP/silent-auth code (and optional recipient)
52
- pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx --code 123456 --recipient +23481xxxxxxx
53
-
54
- # Script prints PowerShell env exports, then run:
55
- pnpm test:contract
56
- ```
57
-
58
- ## Onboarding flow methods
59
-
60
- ```ts
61
- import { FlurClient } from '@nokinc-flur/sdk';
62
-
63
- const client = new FlurClient({ baseUrl: 'https://api.example.com' });
64
-
65
- const started = await client.onboardingStart({
66
- phoneE164: '+14155550123',
67
- appInstanceId: 'app-instance-1',
68
- platform: 'ios',
69
- });
70
- // started: { requestId, checkUrl?, expiresInSec, fallback }
71
-
72
- const completed = await client.onboardingComplete({
73
- requestId: started.requestId,
74
- code: '123456',
75
- appInstanceId: 'app-instance-1',
76
- });
77
- // completed: { sessionToken, userId, restricted, risk_reasons }
78
- ```
79
-
80
- ## Auth + device security methods
81
-
82
- ```ts
83
- const registered = await client.registerDevice(
84
- {
85
- userId: completed.userId,
86
- appInstanceId: 'app-instance-1',
87
- platform: 'ios',
88
- networkSignals: { ip: '1.2.3.4' },
89
- },
90
- { accessToken: completed.sessionToken },
91
- );
92
- // { deviceId, fingerprintHash, driftScore, trustState, stepUpRequired }
93
-
94
- const refreshed = await client.authRefresh({
95
- userId: completed.userId,
96
- refreshToken: completed.sessionToken,
97
- appInstanceId: 'app-instance-1',
98
- fingerprintHash: registered.fingerprintHash,
99
- });
100
- // { refreshToken, stepUpRequired }
101
-
102
- await client.pinSet(
103
- { userId: completed.userId, pin: '123456' },
104
- { accessToken: completed.sessionToken },
105
- );
106
- await client.pinVerify(
107
- { userId: completed.userId, pin: '123456' },
108
- { accessToken: completed.sessionToken },
109
- );
110
- await client.authLogout(
111
- { userId: completed.userId, refreshToken: refreshed.refreshToken },
112
- { accessToken: refreshed.refreshToken },
113
- );
114
- ```
115
-
116
- ## Send-money methods
117
-
118
- ```ts
119
- // Register per-device signing key for send authorization
120
- await client.registerSendDeviceKey(
121
- {
122
- userId: completed.userId,
123
- deviceId: registered.deviceId,
124
- publicKey: '-----BEGIN PUBLIC KEY-----...',
125
- },
126
- { accessToken: completed.sessionToken },
127
- );
128
-
129
- // Obtain challenge and verify signature to get short-lived send token
130
- const challenge = await client.createSendChallenge(
131
- {
132
- userId: completed.userId,
133
- deviceId: registered.deviceId,
134
- },
135
- { accessToken: completed.sessionToken },
136
- );
137
-
138
- const send = await client.verifySendChallenge(
139
- {
140
- userId: completed.userId,
141
- deviceId: registered.deviceId,
142
- challengeId: challenge.challengeId,
143
- signature: 'base64-signature',
144
- },
145
- { accessToken: completed.sessionToken },
146
- );
147
-
148
- // Resolve recipient and create transfer
149
- const recipient = await client.resolveRecipient(
150
- { identifier: '+14155550123' },
151
- { accessToken: completed.sessionToken },
152
- );
153
-
154
- await client.createTransfer(
155
- {
156
- recipientIdentifier: recipient.normalizedIdentifier,
157
- amountMinor: 5000,
158
- currency: 'NGN',
159
- sendAuthToken: send.sendAuthToken,
160
- },
161
- {
162
- accessToken: completed.sessionToken,
163
- deviceId: registered.deviceId,
164
- idempotencyKey: crypto.randomUUID(),
165
- },
166
- );
167
-
168
- // Account + transaction endpoints
169
- await client.accountSummary({ accessToken: completed.sessionToken });
170
- await client.listTransactions({
171
- accessToken: completed.sessionToken,
172
- limit: 20,
173
- });
174
- ```
175
-
176
- ## Error code mapping
177
-
178
- SDK maps backend error payload `code` into typed `FlurError.code` when available:
179
-
180
- - `TOKEN_REPLAYED`
181
- - `SESSION_MISMATCH`
182
- - `PIN_INVALID`
183
- - `PIN_LOCKED`
184
- - `PIN_NOT_SET`
185
- - `STEP_UP_REQUIRED`
186
-
187
- Fallback codes remain:
188
-
189
- - `HTTP_ERROR`, `NETWORK_ERROR`, `TIMEOUT`, `UNKNOWN`
190
-
191
- ## Lean prod release workflow (one-person team)
192
-
193
- PowerShell script (Windows):
194
-
195
- ```powershell
196
- # patch release (recommended for this onboarding addition)
197
- ./scripts/release.ps1 -Bump patch
198
-
199
- # exact version release (example: 1.0.1)
200
- ./scripts/release.ps1 -Version 1.0.1
201
-
202
- # include E2E run (uses default OCI API Gateway URL from release script)
203
- ./scripts/release.ps1 -Version 1.0.1 -RunE2E
204
-
205
- # include E2E run with override URL
206
- ./scripts/release.ps1 -Version 1.0.1 -RunE2E -E2EBaseUrl https://your-api.example.com
207
-
208
- # publish to npm after all checks pass
209
- ./scripts/release.ps1 -Bump patch -Publish
210
- ./scripts/release.ps1 -Version 1.0.1 -Publish
211
- ```
212
-
213
- What it does:
214
-
215
- - verifies git working tree is clean
216
- - runs `pnpm install --frozen-lockfile`
217
- - runs `pnpm lint`, `pnpm typecheck`, `pnpm test`, `pnpm build`
218
- - creates tarball via `npm pack` and smoke-tests install in a temp project
219
- - bumps version (`patch`/`minor`/`major`) and creates git tag
220
- - pushes commit and tag (`git push`, `git push --tags`)
221
- - optionally publishes to npm (`-Publish`)
222
-
223
- Release script aliases:
224
-
225
- ```bash
226
- pnpm release:patch
227
- pnpm release:minor
228
- pnpm release:major
229
- ```
1
+ # flur-sdk (Sprint 1 / Week 1)
2
+
3
+ Typed client scaffold aligned to backend OpenAPI.
4
+
5
+ ## Install (local dev)
6
+
7
+ ```bash
8
+ pnpm install --frozen-lockfile
9
+ pnpm test
10
+ pnpm build
11
+ ```
12
+
13
+ ## Generate types (optional)
14
+
15
+ This repo includes a minimal OpenAPI file at `openapi/flur.openapi.json`.
16
+
17
+ ```bash
18
+ pnpm gen
19
+ ```
20
+
21
+ ## E2E test against a running backend (optional)
22
+
23
+ 1. Start backend locally (see backend README)
24
+ 2. Run (E2E executes when RUN_E2E=1 or FLUR_BASE_URL is set):
25
+
26
+ ```bash
27
+ FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 pnpm test
28
+ ```
29
+
30
+ Contract-focused E2E only:
31
+
32
+ ```bash
33
+ FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1 pnpm test:contract
34
+ ```
35
+
36
+ Optional envs for full send-money E2E coverage:
37
+
38
+ - `FLUR_E2E_SESSION_TOKEN` (required for account/transactions/recipient)
39
+ - `FLUR_E2E_RECIPIENT_IDENTIFIER` (required for recipient resolve / transfer)
40
+ - `FLUR_E2E_SEND_AUTH_TOKEN` (required for transfer create)
41
+ - `FLUR_E2E_DEVICE_ID` (required for transfer create)
42
+ - `FLUR_E2E_AMOUNT_MINOR` (optional, default `100`)
43
+ - `FLUR_E2E_CURRENCY` (optional, default `NGN`)
44
+
45
+ Generate real E2E auth inputs against deployed backend:
46
+
47
+ ```bash
48
+ # Step 1: start onboarding and get requestId
49
+ pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx
50
+
51
+ # Step 2: rerun with OTP/silent-auth code (and optional recipient)
52
+ pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx --code 123456 --recipient +23481xxxxxxx
53
+
54
+ # Script prints PowerShell env exports, then run:
55
+ pnpm test:contract
56
+ ```
57
+
58
+ ## Onboarding flow methods
59
+
60
+ ```ts
61
+ import { FlurClient } from '@nokinc-flur/sdk';
62
+
63
+ const client = new FlurClient({ baseUrl: 'https://api.example.com' });
64
+
65
+ const started = await client.onboardingStart({
66
+ phoneE164: '+14155550123',
67
+ appInstanceId: 'app-instance-1',
68
+ platform: 'ios',
69
+ });
70
+ // started: { requestId, checkUrl?, expiresInSec, fallback }
71
+
72
+ const completed = await client.onboardingComplete({
73
+ requestId: started.requestId,
74
+ code: '123456',
75
+ appInstanceId: 'app-instance-1',
76
+ });
77
+ // completed: { sessionToken, userId, restricted, risk_reasons }
78
+ ```
79
+
80
+ ## Auth + device security methods
81
+
82
+ ```ts
83
+ const registered = await client.registerDevice(
84
+ {
85
+ userId: completed.userId,
86
+ appInstanceId: 'app-instance-1',
87
+ platform: 'ios',
88
+ networkSignals: { ip: '1.2.3.4' },
89
+ },
90
+ { accessToken: completed.sessionToken },
91
+ );
92
+ // { deviceId, fingerprintHash, driftScore, trustState, stepUpRequired }
93
+
94
+ const refreshed = await client.authRefresh({
95
+ userId: completed.userId,
96
+ refreshToken: completed.sessionToken,
97
+ appInstanceId: 'app-instance-1',
98
+ fingerprintHash: registered.fingerprintHash,
99
+ });
100
+ // { refreshToken, stepUpRequired }
101
+
102
+ await client.pinSet(
103
+ { userId: completed.userId, pin: '123456' },
104
+ { accessToken: completed.sessionToken },
105
+ );
106
+ await client.pinVerify(
107
+ { userId: completed.userId, pin: '123456' },
108
+ { accessToken: completed.sessionToken },
109
+ );
110
+ await client.authLogout(
111
+ { userId: completed.userId, refreshToken: refreshed.refreshToken },
112
+ { accessToken: refreshed.refreshToken },
113
+ );
114
+ ```
115
+
116
+ ## Send-money methods
117
+
118
+ ```ts
119
+ // Register per-device signing key for send authorization
120
+ await client.registerSendDeviceKey(
121
+ {
122
+ userId: completed.userId,
123
+ deviceId: registered.deviceId,
124
+ publicKey: '-----BEGIN PUBLIC KEY-----...',
125
+ },
126
+ { accessToken: completed.sessionToken },
127
+ );
128
+
129
+ // Obtain challenge and verify signature to get short-lived send token
130
+ const challenge = await client.createSendChallenge(
131
+ {
132
+ userId: completed.userId,
133
+ deviceId: registered.deviceId,
134
+ },
135
+ { accessToken: completed.sessionToken },
136
+ );
137
+
138
+ const send = await client.verifySendChallenge(
139
+ {
140
+ userId: completed.userId,
141
+ deviceId: registered.deviceId,
142
+ challengeId: challenge.challengeId,
143
+ signature: 'base64-signature',
144
+ },
145
+ { accessToken: completed.sessionToken },
146
+ );
147
+
148
+ // Resolve recipient and create transfer
149
+ const recipient = await client.resolveRecipient(
150
+ { identifier: '+14155550123' },
151
+ { accessToken: completed.sessionToken },
152
+ );
153
+
154
+ await client.createTransfer(
155
+ {
156
+ recipientIdentifier: recipient.normalizedIdentifier,
157
+ amountMinor: 5000,
158
+ currency: 'NGN',
159
+ sendAuthToken: send.sendAuthToken,
160
+ },
161
+ {
162
+ accessToken: completed.sessionToken,
163
+ deviceId: registered.deviceId,
164
+ idempotencyKey: crypto.randomUUID(),
165
+ },
166
+ );
167
+
168
+ // Account + transaction endpoints
169
+ await client.accountSummary({ accessToken: completed.sessionToken });
170
+ await client.listTransactions({
171
+ accessToken: completed.sessionToken,
172
+ limit: 20,
173
+ });
174
+ ```
175
+
176
+ ## Error code mapping
177
+
178
+ SDK maps backend error payload `code` into typed `FlurError.code` when available:
179
+
180
+ - `TOKEN_REPLAYED`
181
+ - `SESSION_MISMATCH`
182
+ - `PIN_INVALID`
183
+ - `PIN_LOCKED`
184
+ - `PIN_NOT_SET`
185
+ - `STEP_UP_REQUIRED`
186
+
187
+ Fallback codes remain:
188
+
189
+ - `HTTP_ERROR`, `NETWORK_ERROR`, `TIMEOUT`, `UNKNOWN`
190
+
191
+ ## Lean prod release workflow (one-person team)
192
+
193
+ PowerShell script (Windows):
194
+
195
+ ```powershell
196
+ # patch release (recommended for this onboarding addition)
197
+ ./scripts/release.ps1 -Bump patch
198
+
199
+ # exact version release (example: 1.0.1)
200
+ ./scripts/release.ps1 -Version 1.0.1
201
+
202
+ # include E2E run (uses default OCI API Gateway URL from release script)
203
+ ./scripts/release.ps1 -Version 1.0.1 -RunE2E
204
+
205
+ # include E2E run with override URL
206
+ ./scripts/release.ps1 -Version 1.0.1 -RunE2E -E2EBaseUrl https://your-api.example.com
207
+
208
+ # publish to npm after all checks pass
209
+ ./scripts/release.ps1 -Bump patch -Publish
210
+ ./scripts/release.ps1 -Version 1.0.1 -Publish
211
+ ```
212
+
213
+ What it does:
214
+
215
+ - verifies git working tree is clean
216
+ - runs `pnpm install --frozen-lockfile`
217
+ - runs `pnpm lint`, `pnpm typecheck`, `pnpm test`, `pnpm build`
218
+ - creates tarball via `npm pack` and smoke-tests install in a temp project
219
+ - bumps version (`patch`/`minor`/`major`) and creates git tag
220
+ - pushes commit and tag (`git push`, `git push --tags`)
221
+ - optionally publishes to npm (`-Publish`)
222
+
223
+ Release script aliases:
224
+
225
+ ```bash
226
+ pnpm release:patch
227
+ pnpm release:minor
228
+ pnpm release:major
229
+ ```