@nokinc-flur/sdk 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +229 -229
- package/dist/index.cjs +833 -788
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1289 -1030
- package/dist/index.d.ts +1289 -1030
- package/dist/index.js +832 -788
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,201 +1,11 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
|
-
import { z as
|
|
2
|
+
import { z as z5 } from "zod";
|
|
3
3
|
|
|
4
4
|
// src/contracts.ts
|
|
5
|
+
import { z as z2 } from "zod";
|
|
6
|
+
|
|
7
|
+
// src/me-offline/client.ts
|
|
5
8
|
import { z } from "zod";
|
|
6
|
-
var E164Regex = /^\+[1-9]\d{7,14}$/;
|
|
7
|
-
var UuidSchema = z.string().uuid();
|
|
8
|
-
var IsoDateSchema = z.string().datetime({ offset: true });
|
|
9
|
-
var CurrencySchema = z.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
10
|
-
var HealthResponseSchema = z.object({
|
|
11
|
-
ok: z.boolean()
|
|
12
|
-
});
|
|
13
|
-
var WelcomeResponseSchema = z.object({
|
|
14
|
-
message: z.string()
|
|
15
|
-
});
|
|
16
|
-
var OnboardingStartRequestSchema = z.object({
|
|
17
|
-
phoneE164: z.string().regex(E164Regex),
|
|
18
|
-
appInstanceId: z.string().min(3),
|
|
19
|
-
platform: z.enum(["android", "ios", "web"]),
|
|
20
|
-
turnstileToken: z.string().min(20).optional(),
|
|
21
|
-
appAttestation: z.object({
|
|
22
|
-
provider: z.enum(["android", "ios", "web"]),
|
|
23
|
-
token: z.string().min(24)
|
|
24
|
-
}).optional(),
|
|
25
|
-
firstName: z.string().trim().min(1).max(80).optional(),
|
|
26
|
-
lastName: z.string().trim().min(1).max(80).optional()
|
|
27
|
-
});
|
|
28
|
-
var OnboardingStartResponseSchema = z.object({
|
|
29
|
-
requestId: z.string().min(1),
|
|
30
|
-
checkUrl: z.string().url().optional(),
|
|
31
|
-
expiresInSec: z.number().int().positive(),
|
|
32
|
-
fallback: z.enum(["SILENT_AUTH", "OTP"])
|
|
33
|
-
});
|
|
34
|
-
var OnboardingCompleteRequestSchema = z.object({
|
|
35
|
-
requestId: z.string().min(1),
|
|
36
|
-
code: z.string().min(1).max(32),
|
|
37
|
-
appInstanceId: z.string().min(3),
|
|
38
|
-
fingerprintHash: z.string().min(3).optional()
|
|
39
|
-
});
|
|
40
|
-
var OnboardingCompleteResponseSchema = z.object({
|
|
41
|
-
sessionToken: z.string().min(1),
|
|
42
|
-
userId: UuidSchema,
|
|
43
|
-
restricted: z.boolean(),
|
|
44
|
-
risk_reasons: z.array(
|
|
45
|
-
z.enum(["SIM_SWAP_RECENT", "ROAMING", "CARRIER_CHANGED"])
|
|
46
|
-
),
|
|
47
|
-
stepUpRequired: z.boolean().optional(),
|
|
48
|
-
riskStatus: z.enum(["ok", "unavailable"]).optional()
|
|
49
|
-
});
|
|
50
|
-
var RegisterDeviceRequestSchema = z.object({
|
|
51
|
-
userId: UuidSchema,
|
|
52
|
-
appInstanceId: z.string().min(3),
|
|
53
|
-
platform: z.string().min(2),
|
|
54
|
-
model: z.string().optional(),
|
|
55
|
-
networkSignals: z.object({
|
|
56
|
-
ip: z.string().min(3),
|
|
57
|
-
asn: z.number().int().optional(),
|
|
58
|
-
country: z.string().min(2).optional(),
|
|
59
|
-
carrier: z.string().optional()
|
|
60
|
-
})
|
|
61
|
-
});
|
|
62
|
-
var RegisterDeviceResponseSchema = z.object({
|
|
63
|
-
deviceId: z.string().min(1),
|
|
64
|
-
fingerprintHash: z.string().min(1),
|
|
65
|
-
driftScore: z.number(),
|
|
66
|
-
trustState: z.enum(["TRUSTED_PRIMARY", "TRUSTED_SECONDARY", "UNVERIFIED"]),
|
|
67
|
-
stepUpRequired: z.boolean()
|
|
68
|
-
});
|
|
69
|
-
var AuthRefreshRequestSchema = z.object({
|
|
70
|
-
userId: UuidSchema,
|
|
71
|
-
refreshToken: z.string().min(8),
|
|
72
|
-
appInstanceId: z.string().min(3),
|
|
73
|
-
fingerprintHash: z.string().min(3)
|
|
74
|
-
});
|
|
75
|
-
var AuthRefreshResponseSchema = z.object({
|
|
76
|
-
refreshToken: z.string().min(8),
|
|
77
|
-
stepUpRequired: z.boolean()
|
|
78
|
-
});
|
|
79
|
-
var AuthLogoutRequestSchema = z.object({
|
|
80
|
-
userId: UuidSchema,
|
|
81
|
-
refreshToken: z.string().min(8)
|
|
82
|
-
});
|
|
83
|
-
var PinSetRequestSchema = z.object({
|
|
84
|
-
userId: UuidSchema,
|
|
85
|
-
pin: z.string().regex(/^\d{6}$/)
|
|
86
|
-
});
|
|
87
|
-
var PinVerifyRequestSchema = z.object({
|
|
88
|
-
userId: UuidSchema,
|
|
89
|
-
pin: z.string().regex(/^\d{6}$/)
|
|
90
|
-
});
|
|
91
|
-
var OkResponseSchema = z.object({
|
|
92
|
-
ok: z.boolean()
|
|
93
|
-
});
|
|
94
|
-
var RegisterSendDeviceKeyRequestSchema = z.object({
|
|
95
|
-
userId: UuidSchema,
|
|
96
|
-
deviceId: z.string().min(3),
|
|
97
|
-
publicKey: z.string().min(32)
|
|
98
|
-
});
|
|
99
|
-
var SEND_AUTH_PURPOSES = ["send_money", "offline_revoke"];
|
|
100
|
-
var SendChallengeRequestSchema = z.object({
|
|
101
|
-
userId: UuidSchema,
|
|
102
|
-
deviceId: z.string().min(3),
|
|
103
|
-
purpose: z.enum(SEND_AUTH_PURPOSES).optional()
|
|
104
|
-
});
|
|
105
|
-
var SendChallengeResponseSchema = z.object({
|
|
106
|
-
challengeId: UuidSchema,
|
|
107
|
-
nonce: z.string().min(1),
|
|
108
|
-
expiresAt: IsoDateSchema
|
|
109
|
-
});
|
|
110
|
-
var SendVerifyRequestSchema = z.object({
|
|
111
|
-
userId: UuidSchema,
|
|
112
|
-
deviceId: z.string().min(3),
|
|
113
|
-
challengeId: UuidSchema,
|
|
114
|
-
signature: z.string().min(16)
|
|
115
|
-
});
|
|
116
|
-
var SendVerifyResponseSchema = z.object({
|
|
117
|
-
sendAuthToken: z.string().min(16)
|
|
118
|
-
});
|
|
119
|
-
var ResolveRecipientRequestSchema = z.object({
|
|
120
|
-
identifier: z.string().min(3)
|
|
121
|
-
});
|
|
122
|
-
var ResolveRecipientResponseSchema = z.object({
|
|
123
|
-
recipientUserId: UuidSchema,
|
|
124
|
-
displayName: z.string().min(1),
|
|
125
|
-
normalizedIdentifier: z.string().regex(E164Regex),
|
|
126
|
-
isActive: z.boolean()
|
|
127
|
-
});
|
|
128
|
-
var CreateTransferRequestSchema = z.object({
|
|
129
|
-
recipientIdentifier: z.string().min(3),
|
|
130
|
-
amountMinor: z.number().int().positive(),
|
|
131
|
-
currency: CurrencySchema,
|
|
132
|
-
sendAuthToken: z.string().min(16)
|
|
133
|
-
});
|
|
134
|
-
var TransferStatusSchema = z.enum(["SETTLED", "PENDING_REVIEW", "DECLINED"]);
|
|
135
|
-
var TransferResponseSchema = z.object({
|
|
136
|
-
transactionId: z.string().min(1),
|
|
137
|
-
status: TransferStatusSchema,
|
|
138
|
-
userStatus: TransferStatusSchema,
|
|
139
|
-
recipientName: z.string().min(1),
|
|
140
|
-
timestamp: IsoDateSchema
|
|
141
|
-
});
|
|
142
|
-
var DirectionSchema = z.enum(["OUTGOING", "INCOMING"]);
|
|
143
|
-
var AccountActivityItemSchema = z.object({
|
|
144
|
-
id: z.string().min(1),
|
|
145
|
-
type: z.string().min(1),
|
|
146
|
-
direction: DirectionSchema,
|
|
147
|
-
name: z.string().min(1),
|
|
148
|
-
identifier: z.string().min(1),
|
|
149
|
-
amountMinor: z.number().int(),
|
|
150
|
-
currency: CurrencySchema,
|
|
151
|
-
status: z.string().min(1),
|
|
152
|
-
timestamp: IsoDateSchema
|
|
153
|
-
});
|
|
154
|
-
var AccountSummaryResponseSchema = z.object({
|
|
155
|
-
/** Authenticated user's stable internal id. */
|
|
156
|
-
userId: UuidSchema,
|
|
157
|
-
/**
|
|
158
|
-
* 10-digit Nigeria Uniform Bank Account Number (NUBAN) allocated by the
|
|
159
|
-
* bank partner. `null` when the user has no partner-allocated account yet.
|
|
160
|
-
*/
|
|
161
|
-
nuban: z.string().regex(/^[0-9]{10}$/).nullable(),
|
|
162
|
-
balance: z.number().int(),
|
|
163
|
-
currency: CurrencySchema,
|
|
164
|
-
dailySendLimit: z.number().int().nonnegative(),
|
|
165
|
-
dailySendRemaining: z.number().int().nonnegative(),
|
|
166
|
-
kycTier: z.string().min(1),
|
|
167
|
-
kycStatus: z.string().min(1),
|
|
168
|
-
recentActivity: z.array(AccountActivityItemSchema)
|
|
169
|
-
});
|
|
170
|
-
var TransactionsListResponseSchema = z.object({
|
|
171
|
-
items: z.array(AccountActivityItemSchema),
|
|
172
|
-
nextCursor: z.string().nullable()
|
|
173
|
-
});
|
|
174
|
-
var TransactionDetailResponseSchema = z.object({
|
|
175
|
-
transactionId: z.string().min(1),
|
|
176
|
-
type: z.string().min(1),
|
|
177
|
-
direction: DirectionSchema,
|
|
178
|
-
counterpartyName: z.string().min(1),
|
|
179
|
-
counterpartyIdentifier: z.string().min(1),
|
|
180
|
-
amountMinor: z.number().int(),
|
|
181
|
-
currency: CurrencySchema,
|
|
182
|
-
status: z.string().min(1),
|
|
183
|
-
timestamp: IsoDateSchema
|
|
184
|
-
});
|
|
185
|
-
var PushRegisterRequestSchema = z.object({
|
|
186
|
-
deviceId: z.string().min(3),
|
|
187
|
-
platform: z.enum(["ios", "android", "web"]),
|
|
188
|
-
token: z.string().min(16)
|
|
189
|
-
});
|
|
190
|
-
var CreatePayLinkResponseSchema = z.object({
|
|
191
|
-
token: z.string().min(1)
|
|
192
|
-
});
|
|
193
|
-
var ResolvePayLinkResponseSchema = z.object({
|
|
194
|
-
recipientUserId: UuidSchema,
|
|
195
|
-
displayName: z.string().min(1),
|
|
196
|
-
normalizedIdentifier: z.string().regex(E164Regex),
|
|
197
|
-
isActive: z.boolean()
|
|
198
|
-
});
|
|
199
9
|
|
|
200
10
|
// src/errors.ts
|
|
201
11
|
var backendErrorCodeSet = /* @__PURE__ */ new Set([
|
|
@@ -299,9 +109,556 @@ async function mapToFlurError(res) {
|
|
|
299
109
|
});
|
|
300
110
|
}
|
|
301
111
|
|
|
112
|
+
// src/me-offline/client.ts
|
|
113
|
+
var Hex64 = z.string().regex(/^[0-9a-f]{64}$/i);
|
|
114
|
+
var Sha256Hex = z.string().regex(/^[0-9a-f]{64}$/i);
|
|
115
|
+
var Base64Std = z.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
116
|
+
var RegisterDeviceKeyInputSchema = z.object({
|
|
117
|
+
deviceId: z.string().min(1).max(128),
|
|
118
|
+
publicKeyHex: Hex64
|
|
119
|
+
});
|
|
120
|
+
var AttestationSecurityLevelSchema = z.enum([
|
|
121
|
+
"STRONGBOX",
|
|
122
|
+
"TEE",
|
|
123
|
+
"SECURE_ENCLAVE",
|
|
124
|
+
"SOFTWARE"
|
|
125
|
+
]);
|
|
126
|
+
var DeviceKeyAlgSchema = z.literal("p256");
|
|
127
|
+
var RegisterDeviceKeyP256InputSchema = z.object({
|
|
128
|
+
deviceId: z.string().min(1).max(128),
|
|
129
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
130
|
+
publicKeySpkiB64: Base64Std.min(64).max(4096),
|
|
131
|
+
/** Base64 of the server-issued enrollment challenge string. */
|
|
132
|
+
challengeB64: Base64Std.min(8).max(1024),
|
|
133
|
+
/** iOS App Attest payload or Android X.509 Key Attestation chain. */
|
|
134
|
+
attestationChainB64: z.array(Base64Std.min(16).max(16384)).min(1).max(16),
|
|
135
|
+
securityLevel: AttestationSecurityLevelSchema
|
|
136
|
+
});
|
|
137
|
+
var P256EnrollmentChallengeInputSchema = z.object({
|
|
138
|
+
deviceId: z.string().min(1).max(128)
|
|
139
|
+
});
|
|
140
|
+
var P256EnrollmentChallengeResultSchema = z.object({
|
|
141
|
+
challenge: z.string().min(16),
|
|
142
|
+
expiresAtMs: z.number().int().positive()
|
|
143
|
+
});
|
|
144
|
+
var DeviceKeyRecordSchema = z.object({
|
|
145
|
+
id: z.string().uuid(),
|
|
146
|
+
userId: z.string().uuid(),
|
|
147
|
+
deviceId: z.string(),
|
|
148
|
+
/** Always 'p256' on the consumer offline rail. Field retained for forward-compat. */
|
|
149
|
+
alg: DeviceKeyAlgSchema.default("p256"),
|
|
150
|
+
/** Legacy ed25519 hex key. Always null on new records (kept for back-compat reads). */
|
|
151
|
+
publicKeyHex: Hex64.nullable().default(null),
|
|
152
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. Required for new records. */
|
|
153
|
+
publicKeySpkiB64: Base64Std.nullable().default(null),
|
|
154
|
+
securityLevel: AttestationSecurityLevelSchema.nullable().default(null),
|
|
155
|
+
hardwareBacked: z.boolean().default(false),
|
|
156
|
+
attestedAtMs: z.number().int().nonnegative().nullable().default(null),
|
|
157
|
+
createdAtMs: z.number().int().nonnegative(),
|
|
158
|
+
revokedAtMs: z.number().int().nonnegative().nullable()
|
|
159
|
+
});
|
|
160
|
+
var ConsumerOACSchema = z.object({
|
|
161
|
+
oacId: z.string().uuid(),
|
|
162
|
+
issuerId: z.string().min(1).max(64),
|
|
163
|
+
userId: z.string().uuid(),
|
|
164
|
+
deviceId: z.string().min(1).max(128),
|
|
165
|
+
/**
|
|
166
|
+
* Always 'p256'. Required on the wire (backend always emits it).
|
|
167
|
+
* Kept as a literal so input/output infer identically and the schema
|
|
168
|
+
* can be nested inside other response shapes without Zod input/output
|
|
169
|
+
* divergence under tsup DTS bundling.
|
|
170
|
+
*/
|
|
171
|
+
alg: z.literal("p256"),
|
|
172
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
173
|
+
devicePubkeySpkiB64: Base64Std.min(64).max(4096),
|
|
174
|
+
perTxCapKobo: z.number().int().positive(),
|
|
175
|
+
cumulativeCapKobo: z.number().int().positive(),
|
|
176
|
+
currency: z.string().length(3),
|
|
177
|
+
validFromMs: z.number().int().nonnegative(),
|
|
178
|
+
validUntilMs: z.number().int().nonnegative(),
|
|
179
|
+
counterSeed: z.number().int().nonnegative(),
|
|
180
|
+
issuedAtMs: z.number().int().nonnegative()
|
|
181
|
+
});
|
|
182
|
+
var SignedConsumerOACSchema = z.object({
|
|
183
|
+
oac: ConsumerOACSchema,
|
|
184
|
+
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
185
|
+
issuerSig: Base64Std.min(16).max(2048),
|
|
186
|
+
/** Issuer's P-256 public key as SubjectPublicKeyInfo DER, base64. */
|
|
187
|
+
issuerPublicKeySpkiB64: Base64Std.min(64).max(4096)
|
|
188
|
+
});
|
|
189
|
+
var OACRecordSchema = SignedConsumerOACSchema.extend({
|
|
190
|
+
currentOfflineSpentKobo: z.number().int().nonnegative(),
|
|
191
|
+
status: z.enum([
|
|
192
|
+
"active",
|
|
193
|
+
"superseded",
|
|
194
|
+
"expired",
|
|
195
|
+
"revoked",
|
|
196
|
+
"disabling",
|
|
197
|
+
"draining",
|
|
198
|
+
"closed"
|
|
199
|
+
]),
|
|
200
|
+
supersededAtMs: z.number().int().nonnegative().nullable(),
|
|
201
|
+
revokedAtMs: z.number().int().nonnegative().nullable(),
|
|
202
|
+
holdId: z.string().uuid().nullable().optional()
|
|
203
|
+
});
|
|
204
|
+
var IssueOACInputSchema = z.object({
|
|
205
|
+
deviceId: z.string().min(1).max(128),
|
|
206
|
+
perTxCapKobo: z.number().int().positive().optional(),
|
|
207
|
+
cumulativeCapKobo: z.number().int().positive().optional(),
|
|
208
|
+
ttlMs: z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
209
|
+
spendableOnlineKobo: z.number().int().nonnegative().optional()
|
|
210
|
+
});
|
|
211
|
+
var IssueAccountOacInputSchema = z.object({
|
|
212
|
+
deviceId: z.string().min(1).max(128),
|
|
213
|
+
perTxCapKobo: z.number().int().positive().optional(),
|
|
214
|
+
cumulativeCapKobo: z.number().int().positive().optional(),
|
|
215
|
+
ttlMs: z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional()
|
|
216
|
+
});
|
|
217
|
+
var EnableOfflineInputSchema = z.object({
|
|
218
|
+
deviceId: z.string().min(1).max(128),
|
|
219
|
+
amountKobo: z.number().int().positive(),
|
|
220
|
+
perTxCapKobo: z.number().int().positive().optional(),
|
|
221
|
+
ttlMs: z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
222
|
+
installId: z.string().min(1).max(128),
|
|
223
|
+
partnerId: z.string().min(1).max(64).optional()
|
|
224
|
+
});
|
|
225
|
+
var ProvisionOfflineAllowanceInputSchema = EnableOfflineInputSchema;
|
|
226
|
+
var DisableOfflineInputSchema = z.object({
|
|
227
|
+
deviceId: z.string().min(1).max(128),
|
|
228
|
+
installId: z.string().min(1).max(128).optional(),
|
|
229
|
+
claims: z.array(z.unknown()).max(256).optional()
|
|
230
|
+
});
|
|
231
|
+
var OfflineHoldRecordSchema = z.object({
|
|
232
|
+
holdId: z.string().uuid(),
|
|
233
|
+
userId: z.string().uuid(),
|
|
234
|
+
deviceId: z.string(),
|
|
235
|
+
partnerId: z.string(),
|
|
236
|
+
adapterKind: z.string(),
|
|
237
|
+
externalHoldRef: z.string().nullable(),
|
|
238
|
+
amountKobo: z.number().int().nonnegative(),
|
|
239
|
+
capturedKobo: z.number().int().nonnegative(),
|
|
240
|
+
releasedKobo: z.number().int().nonnegative(),
|
|
241
|
+
remainingKobo: z.number().int().nonnegative(),
|
|
242
|
+
currency: z.string().length(3),
|
|
243
|
+
status: z.enum([
|
|
244
|
+
"placing",
|
|
245
|
+
"active",
|
|
246
|
+
"disabling",
|
|
247
|
+
"draining",
|
|
248
|
+
"closed",
|
|
249
|
+
"revoked",
|
|
250
|
+
"failed"
|
|
251
|
+
]),
|
|
252
|
+
installId: z.string().nullable(),
|
|
253
|
+
drainDeadlineMs: z.number().int().nonnegative(),
|
|
254
|
+
disableRequestedAtMs: z.number().int().nonnegative().nullable(),
|
|
255
|
+
createdAtMs: z.number().int().nonnegative(),
|
|
256
|
+
closedAtMs: z.number().int().nonnegative().nullable(),
|
|
257
|
+
isTrusted: z.boolean().optional()
|
|
258
|
+
});
|
|
259
|
+
var EnableOfflineResultSchema = z.object({
|
|
260
|
+
hold: OfflineHoldRecordSchema,
|
|
261
|
+
oac: OACRecordSchema
|
|
262
|
+
});
|
|
263
|
+
var ProvisionOfflineAllowanceResultSchema = EnableOfflineResultSchema;
|
|
264
|
+
var DisableOfflineResultSchema = z.object({
|
|
265
|
+
hold: OfflineHoldRecordSchema,
|
|
266
|
+
trusted: z.boolean(),
|
|
267
|
+
settledClaims: z.number().int().nonnegative()
|
|
268
|
+
});
|
|
269
|
+
var OfflineStatusResultSchema = z.object({
|
|
270
|
+
hold: OfflineHoldRecordSchema.nullable(),
|
|
271
|
+
active: OACRecordSchema.nullable()
|
|
272
|
+
});
|
|
273
|
+
var OfflineStateResultSchema = z.object({
|
|
274
|
+
active: OACRecordSchema.nullable()
|
|
275
|
+
});
|
|
276
|
+
var ConsumerPaymentClaimSchema = z.object({
|
|
277
|
+
/** Always 'p256'. Retained for forward-compat and as an explicit domain marker. */
|
|
278
|
+
alg: z.literal("p256").default("p256"),
|
|
279
|
+
oacId: z.string().uuid(),
|
|
280
|
+
encounterId: Sha256Hex.optional(),
|
|
281
|
+
payerUserId: z.string().uuid(),
|
|
282
|
+
payeeUserId: z.string().uuid(),
|
|
283
|
+
payerDeviceId: z.string().min(1).max(128),
|
|
284
|
+
payerNonce: z.string().min(8).max(128),
|
|
285
|
+
payeeNonce: z.string().min(8).max(128),
|
|
286
|
+
amountKobo: z.number().int().positive(),
|
|
287
|
+
currency: z.string().length(3).default("NGN"),
|
|
288
|
+
occurredAtMs: z.number().int().nonnegative(),
|
|
289
|
+
completedAtMs: z.number().int().nonnegative().optional(),
|
|
290
|
+
contextId: z.string().max(128).optional(),
|
|
291
|
+
payerPubkeySpkiB64: Base64Std.min(64).max(4096),
|
|
292
|
+
payerSignatureDerB64: Base64Std.min(16).max(2048),
|
|
293
|
+
payeePubkeySpkiB64: Base64Std.min(64).max(4096).optional(),
|
|
294
|
+
payeeSignatureDerB64: Base64Std.min(16).max(2048).optional()
|
|
295
|
+
});
|
|
296
|
+
var ConsumerSettlementSchema = z.object({
|
|
297
|
+
settlementId: z.string().uuid(),
|
|
298
|
+
settlementKey: Sha256Hex,
|
|
299
|
+
encounterId: Sha256Hex,
|
|
300
|
+
oacId: z.string().uuid(),
|
|
301
|
+
payerUserId: z.string().uuid(),
|
|
302
|
+
payeeUserId: z.string().uuid(),
|
|
303
|
+
amountKobo: z.number().int().positive(),
|
|
304
|
+
currency: z.string().length(3),
|
|
305
|
+
status: z.enum(["SETTLED", "REVIEW"]),
|
|
306
|
+
reviewReason: z.string().nullable(),
|
|
307
|
+
ledgerRef: z.string().nullable(),
|
|
308
|
+
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
309
|
+
issuerSig: Base64Std.min(16).max(2048),
|
|
310
|
+
createdAtMs: z.number().int().nonnegative()
|
|
311
|
+
});
|
|
312
|
+
var ConsumerSettleResultSchema = z.object({
|
|
313
|
+
settlement: ConsumerSettlementSchema,
|
|
314
|
+
encounterId: Sha256Hex,
|
|
315
|
+
replayed: z.boolean()
|
|
316
|
+
});
|
|
317
|
+
var RevokeDeviceKeyInputSchema = z.object({
|
|
318
|
+
deviceId: z.string().min(1).max(128),
|
|
319
|
+
/** Step-up token from /api/v1/auth/send/verify with purpose='offline_revoke'. */
|
|
320
|
+
sendAuthToken: z.string().min(16)
|
|
321
|
+
});
|
|
322
|
+
function createMeOfflineClient(opts) {
|
|
323
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
324
|
+
if (!fetchImpl) {
|
|
325
|
+
throw new Error("createMeOfflineClient: no fetch implementation available");
|
|
326
|
+
}
|
|
327
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
328
|
+
async function call(method, path, body, parser) {
|
|
329
|
+
const init2 = {
|
|
330
|
+
method,
|
|
331
|
+
headers: {
|
|
332
|
+
"content-type": "application/json",
|
|
333
|
+
accept: "application/json"
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
337
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
338
|
+
const text = await resp.text();
|
|
339
|
+
let raw = void 0;
|
|
340
|
+
if (text) {
|
|
341
|
+
try {
|
|
342
|
+
raw = JSON.parse(text);
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (!resp.ok) {
|
|
347
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
348
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
349
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
350
|
+
}
|
|
351
|
+
return parser(raw);
|
|
352
|
+
}
|
|
353
|
+
const deviceKeyItems = z.object({ items: z.array(DeviceKeyRecordSchema) });
|
|
354
|
+
return {
|
|
355
|
+
registerDeviceKey: (input) => call(
|
|
356
|
+
"POST",
|
|
357
|
+
"/v1/me/offline/keys",
|
|
358
|
+
RegisterDeviceKeyInputSchema.parse(input),
|
|
359
|
+
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
360
|
+
),
|
|
361
|
+
issueP256EnrollmentChallenge: (input) => call(
|
|
362
|
+
"POST",
|
|
363
|
+
"/v1/me/offline/keys/p256/challenge",
|
|
364
|
+
P256EnrollmentChallengeInputSchema.parse(input),
|
|
365
|
+
(raw) => P256EnrollmentChallengeResultSchema.parse(raw)
|
|
366
|
+
),
|
|
367
|
+
registerDeviceKeyP256: (input) => call(
|
|
368
|
+
"POST",
|
|
369
|
+
"/v1/me/offline/keys/p256",
|
|
370
|
+
RegisterDeviceKeyP256InputSchema.parse(input),
|
|
371
|
+
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
372
|
+
),
|
|
373
|
+
listDeviceKeys: () => call(
|
|
374
|
+
"GET",
|
|
375
|
+
"/v1/me/offline/keys",
|
|
376
|
+
void 0,
|
|
377
|
+
(raw) => deviceKeyItems.parse(raw)
|
|
378
|
+
),
|
|
379
|
+
revokeDeviceKey: (input) => call(
|
|
380
|
+
"POST",
|
|
381
|
+
"/v1/me/offline/keys/revoke",
|
|
382
|
+
RevokeDeviceKeyInputSchema.parse(input),
|
|
383
|
+
() => void 0
|
|
384
|
+
),
|
|
385
|
+
issueAccountOac: (input) => call(
|
|
386
|
+
"POST",
|
|
387
|
+
"/v1/me/offline/oac",
|
|
388
|
+
IssueAccountOacInputSchema.parse(input),
|
|
389
|
+
(raw) => OACRecordSchema.parse(raw)
|
|
390
|
+
),
|
|
391
|
+
provisionAllowance: (input) => call(
|
|
392
|
+
"POST",
|
|
393
|
+
"/v1/me/offline/allowance",
|
|
394
|
+
ProvisionOfflineAllowanceInputSchema.parse(input),
|
|
395
|
+
(raw) => ProvisionOfflineAllowanceResultSchema.parse(raw)
|
|
396
|
+
),
|
|
397
|
+
enable: (input) => call(
|
|
398
|
+
"POST",
|
|
399
|
+
"/v1/me/offline/enable",
|
|
400
|
+
EnableOfflineInputSchema.parse(input),
|
|
401
|
+
(raw) => EnableOfflineResultSchema.parse(raw)
|
|
402
|
+
),
|
|
403
|
+
refresh: (input) => call(
|
|
404
|
+
"POST",
|
|
405
|
+
"/v1/me/offline/refresh",
|
|
406
|
+
IssueOACInputSchema.parse(input),
|
|
407
|
+
(raw) => OACRecordSchema.parse(raw)
|
|
408
|
+
),
|
|
409
|
+
disable: (input) => call(
|
|
410
|
+
"POST",
|
|
411
|
+
"/v1/me/offline/disable",
|
|
412
|
+
DisableOfflineInputSchema.parse(input),
|
|
413
|
+
(raw) => DisableOfflineResultSchema.parse(raw)
|
|
414
|
+
),
|
|
415
|
+
getStatus: (deviceId) => {
|
|
416
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
417
|
+
return call(
|
|
418
|
+
"GET",
|
|
419
|
+
`/v1/me/offline/status${qs}`,
|
|
420
|
+
void 0,
|
|
421
|
+
(raw) => OfflineStatusResultSchema.parse(raw)
|
|
422
|
+
);
|
|
423
|
+
},
|
|
424
|
+
getState: (deviceId) => {
|
|
425
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
426
|
+
return call(
|
|
427
|
+
"GET",
|
|
428
|
+
`/v1/me/offline/state${qs}`,
|
|
429
|
+
void 0,
|
|
430
|
+
(raw) => OfflineStateResultSchema.parse(raw)
|
|
431
|
+
);
|
|
432
|
+
},
|
|
433
|
+
submitClaim: (claim) => call(
|
|
434
|
+
"POST",
|
|
435
|
+
"/v1/me/offline/claims",
|
|
436
|
+
ConsumerPaymentClaimSchema.parse(claim),
|
|
437
|
+
(raw) => ConsumerSettleResultSchema.parse(raw)
|
|
438
|
+
)
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/contracts.ts
|
|
443
|
+
var E164Regex = /^\+[1-9]\d{7,14}$/;
|
|
444
|
+
var UuidSchema = z2.string().uuid();
|
|
445
|
+
var IsoDateSchema = z2.string().datetime({ offset: true });
|
|
446
|
+
var CurrencySchema = z2.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
447
|
+
var HealthResponseSchema = z2.object({
|
|
448
|
+
ok: z2.boolean()
|
|
449
|
+
});
|
|
450
|
+
var WelcomeResponseSchema = z2.object({
|
|
451
|
+
message: z2.string()
|
|
452
|
+
});
|
|
453
|
+
var OnboardingStartRequestSchema = z2.object({
|
|
454
|
+
phoneE164: z2.string().regex(E164Regex),
|
|
455
|
+
appInstanceId: z2.string().min(3),
|
|
456
|
+
platform: z2.enum(["android", "ios", "web"]),
|
|
457
|
+
turnstileToken: z2.string().min(20).optional(),
|
|
458
|
+
appAttestation: z2.object({
|
|
459
|
+
provider: z2.enum(["android", "ios", "web"]),
|
|
460
|
+
token: z2.string().min(24)
|
|
461
|
+
}).optional(),
|
|
462
|
+
firstName: z2.string().trim().min(1).max(80).optional(),
|
|
463
|
+
lastName: z2.string().trim().min(1).max(80).optional()
|
|
464
|
+
});
|
|
465
|
+
var OnboardingStartResponseSchema = z2.object({
|
|
466
|
+
requestId: z2.string().min(1),
|
|
467
|
+
checkUrl: z2.string().url().optional(),
|
|
468
|
+
expiresInSec: z2.number().int().positive(),
|
|
469
|
+
fallback: z2.enum(["SILENT_AUTH", "OTP"])
|
|
470
|
+
});
|
|
471
|
+
var OnboardingCompleteRequestSchema = z2.object({
|
|
472
|
+
requestId: z2.string().min(1),
|
|
473
|
+
code: z2.string().min(1).max(32),
|
|
474
|
+
appInstanceId: z2.string().min(3),
|
|
475
|
+
fingerprintHash: z2.string().min(3).optional()
|
|
476
|
+
});
|
|
477
|
+
var OnboardingCompleteResponseSchema = z2.object({
|
|
478
|
+
sessionToken: z2.string().min(1),
|
|
479
|
+
userId: UuidSchema,
|
|
480
|
+
restricted: z2.boolean(),
|
|
481
|
+
risk_reasons: z2.array(
|
|
482
|
+
z2.enum(["SIM_SWAP_RECENT", "ROAMING", "CARRIER_CHANGED"])
|
|
483
|
+
),
|
|
484
|
+
stepUpRequired: z2.boolean().optional(),
|
|
485
|
+
riskStatus: z2.enum(["ok", "unavailable"]).optional()
|
|
486
|
+
});
|
|
487
|
+
var RegisterDeviceRequestSchema = z2.object({
|
|
488
|
+
userId: UuidSchema,
|
|
489
|
+
appInstanceId: z2.string().min(3),
|
|
490
|
+
platform: z2.string().min(2),
|
|
491
|
+
model: z2.string().optional(),
|
|
492
|
+
networkSignals: z2.object({
|
|
493
|
+
ip: z2.string().min(3),
|
|
494
|
+
asn: z2.number().int().optional(),
|
|
495
|
+
country: z2.string().min(2).optional(),
|
|
496
|
+
carrier: z2.string().optional()
|
|
497
|
+
})
|
|
498
|
+
});
|
|
499
|
+
var RegisterDeviceResponseSchema = z2.object({
|
|
500
|
+
deviceId: z2.string().min(1),
|
|
501
|
+
fingerprintHash: z2.string().min(1),
|
|
502
|
+
driftScore: z2.number(),
|
|
503
|
+
trustState: z2.enum(["TRUSTED_PRIMARY", "TRUSTED_SECONDARY", "UNVERIFIED"]),
|
|
504
|
+
stepUpRequired: z2.boolean()
|
|
505
|
+
});
|
|
506
|
+
var AuthRefreshRequestSchema = z2.object({
|
|
507
|
+
userId: UuidSchema,
|
|
508
|
+
refreshToken: z2.string().min(8),
|
|
509
|
+
appInstanceId: z2.string().min(3),
|
|
510
|
+
fingerprintHash: z2.string().min(3)
|
|
511
|
+
});
|
|
512
|
+
var AuthRefreshResponseSchema = z2.object({
|
|
513
|
+
refreshToken: z2.string().min(8),
|
|
514
|
+
stepUpRequired: z2.boolean()
|
|
515
|
+
});
|
|
516
|
+
var AuthLogoutRequestSchema = z2.object({
|
|
517
|
+
userId: UuidSchema,
|
|
518
|
+
refreshToken: z2.string().min(8)
|
|
519
|
+
});
|
|
520
|
+
var PinSetRequestSchema = z2.object({
|
|
521
|
+
userId: UuidSchema,
|
|
522
|
+
pin: z2.string().regex(/^\d{6}$/)
|
|
523
|
+
});
|
|
524
|
+
var PinVerifyRequestSchema = z2.object({
|
|
525
|
+
userId: UuidSchema,
|
|
526
|
+
pin: z2.string().regex(/^\d{6}$/)
|
|
527
|
+
});
|
|
528
|
+
var OkResponseSchema = z2.object({
|
|
529
|
+
ok: z2.boolean()
|
|
530
|
+
});
|
|
531
|
+
var RegisterSendDeviceKeyRequestSchema = z2.object({
|
|
532
|
+
userId: UuidSchema,
|
|
533
|
+
deviceId: z2.string().min(3),
|
|
534
|
+
publicKey: z2.string().min(32)
|
|
535
|
+
});
|
|
536
|
+
var SEND_AUTH_PURPOSES = ["send_money", "offline_revoke"];
|
|
537
|
+
var SendChallengeRequestSchema = z2.object({
|
|
538
|
+
userId: UuidSchema,
|
|
539
|
+
deviceId: z2.string().min(3),
|
|
540
|
+
purpose: z2.enum(SEND_AUTH_PURPOSES).optional()
|
|
541
|
+
});
|
|
542
|
+
var SendChallengeResponseSchema = z2.object({
|
|
543
|
+
challengeId: UuidSchema,
|
|
544
|
+
nonce: z2.string().min(1),
|
|
545
|
+
expiresAt: IsoDateSchema
|
|
546
|
+
});
|
|
547
|
+
var SendVerifyRequestSchema = z2.object({
|
|
548
|
+
userId: UuidSchema,
|
|
549
|
+
deviceId: z2.string().min(3),
|
|
550
|
+
challengeId: UuidSchema,
|
|
551
|
+
signature: z2.string().min(16)
|
|
552
|
+
});
|
|
553
|
+
var SendVerifyResponseSchema = z2.object({
|
|
554
|
+
sendAuthToken: z2.string().min(16)
|
|
555
|
+
});
|
|
556
|
+
var ResolveRecipientRequestSchema = z2.object({
|
|
557
|
+
identifier: z2.string().min(3)
|
|
558
|
+
});
|
|
559
|
+
var ResolveRecipientResponseSchema = z2.object({
|
|
560
|
+
recipientUserId: UuidSchema,
|
|
561
|
+
displayName: z2.string().min(1),
|
|
562
|
+
normalizedIdentifier: z2.string().regex(E164Regex),
|
|
563
|
+
isActive: z2.boolean()
|
|
564
|
+
});
|
|
565
|
+
var CreateTransferRequestSchema = z2.object({
|
|
566
|
+
recipientIdentifier: z2.string().min(3),
|
|
567
|
+
amountMinor: z2.number().int().positive(),
|
|
568
|
+
currency: CurrencySchema,
|
|
569
|
+
sendAuthToken: z2.string().min(16)
|
|
570
|
+
});
|
|
571
|
+
var TransferStatusSchema = z2.enum(["SETTLED", "PENDING_REVIEW", "DECLINED"]);
|
|
572
|
+
var TransferResponseSchema = z2.object({
|
|
573
|
+
transactionId: z2.string().min(1),
|
|
574
|
+
status: TransferStatusSchema,
|
|
575
|
+
userStatus: TransferStatusSchema,
|
|
576
|
+
recipientName: z2.string().min(1),
|
|
577
|
+
timestamp: IsoDateSchema,
|
|
578
|
+
/**
|
|
579
|
+
* Fresh issuer-signed OACs returned by the backend's post-commit
|
|
580
|
+
* rotation hook on `SETTLED` transfers.
|
|
581
|
+
*
|
|
582
|
+
* In the current backend implementation this hook rotates LEGACY
|
|
583
|
+
* hold-backed OACs only. Account-funded (no-hold) OACs introduced by
|
|
584
|
+
* the unified-pay-rails refactor are NOT rotated here — their
|
|
585
|
+
* cumulative spend counter and main-balance check are enforced at
|
|
586
|
+
* claim submission time via `createTransfer` under the per-sender
|
|
587
|
+
* advisory lock, so a stale no-hold OAC cannot authorise overspending
|
|
588
|
+
* (it can only resolve to REVIEW on submission).
|
|
589
|
+
*
|
|
590
|
+
* Optional: omitted when the sender has no registered hold-backed
|
|
591
|
+
* devices, when the refresh failed (best-effort), or on retry replays
|
|
592
|
+
* that pre-date the refresh. When present, the entries supersede any
|
|
593
|
+
* locally cached OACs for the listed devices; when absent, clients
|
|
594
|
+
* should fall back to `/v1/me/offline/refresh`.
|
|
595
|
+
*
|
|
596
|
+
* Each entry is validated against the canonical `OACRecordSchema`,
|
|
597
|
+
* so consumers can trust the runtime shape matches the static type.
|
|
598
|
+
*/
|
|
599
|
+
offlineOacs: OACRecordSchema.array().optional()
|
|
600
|
+
});
|
|
601
|
+
var DirectionSchema = z2.enum(["OUTGOING", "INCOMING"]);
|
|
602
|
+
var AccountActivityItemSchema = z2.object({
|
|
603
|
+
id: z2.string().min(1),
|
|
604
|
+
type: z2.string().min(1),
|
|
605
|
+
direction: DirectionSchema,
|
|
606
|
+
name: z2.string().min(1),
|
|
607
|
+
identifier: z2.string().min(1),
|
|
608
|
+
amountMinor: z2.number().int(),
|
|
609
|
+
currency: CurrencySchema,
|
|
610
|
+
status: z2.string().min(1),
|
|
611
|
+
timestamp: IsoDateSchema
|
|
612
|
+
});
|
|
613
|
+
var AccountSummaryResponseSchema = z2.object({
|
|
614
|
+
/** Authenticated user's stable internal id. */
|
|
615
|
+
userId: UuidSchema,
|
|
616
|
+
/**
|
|
617
|
+
* 10-digit Nigeria Uniform Bank Account Number (NUBAN) allocated by the
|
|
618
|
+
* bank partner. `null` when the user has no partner-allocated account yet.
|
|
619
|
+
*/
|
|
620
|
+
nuban: z2.string().regex(/^[0-9]{10}$/).nullable(),
|
|
621
|
+
balance: z2.number().int(),
|
|
622
|
+
currency: CurrencySchema,
|
|
623
|
+
dailySendLimit: z2.number().int().nonnegative(),
|
|
624
|
+
dailySendRemaining: z2.number().int().nonnegative(),
|
|
625
|
+
kycTier: z2.string().min(1),
|
|
626
|
+
kycStatus: z2.string().min(1),
|
|
627
|
+
recentActivity: z2.array(AccountActivityItemSchema)
|
|
628
|
+
});
|
|
629
|
+
var TransactionsListResponseSchema = z2.object({
|
|
630
|
+
items: z2.array(AccountActivityItemSchema),
|
|
631
|
+
nextCursor: z2.string().nullable()
|
|
632
|
+
});
|
|
633
|
+
var TransactionDetailResponseSchema = z2.object({
|
|
634
|
+
transactionId: z2.string().min(1),
|
|
635
|
+
type: z2.string().min(1),
|
|
636
|
+
direction: DirectionSchema,
|
|
637
|
+
counterpartyName: z2.string().min(1),
|
|
638
|
+
counterpartyIdentifier: z2.string().min(1),
|
|
639
|
+
amountMinor: z2.number().int(),
|
|
640
|
+
currency: CurrencySchema,
|
|
641
|
+
status: z2.string().min(1),
|
|
642
|
+
timestamp: IsoDateSchema
|
|
643
|
+
});
|
|
644
|
+
var PushRegisterRequestSchema = z2.object({
|
|
645
|
+
deviceId: z2.string().min(3),
|
|
646
|
+
platform: z2.enum(["ios", "android", "web"]),
|
|
647
|
+
token: z2.string().min(16)
|
|
648
|
+
});
|
|
649
|
+
var CreatePayLinkResponseSchema = z2.object({
|
|
650
|
+
token: z2.string().min(1)
|
|
651
|
+
});
|
|
652
|
+
var ResolvePayLinkResponseSchema = z2.object({
|
|
653
|
+
recipientUserId: UuidSchema,
|
|
654
|
+
displayName: z2.string().min(1),
|
|
655
|
+
normalizedIdentifier: z2.string().regex(E164Regex),
|
|
656
|
+
isActive: z2.boolean()
|
|
657
|
+
});
|
|
658
|
+
|
|
302
659
|
// src/primitives.ts
|
|
303
|
-
import { z as
|
|
304
|
-
var CurrencyCodeSchema =
|
|
660
|
+
import { z as z3 } from "zod";
|
|
661
|
+
var CurrencyCodeSchema = z3.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
305
662
|
var currencyFractionDigits = {
|
|
306
663
|
NGN: 2,
|
|
307
664
|
USD: 2,
|
|
@@ -395,7 +752,7 @@ function moneyMinorToNumber(amountMinor) {
|
|
|
395
752
|
}
|
|
396
753
|
|
|
397
754
|
// src/collections/client.ts
|
|
398
|
-
import { z as
|
|
755
|
+
import { z as z4 } from "zod";
|
|
399
756
|
var MERCHANT_PROFILE_STATUSES = [
|
|
400
757
|
"pending",
|
|
401
758
|
"active",
|
|
@@ -425,172 +782,172 @@ var MERCHANT_PAYOUT_STATUSES = [
|
|
|
425
782
|
"failed",
|
|
426
783
|
"cancelled"
|
|
427
784
|
];
|
|
428
|
-
var MoneyKoboSchema =
|
|
429
|
-
var MetadataSchema =
|
|
430
|
-
|
|
785
|
+
var MoneyKoboSchema = z4.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
786
|
+
var MetadataSchema = z4.record(
|
|
787
|
+
z4.union([z4.string(), z4.number(), z4.boolean(), z4.null()])
|
|
431
788
|
);
|
|
432
|
-
var CurrencySchema2 =
|
|
433
|
-
var ReferenceSchema =
|
|
434
|
-
var MerchantProfileSchema =
|
|
435
|
-
accountId:
|
|
436
|
-
legalName:
|
|
437
|
-
tradingName:
|
|
438
|
-
merchantCategoryCode:
|
|
439
|
-
nqrMerchantId:
|
|
440
|
-
settlementBankCode:
|
|
441
|
-
settlementAccountNumber:
|
|
442
|
-
settlementAccountName:
|
|
443
|
-
settlementSchedule:
|
|
444
|
-
status:
|
|
445
|
-
offlineEnabled:
|
|
789
|
+
var CurrencySchema2 = z4.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
790
|
+
var ReferenceSchema = z4.string().trim().min(6).max(128).regex(/^[A-Za-z0-9._:-]+$/);
|
|
791
|
+
var MerchantProfileSchema = z4.object({
|
|
792
|
+
accountId: z4.string().uuid(),
|
|
793
|
+
legalName: z4.string(),
|
|
794
|
+
tradingName: z4.string(),
|
|
795
|
+
merchantCategoryCode: z4.string().regex(/^\d{4}$/),
|
|
796
|
+
nqrMerchantId: z4.string(),
|
|
797
|
+
settlementBankCode: z4.string(),
|
|
798
|
+
settlementAccountNumber: z4.string(),
|
|
799
|
+
settlementAccountName: z4.string(),
|
|
800
|
+
settlementSchedule: z4.enum(SETTLEMENT_SCHEDULES),
|
|
801
|
+
status: z4.enum(MERCHANT_PROFILE_STATUSES),
|
|
802
|
+
offlineEnabled: z4.boolean(),
|
|
446
803
|
perTxLimitKobo: MoneyKoboSchema,
|
|
447
804
|
dailyLimitKobo: MoneyKoboSchema,
|
|
448
805
|
metadata: MetadataSchema,
|
|
449
|
-
createdAtMs:
|
|
450
|
-
updatedAtMs:
|
|
806
|
+
createdAtMs: z4.number().int().nonnegative(),
|
|
807
|
+
updatedAtMs: z4.number().int().nonnegative()
|
|
451
808
|
});
|
|
452
|
-
var UpsertMerchantProfileInputSchema =
|
|
453
|
-
legalName:
|
|
454
|
-
tradingName:
|
|
455
|
-
merchantCategoryCode:
|
|
456
|
-
nqrMerchantId:
|
|
457
|
-
settlementBankCode:
|
|
458
|
-
settlementAccountNumber:
|
|
459
|
-
settlementAccountName:
|
|
460
|
-
settlementSchedule:
|
|
461
|
-
status:
|
|
462
|
-
offlineEnabled:
|
|
809
|
+
var UpsertMerchantProfileInputSchema = z4.object({
|
|
810
|
+
legalName: z4.string().trim().min(1).max(200),
|
|
811
|
+
tradingName: z4.string().trim().min(1).max(25),
|
|
812
|
+
merchantCategoryCode: z4.string().trim().regex(/^\d{4}$/),
|
|
813
|
+
nqrMerchantId: z4.string().trim().min(3).max(64),
|
|
814
|
+
settlementBankCode: z4.string().trim().min(2).max(16),
|
|
815
|
+
settlementAccountNumber: z4.string().trim().min(5).max(32),
|
|
816
|
+
settlementAccountName: z4.string().trim().min(1).max(200),
|
|
817
|
+
settlementSchedule: z4.enum(SETTLEMENT_SCHEDULES).optional(),
|
|
818
|
+
status: z4.enum(MERCHANT_PROFILE_STATUSES).optional(),
|
|
819
|
+
offlineEnabled: z4.boolean().optional(),
|
|
463
820
|
perTxLimitKobo: MoneyKoboSchema.optional(),
|
|
464
821
|
dailyLimitKobo: MoneyKoboSchema.optional(),
|
|
465
822
|
metadata: MetadataSchema.optional()
|
|
466
823
|
});
|
|
467
|
-
var CollectionIntentSchema =
|
|
468
|
-
intentId:
|
|
469
|
-
accountId:
|
|
470
|
-
terminalId:
|
|
471
|
-
reference:
|
|
472
|
-
amountKobo:
|
|
473
|
-
currency:
|
|
474
|
-
status:
|
|
475
|
-
description:
|
|
476
|
-
nqrPayload:
|
|
477
|
-
provider:
|
|
478
|
-
providerReference:
|
|
824
|
+
var CollectionIntentSchema = z4.object({
|
|
825
|
+
intentId: z4.string().uuid(),
|
|
826
|
+
accountId: z4.string().uuid(),
|
|
827
|
+
terminalId: z4.string().uuid().nullable(),
|
|
828
|
+
reference: z4.string(),
|
|
829
|
+
amountKobo: z4.number().int().positive().nullable(),
|
|
830
|
+
currency: z4.string().length(3),
|
|
831
|
+
status: z4.enum(COLLECTION_INTENT_STATUSES),
|
|
832
|
+
description: z4.string().nullable(),
|
|
833
|
+
nqrPayload: z4.string(),
|
|
834
|
+
provider: z4.string(),
|
|
835
|
+
providerReference: z4.string().nullable(),
|
|
479
836
|
metadata: MetadataSchema,
|
|
480
|
-
expiresAtMs:
|
|
481
|
-
paidAtMs:
|
|
482
|
-
createdAtMs:
|
|
483
|
-
updatedAtMs:
|
|
837
|
+
expiresAtMs: z4.number().int().nonnegative().nullable(),
|
|
838
|
+
paidAtMs: z4.number().int().nonnegative().nullable(),
|
|
839
|
+
createdAtMs: z4.number().int().nonnegative(),
|
|
840
|
+
updatedAtMs: z4.number().int().nonnegative()
|
|
484
841
|
});
|
|
485
|
-
var CreateCollectionIntentInputSchema =
|
|
842
|
+
var CreateCollectionIntentInputSchema = z4.object({
|
|
486
843
|
reference: ReferenceSchema.optional(),
|
|
487
844
|
amountKobo: MoneyKoboSchema.optional(),
|
|
488
845
|
currency: CurrencySchema2.optional(),
|
|
489
|
-
terminalId:
|
|
490
|
-
terminalLabel:
|
|
491
|
-
merchantCity:
|
|
492
|
-
description:
|
|
493
|
-
expiresAtMs:
|
|
494
|
-
provider:
|
|
846
|
+
terminalId: z4.string().uuid().optional(),
|
|
847
|
+
terminalLabel: z4.string().trim().min(1).max(25).optional(),
|
|
848
|
+
merchantCity: z4.string().trim().min(1).max(15).optional(),
|
|
849
|
+
description: z4.string().trim().min(1).max(280).optional(),
|
|
850
|
+
expiresAtMs: z4.number().int().positive().optional(),
|
|
851
|
+
provider: z4.string().trim().min(1).max(40).optional(),
|
|
495
852
|
metadata: MetadataSchema.optional()
|
|
496
853
|
});
|
|
497
|
-
var PublicCollectionIntentSchema =
|
|
498
|
-
intentId:
|
|
499
|
-
reference:
|
|
500
|
-
amountKobo:
|
|
501
|
-
currency:
|
|
502
|
-
status:
|
|
503
|
-
merchantAccountId:
|
|
504
|
-
merchantName:
|
|
505
|
-
merchantCategoryCode:
|
|
506
|
-
description:
|
|
507
|
-
expiresAtMs:
|
|
854
|
+
var PublicCollectionIntentSchema = z4.object({
|
|
855
|
+
intentId: z4.string().uuid(),
|
|
856
|
+
reference: z4.string(),
|
|
857
|
+
amountKobo: z4.number().int().positive().nullable(),
|
|
858
|
+
currency: z4.string().length(3),
|
|
859
|
+
status: z4.enum(COLLECTION_INTENT_STATUSES),
|
|
860
|
+
merchantAccountId: z4.string().uuid(),
|
|
861
|
+
merchantName: z4.string(),
|
|
862
|
+
merchantCategoryCode: z4.string(),
|
|
863
|
+
description: z4.string().nullable(),
|
|
864
|
+
expiresAtMs: z4.number().int().nonnegative().nullable()
|
|
508
865
|
});
|
|
509
|
-
var PayCollectionInputSchema =
|
|
866
|
+
var PayCollectionInputSchema = z4.object({
|
|
510
867
|
reference: ReferenceSchema,
|
|
511
868
|
amountKobo: MoneyKoboSchema.optional(),
|
|
512
869
|
currency: CurrencySchema2.optional(),
|
|
513
|
-
idempotencyKey:
|
|
870
|
+
idempotencyKey: z4.string().trim().min(8).max(160).optional()
|
|
514
871
|
});
|
|
515
|
-
var CollectionPaymentSchema =
|
|
516
|
-
paymentId:
|
|
517
|
-
intentId:
|
|
518
|
-
accountId:
|
|
519
|
-
payerUserId:
|
|
520
|
-
merchantOwnerUserId:
|
|
872
|
+
var CollectionPaymentSchema = z4.object({
|
|
873
|
+
paymentId: z4.string().uuid(),
|
|
874
|
+
intentId: z4.string().uuid(),
|
|
875
|
+
accountId: z4.string().uuid(),
|
|
876
|
+
payerUserId: z4.string().uuid().nullable(),
|
|
877
|
+
merchantOwnerUserId: z4.string().uuid(),
|
|
521
878
|
amountKobo: MoneyKoboSchema,
|
|
522
|
-
currency:
|
|
523
|
-
status:
|
|
524
|
-
provider:
|
|
525
|
-
providerReference:
|
|
526
|
-
idempotencyKey:
|
|
527
|
-
ledgerRef:
|
|
528
|
-
failureCode:
|
|
529
|
-
failureMessage:
|
|
530
|
-
paidAtMs:
|
|
531
|
-
createdAtMs:
|
|
532
|
-
updatedAtMs:
|
|
879
|
+
currency: z4.string().length(3),
|
|
880
|
+
status: z4.enum(COLLECTION_PAYMENT_STATUSES),
|
|
881
|
+
provider: z4.string(),
|
|
882
|
+
providerReference: z4.string().nullable(),
|
|
883
|
+
idempotencyKey: z4.string().nullable(),
|
|
884
|
+
ledgerRef: z4.string(),
|
|
885
|
+
failureCode: z4.string().nullable(),
|
|
886
|
+
failureMessage: z4.string().nullable(),
|
|
887
|
+
paidAtMs: z4.number().int().nonnegative().nullable(),
|
|
888
|
+
createdAtMs: z4.number().int().nonnegative(),
|
|
889
|
+
updatedAtMs: z4.number().int().nonnegative()
|
|
533
890
|
});
|
|
534
|
-
var CollectionPaymentResultSchema =
|
|
891
|
+
var CollectionPaymentResultSchema = z4.object({
|
|
535
892
|
payment: CollectionPaymentSchema,
|
|
536
893
|
intent: CollectionIntentSchema,
|
|
537
|
-
receipt:
|
|
538
|
-
replayed:
|
|
894
|
+
receipt: z4.unknown().optional(),
|
|
895
|
+
replayed: z4.boolean()
|
|
539
896
|
});
|
|
540
|
-
var CollectionReportSummarySchema =
|
|
541
|
-
accountId:
|
|
542
|
-
fromMs:
|
|
543
|
-
toMs:
|
|
544
|
-
currency:
|
|
545
|
-
paidCount:
|
|
546
|
-
paidAmountKobo:
|
|
547
|
-
pendingCount:
|
|
548
|
-
failedCount:
|
|
549
|
-
reversedCount:
|
|
550
|
-
availableBalanceKobo:
|
|
897
|
+
var CollectionReportSummarySchema = z4.object({
|
|
898
|
+
accountId: z4.string().uuid(),
|
|
899
|
+
fromMs: z4.number().int().nonnegative(),
|
|
900
|
+
toMs: z4.number().int().nonnegative(),
|
|
901
|
+
currency: z4.string().length(3),
|
|
902
|
+
paidCount: z4.number().int().nonnegative(),
|
|
903
|
+
paidAmountKobo: z4.number().int().nonnegative(),
|
|
904
|
+
pendingCount: z4.number().int().nonnegative(),
|
|
905
|
+
failedCount: z4.number().int().nonnegative(),
|
|
906
|
+
reversedCount: z4.number().int().nonnegative(),
|
|
907
|
+
availableBalanceKobo: z4.number().int().nonnegative()
|
|
551
908
|
});
|
|
552
|
-
var CollectionStatementSchema =
|
|
553
|
-
accountId:
|
|
554
|
-
year:
|
|
555
|
-
month:
|
|
556
|
-
currency:
|
|
557
|
-
totalPaidKobo:
|
|
558
|
-
items:
|
|
909
|
+
var CollectionStatementSchema = z4.object({
|
|
910
|
+
accountId: z4.string().uuid(),
|
|
911
|
+
year: z4.number().int(),
|
|
912
|
+
month: z4.number().int().min(1).max(12),
|
|
913
|
+
currency: z4.string().length(3),
|
|
914
|
+
totalPaidKobo: z4.number().int().nonnegative(),
|
|
915
|
+
items: z4.array(CollectionPaymentSchema)
|
|
559
916
|
});
|
|
560
|
-
var CreatePayoutInputSchema =
|
|
917
|
+
var CreatePayoutInputSchema = z4.object({
|
|
561
918
|
amountKobo: MoneyKoboSchema,
|
|
562
919
|
currency: CurrencySchema2.optional(),
|
|
563
|
-
idempotencyKey:
|
|
920
|
+
idempotencyKey: z4.string().trim().min(8).max(160)
|
|
564
921
|
});
|
|
565
|
-
var MerchantPayoutSchema =
|
|
566
|
-
payoutId:
|
|
567
|
-
accountId:
|
|
922
|
+
var MerchantPayoutSchema = z4.object({
|
|
923
|
+
payoutId: z4.string().uuid(),
|
|
924
|
+
accountId: z4.string().uuid(),
|
|
568
925
|
amountKobo: MoneyKoboSchema,
|
|
569
|
-
currency:
|
|
570
|
-
status:
|
|
571
|
-
idempotencyKey:
|
|
572
|
-
ledgerRef:
|
|
573
|
-
providerReference:
|
|
574
|
-
requestedByUserId:
|
|
575
|
-
failureCode:
|
|
576
|
-
failureMessage:
|
|
577
|
-
createdAtMs:
|
|
578
|
-
updatedAtMs:
|
|
926
|
+
currency: z4.string().length(3),
|
|
927
|
+
status: z4.enum(MERCHANT_PAYOUT_STATUSES),
|
|
928
|
+
idempotencyKey: z4.string().nullable(),
|
|
929
|
+
ledgerRef: z4.string(),
|
|
930
|
+
providerReference: z4.string().nullable(),
|
|
931
|
+
requestedByUserId: z4.string().uuid().nullable(),
|
|
932
|
+
failureCode: z4.string().nullable(),
|
|
933
|
+
failureMessage: z4.string().nullable(),
|
|
934
|
+
createdAtMs: z4.number().int().nonnegative(),
|
|
935
|
+
updatedAtMs: z4.number().int().nonnegative()
|
|
579
936
|
});
|
|
580
|
-
var ProviderEventInputSchema =
|
|
581
|
-
provider:
|
|
582
|
-
eventId:
|
|
583
|
-
eventType:
|
|
584
|
-
payload:
|
|
937
|
+
var ProviderEventInputSchema = z4.object({
|
|
938
|
+
provider: z4.string().trim().min(1).max(80),
|
|
939
|
+
eventId: z4.string().trim().min(1).max(160),
|
|
940
|
+
eventType: z4.string().trim().min(1).max(120),
|
|
941
|
+
payload: z4.record(z4.unknown()).optional()
|
|
585
942
|
});
|
|
586
|
-
var ProviderEventRecordSchema =
|
|
587
|
-
id:
|
|
588
|
-
provider:
|
|
589
|
-
eventId:
|
|
590
|
-
eventType:
|
|
591
|
-
signatureVerified:
|
|
592
|
-
receivedAtMs:
|
|
593
|
-
processedAtMs:
|
|
943
|
+
var ProviderEventRecordSchema = z4.object({
|
|
944
|
+
id: z4.string().uuid(),
|
|
945
|
+
provider: z4.string(),
|
|
946
|
+
eventId: z4.string(),
|
|
947
|
+
eventType: z4.string(),
|
|
948
|
+
signatureVerified: z4.boolean(),
|
|
949
|
+
receivedAtMs: z4.number().int().nonnegative(),
|
|
950
|
+
processedAtMs: z4.number().int().nonnegative().nullable()
|
|
594
951
|
});
|
|
595
952
|
function createCollectionsClient(opts) {
|
|
596
953
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -1105,7 +1462,7 @@ var FlurClient = class {
|
|
|
1105
1462
|
try {
|
|
1106
1463
|
body = requestSchema ? JSON.stringify(requestSchema.parse(input)) : init2.body;
|
|
1107
1464
|
} catch (err) {
|
|
1108
|
-
if (err instanceof
|
|
1465
|
+
if (err instanceof z5.ZodError) {
|
|
1109
1466
|
throw new FlurError("Invalid request payload", "INVALID_REQUEST", {
|
|
1110
1467
|
details: err.flatten()
|
|
1111
1468
|
});
|
|
@@ -1133,7 +1490,7 @@ var FlurClient = class {
|
|
|
1133
1490
|
try {
|
|
1134
1491
|
return responseSchema.parse(payload);
|
|
1135
1492
|
} catch (err) {
|
|
1136
|
-
if (err instanceof
|
|
1493
|
+
if (err instanceof z5.ZodError) {
|
|
1137
1494
|
throw new FlurError(
|
|
1138
1495
|
"SDK contract validation failed",
|
|
1139
1496
|
"INVALID_REQUEST",
|
|
@@ -1564,7 +1921,7 @@ function constantTimeEqual(a, b) {
|
|
|
1564
1921
|
}
|
|
1565
1922
|
|
|
1566
1923
|
// src/offline/oac.ts
|
|
1567
|
-
import { z as
|
|
1924
|
+
import { z as z6 } from "zod";
|
|
1568
1925
|
|
|
1569
1926
|
// src/crypto/p256-issuer.ts
|
|
1570
1927
|
import { p256 } from "@noble/curves/nist";
|
|
@@ -1646,20 +2003,20 @@ function verifyIssuerP256(bytes, signatureB64, issuerPublicKeySpkiB64) {
|
|
|
1646
2003
|
var OAC_DEFAULT_PER_TX_KOBO = 5e5;
|
|
1647
2004
|
var OAC_DEFAULT_CUMULATIVE_KOBO = 2e6;
|
|
1648
2005
|
var OAC_DEFAULT_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
1649
|
-
var
|
|
1650
|
-
var OACSchema =
|
|
1651
|
-
userId:
|
|
1652
|
-
deviceId:
|
|
2006
|
+
var Base64Std2 = z6.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (standard) string");
|
|
2007
|
+
var OACSchema = z6.object({
|
|
2008
|
+
userId: z6.string().min(1),
|
|
2009
|
+
deviceId: z6.string().min(1),
|
|
1653
2010
|
/** SubjectPublicKeyInfo DER, base64 (P-256). */
|
|
1654
|
-
devicePublicKey:
|
|
1655
|
-
perTxCapKobo:
|
|
1656
|
-
cumulativeCapKobo:
|
|
1657
|
-
validFromMs:
|
|
1658
|
-
validUntilMs:
|
|
1659
|
-
counterSeed:
|
|
1660
|
-
nonce:
|
|
2011
|
+
devicePublicKey: Base64Std2,
|
|
2012
|
+
perTxCapKobo: z6.number().int().nonnegative(),
|
|
2013
|
+
cumulativeCapKobo: z6.number().int().nonnegative(),
|
|
2014
|
+
validFromMs: z6.number().int().nonnegative(),
|
|
2015
|
+
validUntilMs: z6.number().int().positive(),
|
|
2016
|
+
counterSeed: z6.number().int().nonnegative(),
|
|
2017
|
+
nonce: z6.string().min(1),
|
|
1661
2018
|
/** ASN.1 DER ECDSA(SHA-256) signature, base64. */
|
|
1662
|
-
issuerSig:
|
|
2019
|
+
issuerSig: Base64Std2
|
|
1663
2020
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
1664
2021
|
message: "validUntilMs must be greater than validFromMs"
|
|
1665
2022
|
}).refine((v) => v.perTxCapKobo <= v.cumulativeCapKobo, {
|
|
@@ -1753,19 +2110,19 @@ function decodeBase45(s) {
|
|
|
1753
2110
|
}
|
|
1754
2111
|
|
|
1755
2112
|
// src/offline/messages.ts
|
|
1756
|
-
import { z as
|
|
1757
|
-
var Base64Sig =
|
|
1758
|
-
var OfflinePaymentRequestSchema =
|
|
1759
|
-
reference:
|
|
1760
|
-
amountKobo:
|
|
2113
|
+
import { z as z7 } from "zod";
|
|
2114
|
+
var Base64Sig = z7.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (standard) signature");
|
|
2115
|
+
var OfflinePaymentRequestSchema = z7.object({
|
|
2116
|
+
reference: z7.string().min(1),
|
|
2117
|
+
amountKobo: z7.number().int().positive(),
|
|
1761
2118
|
merchantOAC: OACSchema,
|
|
1762
|
-
expiresAtMs:
|
|
2119
|
+
expiresAtMs: z7.number().int().positive(),
|
|
1763
2120
|
merchantSig: Base64Sig
|
|
1764
2121
|
});
|
|
1765
|
-
var OfflinePaymentAuthorizationSchema =
|
|
2122
|
+
var OfflinePaymentAuthorizationSchema = z7.object({
|
|
1766
2123
|
request: OfflinePaymentRequestSchema,
|
|
1767
2124
|
payerOAC: OACSchema,
|
|
1768
|
-
payerCounter:
|
|
2125
|
+
payerCounter: z7.number().int().positive(),
|
|
1769
2126
|
payerSig: Base64Sig
|
|
1770
2127
|
});
|
|
1771
2128
|
function buildPaymentRequest(input) {
|
|
@@ -1877,60 +2234,60 @@ function decodeAuthorizationQR(s) {
|
|
|
1877
2234
|
}
|
|
1878
2235
|
|
|
1879
2236
|
// src/offline/settlements.ts
|
|
1880
|
-
import { z as
|
|
2237
|
+
import { z as z8 } from "zod";
|
|
1881
2238
|
import { sha256 } from "@noble/hashes/sha256";
|
|
1882
2239
|
import { bytesToHex } from "@noble/hashes/utils";
|
|
1883
|
-
var OfflineTokenSchema =
|
|
1884
|
-
tokenId:
|
|
1885
|
-
tokenSerial:
|
|
1886
|
-
issuerAccountId:
|
|
1887
|
-
payerUserId:
|
|
1888
|
-
maxAmountKobo:
|
|
1889
|
-
currency:
|
|
1890
|
-
issuedAtMs:
|
|
1891
|
-
expiresAtMs:
|
|
1892
|
-
issuerSig:
|
|
2240
|
+
var OfflineTokenSchema = z8.object({
|
|
2241
|
+
tokenId: z8.string().uuid(),
|
|
2242
|
+
tokenSerial: z8.string(),
|
|
2243
|
+
issuerAccountId: z8.string().uuid(),
|
|
2244
|
+
payerUserId: z8.string().uuid(),
|
|
2245
|
+
maxAmountKobo: z8.number().int().positive(),
|
|
2246
|
+
currency: z8.string().length(3),
|
|
2247
|
+
issuedAtMs: z8.number().int().nonnegative(),
|
|
2248
|
+
expiresAtMs: z8.number().int().nonnegative(),
|
|
2249
|
+
issuerSig: z8.string()
|
|
1893
2250
|
});
|
|
1894
|
-
var PaymentClaimSchema =
|
|
1895
|
-
encounterId:
|
|
1896
|
-
tokenSerial:
|
|
1897
|
-
payerUserId:
|
|
1898
|
-
payeeUserId:
|
|
1899
|
-
payerNonce:
|
|
1900
|
-
payeeNonce:
|
|
1901
|
-
amountKobo:
|
|
1902
|
-
currency:
|
|
1903
|
-
occurredAtMs:
|
|
1904
|
-
completedAtMs:
|
|
1905
|
-
contextId:
|
|
2251
|
+
var PaymentClaimSchema = z8.object({
|
|
2252
|
+
encounterId: z8.string().regex(/^[0-9a-f]{64}$/i).optional(),
|
|
2253
|
+
tokenSerial: z8.string(),
|
|
2254
|
+
payerUserId: z8.string().uuid(),
|
|
2255
|
+
payeeUserId: z8.string().uuid(),
|
|
2256
|
+
payerNonce: z8.string(),
|
|
2257
|
+
payeeNonce: z8.string(),
|
|
2258
|
+
amountKobo: z8.number().int().positive(),
|
|
2259
|
+
currency: z8.string().length(3).default("NGN"),
|
|
2260
|
+
occurredAtMs: z8.number().int().nonnegative(),
|
|
2261
|
+
completedAtMs: z8.number().int().nonnegative().optional(),
|
|
2262
|
+
contextId: z8.string().optional(),
|
|
1906
2263
|
// Stage 2c: P-256 device keys are now SubjectPublicKeyInfo DER, base64.
|
|
1907
2264
|
// Signatures are ASN.1 DER ECDSA(SHA-256), base64. Backwards-incompatible
|
|
1908
2265
|
// wire change; the backend has the matching widening in offline-settlements
|
|
1909
2266
|
// service + zod schema.
|
|
1910
|
-
payerPubkey:
|
|
1911
|
-
payerSignature:
|
|
1912
|
-
payeePubkey:
|
|
1913
|
-
payeeSignature:
|
|
2267
|
+
payerPubkey: z8.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2268
|
+
payerSignature: z8.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2269
|
+
payeePubkey: z8.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional(),
|
|
2270
|
+
payeeSignature: z8.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional()
|
|
1914
2271
|
});
|
|
1915
|
-
var SettlementSchema =
|
|
1916
|
-
settlementId:
|
|
1917
|
-
settlementKey:
|
|
1918
|
-
encounterId:
|
|
1919
|
-
issuerAccountId:
|
|
1920
|
-
tokenSerial:
|
|
1921
|
-
payerUserId:
|
|
1922
|
-
payeeUserId:
|
|
1923
|
-
amountKobo:
|
|
1924
|
-
currency:
|
|
1925
|
-
receiptId:
|
|
1926
|
-
status:
|
|
1927
|
-
issuerSig:
|
|
1928
|
-
createdAtMs:
|
|
2272
|
+
var SettlementSchema = z8.object({
|
|
2273
|
+
settlementId: z8.string().uuid(),
|
|
2274
|
+
settlementKey: z8.string().regex(/^[0-9a-f]{64}$/i),
|
|
2275
|
+
encounterId: z8.string().regex(/^[0-9a-f]{64}$/i),
|
|
2276
|
+
issuerAccountId: z8.string().uuid(),
|
|
2277
|
+
tokenSerial: z8.string(),
|
|
2278
|
+
payerUserId: z8.string().uuid(),
|
|
2279
|
+
payeeUserId: z8.string().uuid(),
|
|
2280
|
+
amountKobo: z8.number().int().nonnegative(),
|
|
2281
|
+
currency: z8.string().length(3),
|
|
2282
|
+
receiptId: z8.string().nullable(),
|
|
2283
|
+
status: z8.enum(["SETTLED", "REVIEW", "REJECTED"]),
|
|
2284
|
+
issuerSig: z8.string(),
|
|
2285
|
+
createdAtMs: z8.number().int().nonnegative()
|
|
1929
2286
|
});
|
|
1930
|
-
var SettleResponseSchema =
|
|
2287
|
+
var SettleResponseSchema = z8.object({
|
|
1931
2288
|
settlement: SettlementSchema,
|
|
1932
|
-
encounterId:
|
|
1933
|
-
replayed:
|
|
2289
|
+
encounterId: z8.string().regex(/^[0-9a-f]{64}$/i),
|
|
2290
|
+
replayed: z8.boolean()
|
|
1934
2291
|
});
|
|
1935
2292
|
var ENCOUNTER_DOMAIN = "offline:v1:encounter";
|
|
1936
2293
|
async function sha256Hex(input) {
|
|
@@ -2104,7 +2461,7 @@ function createHmacFetch(opts) {
|
|
|
2104
2461
|
}
|
|
2105
2462
|
|
|
2106
2463
|
// src/partner/client.ts
|
|
2107
|
-
import { z as
|
|
2464
|
+
import { z as z9 } from "zod";
|
|
2108
2465
|
import { sha256 as sha2563 } from "@noble/hashes/sha256";
|
|
2109
2466
|
import { hmac as hmac2 } from "@noble/hashes/hmac";
|
|
2110
2467
|
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils";
|
|
@@ -2128,18 +2485,18 @@ var PARTNER_SCOPES = [
|
|
|
2128
2485
|
"partner:payout:write",
|
|
2129
2486
|
"partner:reconciliation:read"
|
|
2130
2487
|
];
|
|
2131
|
-
var ApiCredentialPublicSchema =
|
|
2132
|
-
id:
|
|
2133
|
-
accountId:
|
|
2134
|
-
keyId:
|
|
2135
|
-
scopes:
|
|
2136
|
-
label:
|
|
2137
|
-
createdAtMs:
|
|
2138
|
-
lastUsedAtMs:
|
|
2139
|
-
revokedAtMs:
|
|
2488
|
+
var ApiCredentialPublicSchema = z9.object({
|
|
2489
|
+
id: z9.string().uuid(),
|
|
2490
|
+
accountId: z9.string().uuid(),
|
|
2491
|
+
keyId: z9.string(),
|
|
2492
|
+
scopes: z9.array(z9.enum(PARTNER_SCOPES)),
|
|
2493
|
+
label: z9.string().nullable(),
|
|
2494
|
+
createdAtMs: z9.number().int().nonnegative(),
|
|
2495
|
+
lastUsedAtMs: z9.number().int().nonnegative().nullable(),
|
|
2496
|
+
revokedAtMs: z9.number().int().nonnegative().nullable()
|
|
2140
2497
|
});
|
|
2141
2498
|
var MintedApiCredentialSchema = ApiCredentialPublicSchema.extend({
|
|
2142
|
-
secret:
|
|
2499
|
+
secret: z9.string().min(1)
|
|
2143
2500
|
});
|
|
2144
2501
|
var enc = new TextEncoder();
|
|
2145
2502
|
async function sha256Hex2(input) {
|
|
@@ -2296,8 +2653,8 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2296
2653
|
}
|
|
2297
2654
|
return parser(raw);
|
|
2298
2655
|
}
|
|
2299
|
-
const listSchema =
|
|
2300
|
-
items:
|
|
2656
|
+
const listSchema = z9.object({
|
|
2657
|
+
items: z9.array(ApiCredentialPublicSchema)
|
|
2301
2658
|
});
|
|
2302
2659
|
return {
|
|
2303
2660
|
list: (accountId) => call(
|
|
@@ -2322,7 +2679,7 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2322
2679
|
}
|
|
2323
2680
|
|
|
2324
2681
|
// src/passes/pass.ts
|
|
2325
|
-
import { z as
|
|
2682
|
+
import { z as z10 } from "zod";
|
|
2326
2683
|
var PASS_KINDS = [
|
|
2327
2684
|
"ride-ticket",
|
|
2328
2685
|
"transit-pass",
|
|
@@ -2338,39 +2695,39 @@ var PASS_STATES = [
|
|
|
2338
2695
|
"expired",
|
|
2339
2696
|
"revoked"
|
|
2340
2697
|
];
|
|
2341
|
-
var PassMetadataSchema =
|
|
2342
|
-
|
|
2698
|
+
var PassMetadataSchema = z10.record(
|
|
2699
|
+
z10.union([z10.string(), z10.number(), z10.boolean(), z10.null()])
|
|
2343
2700
|
);
|
|
2344
|
-
var PassSchema =
|
|
2345
|
-
passId:
|
|
2701
|
+
var PassSchema = z10.object({
|
|
2702
|
+
passId: z10.string().min(1),
|
|
2346
2703
|
/** Optional client/template grouping id (server may omit). */
|
|
2347
|
-
templateId:
|
|
2704
|
+
templateId: z10.string().min(1).optional(),
|
|
2348
2705
|
/** Optional human-facing holder identity (server may omit). The cryptographic binding
|
|
2349
2706
|
* is `holderDevicePubkey` below. */
|
|
2350
|
-
holderUserId:
|
|
2351
|
-
kind:
|
|
2352
|
-
issuerId:
|
|
2353
|
-
issuedAtMs:
|
|
2354
|
-
validFromMs:
|
|
2355
|
-
validUntilMs:
|
|
2356
|
-
state:
|
|
2707
|
+
holderUserId: z10.string().min(1).optional(),
|
|
2708
|
+
kind: z10.enum(PASS_KINDS),
|
|
2709
|
+
issuerId: z10.string().min(1),
|
|
2710
|
+
issuedAtMs: z10.number().int().nonnegative(),
|
|
2711
|
+
validFromMs: z10.number().int().nonnegative(),
|
|
2712
|
+
validUntilMs: z10.number().int().positive(),
|
|
2713
|
+
state: z10.enum(PASS_STATES),
|
|
2357
2714
|
metadata: PassMetadataSchema,
|
|
2358
|
-
nonce:
|
|
2715
|
+
nonce: z10.string().min(1),
|
|
2359
2716
|
/** Device id this pass is bound to (FK to backend `device_keys`). */
|
|
2360
|
-
holderDeviceId:
|
|
2717
|
+
holderDeviceId: z10.string().min(1),
|
|
2361
2718
|
/** SubjectPublicKeyInfo DER (P-256) of the bound device, base64. The redemption
|
|
2362
2719
|
* signature is verified against this key — it is the security-critical binding. */
|
|
2363
|
-
holderDevicePubkey:
|
|
2720
|
+
holderDevicePubkey: z10.string().min(64).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2364
2721
|
/** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
|
|
2365
|
-
amountKobo:
|
|
2722
|
+
amountKobo: z10.number().int().nonnegative().optional(),
|
|
2366
2723
|
/** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
|
|
2367
|
-
currency:
|
|
2724
|
+
currency: z10.string().min(3).max(8),
|
|
2368
2725
|
/** Monotonic redemption counter floor. Redemption.counter MUST be > counterSeed. */
|
|
2369
|
-
counterSeed:
|
|
2726
|
+
counterSeed: z10.number().int().nonnegative(),
|
|
2370
2727
|
/** Optional cumulative spend cap in kobo across all redemptions of this pass. */
|
|
2371
|
-
cumulativeCapKobo:
|
|
2728
|
+
cumulativeCapKobo: z10.number().int().nonnegative().optional(),
|
|
2372
2729
|
/** ASN.1 DER ECDSA P-256 signature, base64. */
|
|
2373
|
-
issuerSig:
|
|
2730
|
+
issuerSig: z10.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
|
|
2374
2731
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
2375
2732
|
message: "validUntilMs must be greater than validFromMs"
|
|
2376
2733
|
});
|
|
@@ -2427,20 +2784,20 @@ function isPassWithinValidity(pass, nowMs) {
|
|
|
2427
2784
|
}
|
|
2428
2785
|
|
|
2429
2786
|
// src/passes/redemption.ts
|
|
2430
|
-
import { z as
|
|
2431
|
-
var
|
|
2432
|
-
var RedemptionSchema =
|
|
2787
|
+
import { z as z11 } from "zod";
|
|
2788
|
+
var Base64Std3 = z11.string().min(16).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (std)");
|
|
2789
|
+
var RedemptionSchema = z11.object({
|
|
2433
2790
|
pass: PassSchema,
|
|
2434
|
-
redeemerId:
|
|
2435
|
-
redeemedAtMs:
|
|
2791
|
+
redeemerId: z11.string().min(1),
|
|
2792
|
+
redeemedAtMs: z11.number().int().nonnegative(),
|
|
2436
2793
|
/** Strictly monotonic counter scoped to a single pass. Must be > pass.counterSeed
|
|
2437
2794
|
* and > the redeemer's lastSeenCounter for this pass. */
|
|
2438
|
-
counter:
|
|
2795
|
+
counter: z11.number().int().positive(),
|
|
2439
2796
|
/** Amount being redeemed in kobo (0 for non-monetary passes like ride tickets). */
|
|
2440
|
-
amountKobo:
|
|
2441
|
-
nonce:
|
|
2797
|
+
amountKobo: z11.number().int().nonnegative(),
|
|
2798
|
+
nonce: z11.string().min(1),
|
|
2442
2799
|
/** ASN.1 DER ECDSA P-256 signature over canonicalJSONBytes(unsigned), base64. */
|
|
2443
|
-
holderSig:
|
|
2800
|
+
holderSig: Base64Std3
|
|
2444
2801
|
});
|
|
2445
2802
|
var REDEEMABLE_STATES = /* @__PURE__ */ new Set(["issued", "active"]);
|
|
2446
2803
|
function buildRedemption(input) {
|
|
@@ -2522,40 +2879,40 @@ function verifyRedemption(r, issuerPublicKeySpkiB64) {
|
|
|
2522
2879
|
}
|
|
2523
2880
|
|
|
2524
2881
|
// src/receipts/receipt.ts
|
|
2525
|
-
import { z as
|
|
2882
|
+
import { z as z12 } from "zod";
|
|
2526
2883
|
var RECEIPT_CHANNELS = ["cash", "pass"];
|
|
2527
2884
|
var RECEIPT_KINDS = RECEIPT_CHANNELS;
|
|
2528
|
-
var ReceiptPayloadSchema =
|
|
2529
|
-
|
|
2885
|
+
var ReceiptPayloadSchema = z12.record(
|
|
2886
|
+
z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()])
|
|
2530
2887
|
);
|
|
2531
|
-
var ReceiptSchema =
|
|
2532
|
-
receiptId:
|
|
2533
|
-
channel:
|
|
2888
|
+
var ReceiptSchema = z12.object({
|
|
2889
|
+
receiptId: z12.string().min(1),
|
|
2890
|
+
channel: z12.enum(RECEIPT_CHANNELS),
|
|
2534
2891
|
/** Cash-channel: send_intents.id. Required when channel === 'cash'. */
|
|
2535
|
-
intentId:
|
|
2892
|
+
intentId: z12.string().min(1).optional(),
|
|
2536
2893
|
/** Pass-channel: pass_redemptions.id. Required when channel === 'pass'. */
|
|
2537
|
-
passRedemptionId:
|
|
2538
|
-
payerUserId:
|
|
2539
|
-
payeeUserId:
|
|
2540
|
-
amountKobo:
|
|
2541
|
-
currency:
|
|
2542
|
-
issuedAtMs:
|
|
2543
|
-
issuerId:
|
|
2894
|
+
passRedemptionId: z12.string().min(1).optional(),
|
|
2895
|
+
payerUserId: z12.string().min(1),
|
|
2896
|
+
payeeUserId: z12.string().min(1),
|
|
2897
|
+
amountKobo: z12.number().int().nonnegative(),
|
|
2898
|
+
currency: z12.string().min(3).max(8),
|
|
2899
|
+
issuedAtMs: z12.number().int().nonnegative(),
|
|
2900
|
+
issuerId: z12.string().min(1),
|
|
2544
2901
|
payload: ReceiptPayloadSchema,
|
|
2545
2902
|
/** ASN.1 DER ECDSA P-256 signature, base64. */
|
|
2546
|
-
issuerSig:
|
|
2903
|
+
issuerSig: z12.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
|
|
2547
2904
|
}).superRefine((v, ctx) => {
|
|
2548
2905
|
if (v.channel === "cash") {
|
|
2549
2906
|
if (!v.intentId) {
|
|
2550
2907
|
ctx.addIssue({
|
|
2551
|
-
code:
|
|
2908
|
+
code: z12.ZodIssueCode.custom,
|
|
2552
2909
|
message: "cash receipts require intentId",
|
|
2553
2910
|
path: ["intentId"]
|
|
2554
2911
|
});
|
|
2555
2912
|
}
|
|
2556
2913
|
if (v.passRedemptionId) {
|
|
2557
2914
|
ctx.addIssue({
|
|
2558
|
-
code:
|
|
2915
|
+
code: z12.ZodIssueCode.custom,
|
|
2559
2916
|
message: "cash receipts must not carry passRedemptionId",
|
|
2560
2917
|
path: ["passRedemptionId"]
|
|
2561
2918
|
});
|
|
@@ -2563,14 +2920,14 @@ var ReceiptSchema = z11.object({
|
|
|
2563
2920
|
} else if (v.channel === "pass") {
|
|
2564
2921
|
if (!v.passRedemptionId) {
|
|
2565
2922
|
ctx.addIssue({
|
|
2566
|
-
code:
|
|
2923
|
+
code: z12.ZodIssueCode.custom,
|
|
2567
2924
|
message: "pass receipts require passRedemptionId",
|
|
2568
2925
|
path: ["passRedemptionId"]
|
|
2569
2926
|
});
|
|
2570
2927
|
}
|
|
2571
2928
|
if (v.intentId) {
|
|
2572
2929
|
ctx.addIssue({
|
|
2573
|
-
code:
|
|
2930
|
+
code: z12.ZodIssueCode.custom,
|
|
2574
2931
|
message: "pass receipts must not carry intentId",
|
|
2575
2932
|
path: ["intentId"]
|
|
2576
2933
|
});
|
|
@@ -2872,23 +3229,23 @@ function init(opts) {
|
|
|
2872
3229
|
}
|
|
2873
3230
|
|
|
2874
3231
|
// src/accounts/client.ts
|
|
2875
|
-
import { z as
|
|
3232
|
+
import { z as z13 } from "zod";
|
|
2876
3233
|
var ACCOUNT_TYPES = ["personal", "business", "partner"];
|
|
2877
3234
|
var ACCOUNT_STATUSES = ["active", "suspended", "closed"];
|
|
2878
3235
|
var MEMBERSHIP_ROLES = ["owner", "admin", "driver", "staff"];
|
|
2879
|
-
var AccountSchema =
|
|
2880
|
-
accountId:
|
|
2881
|
-
type:
|
|
2882
|
-
displayName:
|
|
2883
|
-
status:
|
|
2884
|
-
ownerUserId:
|
|
2885
|
-
createdAtMs:
|
|
3236
|
+
var AccountSchema = z13.object({
|
|
3237
|
+
accountId: z13.string().uuid(),
|
|
3238
|
+
type: z13.enum(ACCOUNT_TYPES),
|
|
3239
|
+
displayName: z13.string().min(1),
|
|
3240
|
+
status: z13.enum(ACCOUNT_STATUSES),
|
|
3241
|
+
ownerUserId: z13.string().uuid().nullable(),
|
|
3242
|
+
createdAtMs: z13.number().int().nonnegative()
|
|
2886
3243
|
});
|
|
2887
|
-
var AccountMembershipSchema =
|
|
2888
|
-
accountId:
|
|
2889
|
-
userId:
|
|
2890
|
-
role:
|
|
2891
|
-
createdAtMs:
|
|
3244
|
+
var AccountMembershipSchema = z13.object({
|
|
3245
|
+
accountId: z13.string().uuid(),
|
|
3246
|
+
userId: z13.string().uuid(),
|
|
3247
|
+
role: z13.enum(MEMBERSHIP_ROLES),
|
|
3248
|
+
createdAtMs: z13.number().int().nonnegative()
|
|
2892
3249
|
});
|
|
2893
3250
|
function createAccountsClient(opts) {
|
|
2894
3251
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -2921,9 +3278,9 @@ function createAccountsClient(opts) {
|
|
|
2921
3278
|
}
|
|
2922
3279
|
return parser(raw);
|
|
2923
3280
|
}
|
|
2924
|
-
const itemsSchema =
|
|
2925
|
-
const memberItemsSchema =
|
|
2926
|
-
items:
|
|
3281
|
+
const itemsSchema = z13.object({ items: z13.array(AccountSchema) });
|
|
3282
|
+
const memberItemsSchema = z13.object({
|
|
3283
|
+
items: z13.array(AccountMembershipSchema)
|
|
2927
3284
|
});
|
|
2928
3285
|
return {
|
|
2929
3286
|
listMyAccounts: () => call(
|
|
@@ -2954,320 +3311,6 @@ function createAccountsClient(opts) {
|
|
|
2954
3311
|
};
|
|
2955
3312
|
}
|
|
2956
3313
|
|
|
2957
|
-
// src/me-offline/client.ts
|
|
2958
|
-
import { z as z13 } from "zod";
|
|
2959
|
-
var Hex64 = z13.string().regex(/^[0-9a-f]{64}$/i);
|
|
2960
|
-
var Sha256Hex = z13.string().regex(/^[0-9a-f]{64}$/i);
|
|
2961
|
-
var Base64Std3 = z13.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
2962
|
-
var RegisterDeviceKeyInputSchema = z13.object({
|
|
2963
|
-
deviceId: z13.string().min(1).max(128),
|
|
2964
|
-
publicKeyHex: Hex64
|
|
2965
|
-
});
|
|
2966
|
-
var AttestationSecurityLevelSchema = z13.enum([
|
|
2967
|
-
"STRONGBOX",
|
|
2968
|
-
"TEE",
|
|
2969
|
-
"SECURE_ENCLAVE",
|
|
2970
|
-
"SOFTWARE"
|
|
2971
|
-
]);
|
|
2972
|
-
var DeviceKeyAlgSchema = z13.literal("p256");
|
|
2973
|
-
var RegisterDeviceKeyP256InputSchema = z13.object({
|
|
2974
|
-
deviceId: z13.string().min(1).max(128),
|
|
2975
|
-
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
2976
|
-
publicKeySpkiB64: Base64Std3.min(64).max(4096),
|
|
2977
|
-
/** Base64 of the server-issued enrollment challenge string. */
|
|
2978
|
-
challengeB64: Base64Std3.min(8).max(1024),
|
|
2979
|
-
/** iOS App Attest payload or Android X.509 Key Attestation chain. */
|
|
2980
|
-
attestationChainB64: z13.array(Base64Std3.min(16).max(16384)).min(1).max(16),
|
|
2981
|
-
securityLevel: AttestationSecurityLevelSchema
|
|
2982
|
-
});
|
|
2983
|
-
var P256EnrollmentChallengeInputSchema = z13.object({
|
|
2984
|
-
deviceId: z13.string().min(1).max(128)
|
|
2985
|
-
});
|
|
2986
|
-
var P256EnrollmentChallengeResultSchema = z13.object({
|
|
2987
|
-
challenge: z13.string().min(16),
|
|
2988
|
-
expiresAtMs: z13.number().int().positive()
|
|
2989
|
-
});
|
|
2990
|
-
var DeviceKeyRecordSchema = z13.object({
|
|
2991
|
-
id: z13.string().uuid(),
|
|
2992
|
-
userId: z13.string().uuid(),
|
|
2993
|
-
deviceId: z13.string(),
|
|
2994
|
-
/** Always 'p256' on the consumer offline rail. Field retained for forward-compat. */
|
|
2995
|
-
alg: DeviceKeyAlgSchema.default("p256"),
|
|
2996
|
-
/** Legacy ed25519 hex key. Always null on new records (kept for back-compat reads). */
|
|
2997
|
-
publicKeyHex: Hex64.nullable().default(null),
|
|
2998
|
-
/** P-256 SubjectPublicKeyInfo DER, base64. Required for new records. */
|
|
2999
|
-
publicKeySpkiB64: Base64Std3.nullable().default(null),
|
|
3000
|
-
securityLevel: AttestationSecurityLevelSchema.nullable().default(null),
|
|
3001
|
-
hardwareBacked: z13.boolean().default(false),
|
|
3002
|
-
attestedAtMs: z13.number().int().nonnegative().nullable().default(null),
|
|
3003
|
-
createdAtMs: z13.number().int().nonnegative(),
|
|
3004
|
-
revokedAtMs: z13.number().int().nonnegative().nullable()
|
|
3005
|
-
});
|
|
3006
|
-
var ConsumerOACSchema = z13.object({
|
|
3007
|
-
oacId: z13.string().uuid(),
|
|
3008
|
-
issuerId: z13.string().min(1).max(64),
|
|
3009
|
-
userId: z13.string().uuid(),
|
|
3010
|
-
deviceId: z13.string().min(1).max(128),
|
|
3011
|
-
/** Always 'p256'. Field retained for forward-compat. */
|
|
3012
|
-
alg: z13.literal("p256").default("p256"),
|
|
3013
|
-
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
3014
|
-
devicePubkeySpkiB64: Base64Std3.min(64).max(4096),
|
|
3015
|
-
perTxCapKobo: z13.number().int().positive(),
|
|
3016
|
-
cumulativeCapKobo: z13.number().int().positive(),
|
|
3017
|
-
currency: z13.string().length(3),
|
|
3018
|
-
validFromMs: z13.number().int().nonnegative(),
|
|
3019
|
-
validUntilMs: z13.number().int().nonnegative(),
|
|
3020
|
-
counterSeed: z13.number().int().nonnegative(),
|
|
3021
|
-
issuedAtMs: z13.number().int().nonnegative()
|
|
3022
|
-
});
|
|
3023
|
-
var SignedConsumerOACSchema = z13.object({
|
|
3024
|
-
oac: ConsumerOACSchema,
|
|
3025
|
-
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
3026
|
-
issuerSig: Base64Std3.min(16).max(2048),
|
|
3027
|
-
/** Issuer's P-256 public key as SubjectPublicKeyInfo DER, base64. */
|
|
3028
|
-
issuerPublicKeySpkiB64: Base64Std3.min(64).max(4096)
|
|
3029
|
-
});
|
|
3030
|
-
var OACRecordSchema = SignedConsumerOACSchema.extend({
|
|
3031
|
-
currentOfflineSpentKobo: z13.number().int().nonnegative(),
|
|
3032
|
-
status: z13.enum([
|
|
3033
|
-
"active",
|
|
3034
|
-
"superseded",
|
|
3035
|
-
"expired",
|
|
3036
|
-
"revoked",
|
|
3037
|
-
"disabling",
|
|
3038
|
-
"draining",
|
|
3039
|
-
"closed"
|
|
3040
|
-
]),
|
|
3041
|
-
supersededAtMs: z13.number().int().nonnegative().nullable(),
|
|
3042
|
-
revokedAtMs: z13.number().int().nonnegative().nullable(),
|
|
3043
|
-
holdId: z13.string().uuid().nullable().optional()
|
|
3044
|
-
});
|
|
3045
|
-
var IssueOACInputSchema = z13.object({
|
|
3046
|
-
deviceId: z13.string().min(1).max(128),
|
|
3047
|
-
perTxCapKobo: z13.number().int().positive().optional(),
|
|
3048
|
-
cumulativeCapKobo: z13.number().int().positive().optional(),
|
|
3049
|
-
ttlMs: z13.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
3050
|
-
spendableOnlineKobo: z13.number().int().nonnegative().optional()
|
|
3051
|
-
});
|
|
3052
|
-
var EnableOfflineInputSchema = z13.object({
|
|
3053
|
-
deviceId: z13.string().min(1).max(128),
|
|
3054
|
-
amountKobo: z13.number().int().positive(),
|
|
3055
|
-
perTxCapKobo: z13.number().int().positive().optional(),
|
|
3056
|
-
ttlMs: z13.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
3057
|
-
installId: z13.string().min(1).max(128),
|
|
3058
|
-
partnerId: z13.string().min(1).max(64).optional()
|
|
3059
|
-
});
|
|
3060
|
-
var ProvisionOfflineAllowanceInputSchema = EnableOfflineInputSchema;
|
|
3061
|
-
var DisableOfflineInputSchema = z13.object({
|
|
3062
|
-
deviceId: z13.string().min(1).max(128),
|
|
3063
|
-
installId: z13.string().min(1).max(128).optional(),
|
|
3064
|
-
claims: z13.array(z13.unknown()).max(256).optional()
|
|
3065
|
-
});
|
|
3066
|
-
var OfflineHoldRecordSchema = z13.object({
|
|
3067
|
-
holdId: z13.string().uuid(),
|
|
3068
|
-
userId: z13.string().uuid(),
|
|
3069
|
-
deviceId: z13.string(),
|
|
3070
|
-
partnerId: z13.string(),
|
|
3071
|
-
adapterKind: z13.string(),
|
|
3072
|
-
externalHoldRef: z13.string().nullable(),
|
|
3073
|
-
amountKobo: z13.number().int().nonnegative(),
|
|
3074
|
-
capturedKobo: z13.number().int().nonnegative(),
|
|
3075
|
-
releasedKobo: z13.number().int().nonnegative(),
|
|
3076
|
-
remainingKobo: z13.number().int().nonnegative(),
|
|
3077
|
-
currency: z13.string().length(3),
|
|
3078
|
-
status: z13.enum([
|
|
3079
|
-
"placing",
|
|
3080
|
-
"active",
|
|
3081
|
-
"disabling",
|
|
3082
|
-
"draining",
|
|
3083
|
-
"closed",
|
|
3084
|
-
"revoked",
|
|
3085
|
-
"failed"
|
|
3086
|
-
]),
|
|
3087
|
-
installId: z13.string().nullable(),
|
|
3088
|
-
drainDeadlineMs: z13.number().int().nonnegative(),
|
|
3089
|
-
disableRequestedAtMs: z13.number().int().nonnegative().nullable(),
|
|
3090
|
-
createdAtMs: z13.number().int().nonnegative(),
|
|
3091
|
-
closedAtMs: z13.number().int().nonnegative().nullable(),
|
|
3092
|
-
isTrusted: z13.boolean().optional()
|
|
3093
|
-
});
|
|
3094
|
-
var EnableOfflineResultSchema = z13.object({
|
|
3095
|
-
hold: OfflineHoldRecordSchema,
|
|
3096
|
-
oac: OACRecordSchema
|
|
3097
|
-
});
|
|
3098
|
-
var ProvisionOfflineAllowanceResultSchema = EnableOfflineResultSchema;
|
|
3099
|
-
var DisableOfflineResultSchema = z13.object({
|
|
3100
|
-
hold: OfflineHoldRecordSchema,
|
|
3101
|
-
trusted: z13.boolean(),
|
|
3102
|
-
settledClaims: z13.number().int().nonnegative()
|
|
3103
|
-
});
|
|
3104
|
-
var OfflineStatusResultSchema = z13.object({
|
|
3105
|
-
hold: OfflineHoldRecordSchema.nullable(),
|
|
3106
|
-
active: OACRecordSchema.nullable()
|
|
3107
|
-
});
|
|
3108
|
-
var OfflineStateResultSchema = z13.object({
|
|
3109
|
-
active: OACRecordSchema.nullable()
|
|
3110
|
-
});
|
|
3111
|
-
var ConsumerPaymentClaimSchema = z13.object({
|
|
3112
|
-
/** Always 'p256'. Retained for forward-compat and as an explicit domain marker. */
|
|
3113
|
-
alg: z13.literal("p256").default("p256"),
|
|
3114
|
-
oacId: z13.string().uuid(),
|
|
3115
|
-
encounterId: Sha256Hex.optional(),
|
|
3116
|
-
payerUserId: z13.string().uuid(),
|
|
3117
|
-
payeeUserId: z13.string().uuid(),
|
|
3118
|
-
payerDeviceId: z13.string().min(1).max(128),
|
|
3119
|
-
payerNonce: z13.string().min(8).max(128),
|
|
3120
|
-
payeeNonce: z13.string().min(8).max(128),
|
|
3121
|
-
amountKobo: z13.number().int().positive(),
|
|
3122
|
-
currency: z13.string().length(3).default("NGN"),
|
|
3123
|
-
occurredAtMs: z13.number().int().nonnegative(),
|
|
3124
|
-
completedAtMs: z13.number().int().nonnegative().optional(),
|
|
3125
|
-
contextId: z13.string().max(128).optional(),
|
|
3126
|
-
payerPubkeySpkiB64: Base64Std3.min(64).max(4096),
|
|
3127
|
-
payerSignatureDerB64: Base64Std3.min(16).max(2048),
|
|
3128
|
-
payeePubkeySpkiB64: Base64Std3.min(64).max(4096).optional(),
|
|
3129
|
-
payeeSignatureDerB64: Base64Std3.min(16).max(2048).optional()
|
|
3130
|
-
});
|
|
3131
|
-
var ConsumerSettlementSchema = z13.object({
|
|
3132
|
-
settlementId: z13.string().uuid(),
|
|
3133
|
-
settlementKey: Sha256Hex,
|
|
3134
|
-
encounterId: Sha256Hex,
|
|
3135
|
-
oacId: z13.string().uuid(),
|
|
3136
|
-
payerUserId: z13.string().uuid(),
|
|
3137
|
-
payeeUserId: z13.string().uuid(),
|
|
3138
|
-
amountKobo: z13.number().int().positive(),
|
|
3139
|
-
currency: z13.string().length(3),
|
|
3140
|
-
status: z13.enum(["SETTLED", "REVIEW"]),
|
|
3141
|
-
reviewReason: z13.string().nullable(),
|
|
3142
|
-
ledgerRef: z13.string().nullable(),
|
|
3143
|
-
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
3144
|
-
issuerSig: Base64Std3.min(16).max(2048),
|
|
3145
|
-
createdAtMs: z13.number().int().nonnegative()
|
|
3146
|
-
});
|
|
3147
|
-
var ConsumerSettleResultSchema = z13.object({
|
|
3148
|
-
settlement: ConsumerSettlementSchema,
|
|
3149
|
-
encounterId: Sha256Hex,
|
|
3150
|
-
replayed: z13.boolean()
|
|
3151
|
-
});
|
|
3152
|
-
var RevokeDeviceKeyInputSchema = z13.object({
|
|
3153
|
-
deviceId: z13.string().min(1).max(128),
|
|
3154
|
-
/** Step-up token from /api/v1/auth/send/verify with purpose='offline_revoke'. */
|
|
3155
|
-
sendAuthToken: z13.string().min(16)
|
|
3156
|
-
});
|
|
3157
|
-
function createMeOfflineClient(opts) {
|
|
3158
|
-
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
3159
|
-
if (!fetchImpl) {
|
|
3160
|
-
throw new Error("createMeOfflineClient: no fetch implementation available");
|
|
3161
|
-
}
|
|
3162
|
-
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
3163
|
-
async function call(method, path, body, parser) {
|
|
3164
|
-
const init2 = {
|
|
3165
|
-
method,
|
|
3166
|
-
headers: {
|
|
3167
|
-
"content-type": "application/json",
|
|
3168
|
-
accept: "application/json"
|
|
3169
|
-
}
|
|
3170
|
-
};
|
|
3171
|
-
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
3172
|
-
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
3173
|
-
const text = await resp.text();
|
|
3174
|
-
let raw = void 0;
|
|
3175
|
-
if (text) {
|
|
3176
|
-
try {
|
|
3177
|
-
raw = JSON.parse(text);
|
|
3178
|
-
} catch {
|
|
3179
|
-
}
|
|
3180
|
-
}
|
|
3181
|
-
if (!resp.ok) {
|
|
3182
|
-
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
3183
|
-
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
3184
|
-
throw new FlurApiError(resp.status, code, message, raw);
|
|
3185
|
-
}
|
|
3186
|
-
return parser(raw);
|
|
3187
|
-
}
|
|
3188
|
-
const deviceKeyItems = z13.object({ items: z13.array(DeviceKeyRecordSchema) });
|
|
3189
|
-
return {
|
|
3190
|
-
registerDeviceKey: (input) => call(
|
|
3191
|
-
"POST",
|
|
3192
|
-
"/v1/me/offline/keys",
|
|
3193
|
-
RegisterDeviceKeyInputSchema.parse(input),
|
|
3194
|
-
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
3195
|
-
),
|
|
3196
|
-
issueP256EnrollmentChallenge: (input) => call(
|
|
3197
|
-
"POST",
|
|
3198
|
-
"/v1/me/offline/keys/p256/challenge",
|
|
3199
|
-
P256EnrollmentChallengeInputSchema.parse(input),
|
|
3200
|
-
(raw) => P256EnrollmentChallengeResultSchema.parse(raw)
|
|
3201
|
-
),
|
|
3202
|
-
registerDeviceKeyP256: (input) => call(
|
|
3203
|
-
"POST",
|
|
3204
|
-
"/v1/me/offline/keys/p256",
|
|
3205
|
-
RegisterDeviceKeyP256InputSchema.parse(input),
|
|
3206
|
-
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
3207
|
-
),
|
|
3208
|
-
listDeviceKeys: () => call(
|
|
3209
|
-
"GET",
|
|
3210
|
-
"/v1/me/offline/keys",
|
|
3211
|
-
void 0,
|
|
3212
|
-
(raw) => deviceKeyItems.parse(raw)
|
|
3213
|
-
),
|
|
3214
|
-
revokeDeviceKey: (input) => call(
|
|
3215
|
-
"POST",
|
|
3216
|
-
"/v1/me/offline/keys/revoke",
|
|
3217
|
-
RevokeDeviceKeyInputSchema.parse(input),
|
|
3218
|
-
() => void 0
|
|
3219
|
-
),
|
|
3220
|
-
provisionAllowance: (input) => call(
|
|
3221
|
-
"POST",
|
|
3222
|
-
"/v1/me/offline/allowance",
|
|
3223
|
-
ProvisionOfflineAllowanceInputSchema.parse(input),
|
|
3224
|
-
(raw) => ProvisionOfflineAllowanceResultSchema.parse(raw)
|
|
3225
|
-
),
|
|
3226
|
-
enable: (input) => call(
|
|
3227
|
-
"POST",
|
|
3228
|
-
"/v1/me/offline/enable",
|
|
3229
|
-
EnableOfflineInputSchema.parse(input),
|
|
3230
|
-
(raw) => EnableOfflineResultSchema.parse(raw)
|
|
3231
|
-
),
|
|
3232
|
-
refresh: (input) => call(
|
|
3233
|
-
"POST",
|
|
3234
|
-
"/v1/me/offline/refresh",
|
|
3235
|
-
IssueOACInputSchema.parse(input),
|
|
3236
|
-
(raw) => OACRecordSchema.parse(raw)
|
|
3237
|
-
),
|
|
3238
|
-
disable: (input) => call(
|
|
3239
|
-
"POST",
|
|
3240
|
-
"/v1/me/offline/disable",
|
|
3241
|
-
DisableOfflineInputSchema.parse(input),
|
|
3242
|
-
(raw) => DisableOfflineResultSchema.parse(raw)
|
|
3243
|
-
),
|
|
3244
|
-
getStatus: (deviceId) => {
|
|
3245
|
-
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
3246
|
-
return call(
|
|
3247
|
-
"GET",
|
|
3248
|
-
`/v1/me/offline/status${qs}`,
|
|
3249
|
-
void 0,
|
|
3250
|
-
(raw) => OfflineStatusResultSchema.parse(raw)
|
|
3251
|
-
);
|
|
3252
|
-
},
|
|
3253
|
-
getState: (deviceId) => {
|
|
3254
|
-
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
3255
|
-
return call(
|
|
3256
|
-
"GET",
|
|
3257
|
-
`/v1/me/offline/state${qs}`,
|
|
3258
|
-
void 0,
|
|
3259
|
-
(raw) => OfflineStateResultSchema.parse(raw)
|
|
3260
|
-
);
|
|
3261
|
-
},
|
|
3262
|
-
submitClaim: (claim) => call(
|
|
3263
|
-
"POST",
|
|
3264
|
-
"/v1/me/offline/claims",
|
|
3265
|
-
ConsumerPaymentClaimSchema.parse(claim),
|
|
3266
|
-
(raw) => ConsumerSettleResultSchema.parse(raw)
|
|
3267
|
-
)
|
|
3268
|
-
};
|
|
3269
|
-
}
|
|
3270
|
-
|
|
3271
3314
|
// src/me-offline/signer.ts
|
|
3272
3315
|
import { p256 as p2562 } from "@noble/curves/nist";
|
|
3273
3316
|
var CLAIM_DOMAIN_V2 = "flur:consumer-offline:v2:claim";
|
|
@@ -4223,6 +4266,7 @@ export {
|
|
|
4223
4266
|
HARDENED_ARTIFACT_TYPES,
|
|
4224
4267
|
IdentityArtifactSchema,
|
|
4225
4268
|
IngestFundingResultSchema,
|
|
4269
|
+
IssueAccountOacInputSchema,
|
|
4226
4270
|
IssueOACInputSchema,
|
|
4227
4271
|
LedgerJournalEntryArtifactSchema,
|
|
4228
4272
|
ListPayoutDestinationsResultSchema,
|