@nokinc-flur/sdk 1.1.4 → 1.1.5
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 +229 -229
- package/dist/index.cjs +833 -788
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1289 -1030
- package/dist/index.d.ts +1289 -1030
- package/dist/index.js +832 -788
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
+
```
|