@nokinc-flur/sdk 0.1.7 → 1.0.2
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/LICENSE +26 -15
- package/README.md +229 -198
- package/dist/index.cjs +2781 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2057 -0
- package/dist/index.d.ts +1773 -2
- package/dist/index.js +1918 -7
- package/dist/index.js.map +1 -1
- package/package.json +78 -50
package/LICENSE
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
Copyright (c) 2026 NokInc / Flur. All rights reserved.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
PROPRIETARY AND CONFIDENTIAL
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
of
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
5
|
+
This software (the "Software") is the confidential and proprietary information
|
|
6
|
+
of NokInc / Flur ("Licensor"). It is licensed, not sold, to authorized partners
|
|
7
|
+
and developers ("Licensee") for the sole purpose of integrating with Flur
|
|
8
|
+
services.
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
Licensee MAY:
|
|
11
|
+
* Install and use the Software in applications that consume Flur APIs under
|
|
12
|
+
a valid agreement with Licensor.
|
|
13
|
+
* Make local copies for backup and development purposes.
|
|
14
|
+
|
|
15
|
+
Licensee MAY NOT:
|
|
16
|
+
* Redistribute, sublicense, sell, lease, rent, or otherwise transfer the
|
|
17
|
+
Software or any derivative works thereof to any third party.
|
|
18
|
+
* Reverse engineer, decompile, or disassemble the Software, except to the
|
|
19
|
+
extent permitted by applicable law notwithstanding this limitation.
|
|
20
|
+
* Remove or alter any proprietary notices.
|
|
21
|
+
* Use the Software to build a competing product or service.
|
|
14
22
|
|
|
15
23
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
24
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM,
|
|
27
|
+
OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
|
|
28
|
+
|
|
29
|
+
This license terminates automatically upon any breach. Upon termination,
|
|
30
|
+
Licensee must destroy all copies of the Software in their possession.
|
|
31
|
+
|
|
32
|
+
For licensing inquiries: legal@flur.ng
|
package/README.md
CHANGED
|
@@ -1,198 +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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
await client.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
+
```
|