@nokinc-flur/sdk 0.1.5 → 0.1.7
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 +21 -21
- package/README.md +198 -198
- package/dist/index.d.ts +4 -0
- package/dist/index.js +16 -3
- package/dist/index.js.map +1 -0
- package/openapi/flur.openapi.json +234 -234
- package/package.json +50 -50
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 NokInc Flur
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NokInc Flur
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,198 +1,198 @@
|
|
|
1
|
-
# flur-sdk (Sprint 1 / Week 1)
|
|
2
|
-
|
|
3
|
-
Typed client scaffold aligned to backend OpenAPI.
|
|
4
|
-
|
|
5
|
-
## Install (local dev)
|
|
6
|
-
```bash
|
|
7
|
-
npm ci
|
|
8
|
-
npm test
|
|
9
|
-
npm run build
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Generate types (optional)
|
|
13
|
-
This repo includes a minimal OpenAPI file at `openapi/flur.openapi.json`.
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm run gen
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## E2E test against a running backend (optional)
|
|
20
|
-
1) Start backend locally (see backend README)
|
|
21
|
-
2) Run (E2E executes when RUN_E2E=1 or FLUR_BASE_URL is set):
|
|
22
|
-
```bash
|
|
23
|
-
FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 npm test
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Contract-focused E2E only:
|
|
27
|
-
```bash
|
|
28
|
-
FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1 npm run test:contract
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Optional envs for full send-money E2E coverage:
|
|
32
|
-
- `FLUR_E2E_SESSION_TOKEN` (required for account/transactions/recipient)
|
|
33
|
-
- `FLUR_E2E_RECIPIENT_IDENTIFIER` (required for recipient resolve / transfer)
|
|
34
|
-
- `FLUR_E2E_SEND_AUTH_TOKEN` (required for transfer create)
|
|
35
|
-
- `FLUR_E2E_DEVICE_ID` (required for transfer create)
|
|
36
|
-
- `FLUR_E2E_AMOUNT_MINOR` (optional, default `100`)
|
|
37
|
-
- `FLUR_E2E_CURRENCY` (optional, default `NGN`)
|
|
38
|
-
|
|
39
|
-
Generate real E2E auth inputs against deployed backend:
|
|
40
|
-
```bash
|
|
41
|
-
# Step 1: start onboarding and get requestId
|
|
42
|
-
npm run e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx
|
|
43
|
-
|
|
44
|
-
# Step 2: rerun with OTP/silent-auth code (and optional recipient)
|
|
45
|
-
npm run e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx --code 123456 --recipient +23481xxxxxxx
|
|
46
|
-
|
|
47
|
-
# Script prints PowerShell env exports, then run:
|
|
48
|
-
npm run test:contract
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Onboarding flow methods
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
import { FlurClient } from "@nokinc-flur/sdk";
|
|
55
|
-
|
|
56
|
-
const client = new FlurClient({ baseUrl: "https://api.example.com" });
|
|
57
|
-
|
|
58
|
-
const started = await client.onboardingStart({
|
|
59
|
-
phoneE164: "+14155550123",
|
|
60
|
-
appInstanceId: "app-instance-1",
|
|
61
|
-
platform: "ios",
|
|
62
|
-
});
|
|
63
|
-
// started: { requestId, checkUrl?, expiresInSec, fallback }
|
|
64
|
-
|
|
65
|
-
const completed = await client.onboardingComplete({
|
|
66
|
-
requestId: started.requestId,
|
|
67
|
-
code: "123456",
|
|
68
|
-
appInstanceId: "app-instance-1",
|
|
69
|
-
});
|
|
70
|
-
// completed: { sessionToken, userId, restricted, risk_reasons }
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Auth + device security methods
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
const registered = await client.registerDevice({
|
|
77
|
-
userId: completed.userId,
|
|
78
|
-
appInstanceId: "app-instance-1",
|
|
79
|
-
platform: "ios",
|
|
80
|
-
networkSignals: { ip: "1.2.3.4" },
|
|
81
|
-
}, { accessToken: completed.sessionToken });
|
|
82
|
-
// { deviceId, fingerprintHash, driftScore, trustState, stepUpRequired }
|
|
83
|
-
|
|
84
|
-
const refreshed = await client.authRefresh({
|
|
85
|
-
userId: completed.userId,
|
|
86
|
-
refreshToken: completed.sessionToken,
|
|
87
|
-
appInstanceId: "app-instance-1",
|
|
88
|
-
fingerprintHash: registered.fingerprintHash,
|
|
89
|
-
});
|
|
90
|
-
// { refreshToken, stepUpRequired }
|
|
91
|
-
|
|
92
|
-
await client.pinSet({ userId: completed.userId, pin: "123456" }, { accessToken: completed.sessionToken });
|
|
93
|
-
await client.pinVerify({ userId: completed.userId, pin: "123456" }, { accessToken: completed.sessionToken });
|
|
94
|
-
await client.authLogout(
|
|
95
|
-
{ userId: completed.userId, refreshToken: refreshed.refreshToken },
|
|
96
|
-
{ accessToken: refreshed.refreshToken }
|
|
97
|
-
);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Send-money methods
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
// Register per-device signing key for send authorization
|
|
104
|
-
await client.registerSendDeviceKey({
|
|
105
|
-
userId: completed.userId,
|
|
106
|
-
deviceId: registered.deviceId,
|
|
107
|
-
publicKey: "-----BEGIN PUBLIC KEY-----...",
|
|
108
|
-
}, { accessToken: completed.sessionToken });
|
|
109
|
-
|
|
110
|
-
// Obtain challenge and verify signature to get short-lived send token
|
|
111
|
-
const challenge = await client.createSendChallenge({
|
|
112
|
-
userId: completed.userId,
|
|
113
|
-
deviceId: registered.deviceId,
|
|
114
|
-
}, { accessToken: completed.sessionToken });
|
|
115
|
-
|
|
116
|
-
const send = await client.verifySendChallenge({
|
|
117
|
-
userId: completed.userId,
|
|
118
|
-
deviceId: registered.deviceId,
|
|
119
|
-
challengeId: challenge.challengeId,
|
|
120
|
-
signature: "base64-signature",
|
|
121
|
-
}, { accessToken: completed.sessionToken });
|
|
122
|
-
|
|
123
|
-
// Resolve recipient and create transfer
|
|
124
|
-
const recipient = await client.resolveRecipient(
|
|
125
|
-
{ identifier: "+14155550123" },
|
|
126
|
-
{ accessToken: completed.sessionToken }
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
await client.createTransfer(
|
|
130
|
-
{
|
|
131
|
-
recipientIdentifier: recipient.normalizedIdentifier,
|
|
132
|
-
amountMinor: 5000,
|
|
133
|
-
currency: "NGN",
|
|
134
|
-
sendAuthToken: send.sendAuthToken,
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
accessToken: completed.sessionToken,
|
|
138
|
-
deviceId: registered.deviceId,
|
|
139
|
-
idempotencyKey: crypto.randomUUID(),
|
|
140
|
-
}
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// Account + transaction endpoints
|
|
144
|
-
await client.accountSummary({ accessToken: completed.sessionToken });
|
|
145
|
-
await client.listTransactions({ accessToken: completed.sessionToken, limit: 20 });
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Error code mapping
|
|
149
|
-
|
|
150
|
-
SDK maps backend error payload `code` into typed `FlurError.code` when available:
|
|
151
|
-
- `TOKEN_REPLAYED`
|
|
152
|
-
- `SESSION_MISMATCH`
|
|
153
|
-
- `PIN_INVALID`
|
|
154
|
-
- `PIN_LOCKED`
|
|
155
|
-
- `PIN_NOT_SET`
|
|
156
|
-
- `STEP_UP_REQUIRED`
|
|
157
|
-
|
|
158
|
-
Fallback codes remain:
|
|
159
|
-
- `HTTP_ERROR`, `NETWORK_ERROR`, `TIMEOUT`, `UNKNOWN`
|
|
160
|
-
|
|
161
|
-
## Lean prod release workflow (one-person team)
|
|
162
|
-
|
|
163
|
-
PowerShell script (Windows):
|
|
164
|
-
|
|
165
|
-
```powershell
|
|
166
|
-
# patch release (recommended for this onboarding addition)
|
|
167
|
-
./scripts/release.ps1 -Bump patch
|
|
168
|
-
|
|
169
|
-
# exact version release (example: 1.0.1)
|
|
170
|
-
./scripts/release.ps1 -Version 1.0.1
|
|
171
|
-
|
|
172
|
-
# include E2E run (uses default OCI API Gateway URL from release script)
|
|
173
|
-
./scripts/release.ps1 -Version 1.0.1 -RunE2E
|
|
174
|
-
|
|
175
|
-
# include E2E run with override URL
|
|
176
|
-
./scripts/release.ps1 -Version 1.0.1 -RunE2E -E2EBaseUrl https://your-api.example.com
|
|
177
|
-
|
|
178
|
-
# publish to npm after all checks pass
|
|
179
|
-
./scripts/release.ps1 -Bump patch -Publish
|
|
180
|
-
./scripts/release.ps1 -Version 1.0.1 -Publish
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
What it does:
|
|
184
|
-
- verifies git working tree is clean
|
|
185
|
-
- runs `npm ci`
|
|
186
|
-
- runs `npm run lint`, `npm run typecheck`, `npm test`, `npm run build`
|
|
187
|
-
- creates tarball via `npm pack` and smoke-tests install in a temp project
|
|
188
|
-
- bumps version (`patch`/`minor`/`major`) and creates git tag
|
|
189
|
-
- pushes commit and tag (`git push`, `git push --tags`)
|
|
190
|
-
- optionally publishes to npm (`-Publish`)
|
|
191
|
-
|
|
192
|
-
NPM aliases:
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
npm run release:patch
|
|
196
|
-
npm run release:minor
|
|
197
|
-
npm run release:major
|
|
198
|
-
```
|
|
1
|
+
# flur-sdk (Sprint 1 / Week 1)
|
|
2
|
+
|
|
3
|
+
Typed client scaffold aligned to backend OpenAPI.
|
|
4
|
+
|
|
5
|
+
## Install (local dev)
|
|
6
|
+
```bash
|
|
7
|
+
npm ci
|
|
8
|
+
npm test
|
|
9
|
+
npm run build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Generate types (optional)
|
|
13
|
+
This repo includes a minimal OpenAPI file at `openapi/flur.openapi.json`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run gen
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## E2E test against a running backend (optional)
|
|
20
|
+
1) Start backend locally (see backend README)
|
|
21
|
+
2) Run (E2E executes when RUN_E2E=1 or FLUR_BASE_URL is set):
|
|
22
|
+
```bash
|
|
23
|
+
FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 npm test
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Contract-focused E2E only:
|
|
27
|
+
```bash
|
|
28
|
+
FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1 npm run test:contract
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Optional envs for full send-money E2E coverage:
|
|
32
|
+
- `FLUR_E2E_SESSION_TOKEN` (required for account/transactions/recipient)
|
|
33
|
+
- `FLUR_E2E_RECIPIENT_IDENTIFIER` (required for recipient resolve / transfer)
|
|
34
|
+
- `FLUR_E2E_SEND_AUTH_TOKEN` (required for transfer create)
|
|
35
|
+
- `FLUR_E2E_DEVICE_ID` (required for transfer create)
|
|
36
|
+
- `FLUR_E2E_AMOUNT_MINOR` (optional, default `100`)
|
|
37
|
+
- `FLUR_E2E_CURRENCY` (optional, default `NGN`)
|
|
38
|
+
|
|
39
|
+
Generate real E2E auth inputs against deployed backend:
|
|
40
|
+
```bash
|
|
41
|
+
# Step 1: start onboarding and get requestId
|
|
42
|
+
npm run e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx
|
|
43
|
+
|
|
44
|
+
# Step 2: rerun with OTP/silent-auth code (and optional recipient)
|
|
45
|
+
npm run e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx --code 123456 --recipient +23481xxxxxxx
|
|
46
|
+
|
|
47
|
+
# Script prints PowerShell env exports, then run:
|
|
48
|
+
npm run test:contract
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Onboarding flow methods
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { FlurClient } from "@nokinc-flur/sdk";
|
|
55
|
+
|
|
56
|
+
const client = new FlurClient({ baseUrl: "https://api.example.com" });
|
|
57
|
+
|
|
58
|
+
const started = await client.onboardingStart({
|
|
59
|
+
phoneE164: "+14155550123",
|
|
60
|
+
appInstanceId: "app-instance-1",
|
|
61
|
+
platform: "ios",
|
|
62
|
+
});
|
|
63
|
+
// started: { requestId, checkUrl?, expiresInSec, fallback }
|
|
64
|
+
|
|
65
|
+
const completed = await client.onboardingComplete({
|
|
66
|
+
requestId: started.requestId,
|
|
67
|
+
code: "123456",
|
|
68
|
+
appInstanceId: "app-instance-1",
|
|
69
|
+
});
|
|
70
|
+
// completed: { sessionToken, userId, restricted, risk_reasons }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Auth + device security methods
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const registered = await client.registerDevice({
|
|
77
|
+
userId: completed.userId,
|
|
78
|
+
appInstanceId: "app-instance-1",
|
|
79
|
+
platform: "ios",
|
|
80
|
+
networkSignals: { ip: "1.2.3.4" },
|
|
81
|
+
}, { accessToken: completed.sessionToken });
|
|
82
|
+
// { deviceId, fingerprintHash, driftScore, trustState, stepUpRequired }
|
|
83
|
+
|
|
84
|
+
const refreshed = await client.authRefresh({
|
|
85
|
+
userId: completed.userId,
|
|
86
|
+
refreshToken: completed.sessionToken,
|
|
87
|
+
appInstanceId: "app-instance-1",
|
|
88
|
+
fingerprintHash: registered.fingerprintHash,
|
|
89
|
+
});
|
|
90
|
+
// { refreshToken, stepUpRequired }
|
|
91
|
+
|
|
92
|
+
await client.pinSet({ userId: completed.userId, pin: "123456" }, { accessToken: completed.sessionToken });
|
|
93
|
+
await client.pinVerify({ userId: completed.userId, pin: "123456" }, { accessToken: completed.sessionToken });
|
|
94
|
+
await client.authLogout(
|
|
95
|
+
{ userId: completed.userId, refreshToken: refreshed.refreshToken },
|
|
96
|
+
{ accessToken: refreshed.refreshToken }
|
|
97
|
+
);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Send-money methods
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// Register per-device signing key for send authorization
|
|
104
|
+
await client.registerSendDeviceKey({
|
|
105
|
+
userId: completed.userId,
|
|
106
|
+
deviceId: registered.deviceId,
|
|
107
|
+
publicKey: "-----BEGIN PUBLIC KEY-----...",
|
|
108
|
+
}, { accessToken: completed.sessionToken });
|
|
109
|
+
|
|
110
|
+
// Obtain challenge and verify signature to get short-lived send token
|
|
111
|
+
const challenge = await client.createSendChallenge({
|
|
112
|
+
userId: completed.userId,
|
|
113
|
+
deviceId: registered.deviceId,
|
|
114
|
+
}, { accessToken: completed.sessionToken });
|
|
115
|
+
|
|
116
|
+
const send = await client.verifySendChallenge({
|
|
117
|
+
userId: completed.userId,
|
|
118
|
+
deviceId: registered.deviceId,
|
|
119
|
+
challengeId: challenge.challengeId,
|
|
120
|
+
signature: "base64-signature",
|
|
121
|
+
}, { accessToken: completed.sessionToken });
|
|
122
|
+
|
|
123
|
+
// Resolve recipient and create transfer
|
|
124
|
+
const recipient = await client.resolveRecipient(
|
|
125
|
+
{ identifier: "+14155550123" },
|
|
126
|
+
{ accessToken: completed.sessionToken }
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
await client.createTransfer(
|
|
130
|
+
{
|
|
131
|
+
recipientIdentifier: recipient.normalizedIdentifier,
|
|
132
|
+
amountMinor: 5000,
|
|
133
|
+
currency: "NGN",
|
|
134
|
+
sendAuthToken: send.sendAuthToken,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
accessToken: completed.sessionToken,
|
|
138
|
+
deviceId: registered.deviceId,
|
|
139
|
+
idempotencyKey: crypto.randomUUID(),
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Account + transaction endpoints
|
|
144
|
+
await client.accountSummary({ accessToken: completed.sessionToken });
|
|
145
|
+
await client.listTransactions({ accessToken: completed.sessionToken, limit: 20 });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Error code mapping
|
|
149
|
+
|
|
150
|
+
SDK maps backend error payload `code` into typed `FlurError.code` when available:
|
|
151
|
+
- `TOKEN_REPLAYED`
|
|
152
|
+
- `SESSION_MISMATCH`
|
|
153
|
+
- `PIN_INVALID`
|
|
154
|
+
- `PIN_LOCKED`
|
|
155
|
+
- `PIN_NOT_SET`
|
|
156
|
+
- `STEP_UP_REQUIRED`
|
|
157
|
+
|
|
158
|
+
Fallback codes remain:
|
|
159
|
+
- `HTTP_ERROR`, `NETWORK_ERROR`, `TIMEOUT`, `UNKNOWN`
|
|
160
|
+
|
|
161
|
+
## Lean prod release workflow (one-person team)
|
|
162
|
+
|
|
163
|
+
PowerShell script (Windows):
|
|
164
|
+
|
|
165
|
+
```powershell
|
|
166
|
+
# patch release (recommended for this onboarding addition)
|
|
167
|
+
./scripts/release.ps1 -Bump patch
|
|
168
|
+
|
|
169
|
+
# exact version release (example: 1.0.1)
|
|
170
|
+
./scripts/release.ps1 -Version 1.0.1
|
|
171
|
+
|
|
172
|
+
# include E2E run (uses default OCI API Gateway URL from release script)
|
|
173
|
+
./scripts/release.ps1 -Version 1.0.1 -RunE2E
|
|
174
|
+
|
|
175
|
+
# include E2E run with override URL
|
|
176
|
+
./scripts/release.ps1 -Version 1.0.1 -RunE2E -E2EBaseUrl https://your-api.example.com
|
|
177
|
+
|
|
178
|
+
# publish to npm after all checks pass
|
|
179
|
+
./scripts/release.ps1 -Bump patch -Publish
|
|
180
|
+
./scripts/release.ps1 -Version 1.0.1 -Publish
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
What it does:
|
|
184
|
+
- verifies git working tree is clean
|
|
185
|
+
- runs `npm ci`
|
|
186
|
+
- runs `npm run lint`, `npm run typecheck`, `npm test`, `npm run build`
|
|
187
|
+
- creates tarball via `npm pack` and smoke-tests install in a temp project
|
|
188
|
+
- bumps version (`patch`/`minor`/`major`) and creates git tag
|
|
189
|
+
- pushes commit and tag (`git push`, `git push --tags`)
|
|
190
|
+
- optionally publishes to npm (`-Publish`)
|
|
191
|
+
|
|
192
|
+
NPM aliases:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npm run release:patch
|
|
196
|
+
npm run release:minor
|
|
197
|
+
npm run release:major
|
|
198
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ type FlurClientOptions = {
|
|
|
11
11
|
baseUrl: string;
|
|
12
12
|
fetchImpl?: typeof fetch;
|
|
13
13
|
timeoutMs?: number;
|
|
14
|
+
getExtraHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
14
15
|
};
|
|
15
16
|
type OnboardingFallback = "SILENT_AUTH" | "OTP";
|
|
16
17
|
type OnboardingStartInput = {
|
|
@@ -220,6 +221,7 @@ declare class FlurClient {
|
|
|
220
221
|
private readonly baseUrl;
|
|
221
222
|
private readonly fetchImpl;
|
|
222
223
|
private readonly timeoutMs;
|
|
224
|
+
private readonly getExtraHeaders?;
|
|
223
225
|
constructor(opts: FlurClientOptions);
|
|
224
226
|
health(): Promise<{
|
|
225
227
|
ok: boolean;
|
|
@@ -273,9 +275,11 @@ declare class FlurError extends Error {
|
|
|
273
275
|
readonly code: FlurErrorCode;
|
|
274
276
|
readonly status?: number;
|
|
275
277
|
readonly details?: unknown;
|
|
278
|
+
readonly reqId?: string | null;
|
|
276
279
|
constructor(message: string, code: FlurErrorCode, opts?: {
|
|
277
280
|
status?: number;
|
|
278
281
|
details?: unknown;
|
|
282
|
+
reqId?: string | null;
|
|
279
283
|
});
|
|
280
284
|
}
|
|
281
285
|
|
package/dist/index.js
CHANGED
|
@@ -215,15 +215,18 @@ var FlurError = class extends Error {
|
|
|
215
215
|
code;
|
|
216
216
|
status;
|
|
217
217
|
details;
|
|
218
|
+
reqId;
|
|
218
219
|
constructor(message, code, opts) {
|
|
219
220
|
super(message);
|
|
220
221
|
this.name = "FlurError";
|
|
221
222
|
this.code = code;
|
|
222
223
|
this.status = opts?.status;
|
|
223
224
|
this.details = opts?.details;
|
|
225
|
+
this.reqId = opts?.reqId;
|
|
224
226
|
}
|
|
225
227
|
};
|
|
226
228
|
async function mapToFlurError(res) {
|
|
229
|
+
const reqId = res.headers.get("x-request-id");
|
|
227
230
|
let details = void 0;
|
|
228
231
|
let mappedCode = "HTTP_ERROR";
|
|
229
232
|
let mappedMessage = `HTTP ${res.status}`;
|
|
@@ -245,7 +248,7 @@ async function mapToFlurError(res) {
|
|
|
245
248
|
}
|
|
246
249
|
} catch {
|
|
247
250
|
}
|
|
248
|
-
return new FlurError(mappedMessage, mappedCode, { status: res.status, details });
|
|
251
|
+
return new FlurError(mappedMessage, mappedCode, { status: res.status, details, reqId });
|
|
249
252
|
}
|
|
250
253
|
|
|
251
254
|
// src/primitives.ts
|
|
@@ -348,10 +351,12 @@ var FlurClient = class {
|
|
|
348
351
|
baseUrl;
|
|
349
352
|
fetchImpl;
|
|
350
353
|
timeoutMs;
|
|
354
|
+
getExtraHeaders;
|
|
351
355
|
constructor(opts) {
|
|
352
356
|
this.baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
353
357
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
354
358
|
this.timeoutMs = opts.timeoutMs ?? 1e4;
|
|
359
|
+
this.getExtraHeaders = opts.getExtraHeaders;
|
|
355
360
|
}
|
|
356
361
|
async health() {
|
|
357
362
|
return this.requestJson("/health", { method: "GET" }, void 0, HealthResponseSchema);
|
|
@@ -639,7 +644,6 @@ var FlurClient = class {
|
|
|
639
644
|
{
|
|
640
645
|
method: "POST",
|
|
641
646
|
headers: {
|
|
642
|
-
"content-type": "application/json",
|
|
643
647
|
authorization: `Bearer ${options.accessToken}`
|
|
644
648
|
}
|
|
645
649
|
},
|
|
@@ -677,7 +681,15 @@ var FlurClient = class {
|
|
|
677
681
|
throw err;
|
|
678
682
|
}
|
|
679
683
|
try {
|
|
680
|
-
|
|
684
|
+
let extraHeaders = {};
|
|
685
|
+
if (this.getExtraHeaders) {
|
|
686
|
+
extraHeaders = await this.getExtraHeaders();
|
|
687
|
+
}
|
|
688
|
+
const finalHeaders = {
|
|
689
|
+
...init.headers,
|
|
690
|
+
...extraHeaders
|
|
691
|
+
};
|
|
692
|
+
const res = await this.fetchImpl(url, { ...init, headers: finalHeaders, body, signal: controller.signal });
|
|
681
693
|
if (!res.ok) throw await mapToFlurError(res);
|
|
682
694
|
const payload = await res.json();
|
|
683
695
|
if (!responseSchema) return payload;
|
|
@@ -716,3 +728,4 @@ export {
|
|
|
716
728
|
normalizeE164,
|
|
717
729
|
parseAmountInput
|
|
718
730
|
};
|
|
731
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/contracts.ts","../src/errors.ts","../src/primitives.ts"],"sourcesContent":["import { z, type ZodType } from \"zod\";\r\nimport {\r\n AccountSummaryResponseSchema,\r\n AuthLogoutRequestSchema,\r\n AuthRefreshRequestSchema,\r\n AuthRefreshResponseSchema,\r\n CreatePayLinkResponseSchema,\r\n CreateTransferRequestSchema,\r\n E164Regex,\r\n HealthResponseSchema,\r\n OkResponseSchema,\r\n OnboardingCompleteRequestSchema,\r\n OnboardingCompleteResponseSchema,\r\n OnboardingStartRequestSchema,\r\n OnboardingStartResponseSchema,\r\n PinSetRequestSchema,\r\n PinVerifyRequestSchema,\r\n PushRegisterRequestSchema,\r\n RegisterDeviceRequestSchema,\r\n RegisterDeviceResponseSchema,\r\n RegisterSendDeviceKeyRequestSchema,\r\n ResolvePayLinkResponseSchema,\r\n ResolveRecipientRequestSchema,\r\n ResolveRecipientResponseSchema,\r\n SendChallengeRequestSchema,\r\n SendChallengeResponseSchema,\r\n SendVerifyRequestSchema,\r\n SendVerifyResponseSchema,\r\n TransactionDetailResponseSchema,\r\n TransactionsListResponseSchema,\r\n TransferResponseSchema,\r\n WelcomeResponseSchema,\r\n} from \"./contracts.js\";\r\nimport { FlurError, mapToFlurError } from \"./errors.js\";\r\nimport { type Money, moneyMinorToNumber, normalizeE164 } from \"./primitives.js\";\r\n\r\nexport type FlurClientOptions = {\r\n baseUrl: string;\r\n fetchImpl?: typeof fetch;\r\n timeoutMs?: number;\r\n getExtraHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\r\n};\r\n\r\nexport type OnboardingFallback = \"SILENT_AUTH\" | \"OTP\";\r\n\r\nexport type OnboardingStartInput = {\r\n phoneE164: string;\r\n appInstanceId: string;\r\n platform: \"android\" | \"ios\" | \"web\";\r\n turnstileToken?: string;\r\n appAttestation?: {\r\n provider: \"android\" | \"ios\" | \"web\";\r\n token: string;\r\n };\r\n firstName?: string;\r\n lastName?: string;\r\n};\r\n\r\nexport type OnboardingStartResponse = {\r\n requestId: string;\r\n checkUrl?: string;\r\n expiresInSec: number;\r\n fallback: OnboardingFallback;\r\n};\r\n\r\nexport type OnboardingRiskReason = \"SIM_SWAP_RECENT\" | \"ROAMING\" | \"CARRIER_CHANGED\";\r\n\r\nexport type OnboardingCompleteInput = {\r\n requestId: string;\r\n code: string;\r\n appInstanceId: string;\r\n fingerprintHash?: string;\r\n};\r\n\r\nexport type OnboardingCompleteResponse = {\r\n sessionToken: string;\r\n userId: string;\r\n restricted: boolean;\r\n risk_reasons: OnboardingRiskReason[];\r\n stepUpRequired?: boolean;\r\n riskStatus?: \"ok\" | \"unavailable\";\r\n};\r\n\r\nexport type RegisterDeviceInput = {\r\n userId: string;\r\n appInstanceId: string;\r\n platform: string;\r\n model?: string;\r\n networkSignals: {\r\n ip: string;\r\n asn?: number;\r\n country?: string;\r\n carrier?: string;\r\n };\r\n};\r\n\r\nexport type DeviceTrustState = \"TRUSTED_PRIMARY\" | \"TRUSTED_SECONDARY\" | \"UNVERIFIED\";\r\n\r\nexport type RegisterDeviceResponse = {\r\n deviceId: string;\r\n fingerprintHash: string;\r\n driftScore: number;\r\n trustState: DeviceTrustState;\r\n stepUpRequired: boolean;\r\n};\r\n\r\nexport type AuthRefreshInput = {\r\n userId: string;\r\n refreshToken: string;\r\n appInstanceId: string;\r\n fingerprintHash: string;\r\n};\r\n\r\nexport type AuthRefreshResponse = {\r\n refreshToken: string;\r\n stepUpRequired: boolean;\r\n};\r\n\r\nexport type AuthLogoutInput = {\r\n userId: string;\r\n refreshToken: string;\r\n};\r\n\r\nexport type PinSetInput = {\r\n userId: string;\r\n pin: string;\r\n};\r\n\r\nexport type PinVerifyInput = {\r\n userId: string;\r\n pin: string;\r\n};\r\n\r\nexport type RegisterSendDeviceKeyInput = {\r\n userId: string;\r\n deviceId: string;\r\n publicKey: string;\r\n};\r\n\r\nexport type SendChallengeInput = {\r\n userId: string;\r\n deviceId: string;\r\n};\r\n\r\nexport type SendChallengeResponse = {\r\n challengeId: string;\r\n nonce: string;\r\n expiresAt: string;\r\n};\r\n\r\nexport type SendVerifyInput = {\r\n userId: string;\r\n deviceId: string;\r\n challengeId: string;\r\n signature: string;\r\n};\r\n\r\nexport type SendVerifyResponse = {\r\n sendAuthToken: string;\r\n};\r\n\r\nexport type RecipientResolveInput = {\r\n identifier: string;\r\n};\r\n\r\nexport type RecipientResolveResponse = {\r\n recipientUserId: string;\r\n displayName: string;\r\n normalizedIdentifier: string;\r\n isActive: boolean;\r\n};\r\n\r\nexport type TransferInput = {\r\n recipientIdentifier: string;\r\n amountMinor: number;\r\n currency: string;\r\n sendAuthToken: string;\r\n};\r\n\r\nexport type TransferStatus = \"SETTLED\" | \"PENDING_REVIEW\" | \"DECLINED\";\r\n\r\nexport type TransferResponse = {\r\n transactionId: string;\r\n status: TransferStatus;\r\n userStatus: TransferStatus;\r\n recipientName: string;\r\n timestamp: string;\r\n};\r\n\r\nexport type TransactionDirection = \"OUTGOING\" | \"INCOMING\";\r\n\r\nexport type AccountActivityItem = {\r\n id: string;\r\n type: string;\r\n direction: TransactionDirection;\r\n name: string;\r\n identifier: string;\r\n amountMinor: number;\r\n currency: string;\r\n status: string;\r\n timestamp: string;\r\n};\r\n\r\nexport type AccountSummaryResponse = {\r\n balance: number;\r\n currency: string;\r\n dailySendLimit: number;\r\n dailySendRemaining: number;\r\n kycTier: string;\r\n kycStatus: string;\r\n recentActivity: AccountActivityItem[];\r\n};\r\n\r\nexport type TransactionsListResponse = {\r\n items: AccountActivityItem[];\r\n nextCursor: string | null;\r\n};\r\n\r\nexport type TransactionDetailResponse = {\r\n transactionId: string;\r\n type: string;\r\n direction: TransactionDirection;\r\n counterpartyName: string;\r\n counterpartyIdentifier: string;\r\n amountMinor: number;\r\n currency: string;\r\n status: string;\r\n timestamp: string;\r\n};\r\n\r\nexport type PushPlatform = \"ios\" | \"android\" | \"web\";\r\n\r\nexport type PushRegisterInput = {\r\n deviceId: string;\r\n platform: PushPlatform;\r\n token: string;\r\n};\r\n\r\nexport type CreatePayLinkResponse = {\r\n token: string;\r\n};\r\n\r\nexport type ResolvePayLinkResponse = {\r\n recipientUserId: string;\r\n displayName: string;\r\n normalizedIdentifier: string;\r\n isActive: boolean;\r\n};\r\n\r\nexport type AuthorizedOptions = {\r\n accessToken: string;\r\n};\r\n\r\nexport type CreateTransferOptions = AuthorizedOptions & {\r\n deviceId: string;\r\n idempotencyKey: string;\r\n};\r\n\r\nexport type ListTransactionsOptions = AuthorizedOptions & {\r\n cursor?: string;\r\n limit?: number;\r\n};\r\n\r\nexport type BiometricSigner = {\r\n getOrCreateKeyPair(deviceId: string): Promise<string>;\r\n sign(payload: string): Promise<string>;\r\n};\r\n\r\nexport type SendMoneyInput = {\r\n recipientIdentifier: string;\r\n money: Money;\r\n sendAuthToken: string;\r\n idempotencyKey?: string;\r\n defaultCountry?: string;\r\n};\r\n\r\nexport type SendMoneyOptions = AuthorizedOptions & {\r\n deviceId: string;\r\n};\r\n\r\nexport type AuthorizeSendWithBiometricInput = {\r\n userId: string;\r\n deviceId: string;\r\n accessToken: string;\r\n signer: BiometricSigner;\r\n};\r\n\r\nexport class FlurClient {\r\n private readonly baseUrl: string;\r\n private readonly fetchImpl: typeof fetch;\r\n private readonly timeoutMs: number;\r\n private readonly getExtraHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\r\n\r\n constructor(opts: FlurClientOptions) {\r\n this.baseUrl = opts.baseUrl.replace(/\\/$/, \"\");\r\n this.fetchImpl = opts.fetchImpl ?? fetch;\r\n this.timeoutMs = opts.timeoutMs ?? 10_000;\r\n this.getExtraHeaders = opts.getExtraHeaders;\r\n }\r\n\r\n async health(): Promise<{ ok: boolean }> {\r\n return this.requestJson(\"/health\", { method: \"GET\" }, undefined, HealthResponseSchema);\r\n }\r\n\r\n async welcome(): Promise<{ message: string }> {\r\n return this.requestJson(\"/welcome\", { method: \"GET\" }, undefined, WelcomeResponseSchema);\r\n }\r\n\r\n async onboardingStart(input: OnboardingStartInput): Promise<OnboardingStartResponse> {\r\n return this.requestJson(\r\n \"/v1/onboarding/start\",\r\n {\r\n method: \"POST\",\r\n headers: { \"content-type\": \"application/json\" },\r\n },\r\n OnboardingStartRequestSchema,\r\n OnboardingStartResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async onboardingComplete(input: OnboardingCompleteInput): Promise<OnboardingCompleteResponse> {\r\n return this.requestJson(\r\n \"/v1/onboarding/complete\",\r\n {\r\n method: \"POST\",\r\n headers: { \"content-type\": \"application/json\" },\r\n },\r\n OnboardingCompleteRequestSchema,\r\n OnboardingCompleteResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async registerDevice(input: RegisterDeviceInput, options: AuthorizedOptions): Promise<RegisterDeviceResponse> {\r\n return this.requestJson(\r\n \"/v1/devices/register\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n RegisterDeviceRequestSchema,\r\n RegisterDeviceResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async authRefresh(input: AuthRefreshInput): Promise<AuthRefreshResponse> {\r\n return this.requestJson(\r\n \"/v1/auth/refresh\",\r\n {\r\n method: \"POST\",\r\n headers: { \"content-type\": \"application/json\" },\r\n },\r\n AuthRefreshRequestSchema,\r\n AuthRefreshResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async authLogout(input: AuthLogoutInput, options: AuthorizedOptions): Promise<{ ok: boolean }> {\r\n return this.requestJson(\r\n \"/v1/auth/logout\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n AuthLogoutRequestSchema,\r\n OkResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async pinSet(input: PinSetInput, options: AuthorizedOptions): Promise<{ ok: boolean }> {\r\n return this.requestJson(\r\n \"/v1/auth/pin/set\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n PinSetRequestSchema,\r\n OkResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async pinVerify(input: PinVerifyInput, options: AuthorizedOptions): Promise<{ ok: boolean }> {\r\n return this.requestJson(\r\n \"/v1/auth/pin/verify\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n PinVerifyRequestSchema,\r\n OkResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async registerSendDeviceKey(input: RegisterSendDeviceKeyInput, options: AuthorizedOptions): Promise<{ ok: boolean }> {\r\n return this.requestJson(\r\n \"/api/v1/auth/send/device-key\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n RegisterSendDeviceKeyRequestSchema,\r\n OkResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async createSendChallenge(input: SendChallengeInput, options: AuthorizedOptions): Promise<SendChallengeResponse> {\r\n return this.requestJson(\r\n \"/api/v1/auth/send/challenge\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n SendChallengeRequestSchema,\r\n SendChallengeResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async verifySendChallenge(input: SendVerifyInput, options: AuthorizedOptions): Promise<SendVerifyResponse> {\r\n return this.requestJson(\r\n \"/api/v1/auth/send/verify\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n SendVerifyRequestSchema,\r\n SendVerifyResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async registerBiometricDeviceKey(input: {\r\n userId: string;\r\n deviceId: string;\r\n accessToken: string;\r\n signer: BiometricSigner;\r\n }): Promise<{ ok: boolean }> {\r\n const publicKey = await input.signer.getOrCreateKeyPair(input.deviceId);\r\n return this.registerSendDeviceKey({\r\n userId: input.userId,\r\n deviceId: input.deviceId,\r\n publicKey,\r\n }, { accessToken: input.accessToken });\r\n }\r\n\r\n async authorizeSendWithBiometric(input: AuthorizeSendWithBiometricInput): Promise<SendVerifyResponse> {\r\n const challenge = await this.createSendChallenge({\r\n userId: input.userId,\r\n deviceId: input.deviceId,\r\n }, { accessToken: input.accessToken });\r\n\r\n const signature = await input.signer.sign(challenge.nonce);\r\n return this.verifySendChallenge({\r\n userId: input.userId,\r\n deviceId: input.deviceId,\r\n challengeId: challenge.challengeId,\r\n signature,\r\n }, { accessToken: input.accessToken });\r\n }\r\n\r\n async resolveRecipient(input: RecipientResolveInput, options: AuthorizedOptions): Promise<RecipientResolveResponse> {\r\n return this.requestJson(\r\n \"/api/v1/recipients/resolve\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n ResolveRecipientRequestSchema,\r\n ResolveRecipientResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async createTransfer(input: TransferInput, options: CreateTransferOptions): Promise<TransferResponse> {\r\n return this.requestJson(\r\n \"/api/v1/transfers\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n \"x-device-id\": options.deviceId,\r\n \"x-idempotency-key\": options.idempotencyKey,\r\n },\r\n },\r\n CreateTransferRequestSchema,\r\n TransferResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async sendMoney(input: SendMoneyInput, options: SendMoneyOptions): Promise<TransferResponse> {\r\n const normalizedRecipient = E164Regex.test(input.recipientIdentifier)\r\n ? input.recipientIdentifier\r\n : normalizeE164(input.recipientIdentifier, input.defaultCountry);\r\n\r\n const idempotencyKey = input.idempotencyKey ?? getSecureRandomUuid();\r\n return this.createTransfer(\r\n {\r\n recipientIdentifier: normalizedRecipient,\r\n amountMinor: moneyMinorToNumber(input.money.amountMinor),\r\n currency: input.money.currency,\r\n sendAuthToken: input.sendAuthToken,\r\n },\r\n {\r\n accessToken: options.accessToken,\r\n deviceId: options.deviceId,\r\n idempotencyKey,\r\n }\r\n );\r\n }\r\n\r\n async accountSummary(options: AuthorizedOptions): Promise<AccountSummaryResponse> {\r\n return this.requestJson(\r\n \"/api/v1/account/summary\",\r\n {\r\n method: \"GET\",\r\n headers: {\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n undefined,\r\n AccountSummaryResponseSchema\r\n );\r\n }\r\n\r\n async listTransactions(options: ListTransactionsOptions): Promise<TransactionsListResponse> {\r\n const query = new URLSearchParams();\r\n if (typeof options.cursor === \"string\" && options.cursor.trim().length > 0) {\r\n query.set(\"cursor\", options.cursor);\r\n }\r\n if (typeof options.limit === \"number\") {\r\n query.set(\"limit\", String(options.limit));\r\n }\r\n\r\n const path = query.size > 0 ? `/api/v1/transactions?${query.toString()}` : \"/api/v1/transactions\";\r\n return this.requestJson(\r\n path,\r\n {\r\n method: \"GET\",\r\n headers: {\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n undefined,\r\n TransactionsListResponseSchema\r\n );\r\n }\r\n\r\n async transactionDetail(transactionId: string, options: AuthorizedOptions): Promise<TransactionDetailResponse> {\r\n const encodedId = encodeURIComponent(transactionId);\r\n return this.requestJson(\r\n `/api/v1/transactions/${encodedId}`,\r\n {\r\n method: \"GET\",\r\n headers: {\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n undefined,\r\n TransactionDetailResponseSchema\r\n );\r\n }\r\n\r\n async registerPushToken(input: PushRegisterInput, options: AuthorizedOptions): Promise<{ ok: boolean }> {\r\n return this.requestJson(\r\n \"/api/v1/push/register\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n PushRegisterRequestSchema,\r\n OkResponseSchema,\r\n input\r\n );\r\n }\r\n\r\n async createPayLink(options: AuthorizedOptions): Promise<CreatePayLinkResponse> {\r\n return this.requestJson(\r\n \"/api/v1/pay-links\",\r\n {\r\n method: \"POST\",\r\n headers: {\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n undefined,\r\n CreatePayLinkResponseSchema,\r\n );\r\n }\r\n\r\n async resolvePayLink(token: string, options: AuthorizedOptions): Promise<ResolvePayLinkResponse> {\r\n const encodedToken = encodeURIComponent(token);\r\n return this.requestJson(\r\n `/api/v1/pay-links/${encodedToken}`,\r\n {\r\n method: \"GET\",\r\n headers: {\r\n authorization: `Bearer ${options.accessToken}`,\r\n },\r\n },\r\n undefined,\r\n ResolvePayLinkResponseSchema,\r\n );\r\n }\r\n\r\n private async requestJson<TInput, TOutput>(\r\n path: string,\r\n init: RequestInit,\r\n requestSchema?: ZodType<TInput>,\r\n responseSchema?: ZodType<TOutput>,\r\n input?: TInput\r\n ): Promise<TOutput> {\r\n const url = `${this.baseUrl}${path}`;\r\n const controller = new AbortController();\r\n const t = setTimeout(() => controller.abort(), this.timeoutMs);\r\n let body = init.body;\r\n try {\r\n body = requestSchema ? JSON.stringify(requestSchema.parse(input)) : init.body;\r\n } catch (err: unknown) {\r\n if (err instanceof z.ZodError) {\r\n throw new FlurError(\"Invalid request payload\", \"INVALID_REQUEST\", {\r\n details: err.flatten(),\r\n });\r\n }\r\n throw err;\r\n }\r\n\r\n try {\r\n let extraHeaders: Record<string, string> = {};\r\n if (this.getExtraHeaders) {\r\n extraHeaders = await this.getExtraHeaders();\r\n }\r\n\r\n const finalHeaders = {\r\n ...init.headers,\r\n ...extraHeaders,\r\n };\r\n\r\n const res = await this.fetchImpl(url, { ...init, headers: finalHeaders, body, signal: controller.signal });\r\n if (!res.ok) throw await mapToFlurError(res);\r\n const payload = (await res.json()) as unknown;\r\n if (!responseSchema) return payload as TOutput;\r\n try {\r\n return responseSchema.parse(payload);\r\n } catch (err: unknown) {\r\n if (err instanceof z.ZodError) {\r\n throw new FlurError(\"SDK contract validation failed\", \"INVALID_REQUEST\", {\r\n details: err.flatten(),\r\n });\r\n }\r\n throw err;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof Error && err.name === \"AbortError\") {\r\n throw new FlurError(\"Request timed out\", \"TIMEOUT\");\r\n }\r\n if (err instanceof FlurError) throw err;\r\n throw new FlurError(\"Network error\", \"NETWORK_ERROR\", { details: String(err) });\r\n } finally {\r\n clearTimeout(t);\r\n }\r\n }\r\n}\r\n\r\nfunction getSecureRandomUuid(): string {\r\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\r\n return globalThis.crypto.randomUUID();\r\n }\r\n throw new FlurError(\"Secure UUID generator unavailable; provide idempotencyKey\", \"INVALID_REQUEST\");\r\n}\r\n","import { z } from \"zod\";\r\n\r\nexport const E164Regex = /^\\+[1-9]\\d{7,14}$/;\r\n\r\nconst UuidSchema = z.string().uuid();\r\nconst IsoDateSchema = z.string().datetime({ offset: true });\r\nconst CurrencySchema = z.string().trim().length(3).transform((value) => value.toUpperCase());\r\n\r\nexport const HealthResponseSchema = z.object({\r\n ok: z.boolean(),\r\n});\r\n\r\nexport const WelcomeResponseSchema = z.object({\r\n message: z.string(),\r\n});\r\n\r\nexport const OnboardingStartRequestSchema = z.object({\r\n phoneE164: z.string().regex(E164Regex),\r\n appInstanceId: z.string().min(3),\r\n platform: z.enum([\"android\", \"ios\", \"web\"]),\r\n turnstileToken: z.string().min(20).optional(),\r\n appAttestation: z\r\n .object({\r\n provider: z.enum([\"android\", \"ios\", \"web\"]),\r\n token: z.string().min(24),\r\n })\r\n .optional(),\r\n firstName: z.string().trim().min(1).max(80).optional(),\r\n lastName: z.string().trim().min(1).max(80).optional(),\r\n});\r\n\r\nexport const OnboardingStartResponseSchema = z.object({\r\n requestId: z.string().min(1),\r\n checkUrl: z.string().url().optional(),\r\n expiresInSec: z.number().int().positive(),\r\n fallback: z.enum([\"SILENT_AUTH\", \"OTP\"]),\r\n});\r\n\r\nexport const OnboardingCompleteRequestSchema = z.object({\r\n requestId: z.string().min(1),\r\n code: z.string().min(1).max(32),\r\n appInstanceId: z.string().min(3),\r\n fingerprintHash: z.string().min(3).optional(),\r\n});\r\n\r\nexport const OnboardingCompleteResponseSchema = z.object({\r\n sessionToken: z.string().min(1),\r\n userId: UuidSchema,\r\n restricted: z.boolean(),\r\n risk_reasons: z.array(z.enum([\"SIM_SWAP_RECENT\", \"ROAMING\", \"CARRIER_CHANGED\"])),\r\n stepUpRequired: z.boolean().optional(),\r\n riskStatus: z.enum([\"ok\", \"unavailable\"]).optional(),\r\n});\r\n\r\nexport const RegisterDeviceRequestSchema = z.object({\r\n userId: UuidSchema,\r\n appInstanceId: z.string().min(3),\r\n platform: z.string().min(2),\r\n model: z.string().optional(),\r\n networkSignals: z.object({\r\n ip: z.string().min(3),\r\n asn: z.number().int().optional(),\r\n country: z.string().min(2).optional(),\r\n carrier: z.string().optional(),\r\n }),\r\n});\r\n\r\nexport const RegisterDeviceResponseSchema = z.object({\r\n deviceId: z.string().min(1),\r\n fingerprintHash: z.string().min(1),\r\n driftScore: z.number(),\r\n trustState: z.enum([\"TRUSTED_PRIMARY\", \"TRUSTED_SECONDARY\", \"UNVERIFIED\"]),\r\n stepUpRequired: z.boolean(),\r\n});\r\n\r\nexport const AuthRefreshRequestSchema = z.object({\r\n userId: UuidSchema,\r\n refreshToken: z.string().min(8),\r\n appInstanceId: z.string().min(3),\r\n fingerprintHash: z.string().min(3),\r\n});\r\n\r\nexport const AuthRefreshResponseSchema = z.object({\r\n refreshToken: z.string().min(8),\r\n stepUpRequired: z.boolean(),\r\n});\r\n\r\nexport const AuthLogoutRequestSchema = z.object({\r\n userId: UuidSchema,\r\n refreshToken: z.string().min(8),\r\n});\r\n\r\nexport const PinSetRequestSchema = z.object({\r\n userId: UuidSchema,\r\n pin: z.string().regex(/^\\d{6}$/),\r\n});\r\n\r\nexport const PinVerifyRequestSchema = z.object({\r\n userId: UuidSchema,\r\n pin: z.string().regex(/^\\d{6}$/),\r\n});\r\n\r\nexport const OkResponseSchema = z.object({\r\n ok: z.boolean(),\r\n});\r\n\r\nexport const RegisterSendDeviceKeyRequestSchema = z.object({\r\n userId: UuidSchema,\r\n deviceId: z.string().min(3),\r\n publicKey: z.string().min(32),\r\n});\r\n\r\nexport const SendChallengeRequestSchema = z.object({\r\n userId: UuidSchema,\r\n deviceId: z.string().min(3),\r\n});\r\n\r\nexport const SendChallengeResponseSchema = z.object({\r\n challengeId: UuidSchema,\r\n nonce: z.string().min(1),\r\n expiresAt: IsoDateSchema,\r\n});\r\n\r\nexport const SendVerifyRequestSchema = z.object({\r\n userId: UuidSchema,\r\n deviceId: z.string().min(3),\r\n challengeId: UuidSchema,\r\n signature: z.string().min(16),\r\n});\r\n\r\nexport const SendVerifyResponseSchema = z.object({\r\n sendAuthToken: z.string().min(16),\r\n});\r\n\r\nexport const ResolveRecipientRequestSchema = z.object({\r\n identifier: z.string().min(3),\r\n});\r\n\r\nexport const ResolveRecipientResponseSchema = z.object({\r\n recipientUserId: UuidSchema,\r\n displayName: z.string().min(1),\r\n normalizedIdentifier: z.string().regex(E164Regex),\r\n isActive: z.boolean(),\r\n});\r\n\r\nexport const CreateTransferRequestSchema = z.object({\r\n recipientIdentifier: z.string().min(3),\r\n amountMinor: z.number().int().positive(),\r\n currency: CurrencySchema,\r\n sendAuthToken: z.string().min(16),\r\n});\r\n\r\nconst TransferStatusSchema = z.enum([\"SETTLED\", \"PENDING_REVIEW\", \"DECLINED\"]);\r\n\r\nexport const TransferResponseSchema = z.object({\r\n transactionId: z.string().min(1),\r\n status: TransferStatusSchema,\r\n userStatus: TransferStatusSchema,\r\n recipientName: z.string().min(1),\r\n timestamp: IsoDateSchema,\r\n});\r\n\r\nconst DirectionSchema = z.enum([\"OUTGOING\", \"INCOMING\"]);\r\n\r\nexport const AccountActivityItemSchema = z.object({\r\n id: z.string().min(1),\r\n type: z.string().min(1),\r\n direction: DirectionSchema,\r\n name: z.string().min(1),\r\n identifier: z.string().min(1),\r\n amountMinor: z.number().int(),\r\n currency: CurrencySchema,\r\n status: z.string().min(1),\r\n timestamp: IsoDateSchema,\r\n});\r\n\r\nexport const AccountSummaryResponseSchema = z.object({\r\n balance: z.number().int(),\r\n currency: CurrencySchema,\r\n dailySendLimit: z.number().int().nonnegative(),\r\n dailySendRemaining: z.number().int().nonnegative(),\r\n kycTier: z.string().min(1),\r\n kycStatus: z.string().min(1),\r\n recentActivity: z.array(AccountActivityItemSchema),\r\n});\r\n\r\nexport const TransactionsListResponseSchema = z.object({\r\n items: z.array(AccountActivityItemSchema),\r\n nextCursor: z.string().nullable(),\r\n});\r\n\r\nexport const TransactionDetailResponseSchema = z.object({\r\n transactionId: z.string().min(1),\r\n type: z.string().min(1),\r\n direction: DirectionSchema,\r\n counterpartyName: z.string().min(1),\r\n counterpartyIdentifier: z.string().min(1),\r\n amountMinor: z.number().int(),\r\n currency: CurrencySchema,\r\n status: z.string().min(1),\r\n timestamp: IsoDateSchema,\r\n});\r\n\r\nexport const PushRegisterRequestSchema = z.object({\r\n deviceId: z.string().min(3),\r\n platform: z.enum([\"ios\", \"android\", \"web\"]),\r\n token: z.string().min(16),\r\n});\r\n\r\nexport const CreatePayLinkResponseSchema = z.object({\r\n token: z.string().min(1),\r\n});\r\n\r\nexport const ResolvePayLinkResponseSchema = z.object({\r\n recipientUserId: UuidSchema,\r\n displayName: z.string().min(1),\r\n normalizedIdentifier: z.string().regex(E164Regex),\r\n isActive: z.boolean(),\r\n});\r\n","export type FlurErrorCode =\r\n | \"NETWORK_ERROR\"\r\n | \"HTTP_ERROR\"\r\n | \"TIMEOUT\"\r\n | \"UNKNOWN\"\r\n | \"UNAUTHORIZED\"\r\n | \"INVALID_REQUEST\"\r\n | \"RATE_LIMITED\"\r\n | \"NOT_FOUND\"\r\n | \"USER_NOT_FOUND\"\r\n | \"TOKEN_REPLAYED\"\r\n | \"SESSION_MISMATCH\"\r\n | \"PIN_INVALID\"\r\n | \"PIN_LOCKED\"\r\n | \"PIN_NOT_SET\"\r\n | \"STEP_UP_REQUIRED\"\r\n | \"DEVICE_KEY_NOT_REGISTERED\"\r\n | \"CHALLENGE_INVALID\"\r\n | \"CHALLENGE_EXPIRED\"\r\n | \"DEVICE_KEY_REVOKED\"\r\n | \"SIGNATURE_INVALID\"\r\n | \"INVALID_RECIPIENT\"\r\n | \"SEND_AUTH_INVALID\"\r\n | \"IDEMPOTENCY_KEY_CONFLICT\"\r\n | \"IDEMPOTENCY_IN_PROGRESS\"\r\n | \"CANNOT_SEND_TO_SELF\"\r\n | \"INSUFFICIENT_FUNDS\";\r\n\r\nconst backendErrorCodeSet: ReadonlySet<FlurErrorCode> = new Set([\r\n \"UNAUTHORIZED\",\r\n \"INVALID_REQUEST\",\r\n \"RATE_LIMITED\",\r\n \"NOT_FOUND\",\r\n \"USER_NOT_FOUND\",\r\n \"TOKEN_REPLAYED\",\r\n \"SESSION_MISMATCH\",\r\n \"PIN_INVALID\",\r\n \"PIN_LOCKED\",\r\n \"PIN_NOT_SET\",\r\n \"STEP_UP_REQUIRED\",\r\n \"DEVICE_KEY_NOT_REGISTERED\",\r\n \"CHALLENGE_INVALID\",\r\n \"CHALLENGE_EXPIRED\",\r\n \"DEVICE_KEY_REVOKED\",\r\n \"SIGNATURE_INVALID\",\r\n \"INVALID_RECIPIENT\",\r\n \"SEND_AUTH_INVALID\",\r\n \"IDEMPOTENCY_KEY_CONFLICT\",\r\n \"IDEMPOTENCY_IN_PROGRESS\",\r\n \"CANNOT_SEND_TO_SELF\",\r\n \"INSUFFICIENT_FUNDS\",\r\n]);\r\n\r\nexport class FlurError extends Error {\r\n public readonly code: FlurErrorCode;\r\n public readonly status?: number;\r\n public readonly details?: unknown;\r\n public readonly reqId?: string | null;\r\n\r\n constructor(message: string, code: FlurErrorCode, opts?: { status?: number; details?: unknown; reqId?: string | null }) {\r\n super(message);\r\n this.name = \"FlurError\";\r\n this.code = code;\r\n this.status = opts?.status;\r\n this.details = opts?.details;\r\n this.reqId = opts?.reqId;\r\n }\r\n}\r\n\r\nexport async function mapToFlurError(res: Response): Promise<FlurError> {\r\n const reqId = res.headers.get(\"x-request-id\");\r\n let details: unknown = undefined;\r\n let mappedCode: FlurErrorCode = \"HTTP_ERROR\";\r\n let mappedMessage = `HTTP ${res.status}`;\r\n try {\r\n const ct = res.headers.get(\"content-type\") ?? \"\";\r\n details = ct.includes(\"application/json\") ? await res.json() : await res.text();\r\n if (details && typeof details === \"object\") {\r\n const candidateCode = (details as { code?: unknown }).code;\r\n const candidateMessage = (details as { message?: unknown; error?: unknown }).message;\r\n const fallbackMessage = (details as { error?: unknown }).error;\r\n\r\n if (typeof candidateCode === \"string\" && backendErrorCodeSet.has(candidateCode as FlurErrorCode)) {\r\n mappedCode = candidateCode as FlurErrorCode;\r\n }\r\n\r\n if (typeof candidateMessage === \"string\" && candidateMessage.length > 0) {\r\n mappedMessage = candidateMessage;\r\n } else if (typeof fallbackMessage === \"string\" && fallbackMessage.length > 0) {\r\n mappedMessage = fallbackMessage;\r\n }\r\n }\r\n } catch {\r\n // ignore parse issues\r\n }\r\n return new FlurError(mappedMessage, mappedCode, { status: res.status, details, reqId });\r\n}\r\n","import { z } from \"zod\";\r\nimport { FlurError } from \"./errors.js\";\r\nimport { E164Regex } from \"./contracts.js\";\r\n\r\nexport type Money = {\r\n amountMinor: bigint;\r\n currency: string;\r\n};\r\n\r\nconst CurrencyCodeSchema = z.string().trim().length(3).transform((value) => value.toUpperCase());\r\n\r\nconst currencyFractionDigits: Record<string, number> = {\r\n NGN: 2,\r\n USD: 2,\r\n EUR: 2,\r\n GBP: 2,\r\n CAD: 2,\r\n JPY: 0,\r\n};\r\n\r\nfunction getFractionDigits(currency: string): number {\r\n return currencyFractionDigits[currency] ?? 2;\r\n}\r\n\r\nexport function parseAmountInput(value: string, currency: string): Money {\r\n const normalizedCurrency = CurrencyCodeSchema.parse(currency);\r\n const raw = value.trim();\r\n\r\n if (!/^\\d+(\\.\\d+)?$/.test(raw)) {\r\n throw new FlurError(\"Invalid amount format\", \"INVALID_REQUEST\");\r\n }\r\n\r\n const [whole, fraction = \"\"] = raw.split(\".\");\r\n const fractionDigits = getFractionDigits(normalizedCurrency);\r\n\r\n if (fraction.length > fractionDigits) {\r\n throw new FlurError(\"Too many decimal places for currency\", \"INVALID_REQUEST\");\r\n }\r\n\r\n const paddedFraction = fraction.padEnd(fractionDigits, \"0\");\r\n const minorAsString = `${whole}${paddedFraction}`.replace(/^0+(\\d)/, \"$1\") || \"0\";\r\n const amountMinor = BigInt(minorAsString);\r\n\r\n if (amountMinor < 0n) {\r\n throw new FlurError(\"Amount cannot be negative\", \"INVALID_REQUEST\");\r\n }\r\n\r\n return {\r\n amountMinor,\r\n currency: normalizedCurrency,\r\n };\r\n}\r\n\r\nexport function formatAmount(amountMinor: bigint, currency: string): string {\r\n const normalizedCurrency = CurrencyCodeSchema.parse(currency);\r\n if (amountMinor < 0n) {\r\n throw new FlurError(\"Amount cannot be negative\", \"INVALID_REQUEST\");\r\n }\r\n\r\n const fractionDigits = getFractionDigits(normalizedCurrency);\r\n if (fractionDigits === 0) {\r\n return amountMinor.toString();\r\n }\r\n\r\n const divisor = 10n ** BigInt(fractionDigits);\r\n const whole = amountMinor / divisor;\r\n const fraction = (amountMinor % divisor).toString().padStart(fractionDigits, \"0\");\r\n return `${whole.toString()}.${fraction}`;\r\n}\r\n\r\nconst defaultCountryDialingCode: Record<string, string> = {\r\n NG: \"234\",\r\n US: \"1\",\r\n CA: \"1\",\r\n GB: \"44\",\r\n};\r\n\r\nexport function normalizeE164(input: string, defaultCountry?: string): string {\r\n const trimmed = input.trim();\r\n\r\n if (trimmed.startsWith(\"+\")) {\r\n if (!E164Regex.test(trimmed)) {\r\n throw new FlurError(\"Invalid phone number\", \"INVALID_REQUEST\");\r\n }\r\n return trimmed;\r\n }\r\n\r\n const digits = trimmed.replace(/\\D/g, \"\");\r\n if (digits.length === 0) {\r\n throw new FlurError(\"Invalid phone number\", \"INVALID_REQUEST\");\r\n }\r\n\r\n if (digits.startsWith(\"00\")) {\r\n const candidate = `+${digits.slice(2)}`;\r\n if (!E164Regex.test(candidate)) {\r\n throw new FlurError(\"Invalid phone number\", \"INVALID_REQUEST\");\r\n }\r\n return candidate;\r\n }\r\n\r\n const normalizedCountry = defaultCountry?.trim().toUpperCase();\r\n const countryCode = normalizedCountry ? defaultCountryDialingCode[normalizedCountry] : undefined;\r\n if (!countryCode) {\r\n throw new FlurError(\"Invalid phone number\", \"INVALID_REQUEST\");\r\n }\r\n\r\n const localDigits = digits.startsWith(\"0\") ? digits.slice(1) : digits;\r\n const candidate = `+${countryCode}${localDigits}`;\r\n if (!E164Regex.test(candidate)) {\r\n throw new FlurError(\"Invalid phone number\", \"INVALID_REQUEST\");\r\n }\r\n return candidate;\r\n}\r\n\r\nexport function moneyMinorToNumber(amountMinor: bigint): number {\r\n const asNumber = Number(amountMinor);\r\n if (!Number.isSafeInteger(asNumber)) {\r\n throw new FlurError(\"Amount exceeds safe integer range\", \"INVALID_REQUEST\");\r\n }\r\n return asNumber;\r\n}\r\n"],"mappings":"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","names":["z","z","z","candidate","z"]}
|
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
{
|
|
2
|
-
"openapi": "3.0.3",
|
|
3
|
-
"info": {
|
|
4
|
-
"title": "Flur API",
|
|
5
|
-
"version": "0.1.0"
|
|
6
|
-
},
|
|
7
|
-
"paths": {
|
|
8
|
-
"/health": {
|
|
9
|
-
"get": {
|
|
10
|
-
"responses": {
|
|
11
|
-
"200": {
|
|
12
|
-
"description": "OK",
|
|
13
|
-
"content": {
|
|
14
|
-
"application/json": {
|
|
15
|
-
"schema": {
|
|
16
|
-
"type": "object",
|
|
17
|
-
"properties": {
|
|
18
|
-
"ok": {
|
|
19
|
-
"type": "boolean"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"required": [
|
|
23
|
-
"ok"
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"/welcome": {
|
|
33
|
-
"get": {
|
|
34
|
-
"responses": {
|
|
35
|
-
"200": {
|
|
36
|
-
"description": "OK",
|
|
37
|
-
"content": {
|
|
38
|
-
"application/json": {
|
|
39
|
-
"schema": {
|
|
40
|
-
"type": "object",
|
|
41
|
-
"properties": {
|
|
42
|
-
"message": {
|
|
43
|
-
"type": "string",
|
|
44
|
-
"example": "welcome to flur wallet. Today is 2026-02-07T19:09:53.238Z"
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"required": [
|
|
48
|
-
"message"
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
"/v1/onboarding/start": {
|
|
58
|
-
"post": {
|
|
59
|
-
"requestBody": {
|
|
60
|
-
"required": true,
|
|
61
|
-
"content": {
|
|
62
|
-
"application/json": {
|
|
63
|
-
"schema": {
|
|
64
|
-
"type": "object",
|
|
65
|
-
"properties": {
|
|
66
|
-
"phoneE164": {
|
|
67
|
-
"type": "string"
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"required": [
|
|
71
|
-
"phoneE164"
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
"responses": {
|
|
78
|
-
"200": {
|
|
79
|
-
"description": "Verification started",
|
|
80
|
-
"content": {
|
|
81
|
-
"application/json": {
|
|
82
|
-
"schema": {
|
|
83
|
-
"type": "object",
|
|
84
|
-
"properties": {
|
|
85
|
-
"requestId": {
|
|
86
|
-
"type": "string"
|
|
87
|
-
},
|
|
88
|
-
"checkUrl": {
|
|
89
|
-
"type": "string"
|
|
90
|
-
},
|
|
91
|
-
"expiresInSec": {
|
|
92
|
-
"type": "integer"
|
|
93
|
-
},
|
|
94
|
-
"fallback": {
|
|
95
|
-
"type": "string",
|
|
96
|
-
"enum": [
|
|
97
|
-
"SILENT_AUTH",
|
|
98
|
-
"OTP"
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
"required": [
|
|
103
|
-
"requestId",
|
|
104
|
-
"expiresInSec",
|
|
105
|
-
"fallback"
|
|
106
|
-
]
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
"400": {
|
|
112
|
-
"description": "Unable to start verification",
|
|
113
|
-
"content": {
|
|
114
|
-
"application/json": {
|
|
115
|
-
"schema": {
|
|
116
|
-
"type": "object",
|
|
117
|
-
"properties": {
|
|
118
|
-
"error": {
|
|
119
|
-
"type": "string"
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
"required": [
|
|
123
|
-
"error"
|
|
124
|
-
]
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
"429": {
|
|
130
|
-
"description": "Rate limited",
|
|
131
|
-
"content": {
|
|
132
|
-
"application/json": {
|
|
133
|
-
"schema": {
|
|
134
|
-
"type": "object",
|
|
135
|
-
"properties": {
|
|
136
|
-
"error": {
|
|
137
|
-
"type": "string"
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
"required": [
|
|
141
|
-
"error"
|
|
142
|
-
]
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
"/v1/onboarding/complete": {
|
|
151
|
-
"post": {
|
|
152
|
-
"requestBody": {
|
|
153
|
-
"required": true,
|
|
154
|
-
"content": {
|
|
155
|
-
"application/json": {
|
|
156
|
-
"schema": {
|
|
157
|
-
"type": "object",
|
|
158
|
-
"properties": {
|
|
159
|
-
"requestId": {
|
|
160
|
-
"type": "string"
|
|
161
|
-
},
|
|
162
|
-
"code": {
|
|
163
|
-
"type": "string"
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
"required": [
|
|
167
|
-
"requestId",
|
|
168
|
-
"code"
|
|
169
|
-
]
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
"responses": {
|
|
175
|
-
"200": {
|
|
176
|
-
"description": "Verification completed",
|
|
177
|
-
"content": {
|
|
178
|
-
"application/json": {
|
|
179
|
-
"schema": {
|
|
180
|
-
"type": "object",
|
|
181
|
-
"properties": {
|
|
182
|
-
"sessionToken": {
|
|
183
|
-
"type": "string"
|
|
184
|
-
},
|
|
185
|
-
"userId": {
|
|
186
|
-
"type": "string"
|
|
187
|
-
},
|
|
188
|
-
"restricted": {
|
|
189
|
-
"type": "boolean"
|
|
190
|
-
},
|
|
191
|
-
"risk_reasons": {
|
|
192
|
-
"type": "array",
|
|
193
|
-
"items": {
|
|
194
|
-
"type": "string",
|
|
195
|
-
"enum": [
|
|
196
|
-
"SIM_SWAP_RECENT",
|
|
197
|
-
"ROAMING",
|
|
198
|
-
"CARRIER_CHANGED"
|
|
199
|
-
]
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
"required": [
|
|
204
|
-
"sessionToken",
|
|
205
|
-
"userId",
|
|
206
|
-
"restricted",
|
|
207
|
-
"risk_reasons"
|
|
208
|
-
]
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
"400": {
|
|
214
|
-
"description": "Verification failed",
|
|
215
|
-
"content": {
|
|
216
|
-
"application/json": {
|
|
217
|
-
"schema": {
|
|
218
|
-
"type": "object",
|
|
219
|
-
"properties": {
|
|
220
|
-
"error": {
|
|
221
|
-
"type": "string"
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
"required": [
|
|
225
|
-
"error"
|
|
226
|
-
]
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.3",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Flur API",
|
|
5
|
+
"version": "0.1.0"
|
|
6
|
+
},
|
|
7
|
+
"paths": {
|
|
8
|
+
"/health": {
|
|
9
|
+
"get": {
|
|
10
|
+
"responses": {
|
|
11
|
+
"200": {
|
|
12
|
+
"description": "OK",
|
|
13
|
+
"content": {
|
|
14
|
+
"application/json": {
|
|
15
|
+
"schema": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"ok": {
|
|
19
|
+
"type": "boolean"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"ok"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"/welcome": {
|
|
33
|
+
"get": {
|
|
34
|
+
"responses": {
|
|
35
|
+
"200": {
|
|
36
|
+
"description": "OK",
|
|
37
|
+
"content": {
|
|
38
|
+
"application/json": {
|
|
39
|
+
"schema": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"message": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"example": "welcome to flur wallet. Today is 2026-02-07T19:09:53.238Z"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"message"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"/v1/onboarding/start": {
|
|
58
|
+
"post": {
|
|
59
|
+
"requestBody": {
|
|
60
|
+
"required": true,
|
|
61
|
+
"content": {
|
|
62
|
+
"application/json": {
|
|
63
|
+
"schema": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"phoneE164": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"required": [
|
|
71
|
+
"phoneE164"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"responses": {
|
|
78
|
+
"200": {
|
|
79
|
+
"description": "Verification started",
|
|
80
|
+
"content": {
|
|
81
|
+
"application/json": {
|
|
82
|
+
"schema": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"properties": {
|
|
85
|
+
"requestId": {
|
|
86
|
+
"type": "string"
|
|
87
|
+
},
|
|
88
|
+
"checkUrl": {
|
|
89
|
+
"type": "string"
|
|
90
|
+
},
|
|
91
|
+
"expiresInSec": {
|
|
92
|
+
"type": "integer"
|
|
93
|
+
},
|
|
94
|
+
"fallback": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"enum": [
|
|
97
|
+
"SILENT_AUTH",
|
|
98
|
+
"OTP"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"required": [
|
|
103
|
+
"requestId",
|
|
104
|
+
"expiresInSec",
|
|
105
|
+
"fallback"
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"400": {
|
|
112
|
+
"description": "Unable to start verification",
|
|
113
|
+
"content": {
|
|
114
|
+
"application/json": {
|
|
115
|
+
"schema": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"properties": {
|
|
118
|
+
"error": {
|
|
119
|
+
"type": "string"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"required": [
|
|
123
|
+
"error"
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"429": {
|
|
130
|
+
"description": "Rate limited",
|
|
131
|
+
"content": {
|
|
132
|
+
"application/json": {
|
|
133
|
+
"schema": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"properties": {
|
|
136
|
+
"error": {
|
|
137
|
+
"type": "string"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"required": [
|
|
141
|
+
"error"
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"/v1/onboarding/complete": {
|
|
151
|
+
"post": {
|
|
152
|
+
"requestBody": {
|
|
153
|
+
"required": true,
|
|
154
|
+
"content": {
|
|
155
|
+
"application/json": {
|
|
156
|
+
"schema": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"properties": {
|
|
159
|
+
"requestId": {
|
|
160
|
+
"type": "string"
|
|
161
|
+
},
|
|
162
|
+
"code": {
|
|
163
|
+
"type": "string"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": [
|
|
167
|
+
"requestId",
|
|
168
|
+
"code"
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"responses": {
|
|
175
|
+
"200": {
|
|
176
|
+
"description": "Verification completed",
|
|
177
|
+
"content": {
|
|
178
|
+
"application/json": {
|
|
179
|
+
"schema": {
|
|
180
|
+
"type": "object",
|
|
181
|
+
"properties": {
|
|
182
|
+
"sessionToken": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
},
|
|
185
|
+
"userId": {
|
|
186
|
+
"type": "string"
|
|
187
|
+
},
|
|
188
|
+
"restricted": {
|
|
189
|
+
"type": "boolean"
|
|
190
|
+
},
|
|
191
|
+
"risk_reasons": {
|
|
192
|
+
"type": "array",
|
|
193
|
+
"items": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"enum": [
|
|
196
|
+
"SIM_SWAP_RECENT",
|
|
197
|
+
"ROAMING",
|
|
198
|
+
"CARRIER_CHANGED"
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"required": [
|
|
204
|
+
"sessionToken",
|
|
205
|
+
"userId",
|
|
206
|
+
"restricted",
|
|
207
|
+
"risk_reasons"
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"400": {
|
|
214
|
+
"description": "Verification failed",
|
|
215
|
+
"content": {
|
|
216
|
+
"application/json": {
|
|
217
|
+
"schema": {
|
|
218
|
+
"type": "object",
|
|
219
|
+
"properties": {
|
|
220
|
+
"error": {
|
|
221
|
+
"type": "string"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"required": [
|
|
225
|
+
"error"
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
235
|
}
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@nokinc-flur/sdk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Flur Wallet SDK (sprint 1 scaffold)",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"types": "dist/index.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"default": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"openapi",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"lint": "eslint .",
|
|
23
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
|
-
"test": "vitest run",
|
|
25
|
-
"test:contract": "vitest run test/e2e.health.test.ts test/e2e.send-money.test.ts",
|
|
26
|
-
"e2e:bootstrap": "node scripts/bootstrap-e2e-sendmoney.mjs",
|
|
27
|
-
"build": "tsup src/index.ts --format esm --dts",
|
|
28
|
-
"gen": "openapi-typescript openapi/flur.openapi.json -o src/gen/openapi-types.ts",
|
|
29
|
-
"prod:release": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/prod-build-publish-npm.ps1",
|
|
30
|
-
"prod:release:publish": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/prod-build-publish-npm.ps1 -Publish",
|
|
31
|
-
"release:patch": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1 -Bump patch",
|
|
32
|
-
"release:minor": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1 -Bump minor",
|
|
33
|
-
"release:major": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1 -Bump major"
|
|
34
|
-
},
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"zod": "^3.23.8"
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"@types/node": "^20.11.30",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
|
41
|
-
"@typescript-eslint/parser": "^7.7.1",
|
|
42
|
-
"eslint": "^8.57.0",
|
|
43
|
-
"eslint-config-prettier": "^9.1.0",
|
|
44
|
-
"openapi-typescript": "^7.5.1",
|
|
45
|
-
"prettier": "^3.2.5",
|
|
46
|
-
"tsup": "^8.0.2",
|
|
47
|
-
"typescript": "^5.4.5",
|
|
48
|
-
"vitest": "^1.5.0"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@nokinc-flur/sdk",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "Flur Wallet SDK (sprint 1 scaffold)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"openapi",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:contract": "vitest run test/e2e.health.test.ts test/e2e.send-money.test.ts",
|
|
26
|
+
"e2e:bootstrap": "node scripts/bootstrap-e2e-sendmoney.mjs",
|
|
27
|
+
"build": "tsup src/index.ts --format esm --dts --sourcemap",
|
|
28
|
+
"gen": "openapi-typescript openapi/flur.openapi.json -o src/gen/openapi-types.ts",
|
|
29
|
+
"prod:release": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/prod-build-publish-npm.ps1",
|
|
30
|
+
"prod:release:publish": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/prod-build-publish-npm.ps1 -Publish",
|
|
31
|
+
"release:patch": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1 -Bump patch",
|
|
32
|
+
"release:minor": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1 -Bump minor",
|
|
33
|
+
"release:major": "pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1 -Bump major"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"zod": "^3.23.8"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.11.30",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
|
41
|
+
"@typescript-eslint/parser": "^7.7.1",
|
|
42
|
+
"eslint": "^8.57.0",
|
|
43
|
+
"eslint-config-prettier": "^9.1.0",
|
|
44
|
+
"openapi-typescript": "^7.5.1",
|
|
45
|
+
"prettier": "^3.2.5",
|
|
46
|
+
"tsup": "^8.0.2",
|
|
47
|
+
"typescript": "^5.4.5",
|
|
48
|
+
"vitest": "^1.5.0"
|
|
49
|
+
}
|
|
50
|
+
}
|