@nokinc-flur/sdk 0.1.6 → 1.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/LICENSE +32 -21
- package/README.md +100 -69
- package/dist/index.cjs +2294 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1593 -0
- package/dist/index.d.ts +1313 -2
- package/dist/index.js +1472 -6
- package/dist/index.js.map +1 -0
- package/openapi/flur.openapi.json +234 -234
- package/package.json +34 -8
package/LICENSE
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
of
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
copies
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Copyright (c) 2026 NokInc / Flur. All rights reserved.
|
|
2
|
+
|
|
3
|
+
PROPRIETARY AND CONFIDENTIAL
|
|
4
|
+
|
|
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.
|
|
9
|
+
|
|
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.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
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
|
@@ -3,32 +3,38 @@
|
|
|
3
3
|
Typed client scaffold aligned to backend OpenAPI.
|
|
4
4
|
|
|
5
5
|
## Install (local dev)
|
|
6
|
+
|
|
6
7
|
```bash
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
pnpm install --frozen-lockfile
|
|
9
|
+
pnpm test
|
|
10
|
+
pnpm build
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
## Generate types (optional)
|
|
14
|
+
|
|
13
15
|
This repo includes a minimal OpenAPI file at `openapi/flur.openapi.json`.
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
|
|
18
|
+
pnpm gen
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
## E2E test against a running backend (optional)
|
|
20
|
-
|
|
21
|
-
|
|
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
|
+
|
|
22
26
|
```bash
|
|
23
|
-
FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1
|
|
27
|
+
FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 pnpm test
|
|
24
28
|
```
|
|
25
29
|
|
|
26
30
|
Contract-focused E2E only:
|
|
31
|
+
|
|
27
32
|
```bash
|
|
28
|
-
FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1
|
|
33
|
+
FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1 pnpm test:contract
|
|
29
34
|
```
|
|
30
35
|
|
|
31
36
|
Optional envs for full send-money E2E coverage:
|
|
37
|
+
|
|
32
38
|
- `FLUR_E2E_SESSION_TOKEN` (required for account/transactions/recipient)
|
|
33
39
|
- `FLUR_E2E_RECIPIENT_IDENTIFIER` (required for recipient resolve / transfer)
|
|
34
40
|
- `FLUR_E2E_SEND_AUTH_TOKEN` (required for transfer create)
|
|
@@ -37,35 +43,36 @@ Optional envs for full send-money E2E coverage:
|
|
|
37
43
|
- `FLUR_E2E_CURRENCY` (optional, default `NGN`)
|
|
38
44
|
|
|
39
45
|
Generate real E2E auth inputs against deployed backend:
|
|
46
|
+
|
|
40
47
|
```bash
|
|
41
48
|
# Step 1: start onboarding and get requestId
|
|
42
|
-
|
|
49
|
+
pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx
|
|
43
50
|
|
|
44
51
|
# Step 2: rerun with OTP/silent-auth code (and optional recipient)
|
|
45
|
-
|
|
52
|
+
pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx --code 123456 --recipient +23481xxxxxxx
|
|
46
53
|
|
|
47
54
|
# Script prints PowerShell env exports, then run:
|
|
48
|
-
|
|
55
|
+
pnpm test:contract
|
|
49
56
|
```
|
|
50
57
|
|
|
51
58
|
## Onboarding flow methods
|
|
52
59
|
|
|
53
60
|
```ts
|
|
54
|
-
import { FlurClient } from
|
|
61
|
+
import { FlurClient } from '@nokinc-flur/sdk';
|
|
55
62
|
|
|
56
|
-
const client = new FlurClient({ baseUrl:
|
|
63
|
+
const client = new FlurClient({ baseUrl: 'https://api.example.com' });
|
|
57
64
|
|
|
58
65
|
const started = await client.onboardingStart({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
phoneE164: '+14155550123',
|
|
67
|
+
appInstanceId: 'app-instance-1',
|
|
68
|
+
platform: 'ios',
|
|
62
69
|
});
|
|
63
70
|
// started: { requestId, checkUrl?, expiresInSec, fallback }
|
|
64
71
|
|
|
65
72
|
const completed = await client.onboardingComplete({
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
requestId: started.requestId,
|
|
74
|
+
code: '123456',
|
|
75
|
+
appInstanceId: 'app-instance-1',
|
|
69
76
|
});
|
|
70
77
|
// completed: { sessionToken, userId, restricted, risk_reasons }
|
|
71
78
|
```
|
|
@@ -73,27 +80,36 @@ const completed = await client.onboardingComplete({
|
|
|
73
80
|
## Auth + device security methods
|
|
74
81
|
|
|
75
82
|
```ts
|
|
76
|
-
const registered = await client.registerDevice(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
);
|
|
82
92
|
// { deviceId, fingerprintHash, driftScore, trustState, stepUpRequired }
|
|
83
93
|
|
|
84
94
|
const refreshed = await client.authRefresh({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
userId: completed.userId,
|
|
96
|
+
refreshToken: completed.sessionToken,
|
|
97
|
+
appInstanceId: 'app-instance-1',
|
|
98
|
+
fingerprintHash: registered.fingerprintHash,
|
|
89
99
|
});
|
|
90
100
|
// { refreshToken, stepUpRequired }
|
|
91
101
|
|
|
92
|
-
await client.pinSet(
|
|
93
|
-
|
|
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
|
+
);
|
|
94
110
|
await client.authLogout(
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
{ userId: completed.userId, refreshToken: refreshed.refreshToken },
|
|
112
|
+
{ accessToken: refreshed.refreshToken },
|
|
97
113
|
);
|
|
98
114
|
```
|
|
99
115
|
|
|
@@ -101,53 +117,66 @@ await client.authLogout(
|
|
|
101
117
|
|
|
102
118
|
```ts
|
|
103
119
|
// Register per-device signing key for send authorization
|
|
104
|
-
await client.registerSendDeviceKey(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
await client.registerSendDeviceKey(
|
|
121
|
+
{
|
|
122
|
+
userId: completed.userId,
|
|
123
|
+
deviceId: registered.deviceId,
|
|
124
|
+
publicKey: '-----BEGIN PUBLIC KEY-----...',
|
|
125
|
+
},
|
|
126
|
+
{ accessToken: completed.sessionToken },
|
|
127
|
+
);
|
|
109
128
|
|
|
110
129
|
// Obtain challenge and verify signature to get short-lived send token
|
|
111
|
-
const challenge = await client.createSendChallenge(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
);
|
|
122
147
|
|
|
123
148
|
// Resolve recipient and create transfer
|
|
124
149
|
const recipient = await client.resolveRecipient(
|
|
125
|
-
|
|
126
|
-
|
|
150
|
+
{ identifier: '+14155550123' },
|
|
151
|
+
{ accessToken: completed.sessionToken },
|
|
127
152
|
);
|
|
128
153
|
|
|
129
154
|
await client.createTransfer(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
+
},
|
|
141
166
|
);
|
|
142
167
|
|
|
143
168
|
// Account + transaction endpoints
|
|
144
169
|
await client.accountSummary({ accessToken: completed.sessionToken });
|
|
145
|
-
await client.listTransactions({
|
|
170
|
+
await client.listTransactions({
|
|
171
|
+
accessToken: completed.sessionToken,
|
|
172
|
+
limit: 20,
|
|
173
|
+
});
|
|
146
174
|
```
|
|
147
175
|
|
|
148
176
|
## Error code mapping
|
|
149
177
|
|
|
150
178
|
SDK maps backend error payload `code` into typed `FlurError.code` when available:
|
|
179
|
+
|
|
151
180
|
- `TOKEN_REPLAYED`
|
|
152
181
|
- `SESSION_MISMATCH`
|
|
153
182
|
- `PIN_INVALID`
|
|
@@ -156,6 +185,7 @@ SDK maps backend error payload `code` into typed `FlurError.code` when available
|
|
|
156
185
|
- `STEP_UP_REQUIRED`
|
|
157
186
|
|
|
158
187
|
Fallback codes remain:
|
|
188
|
+
|
|
159
189
|
- `HTTP_ERROR`, `NETWORK_ERROR`, `TIMEOUT`, `UNKNOWN`
|
|
160
190
|
|
|
161
191
|
## Lean prod release workflow (one-person team)
|
|
@@ -181,18 +211,19 @@ PowerShell script (Windows):
|
|
|
181
211
|
```
|
|
182
212
|
|
|
183
213
|
What it does:
|
|
214
|
+
|
|
184
215
|
- verifies git working tree is clean
|
|
185
|
-
- runs `
|
|
186
|
-
- runs `
|
|
216
|
+
- runs `pnpm install --frozen-lockfile`
|
|
217
|
+
- runs `pnpm lint`, `pnpm typecheck`, `pnpm test`, `pnpm build`
|
|
187
218
|
- creates tarball via `npm pack` and smoke-tests install in a temp project
|
|
188
219
|
- bumps version (`patch`/`minor`/`major`) and creates git tag
|
|
189
220
|
- pushes commit and tag (`git push`, `git push --tags`)
|
|
190
221
|
- optionally publishes to npm (`-Publish`)
|
|
191
222
|
|
|
192
|
-
|
|
223
|
+
Release script aliases:
|
|
193
224
|
|
|
194
225
|
```bash
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
226
|
+
pnpm release:patch
|
|
227
|
+
pnpm release:minor
|
|
228
|
+
pnpm release:major
|
|
198
229
|
```
|