@nokinc-flur/sdk 1.1.3 → 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 +1085 -981
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1428 -1434
- package/dist/index.d.ts +1428 -1434
- package/dist/index.js +1082 -976
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,7 @@ __export(index_exports, {
|
|
|
69
69
|
HARDENED_ARTIFACT_TYPES: () => HARDENED_ARTIFACT_TYPES,
|
|
70
70
|
IdentityArtifactSchema: () => IdentityArtifactSchema,
|
|
71
71
|
IngestFundingResultSchema: () => IngestFundingResultSchema,
|
|
72
|
+
IssueAccountOacInputSchema: () => IssueAccountOacInputSchema,
|
|
72
73
|
IssueOACInputSchema: () => IssueOACInputSchema,
|
|
73
74
|
LedgerJournalEntryArtifactSchema: () => LedgerJournalEntryArtifactSchema,
|
|
74
75
|
ListPayoutDestinationsResultSchema: () => ListPayoutDestinationsResultSchema,
|
|
@@ -86,6 +87,7 @@ __export(index_exports, {
|
|
|
86
87
|
OAC_DEFAULT_CUMULATIVE_KOBO: () => OAC_DEFAULT_CUMULATIVE_KOBO,
|
|
87
88
|
OAC_DEFAULT_PER_TX_KOBO: () => OAC_DEFAULT_PER_TX_KOBO,
|
|
88
89
|
OAC_DEFAULT_VALIDITY_MS: () => OAC_DEFAULT_VALIDITY_MS,
|
|
90
|
+
OFFLINE_CLAIM_SMS_PREFIX: () => OFFLINE_CLAIM_SMS_PREFIX,
|
|
89
91
|
OfflineClaimArtifactSchema: () => OfflineClaimArtifactSchema,
|
|
90
92
|
OfflineHoldRecordSchema: () => OfflineHoldRecordSchema,
|
|
91
93
|
OfflinePaymentAuthorizationArtifactSchema: () => OfflinePaymentAuthorizationArtifactSchema,
|
|
@@ -181,20 +183,21 @@ __export(index_exports, {
|
|
|
181
183
|
createPassesClient: () => createPassesClient,
|
|
182
184
|
createReceiptArtifactUri: () => createReceiptArtifactUri,
|
|
183
185
|
createReceiptsClient: () => createReceiptsClient,
|
|
184
|
-
createSoftwareEd25519Signer: () => createSoftwareEd25519Signer,
|
|
185
186
|
createSoftwareP256Signer: () => createSoftwareP256Signer,
|
|
186
187
|
decodeArtifactUri: () => decodeArtifactUri,
|
|
187
188
|
decodeAuthorizationQR: () => decodeAuthorizationQR,
|
|
188
189
|
decodeBase45: () => decodeBase45,
|
|
190
|
+
decodeOfflineClaimSmsMessage: () => decodeOfflineClaimSmsMessage,
|
|
189
191
|
decodePaymentRequestQR: () => decodePaymentRequestQR,
|
|
190
192
|
encodeArtifactUri: () => encodeArtifactUri,
|
|
191
193
|
encodeAuthorizationQR: () => encodeAuthorizationQR,
|
|
192
194
|
encodeBase45: () => encodeBase45,
|
|
193
195
|
encodeNQR: () => encodeNQR,
|
|
196
|
+
encodeOfflineClaimSmsMessage: () => encodeOfflineClaimSmsMessage,
|
|
194
197
|
encodePaymentRequestQR: () => encodePaymentRequestQR,
|
|
198
|
+
extractOfflineClaimSmsToken: () => extractOfflineClaimSmsToken,
|
|
195
199
|
formatAmount: () => formatAmount,
|
|
196
200
|
generateDynamicQR: () => generateDynamicQR,
|
|
197
|
-
generateKeyPair: () => generateKeyPair,
|
|
198
201
|
generateStaticQR: () => generateStaticQR,
|
|
199
202
|
init: () => init,
|
|
200
203
|
isHardenedArtifactType: () => isHardenedArtifactType,
|
|
@@ -205,13 +208,10 @@ __export(index_exports, {
|
|
|
205
208
|
parseAmountInput: () => parseAmountInput,
|
|
206
209
|
parseNQR: () => parseNQR,
|
|
207
210
|
parseQR: () => parseQR,
|
|
208
|
-
publicKeyFromPrivate: () => publicKeyFromPrivate,
|
|
209
211
|
readTLV: () => readTLV,
|
|
210
212
|
routingHint: () => routingHint,
|
|
211
|
-
sign: () => sign,
|
|
212
213
|
signArtifact: () => signArtifact,
|
|
213
214
|
signAuthorization: () => signAuthorization,
|
|
214
|
-
signCanonical: () => signCanonical,
|
|
215
215
|
signOAC: () => signOAC,
|
|
216
216
|
signPartnerRequest: () => signPartnerRequest,
|
|
217
217
|
signPass: () => signPass,
|
|
@@ -219,11 +219,9 @@ __export(index_exports, {
|
|
|
219
219
|
signReceipt: () => signReceipt,
|
|
220
220
|
signRedemption: () => signRedemption,
|
|
221
221
|
signRequestHMAC: () => signRequestHMAC,
|
|
222
|
-
verify: () => verify,
|
|
223
222
|
verifyArtifactSignature: () => verifyArtifactSignature,
|
|
224
223
|
verifyArtifactUri: () => verifyArtifactUri,
|
|
225
224
|
verifyAuthorization: () => verifyAuthorization,
|
|
226
|
-
verifyCanonical: () => verifyCanonical,
|
|
227
225
|
verifyClaimSignature: () => verifyClaimSignature,
|
|
228
226
|
verifyOAC: () => verifyOAC,
|
|
229
227
|
verifyPass: () => verifyPass,
|
|
@@ -236,203 +234,13 @@ __export(index_exports, {
|
|
|
236
234
|
module.exports = __toCommonJS(index_exports);
|
|
237
235
|
|
|
238
236
|
// src/client.ts
|
|
239
|
-
var
|
|
237
|
+
var import_zod5 = require("zod");
|
|
240
238
|
|
|
241
239
|
// src/contracts.ts
|
|
240
|
+
var import_zod2 = require("zod");
|
|
241
|
+
|
|
242
|
+
// src/me-offline/client.ts
|
|
242
243
|
var import_zod = require("zod");
|
|
243
|
-
var E164Regex = /^\+[1-9]\d{7,14}$/;
|
|
244
|
-
var UuidSchema = import_zod.z.string().uuid();
|
|
245
|
-
var IsoDateSchema = import_zod.z.string().datetime({ offset: true });
|
|
246
|
-
var CurrencySchema = import_zod.z.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
247
|
-
var HealthResponseSchema = import_zod.z.object({
|
|
248
|
-
ok: import_zod.z.boolean()
|
|
249
|
-
});
|
|
250
|
-
var WelcomeResponseSchema = import_zod.z.object({
|
|
251
|
-
message: import_zod.z.string()
|
|
252
|
-
});
|
|
253
|
-
var OnboardingStartRequestSchema = import_zod.z.object({
|
|
254
|
-
phoneE164: import_zod.z.string().regex(E164Regex),
|
|
255
|
-
appInstanceId: import_zod.z.string().min(3),
|
|
256
|
-
platform: import_zod.z.enum(["android", "ios", "web"]),
|
|
257
|
-
turnstileToken: import_zod.z.string().min(20).optional(),
|
|
258
|
-
appAttestation: import_zod.z.object({
|
|
259
|
-
provider: import_zod.z.enum(["android", "ios", "web"]),
|
|
260
|
-
token: import_zod.z.string().min(24)
|
|
261
|
-
}).optional(),
|
|
262
|
-
firstName: import_zod.z.string().trim().min(1).max(80).optional(),
|
|
263
|
-
lastName: import_zod.z.string().trim().min(1).max(80).optional()
|
|
264
|
-
});
|
|
265
|
-
var OnboardingStartResponseSchema = import_zod.z.object({
|
|
266
|
-
requestId: import_zod.z.string().min(1),
|
|
267
|
-
checkUrl: import_zod.z.string().url().optional(),
|
|
268
|
-
expiresInSec: import_zod.z.number().int().positive(),
|
|
269
|
-
fallback: import_zod.z.enum(["SILENT_AUTH", "OTP"])
|
|
270
|
-
});
|
|
271
|
-
var OnboardingCompleteRequestSchema = import_zod.z.object({
|
|
272
|
-
requestId: import_zod.z.string().min(1),
|
|
273
|
-
code: import_zod.z.string().min(1).max(32),
|
|
274
|
-
appInstanceId: import_zod.z.string().min(3),
|
|
275
|
-
fingerprintHash: import_zod.z.string().min(3).optional()
|
|
276
|
-
});
|
|
277
|
-
var OnboardingCompleteResponseSchema = import_zod.z.object({
|
|
278
|
-
sessionToken: import_zod.z.string().min(1),
|
|
279
|
-
userId: UuidSchema,
|
|
280
|
-
restricted: import_zod.z.boolean(),
|
|
281
|
-
risk_reasons: import_zod.z.array(
|
|
282
|
-
import_zod.z.enum(["SIM_SWAP_RECENT", "ROAMING", "CARRIER_CHANGED"])
|
|
283
|
-
),
|
|
284
|
-
stepUpRequired: import_zod.z.boolean().optional(),
|
|
285
|
-
riskStatus: import_zod.z.enum(["ok", "unavailable"]).optional()
|
|
286
|
-
});
|
|
287
|
-
var RegisterDeviceRequestSchema = import_zod.z.object({
|
|
288
|
-
userId: UuidSchema,
|
|
289
|
-
appInstanceId: import_zod.z.string().min(3),
|
|
290
|
-
platform: import_zod.z.string().min(2),
|
|
291
|
-
model: import_zod.z.string().optional(),
|
|
292
|
-
networkSignals: import_zod.z.object({
|
|
293
|
-
ip: import_zod.z.string().min(3),
|
|
294
|
-
asn: import_zod.z.number().int().optional(),
|
|
295
|
-
country: import_zod.z.string().min(2).optional(),
|
|
296
|
-
carrier: import_zod.z.string().optional()
|
|
297
|
-
})
|
|
298
|
-
});
|
|
299
|
-
var RegisterDeviceResponseSchema = import_zod.z.object({
|
|
300
|
-
deviceId: import_zod.z.string().min(1),
|
|
301
|
-
fingerprintHash: import_zod.z.string().min(1),
|
|
302
|
-
driftScore: import_zod.z.number(),
|
|
303
|
-
trustState: import_zod.z.enum(["TRUSTED_PRIMARY", "TRUSTED_SECONDARY", "UNVERIFIED"]),
|
|
304
|
-
stepUpRequired: import_zod.z.boolean()
|
|
305
|
-
});
|
|
306
|
-
var AuthRefreshRequestSchema = import_zod.z.object({
|
|
307
|
-
userId: UuidSchema,
|
|
308
|
-
refreshToken: import_zod.z.string().min(8),
|
|
309
|
-
appInstanceId: import_zod.z.string().min(3),
|
|
310
|
-
fingerprintHash: import_zod.z.string().min(3)
|
|
311
|
-
});
|
|
312
|
-
var AuthRefreshResponseSchema = import_zod.z.object({
|
|
313
|
-
refreshToken: import_zod.z.string().min(8),
|
|
314
|
-
stepUpRequired: import_zod.z.boolean()
|
|
315
|
-
});
|
|
316
|
-
var AuthLogoutRequestSchema = import_zod.z.object({
|
|
317
|
-
userId: UuidSchema,
|
|
318
|
-
refreshToken: import_zod.z.string().min(8)
|
|
319
|
-
});
|
|
320
|
-
var PinSetRequestSchema = import_zod.z.object({
|
|
321
|
-
userId: UuidSchema,
|
|
322
|
-
pin: import_zod.z.string().regex(/^\d{6}$/)
|
|
323
|
-
});
|
|
324
|
-
var PinVerifyRequestSchema = import_zod.z.object({
|
|
325
|
-
userId: UuidSchema,
|
|
326
|
-
pin: import_zod.z.string().regex(/^\d{6}$/)
|
|
327
|
-
});
|
|
328
|
-
var OkResponseSchema = import_zod.z.object({
|
|
329
|
-
ok: import_zod.z.boolean()
|
|
330
|
-
});
|
|
331
|
-
var RegisterSendDeviceKeyRequestSchema = import_zod.z.object({
|
|
332
|
-
userId: UuidSchema,
|
|
333
|
-
deviceId: import_zod.z.string().min(3),
|
|
334
|
-
publicKey: import_zod.z.string().min(32)
|
|
335
|
-
});
|
|
336
|
-
var SEND_AUTH_PURPOSES = ["send_money", "offline_revoke"];
|
|
337
|
-
var SendChallengeRequestSchema = import_zod.z.object({
|
|
338
|
-
userId: UuidSchema,
|
|
339
|
-
deviceId: import_zod.z.string().min(3),
|
|
340
|
-
purpose: import_zod.z.enum(SEND_AUTH_PURPOSES).optional()
|
|
341
|
-
});
|
|
342
|
-
var SendChallengeResponseSchema = import_zod.z.object({
|
|
343
|
-
challengeId: UuidSchema,
|
|
344
|
-
nonce: import_zod.z.string().min(1),
|
|
345
|
-
expiresAt: IsoDateSchema
|
|
346
|
-
});
|
|
347
|
-
var SendVerifyRequestSchema = import_zod.z.object({
|
|
348
|
-
userId: UuidSchema,
|
|
349
|
-
deviceId: import_zod.z.string().min(3),
|
|
350
|
-
challengeId: UuidSchema,
|
|
351
|
-
signature: import_zod.z.string().min(16)
|
|
352
|
-
});
|
|
353
|
-
var SendVerifyResponseSchema = import_zod.z.object({
|
|
354
|
-
sendAuthToken: import_zod.z.string().min(16)
|
|
355
|
-
});
|
|
356
|
-
var ResolveRecipientRequestSchema = import_zod.z.object({
|
|
357
|
-
identifier: import_zod.z.string().min(3)
|
|
358
|
-
});
|
|
359
|
-
var ResolveRecipientResponseSchema = import_zod.z.object({
|
|
360
|
-
recipientUserId: UuidSchema,
|
|
361
|
-
displayName: import_zod.z.string().min(1),
|
|
362
|
-
normalizedIdentifier: import_zod.z.string().regex(E164Regex),
|
|
363
|
-
isActive: import_zod.z.boolean()
|
|
364
|
-
});
|
|
365
|
-
var CreateTransferRequestSchema = import_zod.z.object({
|
|
366
|
-
recipientIdentifier: import_zod.z.string().min(3),
|
|
367
|
-
amountMinor: import_zod.z.number().int().positive(),
|
|
368
|
-
currency: CurrencySchema,
|
|
369
|
-
sendAuthToken: import_zod.z.string().min(16)
|
|
370
|
-
});
|
|
371
|
-
var TransferStatusSchema = import_zod.z.enum(["SETTLED", "PENDING_REVIEW", "DECLINED"]);
|
|
372
|
-
var TransferResponseSchema = import_zod.z.object({
|
|
373
|
-
transactionId: import_zod.z.string().min(1),
|
|
374
|
-
status: TransferStatusSchema,
|
|
375
|
-
userStatus: TransferStatusSchema,
|
|
376
|
-
recipientName: import_zod.z.string().min(1),
|
|
377
|
-
timestamp: IsoDateSchema
|
|
378
|
-
});
|
|
379
|
-
var DirectionSchema = import_zod.z.enum(["OUTGOING", "INCOMING"]);
|
|
380
|
-
var AccountActivityItemSchema = import_zod.z.object({
|
|
381
|
-
id: import_zod.z.string().min(1),
|
|
382
|
-
type: import_zod.z.string().min(1),
|
|
383
|
-
direction: DirectionSchema,
|
|
384
|
-
name: import_zod.z.string().min(1),
|
|
385
|
-
identifier: import_zod.z.string().min(1),
|
|
386
|
-
amountMinor: import_zod.z.number().int(),
|
|
387
|
-
currency: CurrencySchema,
|
|
388
|
-
status: import_zod.z.string().min(1),
|
|
389
|
-
timestamp: IsoDateSchema
|
|
390
|
-
});
|
|
391
|
-
var AccountSummaryResponseSchema = import_zod.z.object({
|
|
392
|
-
/** Authenticated user's stable internal id. */
|
|
393
|
-
userId: UuidSchema,
|
|
394
|
-
/**
|
|
395
|
-
* 10-digit Nigeria Uniform Bank Account Number (NUBAN) allocated by the
|
|
396
|
-
* bank partner. `null` when the user has no partner-allocated account yet.
|
|
397
|
-
*/
|
|
398
|
-
nuban: import_zod.z.string().regex(/^[0-9]{10}$/).nullable(),
|
|
399
|
-
balance: import_zod.z.number().int(),
|
|
400
|
-
currency: CurrencySchema,
|
|
401
|
-
dailySendLimit: import_zod.z.number().int().nonnegative(),
|
|
402
|
-
dailySendRemaining: import_zod.z.number().int().nonnegative(),
|
|
403
|
-
kycTier: import_zod.z.string().min(1),
|
|
404
|
-
kycStatus: import_zod.z.string().min(1),
|
|
405
|
-
recentActivity: import_zod.z.array(AccountActivityItemSchema)
|
|
406
|
-
});
|
|
407
|
-
var TransactionsListResponseSchema = import_zod.z.object({
|
|
408
|
-
items: import_zod.z.array(AccountActivityItemSchema),
|
|
409
|
-
nextCursor: import_zod.z.string().nullable()
|
|
410
|
-
});
|
|
411
|
-
var TransactionDetailResponseSchema = import_zod.z.object({
|
|
412
|
-
transactionId: import_zod.z.string().min(1),
|
|
413
|
-
type: import_zod.z.string().min(1),
|
|
414
|
-
direction: DirectionSchema,
|
|
415
|
-
counterpartyName: import_zod.z.string().min(1),
|
|
416
|
-
counterpartyIdentifier: import_zod.z.string().min(1),
|
|
417
|
-
amountMinor: import_zod.z.number().int(),
|
|
418
|
-
currency: CurrencySchema,
|
|
419
|
-
status: import_zod.z.string().min(1),
|
|
420
|
-
timestamp: IsoDateSchema
|
|
421
|
-
});
|
|
422
|
-
var PushRegisterRequestSchema = import_zod.z.object({
|
|
423
|
-
deviceId: import_zod.z.string().min(3),
|
|
424
|
-
platform: import_zod.z.enum(["ios", "android", "web"]),
|
|
425
|
-
token: import_zod.z.string().min(16)
|
|
426
|
-
});
|
|
427
|
-
var CreatePayLinkResponseSchema = import_zod.z.object({
|
|
428
|
-
token: import_zod.z.string().min(1)
|
|
429
|
-
});
|
|
430
|
-
var ResolvePayLinkResponseSchema = import_zod.z.object({
|
|
431
|
-
recipientUserId: UuidSchema,
|
|
432
|
-
displayName: import_zod.z.string().min(1),
|
|
433
|
-
normalizedIdentifier: import_zod.z.string().regex(E164Regex),
|
|
434
|
-
isActive: import_zod.z.boolean()
|
|
435
|
-
});
|
|
436
244
|
|
|
437
245
|
// src/errors.ts
|
|
438
246
|
var backendErrorCodeSet = /* @__PURE__ */ new Set([
|
|
@@ -536,9 +344,556 @@ async function mapToFlurError(res) {
|
|
|
536
344
|
});
|
|
537
345
|
}
|
|
538
346
|
|
|
347
|
+
// src/me-offline/client.ts
|
|
348
|
+
var Hex64 = import_zod.z.string().regex(/^[0-9a-f]{64}$/i);
|
|
349
|
+
var Sha256Hex = import_zod.z.string().regex(/^[0-9a-f]{64}$/i);
|
|
350
|
+
var Base64Std = import_zod.z.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
351
|
+
var RegisterDeviceKeyInputSchema = import_zod.z.object({
|
|
352
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
353
|
+
publicKeyHex: Hex64
|
|
354
|
+
});
|
|
355
|
+
var AttestationSecurityLevelSchema = import_zod.z.enum([
|
|
356
|
+
"STRONGBOX",
|
|
357
|
+
"TEE",
|
|
358
|
+
"SECURE_ENCLAVE",
|
|
359
|
+
"SOFTWARE"
|
|
360
|
+
]);
|
|
361
|
+
var DeviceKeyAlgSchema = import_zod.z.literal("p256");
|
|
362
|
+
var RegisterDeviceKeyP256InputSchema = import_zod.z.object({
|
|
363
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
364
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
365
|
+
publicKeySpkiB64: Base64Std.min(64).max(4096),
|
|
366
|
+
/** Base64 of the server-issued enrollment challenge string. */
|
|
367
|
+
challengeB64: Base64Std.min(8).max(1024),
|
|
368
|
+
/** iOS App Attest payload or Android X.509 Key Attestation chain. */
|
|
369
|
+
attestationChainB64: import_zod.z.array(Base64Std.min(16).max(16384)).min(1).max(16),
|
|
370
|
+
securityLevel: AttestationSecurityLevelSchema
|
|
371
|
+
});
|
|
372
|
+
var P256EnrollmentChallengeInputSchema = import_zod.z.object({
|
|
373
|
+
deviceId: import_zod.z.string().min(1).max(128)
|
|
374
|
+
});
|
|
375
|
+
var P256EnrollmentChallengeResultSchema = import_zod.z.object({
|
|
376
|
+
challenge: import_zod.z.string().min(16),
|
|
377
|
+
expiresAtMs: import_zod.z.number().int().positive()
|
|
378
|
+
});
|
|
379
|
+
var DeviceKeyRecordSchema = import_zod.z.object({
|
|
380
|
+
id: import_zod.z.string().uuid(),
|
|
381
|
+
userId: import_zod.z.string().uuid(),
|
|
382
|
+
deviceId: import_zod.z.string(),
|
|
383
|
+
/** Always 'p256' on the consumer offline rail. Field retained for forward-compat. */
|
|
384
|
+
alg: DeviceKeyAlgSchema.default("p256"),
|
|
385
|
+
/** Legacy ed25519 hex key. Always null on new records (kept for back-compat reads). */
|
|
386
|
+
publicKeyHex: Hex64.nullable().default(null),
|
|
387
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. Required for new records. */
|
|
388
|
+
publicKeySpkiB64: Base64Std.nullable().default(null),
|
|
389
|
+
securityLevel: AttestationSecurityLevelSchema.nullable().default(null),
|
|
390
|
+
hardwareBacked: import_zod.z.boolean().default(false),
|
|
391
|
+
attestedAtMs: import_zod.z.number().int().nonnegative().nullable().default(null),
|
|
392
|
+
createdAtMs: import_zod.z.number().int().nonnegative(),
|
|
393
|
+
revokedAtMs: import_zod.z.number().int().nonnegative().nullable()
|
|
394
|
+
});
|
|
395
|
+
var ConsumerOACSchema = import_zod.z.object({
|
|
396
|
+
oacId: import_zod.z.string().uuid(),
|
|
397
|
+
issuerId: import_zod.z.string().min(1).max(64),
|
|
398
|
+
userId: import_zod.z.string().uuid(),
|
|
399
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
400
|
+
/**
|
|
401
|
+
* Always 'p256'. Required on the wire (backend always emits it).
|
|
402
|
+
* Kept as a literal so input/output infer identically and the schema
|
|
403
|
+
* can be nested inside other response shapes without Zod input/output
|
|
404
|
+
* divergence under tsup DTS bundling.
|
|
405
|
+
*/
|
|
406
|
+
alg: import_zod.z.literal("p256"),
|
|
407
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
408
|
+
devicePubkeySpkiB64: Base64Std.min(64).max(4096),
|
|
409
|
+
perTxCapKobo: import_zod.z.number().int().positive(),
|
|
410
|
+
cumulativeCapKobo: import_zod.z.number().int().positive(),
|
|
411
|
+
currency: import_zod.z.string().length(3),
|
|
412
|
+
validFromMs: import_zod.z.number().int().nonnegative(),
|
|
413
|
+
validUntilMs: import_zod.z.number().int().nonnegative(),
|
|
414
|
+
counterSeed: import_zod.z.number().int().nonnegative(),
|
|
415
|
+
issuedAtMs: import_zod.z.number().int().nonnegative()
|
|
416
|
+
});
|
|
417
|
+
var SignedConsumerOACSchema = import_zod.z.object({
|
|
418
|
+
oac: ConsumerOACSchema,
|
|
419
|
+
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
420
|
+
issuerSig: Base64Std.min(16).max(2048),
|
|
421
|
+
/** Issuer's P-256 public key as SubjectPublicKeyInfo DER, base64. */
|
|
422
|
+
issuerPublicKeySpkiB64: Base64Std.min(64).max(4096)
|
|
423
|
+
});
|
|
424
|
+
var OACRecordSchema = SignedConsumerOACSchema.extend({
|
|
425
|
+
currentOfflineSpentKobo: import_zod.z.number().int().nonnegative(),
|
|
426
|
+
status: import_zod.z.enum([
|
|
427
|
+
"active",
|
|
428
|
+
"superseded",
|
|
429
|
+
"expired",
|
|
430
|
+
"revoked",
|
|
431
|
+
"disabling",
|
|
432
|
+
"draining",
|
|
433
|
+
"closed"
|
|
434
|
+
]),
|
|
435
|
+
supersededAtMs: import_zod.z.number().int().nonnegative().nullable(),
|
|
436
|
+
revokedAtMs: import_zod.z.number().int().nonnegative().nullable(),
|
|
437
|
+
holdId: import_zod.z.string().uuid().nullable().optional()
|
|
438
|
+
});
|
|
439
|
+
var IssueOACInputSchema = import_zod.z.object({
|
|
440
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
441
|
+
perTxCapKobo: import_zod.z.number().int().positive().optional(),
|
|
442
|
+
cumulativeCapKobo: import_zod.z.number().int().positive().optional(),
|
|
443
|
+
ttlMs: import_zod.z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
444
|
+
spendableOnlineKobo: import_zod.z.number().int().nonnegative().optional()
|
|
445
|
+
});
|
|
446
|
+
var IssueAccountOacInputSchema = import_zod.z.object({
|
|
447
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
448
|
+
perTxCapKobo: import_zod.z.number().int().positive().optional(),
|
|
449
|
+
cumulativeCapKobo: import_zod.z.number().int().positive().optional(),
|
|
450
|
+
ttlMs: import_zod.z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional()
|
|
451
|
+
});
|
|
452
|
+
var EnableOfflineInputSchema = import_zod.z.object({
|
|
453
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
454
|
+
amountKobo: import_zod.z.number().int().positive(),
|
|
455
|
+
perTxCapKobo: import_zod.z.number().int().positive().optional(),
|
|
456
|
+
ttlMs: import_zod.z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
457
|
+
installId: import_zod.z.string().min(1).max(128),
|
|
458
|
+
partnerId: import_zod.z.string().min(1).max(64).optional()
|
|
459
|
+
});
|
|
460
|
+
var ProvisionOfflineAllowanceInputSchema = EnableOfflineInputSchema;
|
|
461
|
+
var DisableOfflineInputSchema = import_zod.z.object({
|
|
462
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
463
|
+
installId: import_zod.z.string().min(1).max(128).optional(),
|
|
464
|
+
claims: import_zod.z.array(import_zod.z.unknown()).max(256).optional()
|
|
465
|
+
});
|
|
466
|
+
var OfflineHoldRecordSchema = import_zod.z.object({
|
|
467
|
+
holdId: import_zod.z.string().uuid(),
|
|
468
|
+
userId: import_zod.z.string().uuid(),
|
|
469
|
+
deviceId: import_zod.z.string(),
|
|
470
|
+
partnerId: import_zod.z.string(),
|
|
471
|
+
adapterKind: import_zod.z.string(),
|
|
472
|
+
externalHoldRef: import_zod.z.string().nullable(),
|
|
473
|
+
amountKobo: import_zod.z.number().int().nonnegative(),
|
|
474
|
+
capturedKobo: import_zod.z.number().int().nonnegative(),
|
|
475
|
+
releasedKobo: import_zod.z.number().int().nonnegative(),
|
|
476
|
+
remainingKobo: import_zod.z.number().int().nonnegative(),
|
|
477
|
+
currency: import_zod.z.string().length(3),
|
|
478
|
+
status: import_zod.z.enum([
|
|
479
|
+
"placing",
|
|
480
|
+
"active",
|
|
481
|
+
"disabling",
|
|
482
|
+
"draining",
|
|
483
|
+
"closed",
|
|
484
|
+
"revoked",
|
|
485
|
+
"failed"
|
|
486
|
+
]),
|
|
487
|
+
installId: import_zod.z.string().nullable(),
|
|
488
|
+
drainDeadlineMs: import_zod.z.number().int().nonnegative(),
|
|
489
|
+
disableRequestedAtMs: import_zod.z.number().int().nonnegative().nullable(),
|
|
490
|
+
createdAtMs: import_zod.z.number().int().nonnegative(),
|
|
491
|
+
closedAtMs: import_zod.z.number().int().nonnegative().nullable(),
|
|
492
|
+
isTrusted: import_zod.z.boolean().optional()
|
|
493
|
+
});
|
|
494
|
+
var EnableOfflineResultSchema = import_zod.z.object({
|
|
495
|
+
hold: OfflineHoldRecordSchema,
|
|
496
|
+
oac: OACRecordSchema
|
|
497
|
+
});
|
|
498
|
+
var ProvisionOfflineAllowanceResultSchema = EnableOfflineResultSchema;
|
|
499
|
+
var DisableOfflineResultSchema = import_zod.z.object({
|
|
500
|
+
hold: OfflineHoldRecordSchema,
|
|
501
|
+
trusted: import_zod.z.boolean(),
|
|
502
|
+
settledClaims: import_zod.z.number().int().nonnegative()
|
|
503
|
+
});
|
|
504
|
+
var OfflineStatusResultSchema = import_zod.z.object({
|
|
505
|
+
hold: OfflineHoldRecordSchema.nullable(),
|
|
506
|
+
active: OACRecordSchema.nullable()
|
|
507
|
+
});
|
|
508
|
+
var OfflineStateResultSchema = import_zod.z.object({
|
|
509
|
+
active: OACRecordSchema.nullable()
|
|
510
|
+
});
|
|
511
|
+
var ConsumerPaymentClaimSchema = import_zod.z.object({
|
|
512
|
+
/** Always 'p256'. Retained for forward-compat and as an explicit domain marker. */
|
|
513
|
+
alg: import_zod.z.literal("p256").default("p256"),
|
|
514
|
+
oacId: import_zod.z.string().uuid(),
|
|
515
|
+
encounterId: Sha256Hex.optional(),
|
|
516
|
+
payerUserId: import_zod.z.string().uuid(),
|
|
517
|
+
payeeUserId: import_zod.z.string().uuid(),
|
|
518
|
+
payerDeviceId: import_zod.z.string().min(1).max(128),
|
|
519
|
+
payerNonce: import_zod.z.string().min(8).max(128),
|
|
520
|
+
payeeNonce: import_zod.z.string().min(8).max(128),
|
|
521
|
+
amountKobo: import_zod.z.number().int().positive(),
|
|
522
|
+
currency: import_zod.z.string().length(3).default("NGN"),
|
|
523
|
+
occurredAtMs: import_zod.z.number().int().nonnegative(),
|
|
524
|
+
completedAtMs: import_zod.z.number().int().nonnegative().optional(),
|
|
525
|
+
contextId: import_zod.z.string().max(128).optional(),
|
|
526
|
+
payerPubkeySpkiB64: Base64Std.min(64).max(4096),
|
|
527
|
+
payerSignatureDerB64: Base64Std.min(16).max(2048),
|
|
528
|
+
payeePubkeySpkiB64: Base64Std.min(64).max(4096).optional(),
|
|
529
|
+
payeeSignatureDerB64: Base64Std.min(16).max(2048).optional()
|
|
530
|
+
});
|
|
531
|
+
var ConsumerSettlementSchema = import_zod.z.object({
|
|
532
|
+
settlementId: import_zod.z.string().uuid(),
|
|
533
|
+
settlementKey: Sha256Hex,
|
|
534
|
+
encounterId: Sha256Hex,
|
|
535
|
+
oacId: import_zod.z.string().uuid(),
|
|
536
|
+
payerUserId: import_zod.z.string().uuid(),
|
|
537
|
+
payeeUserId: import_zod.z.string().uuid(),
|
|
538
|
+
amountKobo: import_zod.z.number().int().positive(),
|
|
539
|
+
currency: import_zod.z.string().length(3),
|
|
540
|
+
status: import_zod.z.enum(["SETTLED", "REVIEW"]),
|
|
541
|
+
reviewReason: import_zod.z.string().nullable(),
|
|
542
|
+
ledgerRef: import_zod.z.string().nullable(),
|
|
543
|
+
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
544
|
+
issuerSig: Base64Std.min(16).max(2048),
|
|
545
|
+
createdAtMs: import_zod.z.number().int().nonnegative()
|
|
546
|
+
});
|
|
547
|
+
var ConsumerSettleResultSchema = import_zod.z.object({
|
|
548
|
+
settlement: ConsumerSettlementSchema,
|
|
549
|
+
encounterId: Sha256Hex,
|
|
550
|
+
replayed: import_zod.z.boolean()
|
|
551
|
+
});
|
|
552
|
+
var RevokeDeviceKeyInputSchema = import_zod.z.object({
|
|
553
|
+
deviceId: import_zod.z.string().min(1).max(128),
|
|
554
|
+
/** Step-up token from /api/v1/auth/send/verify with purpose='offline_revoke'. */
|
|
555
|
+
sendAuthToken: import_zod.z.string().min(16)
|
|
556
|
+
});
|
|
557
|
+
function createMeOfflineClient(opts) {
|
|
558
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
559
|
+
if (!fetchImpl) {
|
|
560
|
+
throw new Error("createMeOfflineClient: no fetch implementation available");
|
|
561
|
+
}
|
|
562
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
563
|
+
async function call(method, path, body, parser) {
|
|
564
|
+
const init2 = {
|
|
565
|
+
method,
|
|
566
|
+
headers: {
|
|
567
|
+
"content-type": "application/json",
|
|
568
|
+
accept: "application/json"
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
572
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
573
|
+
const text = await resp.text();
|
|
574
|
+
let raw = void 0;
|
|
575
|
+
if (text) {
|
|
576
|
+
try {
|
|
577
|
+
raw = JSON.parse(text);
|
|
578
|
+
} catch {
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (!resp.ok) {
|
|
582
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
583
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
584
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
585
|
+
}
|
|
586
|
+
return parser(raw);
|
|
587
|
+
}
|
|
588
|
+
const deviceKeyItems = import_zod.z.object({ items: import_zod.z.array(DeviceKeyRecordSchema) });
|
|
589
|
+
return {
|
|
590
|
+
registerDeviceKey: (input) => call(
|
|
591
|
+
"POST",
|
|
592
|
+
"/v1/me/offline/keys",
|
|
593
|
+
RegisterDeviceKeyInputSchema.parse(input),
|
|
594
|
+
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
595
|
+
),
|
|
596
|
+
issueP256EnrollmentChallenge: (input) => call(
|
|
597
|
+
"POST",
|
|
598
|
+
"/v1/me/offline/keys/p256/challenge",
|
|
599
|
+
P256EnrollmentChallengeInputSchema.parse(input),
|
|
600
|
+
(raw) => P256EnrollmentChallengeResultSchema.parse(raw)
|
|
601
|
+
),
|
|
602
|
+
registerDeviceKeyP256: (input) => call(
|
|
603
|
+
"POST",
|
|
604
|
+
"/v1/me/offline/keys/p256",
|
|
605
|
+
RegisterDeviceKeyP256InputSchema.parse(input),
|
|
606
|
+
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
607
|
+
),
|
|
608
|
+
listDeviceKeys: () => call(
|
|
609
|
+
"GET",
|
|
610
|
+
"/v1/me/offline/keys",
|
|
611
|
+
void 0,
|
|
612
|
+
(raw) => deviceKeyItems.parse(raw)
|
|
613
|
+
),
|
|
614
|
+
revokeDeviceKey: (input) => call(
|
|
615
|
+
"POST",
|
|
616
|
+
"/v1/me/offline/keys/revoke",
|
|
617
|
+
RevokeDeviceKeyInputSchema.parse(input),
|
|
618
|
+
() => void 0
|
|
619
|
+
),
|
|
620
|
+
issueAccountOac: (input) => call(
|
|
621
|
+
"POST",
|
|
622
|
+
"/v1/me/offline/oac",
|
|
623
|
+
IssueAccountOacInputSchema.parse(input),
|
|
624
|
+
(raw) => OACRecordSchema.parse(raw)
|
|
625
|
+
),
|
|
626
|
+
provisionAllowance: (input) => call(
|
|
627
|
+
"POST",
|
|
628
|
+
"/v1/me/offline/allowance",
|
|
629
|
+
ProvisionOfflineAllowanceInputSchema.parse(input),
|
|
630
|
+
(raw) => ProvisionOfflineAllowanceResultSchema.parse(raw)
|
|
631
|
+
),
|
|
632
|
+
enable: (input) => call(
|
|
633
|
+
"POST",
|
|
634
|
+
"/v1/me/offline/enable",
|
|
635
|
+
EnableOfflineInputSchema.parse(input),
|
|
636
|
+
(raw) => EnableOfflineResultSchema.parse(raw)
|
|
637
|
+
),
|
|
638
|
+
refresh: (input) => call(
|
|
639
|
+
"POST",
|
|
640
|
+
"/v1/me/offline/refresh",
|
|
641
|
+
IssueOACInputSchema.parse(input),
|
|
642
|
+
(raw) => OACRecordSchema.parse(raw)
|
|
643
|
+
),
|
|
644
|
+
disable: (input) => call(
|
|
645
|
+
"POST",
|
|
646
|
+
"/v1/me/offline/disable",
|
|
647
|
+
DisableOfflineInputSchema.parse(input),
|
|
648
|
+
(raw) => DisableOfflineResultSchema.parse(raw)
|
|
649
|
+
),
|
|
650
|
+
getStatus: (deviceId) => {
|
|
651
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
652
|
+
return call(
|
|
653
|
+
"GET",
|
|
654
|
+
`/v1/me/offline/status${qs}`,
|
|
655
|
+
void 0,
|
|
656
|
+
(raw) => OfflineStatusResultSchema.parse(raw)
|
|
657
|
+
);
|
|
658
|
+
},
|
|
659
|
+
getState: (deviceId) => {
|
|
660
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
661
|
+
return call(
|
|
662
|
+
"GET",
|
|
663
|
+
`/v1/me/offline/state${qs}`,
|
|
664
|
+
void 0,
|
|
665
|
+
(raw) => OfflineStateResultSchema.parse(raw)
|
|
666
|
+
);
|
|
667
|
+
},
|
|
668
|
+
submitClaim: (claim) => call(
|
|
669
|
+
"POST",
|
|
670
|
+
"/v1/me/offline/claims",
|
|
671
|
+
ConsumerPaymentClaimSchema.parse(claim),
|
|
672
|
+
(raw) => ConsumerSettleResultSchema.parse(raw)
|
|
673
|
+
)
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/contracts.ts
|
|
678
|
+
var E164Regex = /^\+[1-9]\d{7,14}$/;
|
|
679
|
+
var UuidSchema = import_zod2.z.string().uuid();
|
|
680
|
+
var IsoDateSchema = import_zod2.z.string().datetime({ offset: true });
|
|
681
|
+
var CurrencySchema = import_zod2.z.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
682
|
+
var HealthResponseSchema = import_zod2.z.object({
|
|
683
|
+
ok: import_zod2.z.boolean()
|
|
684
|
+
});
|
|
685
|
+
var WelcomeResponseSchema = import_zod2.z.object({
|
|
686
|
+
message: import_zod2.z.string()
|
|
687
|
+
});
|
|
688
|
+
var OnboardingStartRequestSchema = import_zod2.z.object({
|
|
689
|
+
phoneE164: import_zod2.z.string().regex(E164Regex),
|
|
690
|
+
appInstanceId: import_zod2.z.string().min(3),
|
|
691
|
+
platform: import_zod2.z.enum(["android", "ios", "web"]),
|
|
692
|
+
turnstileToken: import_zod2.z.string().min(20).optional(),
|
|
693
|
+
appAttestation: import_zod2.z.object({
|
|
694
|
+
provider: import_zod2.z.enum(["android", "ios", "web"]),
|
|
695
|
+
token: import_zod2.z.string().min(24)
|
|
696
|
+
}).optional(),
|
|
697
|
+
firstName: import_zod2.z.string().trim().min(1).max(80).optional(),
|
|
698
|
+
lastName: import_zod2.z.string().trim().min(1).max(80).optional()
|
|
699
|
+
});
|
|
700
|
+
var OnboardingStartResponseSchema = import_zod2.z.object({
|
|
701
|
+
requestId: import_zod2.z.string().min(1),
|
|
702
|
+
checkUrl: import_zod2.z.string().url().optional(),
|
|
703
|
+
expiresInSec: import_zod2.z.number().int().positive(),
|
|
704
|
+
fallback: import_zod2.z.enum(["SILENT_AUTH", "OTP"])
|
|
705
|
+
});
|
|
706
|
+
var OnboardingCompleteRequestSchema = import_zod2.z.object({
|
|
707
|
+
requestId: import_zod2.z.string().min(1),
|
|
708
|
+
code: import_zod2.z.string().min(1).max(32),
|
|
709
|
+
appInstanceId: import_zod2.z.string().min(3),
|
|
710
|
+
fingerprintHash: import_zod2.z.string().min(3).optional()
|
|
711
|
+
});
|
|
712
|
+
var OnboardingCompleteResponseSchema = import_zod2.z.object({
|
|
713
|
+
sessionToken: import_zod2.z.string().min(1),
|
|
714
|
+
userId: UuidSchema,
|
|
715
|
+
restricted: import_zod2.z.boolean(),
|
|
716
|
+
risk_reasons: import_zod2.z.array(
|
|
717
|
+
import_zod2.z.enum(["SIM_SWAP_RECENT", "ROAMING", "CARRIER_CHANGED"])
|
|
718
|
+
),
|
|
719
|
+
stepUpRequired: import_zod2.z.boolean().optional(),
|
|
720
|
+
riskStatus: import_zod2.z.enum(["ok", "unavailable"]).optional()
|
|
721
|
+
});
|
|
722
|
+
var RegisterDeviceRequestSchema = import_zod2.z.object({
|
|
723
|
+
userId: UuidSchema,
|
|
724
|
+
appInstanceId: import_zod2.z.string().min(3),
|
|
725
|
+
platform: import_zod2.z.string().min(2),
|
|
726
|
+
model: import_zod2.z.string().optional(),
|
|
727
|
+
networkSignals: import_zod2.z.object({
|
|
728
|
+
ip: import_zod2.z.string().min(3),
|
|
729
|
+
asn: import_zod2.z.number().int().optional(),
|
|
730
|
+
country: import_zod2.z.string().min(2).optional(),
|
|
731
|
+
carrier: import_zod2.z.string().optional()
|
|
732
|
+
})
|
|
733
|
+
});
|
|
734
|
+
var RegisterDeviceResponseSchema = import_zod2.z.object({
|
|
735
|
+
deviceId: import_zod2.z.string().min(1),
|
|
736
|
+
fingerprintHash: import_zod2.z.string().min(1),
|
|
737
|
+
driftScore: import_zod2.z.number(),
|
|
738
|
+
trustState: import_zod2.z.enum(["TRUSTED_PRIMARY", "TRUSTED_SECONDARY", "UNVERIFIED"]),
|
|
739
|
+
stepUpRequired: import_zod2.z.boolean()
|
|
740
|
+
});
|
|
741
|
+
var AuthRefreshRequestSchema = import_zod2.z.object({
|
|
742
|
+
userId: UuidSchema,
|
|
743
|
+
refreshToken: import_zod2.z.string().min(8),
|
|
744
|
+
appInstanceId: import_zod2.z.string().min(3),
|
|
745
|
+
fingerprintHash: import_zod2.z.string().min(3)
|
|
746
|
+
});
|
|
747
|
+
var AuthRefreshResponseSchema = import_zod2.z.object({
|
|
748
|
+
refreshToken: import_zod2.z.string().min(8),
|
|
749
|
+
stepUpRequired: import_zod2.z.boolean()
|
|
750
|
+
});
|
|
751
|
+
var AuthLogoutRequestSchema = import_zod2.z.object({
|
|
752
|
+
userId: UuidSchema,
|
|
753
|
+
refreshToken: import_zod2.z.string().min(8)
|
|
754
|
+
});
|
|
755
|
+
var PinSetRequestSchema = import_zod2.z.object({
|
|
756
|
+
userId: UuidSchema,
|
|
757
|
+
pin: import_zod2.z.string().regex(/^\d{6}$/)
|
|
758
|
+
});
|
|
759
|
+
var PinVerifyRequestSchema = import_zod2.z.object({
|
|
760
|
+
userId: UuidSchema,
|
|
761
|
+
pin: import_zod2.z.string().regex(/^\d{6}$/)
|
|
762
|
+
});
|
|
763
|
+
var OkResponseSchema = import_zod2.z.object({
|
|
764
|
+
ok: import_zod2.z.boolean()
|
|
765
|
+
});
|
|
766
|
+
var RegisterSendDeviceKeyRequestSchema = import_zod2.z.object({
|
|
767
|
+
userId: UuidSchema,
|
|
768
|
+
deviceId: import_zod2.z.string().min(3),
|
|
769
|
+
publicKey: import_zod2.z.string().min(32)
|
|
770
|
+
});
|
|
771
|
+
var SEND_AUTH_PURPOSES = ["send_money", "offline_revoke"];
|
|
772
|
+
var SendChallengeRequestSchema = import_zod2.z.object({
|
|
773
|
+
userId: UuidSchema,
|
|
774
|
+
deviceId: import_zod2.z.string().min(3),
|
|
775
|
+
purpose: import_zod2.z.enum(SEND_AUTH_PURPOSES).optional()
|
|
776
|
+
});
|
|
777
|
+
var SendChallengeResponseSchema = import_zod2.z.object({
|
|
778
|
+
challengeId: UuidSchema,
|
|
779
|
+
nonce: import_zod2.z.string().min(1),
|
|
780
|
+
expiresAt: IsoDateSchema
|
|
781
|
+
});
|
|
782
|
+
var SendVerifyRequestSchema = import_zod2.z.object({
|
|
783
|
+
userId: UuidSchema,
|
|
784
|
+
deviceId: import_zod2.z.string().min(3),
|
|
785
|
+
challengeId: UuidSchema,
|
|
786
|
+
signature: import_zod2.z.string().min(16)
|
|
787
|
+
});
|
|
788
|
+
var SendVerifyResponseSchema = import_zod2.z.object({
|
|
789
|
+
sendAuthToken: import_zod2.z.string().min(16)
|
|
790
|
+
});
|
|
791
|
+
var ResolveRecipientRequestSchema = import_zod2.z.object({
|
|
792
|
+
identifier: import_zod2.z.string().min(3)
|
|
793
|
+
});
|
|
794
|
+
var ResolveRecipientResponseSchema = import_zod2.z.object({
|
|
795
|
+
recipientUserId: UuidSchema,
|
|
796
|
+
displayName: import_zod2.z.string().min(1),
|
|
797
|
+
normalizedIdentifier: import_zod2.z.string().regex(E164Regex),
|
|
798
|
+
isActive: import_zod2.z.boolean()
|
|
799
|
+
});
|
|
800
|
+
var CreateTransferRequestSchema = import_zod2.z.object({
|
|
801
|
+
recipientIdentifier: import_zod2.z.string().min(3),
|
|
802
|
+
amountMinor: import_zod2.z.number().int().positive(),
|
|
803
|
+
currency: CurrencySchema,
|
|
804
|
+
sendAuthToken: import_zod2.z.string().min(16)
|
|
805
|
+
});
|
|
806
|
+
var TransferStatusSchema = import_zod2.z.enum(["SETTLED", "PENDING_REVIEW", "DECLINED"]);
|
|
807
|
+
var TransferResponseSchema = import_zod2.z.object({
|
|
808
|
+
transactionId: import_zod2.z.string().min(1),
|
|
809
|
+
status: TransferStatusSchema,
|
|
810
|
+
userStatus: TransferStatusSchema,
|
|
811
|
+
recipientName: import_zod2.z.string().min(1),
|
|
812
|
+
timestamp: IsoDateSchema,
|
|
813
|
+
/**
|
|
814
|
+
* Fresh issuer-signed OACs returned by the backend's post-commit
|
|
815
|
+
* rotation hook on `SETTLED` transfers.
|
|
816
|
+
*
|
|
817
|
+
* In the current backend implementation this hook rotates LEGACY
|
|
818
|
+
* hold-backed OACs only. Account-funded (no-hold) OACs introduced by
|
|
819
|
+
* the unified-pay-rails refactor are NOT rotated here — their
|
|
820
|
+
* cumulative spend counter and main-balance check are enforced at
|
|
821
|
+
* claim submission time via `createTransfer` under the per-sender
|
|
822
|
+
* advisory lock, so a stale no-hold OAC cannot authorise overspending
|
|
823
|
+
* (it can only resolve to REVIEW on submission).
|
|
824
|
+
*
|
|
825
|
+
* Optional: omitted when the sender has no registered hold-backed
|
|
826
|
+
* devices, when the refresh failed (best-effort), or on retry replays
|
|
827
|
+
* that pre-date the refresh. When present, the entries supersede any
|
|
828
|
+
* locally cached OACs for the listed devices; when absent, clients
|
|
829
|
+
* should fall back to `/v1/me/offline/refresh`.
|
|
830
|
+
*
|
|
831
|
+
* Each entry is validated against the canonical `OACRecordSchema`,
|
|
832
|
+
* so consumers can trust the runtime shape matches the static type.
|
|
833
|
+
*/
|
|
834
|
+
offlineOacs: OACRecordSchema.array().optional()
|
|
835
|
+
});
|
|
836
|
+
var DirectionSchema = import_zod2.z.enum(["OUTGOING", "INCOMING"]);
|
|
837
|
+
var AccountActivityItemSchema = import_zod2.z.object({
|
|
838
|
+
id: import_zod2.z.string().min(1),
|
|
839
|
+
type: import_zod2.z.string().min(1),
|
|
840
|
+
direction: DirectionSchema,
|
|
841
|
+
name: import_zod2.z.string().min(1),
|
|
842
|
+
identifier: import_zod2.z.string().min(1),
|
|
843
|
+
amountMinor: import_zod2.z.number().int(),
|
|
844
|
+
currency: CurrencySchema,
|
|
845
|
+
status: import_zod2.z.string().min(1),
|
|
846
|
+
timestamp: IsoDateSchema
|
|
847
|
+
});
|
|
848
|
+
var AccountSummaryResponseSchema = import_zod2.z.object({
|
|
849
|
+
/** Authenticated user's stable internal id. */
|
|
850
|
+
userId: UuidSchema,
|
|
851
|
+
/**
|
|
852
|
+
* 10-digit Nigeria Uniform Bank Account Number (NUBAN) allocated by the
|
|
853
|
+
* bank partner. `null` when the user has no partner-allocated account yet.
|
|
854
|
+
*/
|
|
855
|
+
nuban: import_zod2.z.string().regex(/^[0-9]{10}$/).nullable(),
|
|
856
|
+
balance: import_zod2.z.number().int(),
|
|
857
|
+
currency: CurrencySchema,
|
|
858
|
+
dailySendLimit: import_zod2.z.number().int().nonnegative(),
|
|
859
|
+
dailySendRemaining: import_zod2.z.number().int().nonnegative(),
|
|
860
|
+
kycTier: import_zod2.z.string().min(1),
|
|
861
|
+
kycStatus: import_zod2.z.string().min(1),
|
|
862
|
+
recentActivity: import_zod2.z.array(AccountActivityItemSchema)
|
|
863
|
+
});
|
|
864
|
+
var TransactionsListResponseSchema = import_zod2.z.object({
|
|
865
|
+
items: import_zod2.z.array(AccountActivityItemSchema),
|
|
866
|
+
nextCursor: import_zod2.z.string().nullable()
|
|
867
|
+
});
|
|
868
|
+
var TransactionDetailResponseSchema = import_zod2.z.object({
|
|
869
|
+
transactionId: import_zod2.z.string().min(1),
|
|
870
|
+
type: import_zod2.z.string().min(1),
|
|
871
|
+
direction: DirectionSchema,
|
|
872
|
+
counterpartyName: import_zod2.z.string().min(1),
|
|
873
|
+
counterpartyIdentifier: import_zod2.z.string().min(1),
|
|
874
|
+
amountMinor: import_zod2.z.number().int(),
|
|
875
|
+
currency: CurrencySchema,
|
|
876
|
+
status: import_zod2.z.string().min(1),
|
|
877
|
+
timestamp: IsoDateSchema
|
|
878
|
+
});
|
|
879
|
+
var PushRegisterRequestSchema = import_zod2.z.object({
|
|
880
|
+
deviceId: import_zod2.z.string().min(3),
|
|
881
|
+
platform: import_zod2.z.enum(["ios", "android", "web"]),
|
|
882
|
+
token: import_zod2.z.string().min(16)
|
|
883
|
+
});
|
|
884
|
+
var CreatePayLinkResponseSchema = import_zod2.z.object({
|
|
885
|
+
token: import_zod2.z.string().min(1)
|
|
886
|
+
});
|
|
887
|
+
var ResolvePayLinkResponseSchema = import_zod2.z.object({
|
|
888
|
+
recipientUserId: UuidSchema,
|
|
889
|
+
displayName: import_zod2.z.string().min(1),
|
|
890
|
+
normalizedIdentifier: import_zod2.z.string().regex(E164Regex),
|
|
891
|
+
isActive: import_zod2.z.boolean()
|
|
892
|
+
});
|
|
893
|
+
|
|
539
894
|
// src/primitives.ts
|
|
540
|
-
var
|
|
541
|
-
var CurrencyCodeSchema =
|
|
895
|
+
var import_zod3 = require("zod");
|
|
896
|
+
var CurrencyCodeSchema = import_zod3.z.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
542
897
|
var currencyFractionDigits = {
|
|
543
898
|
NGN: 2,
|
|
544
899
|
USD: 2,
|
|
@@ -632,7 +987,7 @@ function moneyMinorToNumber(amountMinor) {
|
|
|
632
987
|
}
|
|
633
988
|
|
|
634
989
|
// src/collections/client.ts
|
|
635
|
-
var
|
|
990
|
+
var import_zod4 = require("zod");
|
|
636
991
|
var MERCHANT_PROFILE_STATUSES = [
|
|
637
992
|
"pending",
|
|
638
993
|
"active",
|
|
@@ -662,172 +1017,172 @@ var MERCHANT_PAYOUT_STATUSES = [
|
|
|
662
1017
|
"failed",
|
|
663
1018
|
"cancelled"
|
|
664
1019
|
];
|
|
665
|
-
var MoneyKoboSchema =
|
|
666
|
-
var MetadataSchema =
|
|
667
|
-
|
|
1020
|
+
var MoneyKoboSchema = import_zod4.z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
1021
|
+
var MetadataSchema = import_zod4.z.record(
|
|
1022
|
+
import_zod4.z.union([import_zod4.z.string(), import_zod4.z.number(), import_zod4.z.boolean(), import_zod4.z.null()])
|
|
668
1023
|
);
|
|
669
|
-
var CurrencySchema2 =
|
|
670
|
-
var ReferenceSchema =
|
|
671
|
-
var MerchantProfileSchema =
|
|
672
|
-
accountId:
|
|
673
|
-
legalName:
|
|
674
|
-
tradingName:
|
|
675
|
-
merchantCategoryCode:
|
|
676
|
-
nqrMerchantId:
|
|
677
|
-
settlementBankCode:
|
|
678
|
-
settlementAccountNumber:
|
|
679
|
-
settlementAccountName:
|
|
680
|
-
settlementSchedule:
|
|
681
|
-
status:
|
|
682
|
-
offlineEnabled:
|
|
1024
|
+
var CurrencySchema2 = import_zod4.z.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
1025
|
+
var ReferenceSchema = import_zod4.z.string().trim().min(6).max(128).regex(/^[A-Za-z0-9._:-]+$/);
|
|
1026
|
+
var MerchantProfileSchema = import_zod4.z.object({
|
|
1027
|
+
accountId: import_zod4.z.string().uuid(),
|
|
1028
|
+
legalName: import_zod4.z.string(),
|
|
1029
|
+
tradingName: import_zod4.z.string(),
|
|
1030
|
+
merchantCategoryCode: import_zod4.z.string().regex(/^\d{4}$/),
|
|
1031
|
+
nqrMerchantId: import_zod4.z.string(),
|
|
1032
|
+
settlementBankCode: import_zod4.z.string(),
|
|
1033
|
+
settlementAccountNumber: import_zod4.z.string(),
|
|
1034
|
+
settlementAccountName: import_zod4.z.string(),
|
|
1035
|
+
settlementSchedule: import_zod4.z.enum(SETTLEMENT_SCHEDULES),
|
|
1036
|
+
status: import_zod4.z.enum(MERCHANT_PROFILE_STATUSES),
|
|
1037
|
+
offlineEnabled: import_zod4.z.boolean(),
|
|
683
1038
|
perTxLimitKobo: MoneyKoboSchema,
|
|
684
1039
|
dailyLimitKobo: MoneyKoboSchema,
|
|
685
1040
|
metadata: MetadataSchema,
|
|
686
|
-
createdAtMs:
|
|
687
|
-
updatedAtMs:
|
|
1041
|
+
createdAtMs: import_zod4.z.number().int().nonnegative(),
|
|
1042
|
+
updatedAtMs: import_zod4.z.number().int().nonnegative()
|
|
688
1043
|
});
|
|
689
|
-
var UpsertMerchantProfileInputSchema =
|
|
690
|
-
legalName:
|
|
691
|
-
tradingName:
|
|
692
|
-
merchantCategoryCode:
|
|
693
|
-
nqrMerchantId:
|
|
694
|
-
settlementBankCode:
|
|
695
|
-
settlementAccountNumber:
|
|
696
|
-
settlementAccountName:
|
|
697
|
-
settlementSchedule:
|
|
698
|
-
status:
|
|
699
|
-
offlineEnabled:
|
|
1044
|
+
var UpsertMerchantProfileInputSchema = import_zod4.z.object({
|
|
1045
|
+
legalName: import_zod4.z.string().trim().min(1).max(200),
|
|
1046
|
+
tradingName: import_zod4.z.string().trim().min(1).max(25),
|
|
1047
|
+
merchantCategoryCode: import_zod4.z.string().trim().regex(/^\d{4}$/),
|
|
1048
|
+
nqrMerchantId: import_zod4.z.string().trim().min(3).max(64),
|
|
1049
|
+
settlementBankCode: import_zod4.z.string().trim().min(2).max(16),
|
|
1050
|
+
settlementAccountNumber: import_zod4.z.string().trim().min(5).max(32),
|
|
1051
|
+
settlementAccountName: import_zod4.z.string().trim().min(1).max(200),
|
|
1052
|
+
settlementSchedule: import_zod4.z.enum(SETTLEMENT_SCHEDULES).optional(),
|
|
1053
|
+
status: import_zod4.z.enum(MERCHANT_PROFILE_STATUSES).optional(),
|
|
1054
|
+
offlineEnabled: import_zod4.z.boolean().optional(),
|
|
700
1055
|
perTxLimitKobo: MoneyKoboSchema.optional(),
|
|
701
1056
|
dailyLimitKobo: MoneyKoboSchema.optional(),
|
|
702
1057
|
metadata: MetadataSchema.optional()
|
|
703
1058
|
});
|
|
704
|
-
var CollectionIntentSchema =
|
|
705
|
-
intentId:
|
|
706
|
-
accountId:
|
|
707
|
-
terminalId:
|
|
708
|
-
reference:
|
|
709
|
-
amountKobo:
|
|
710
|
-
currency:
|
|
711
|
-
status:
|
|
712
|
-
description:
|
|
713
|
-
nqrPayload:
|
|
714
|
-
provider:
|
|
715
|
-
providerReference:
|
|
1059
|
+
var CollectionIntentSchema = import_zod4.z.object({
|
|
1060
|
+
intentId: import_zod4.z.string().uuid(),
|
|
1061
|
+
accountId: import_zod4.z.string().uuid(),
|
|
1062
|
+
terminalId: import_zod4.z.string().uuid().nullable(),
|
|
1063
|
+
reference: import_zod4.z.string(),
|
|
1064
|
+
amountKobo: import_zod4.z.number().int().positive().nullable(),
|
|
1065
|
+
currency: import_zod4.z.string().length(3),
|
|
1066
|
+
status: import_zod4.z.enum(COLLECTION_INTENT_STATUSES),
|
|
1067
|
+
description: import_zod4.z.string().nullable(),
|
|
1068
|
+
nqrPayload: import_zod4.z.string(),
|
|
1069
|
+
provider: import_zod4.z.string(),
|
|
1070
|
+
providerReference: import_zod4.z.string().nullable(),
|
|
716
1071
|
metadata: MetadataSchema,
|
|
717
|
-
expiresAtMs:
|
|
718
|
-
paidAtMs:
|
|
719
|
-
createdAtMs:
|
|
720
|
-
updatedAtMs:
|
|
1072
|
+
expiresAtMs: import_zod4.z.number().int().nonnegative().nullable(),
|
|
1073
|
+
paidAtMs: import_zod4.z.number().int().nonnegative().nullable(),
|
|
1074
|
+
createdAtMs: import_zod4.z.number().int().nonnegative(),
|
|
1075
|
+
updatedAtMs: import_zod4.z.number().int().nonnegative()
|
|
721
1076
|
});
|
|
722
|
-
var CreateCollectionIntentInputSchema =
|
|
1077
|
+
var CreateCollectionIntentInputSchema = import_zod4.z.object({
|
|
723
1078
|
reference: ReferenceSchema.optional(),
|
|
724
1079
|
amountKobo: MoneyKoboSchema.optional(),
|
|
725
1080
|
currency: CurrencySchema2.optional(),
|
|
726
|
-
terminalId:
|
|
727
|
-
terminalLabel:
|
|
728
|
-
merchantCity:
|
|
729
|
-
description:
|
|
730
|
-
expiresAtMs:
|
|
731
|
-
provider:
|
|
1081
|
+
terminalId: import_zod4.z.string().uuid().optional(),
|
|
1082
|
+
terminalLabel: import_zod4.z.string().trim().min(1).max(25).optional(),
|
|
1083
|
+
merchantCity: import_zod4.z.string().trim().min(1).max(15).optional(),
|
|
1084
|
+
description: import_zod4.z.string().trim().min(1).max(280).optional(),
|
|
1085
|
+
expiresAtMs: import_zod4.z.number().int().positive().optional(),
|
|
1086
|
+
provider: import_zod4.z.string().trim().min(1).max(40).optional(),
|
|
732
1087
|
metadata: MetadataSchema.optional()
|
|
733
1088
|
});
|
|
734
|
-
var PublicCollectionIntentSchema =
|
|
735
|
-
intentId:
|
|
736
|
-
reference:
|
|
737
|
-
amountKobo:
|
|
738
|
-
currency:
|
|
739
|
-
status:
|
|
740
|
-
merchantAccountId:
|
|
741
|
-
merchantName:
|
|
742
|
-
merchantCategoryCode:
|
|
743
|
-
description:
|
|
744
|
-
expiresAtMs:
|
|
1089
|
+
var PublicCollectionIntentSchema = import_zod4.z.object({
|
|
1090
|
+
intentId: import_zod4.z.string().uuid(),
|
|
1091
|
+
reference: import_zod4.z.string(),
|
|
1092
|
+
amountKobo: import_zod4.z.number().int().positive().nullable(),
|
|
1093
|
+
currency: import_zod4.z.string().length(3),
|
|
1094
|
+
status: import_zod4.z.enum(COLLECTION_INTENT_STATUSES),
|
|
1095
|
+
merchantAccountId: import_zod4.z.string().uuid(),
|
|
1096
|
+
merchantName: import_zod4.z.string(),
|
|
1097
|
+
merchantCategoryCode: import_zod4.z.string(),
|
|
1098
|
+
description: import_zod4.z.string().nullable(),
|
|
1099
|
+
expiresAtMs: import_zod4.z.number().int().nonnegative().nullable()
|
|
745
1100
|
});
|
|
746
|
-
var PayCollectionInputSchema =
|
|
1101
|
+
var PayCollectionInputSchema = import_zod4.z.object({
|
|
747
1102
|
reference: ReferenceSchema,
|
|
748
1103
|
amountKobo: MoneyKoboSchema.optional(),
|
|
749
1104
|
currency: CurrencySchema2.optional(),
|
|
750
|
-
idempotencyKey:
|
|
1105
|
+
idempotencyKey: import_zod4.z.string().trim().min(8).max(160).optional()
|
|
751
1106
|
});
|
|
752
|
-
var CollectionPaymentSchema =
|
|
753
|
-
paymentId:
|
|
754
|
-
intentId:
|
|
755
|
-
accountId:
|
|
756
|
-
payerUserId:
|
|
757
|
-
merchantOwnerUserId:
|
|
1107
|
+
var CollectionPaymentSchema = import_zod4.z.object({
|
|
1108
|
+
paymentId: import_zod4.z.string().uuid(),
|
|
1109
|
+
intentId: import_zod4.z.string().uuid(),
|
|
1110
|
+
accountId: import_zod4.z.string().uuid(),
|
|
1111
|
+
payerUserId: import_zod4.z.string().uuid().nullable(),
|
|
1112
|
+
merchantOwnerUserId: import_zod4.z.string().uuid(),
|
|
758
1113
|
amountKobo: MoneyKoboSchema,
|
|
759
|
-
currency:
|
|
760
|
-
status:
|
|
761
|
-
provider:
|
|
762
|
-
providerReference:
|
|
763
|
-
idempotencyKey:
|
|
764
|
-
ledgerRef:
|
|
765
|
-
failureCode:
|
|
766
|
-
failureMessage:
|
|
767
|
-
paidAtMs:
|
|
768
|
-
createdAtMs:
|
|
769
|
-
updatedAtMs:
|
|
1114
|
+
currency: import_zod4.z.string().length(3),
|
|
1115
|
+
status: import_zod4.z.enum(COLLECTION_PAYMENT_STATUSES),
|
|
1116
|
+
provider: import_zod4.z.string(),
|
|
1117
|
+
providerReference: import_zod4.z.string().nullable(),
|
|
1118
|
+
idempotencyKey: import_zod4.z.string().nullable(),
|
|
1119
|
+
ledgerRef: import_zod4.z.string(),
|
|
1120
|
+
failureCode: import_zod4.z.string().nullable(),
|
|
1121
|
+
failureMessage: import_zod4.z.string().nullable(),
|
|
1122
|
+
paidAtMs: import_zod4.z.number().int().nonnegative().nullable(),
|
|
1123
|
+
createdAtMs: import_zod4.z.number().int().nonnegative(),
|
|
1124
|
+
updatedAtMs: import_zod4.z.number().int().nonnegative()
|
|
770
1125
|
});
|
|
771
|
-
var CollectionPaymentResultSchema =
|
|
1126
|
+
var CollectionPaymentResultSchema = import_zod4.z.object({
|
|
772
1127
|
payment: CollectionPaymentSchema,
|
|
773
1128
|
intent: CollectionIntentSchema,
|
|
774
|
-
receipt:
|
|
775
|
-
replayed:
|
|
1129
|
+
receipt: import_zod4.z.unknown().optional(),
|
|
1130
|
+
replayed: import_zod4.z.boolean()
|
|
776
1131
|
});
|
|
777
|
-
var CollectionReportSummarySchema =
|
|
778
|
-
accountId:
|
|
779
|
-
fromMs:
|
|
780
|
-
toMs:
|
|
781
|
-
currency:
|
|
782
|
-
paidCount:
|
|
783
|
-
paidAmountKobo:
|
|
784
|
-
pendingCount:
|
|
785
|
-
failedCount:
|
|
786
|
-
reversedCount:
|
|
787
|
-
availableBalanceKobo:
|
|
1132
|
+
var CollectionReportSummarySchema = import_zod4.z.object({
|
|
1133
|
+
accountId: import_zod4.z.string().uuid(),
|
|
1134
|
+
fromMs: import_zod4.z.number().int().nonnegative(),
|
|
1135
|
+
toMs: import_zod4.z.number().int().nonnegative(),
|
|
1136
|
+
currency: import_zod4.z.string().length(3),
|
|
1137
|
+
paidCount: import_zod4.z.number().int().nonnegative(),
|
|
1138
|
+
paidAmountKobo: import_zod4.z.number().int().nonnegative(),
|
|
1139
|
+
pendingCount: import_zod4.z.number().int().nonnegative(),
|
|
1140
|
+
failedCount: import_zod4.z.number().int().nonnegative(),
|
|
1141
|
+
reversedCount: import_zod4.z.number().int().nonnegative(),
|
|
1142
|
+
availableBalanceKobo: import_zod4.z.number().int().nonnegative()
|
|
788
1143
|
});
|
|
789
|
-
var CollectionStatementSchema =
|
|
790
|
-
accountId:
|
|
791
|
-
year:
|
|
792
|
-
month:
|
|
793
|
-
currency:
|
|
794
|
-
totalPaidKobo:
|
|
795
|
-
items:
|
|
1144
|
+
var CollectionStatementSchema = import_zod4.z.object({
|
|
1145
|
+
accountId: import_zod4.z.string().uuid(),
|
|
1146
|
+
year: import_zod4.z.number().int(),
|
|
1147
|
+
month: import_zod4.z.number().int().min(1).max(12),
|
|
1148
|
+
currency: import_zod4.z.string().length(3),
|
|
1149
|
+
totalPaidKobo: import_zod4.z.number().int().nonnegative(),
|
|
1150
|
+
items: import_zod4.z.array(CollectionPaymentSchema)
|
|
796
1151
|
});
|
|
797
|
-
var CreatePayoutInputSchema =
|
|
1152
|
+
var CreatePayoutInputSchema = import_zod4.z.object({
|
|
798
1153
|
amountKobo: MoneyKoboSchema,
|
|
799
1154
|
currency: CurrencySchema2.optional(),
|
|
800
|
-
idempotencyKey:
|
|
1155
|
+
idempotencyKey: import_zod4.z.string().trim().min(8).max(160)
|
|
801
1156
|
});
|
|
802
|
-
var MerchantPayoutSchema =
|
|
803
|
-
payoutId:
|
|
804
|
-
accountId:
|
|
1157
|
+
var MerchantPayoutSchema = import_zod4.z.object({
|
|
1158
|
+
payoutId: import_zod4.z.string().uuid(),
|
|
1159
|
+
accountId: import_zod4.z.string().uuid(),
|
|
805
1160
|
amountKobo: MoneyKoboSchema,
|
|
806
|
-
currency:
|
|
807
|
-
status:
|
|
808
|
-
idempotencyKey:
|
|
809
|
-
ledgerRef:
|
|
810
|
-
providerReference:
|
|
811
|
-
requestedByUserId:
|
|
812
|
-
failureCode:
|
|
813
|
-
failureMessage:
|
|
814
|
-
createdAtMs:
|
|
815
|
-
updatedAtMs:
|
|
1161
|
+
currency: import_zod4.z.string().length(3),
|
|
1162
|
+
status: import_zod4.z.enum(MERCHANT_PAYOUT_STATUSES),
|
|
1163
|
+
idempotencyKey: import_zod4.z.string().nullable(),
|
|
1164
|
+
ledgerRef: import_zod4.z.string(),
|
|
1165
|
+
providerReference: import_zod4.z.string().nullable(),
|
|
1166
|
+
requestedByUserId: import_zod4.z.string().uuid().nullable(),
|
|
1167
|
+
failureCode: import_zod4.z.string().nullable(),
|
|
1168
|
+
failureMessage: import_zod4.z.string().nullable(),
|
|
1169
|
+
createdAtMs: import_zod4.z.number().int().nonnegative(),
|
|
1170
|
+
updatedAtMs: import_zod4.z.number().int().nonnegative()
|
|
816
1171
|
});
|
|
817
|
-
var ProviderEventInputSchema =
|
|
818
|
-
provider:
|
|
819
|
-
eventId:
|
|
820
|
-
eventType:
|
|
821
|
-
payload:
|
|
1172
|
+
var ProviderEventInputSchema = import_zod4.z.object({
|
|
1173
|
+
provider: import_zod4.z.string().trim().min(1).max(80),
|
|
1174
|
+
eventId: import_zod4.z.string().trim().min(1).max(160),
|
|
1175
|
+
eventType: import_zod4.z.string().trim().min(1).max(120),
|
|
1176
|
+
payload: import_zod4.z.record(import_zod4.z.unknown()).optional()
|
|
822
1177
|
});
|
|
823
|
-
var ProviderEventRecordSchema =
|
|
824
|
-
id:
|
|
825
|
-
provider:
|
|
826
|
-
eventId:
|
|
827
|
-
eventType:
|
|
828
|
-
signatureVerified:
|
|
829
|
-
receivedAtMs:
|
|
830
|
-
processedAtMs:
|
|
1178
|
+
var ProviderEventRecordSchema = import_zod4.z.object({
|
|
1179
|
+
id: import_zod4.z.string().uuid(),
|
|
1180
|
+
provider: import_zod4.z.string(),
|
|
1181
|
+
eventId: import_zod4.z.string(),
|
|
1182
|
+
eventType: import_zod4.z.string(),
|
|
1183
|
+
signatureVerified: import_zod4.z.boolean(),
|
|
1184
|
+
receivedAtMs: import_zod4.z.number().int().nonnegative(),
|
|
1185
|
+
processedAtMs: import_zod4.z.number().int().nonnegative().nullable()
|
|
831
1186
|
});
|
|
832
1187
|
function createCollectionsClient(opts) {
|
|
833
1188
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -1342,7 +1697,7 @@ var FlurClient = class {
|
|
|
1342
1697
|
try {
|
|
1343
1698
|
body = requestSchema ? JSON.stringify(requestSchema.parse(input)) : init2.body;
|
|
1344
1699
|
} catch (err) {
|
|
1345
|
-
if (err instanceof
|
|
1700
|
+
if (err instanceof import_zod5.z.ZodError) {
|
|
1346
1701
|
throw new FlurError("Invalid request payload", "INVALID_REQUEST", {
|
|
1347
1702
|
details: err.flatten()
|
|
1348
1703
|
});
|
|
@@ -1370,7 +1725,7 @@ var FlurClient = class {
|
|
|
1370
1725
|
try {
|
|
1371
1726
|
return responseSchema.parse(payload);
|
|
1372
1727
|
} catch (err) {
|
|
1373
|
-
if (err instanceof
|
|
1728
|
+
if (err instanceof import_zod5.z.ZodError) {
|
|
1374
1729
|
throw new FlurError(
|
|
1375
1730
|
"SDK contract validation failed",
|
|
1376
1731
|
"INVALID_REQUEST",
|
|
@@ -1800,64 +2155,113 @@ function constantTimeEqual(a, b) {
|
|
|
1800
2155
|
return diff === 0;
|
|
1801
2156
|
}
|
|
1802
2157
|
|
|
1803
|
-
// src/
|
|
1804
|
-
var
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
2158
|
+
// src/offline/oac.ts
|
|
2159
|
+
var import_zod6 = require("zod");
|
|
2160
|
+
|
|
2161
|
+
// src/crypto/p256-issuer.ts
|
|
2162
|
+
var import_nist = require("@noble/curves/nist");
|
|
2163
|
+
function bytesToBase64(bytes) {
|
|
2164
|
+
if (typeof Buffer !== "undefined") {
|
|
2165
|
+
return Buffer.from(bytes).toString("base64");
|
|
2166
|
+
}
|
|
2167
|
+
let bin = "";
|
|
2168
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
2169
|
+
return btoa(bin);
|
|
1809
2170
|
}
|
|
1810
|
-
function
|
|
1811
|
-
|
|
2171
|
+
function base64ToBytes(b64) {
|
|
2172
|
+
if (typeof Buffer !== "undefined") {
|
|
2173
|
+
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
2174
|
+
}
|
|
2175
|
+
const bin = atob(b64);
|
|
2176
|
+
const out = new Uint8Array(bin.length);
|
|
2177
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
2178
|
+
return out;
|
|
1812
2179
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
2180
|
+
var P256_SPKI_HEADER = new Uint8Array([
|
|
2181
|
+
48,
|
|
2182
|
+
89,
|
|
2183
|
+
48,
|
|
2184
|
+
19,
|
|
2185
|
+
6,
|
|
2186
|
+
7,
|
|
2187
|
+
42,
|
|
2188
|
+
134,
|
|
2189
|
+
72,
|
|
2190
|
+
206,
|
|
2191
|
+
61,
|
|
2192
|
+
2,
|
|
2193
|
+
1,
|
|
2194
|
+
6,
|
|
2195
|
+
8,
|
|
2196
|
+
42,
|
|
2197
|
+
134,
|
|
2198
|
+
72,
|
|
2199
|
+
206,
|
|
2200
|
+
61,
|
|
2201
|
+
3,
|
|
2202
|
+
1,
|
|
2203
|
+
7,
|
|
2204
|
+
3,
|
|
2205
|
+
66,
|
|
2206
|
+
0
|
|
2207
|
+
]);
|
|
2208
|
+
function p256SpkiB64ToRaw(spkiB64) {
|
|
2209
|
+
const spki = base64ToBytes(spkiB64);
|
|
2210
|
+
if (spki.length !== P256_SPKI_HEADER.length + 65) {
|
|
2211
|
+
throw new Error("p256: invalid SPKI length");
|
|
2212
|
+
}
|
|
2213
|
+
for (let i = 0; i < P256_SPKI_HEADER.length; i++) {
|
|
2214
|
+
if (spki[i] !== P256_SPKI_HEADER[i]) {
|
|
2215
|
+
throw new Error("p256: invalid SPKI header");
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
return spki.slice(P256_SPKI_HEADER.length);
|
|
2219
|
+
}
|
|
2220
|
+
function signIssuerP256(bytes, issuerPrivateKey) {
|
|
2221
|
+
const sig = import_nist.p256.sign(bytes, issuerPrivateKey, { prehash: true });
|
|
2222
|
+
return bytesToBase64(sig.toBytes("der"));
|
|
1815
2223
|
}
|
|
1816
|
-
function
|
|
2224
|
+
function verifyIssuerP256(bytes, signatureB64, issuerPublicKeySpkiB64) {
|
|
1817
2225
|
try {
|
|
1818
|
-
|
|
2226
|
+
const pubRaw = p256SpkiB64ToRaw(issuerPublicKeySpkiB64);
|
|
2227
|
+
const sigBytes = base64ToBytes(signatureB64);
|
|
2228
|
+
return import_nist.p256.verify(sigBytes, bytes, pubRaw, {
|
|
2229
|
+
prehash: true,
|
|
2230
|
+
format: "der"
|
|
2231
|
+
});
|
|
1819
2232
|
} catch {
|
|
1820
2233
|
return false;
|
|
1821
2234
|
}
|
|
1822
2235
|
}
|
|
1823
|
-
function signCanonical(value, privateKey) {
|
|
1824
|
-
return sign(canonicalJSONBytes(value), privateKey);
|
|
1825
|
-
}
|
|
1826
|
-
function verifyCanonical(value, signature, publicKey) {
|
|
1827
|
-
return verify(canonicalJSONBytes(value), signature, publicKey);
|
|
1828
|
-
}
|
|
1829
2236
|
|
|
1830
2237
|
// src/offline/oac.ts
|
|
1831
|
-
var import_zod5 = require("zod");
|
|
1832
2238
|
var OAC_DEFAULT_PER_TX_KOBO = 5e5;
|
|
1833
2239
|
var OAC_DEFAULT_CUMULATIVE_KOBO = 2e6;
|
|
1834
2240
|
var OAC_DEFAULT_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
1835
|
-
var
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
)
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
issuerSig: HexString(64)
|
|
2241
|
+
var Base64Std2 = import_zod6.z.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (standard) string");
|
|
2242
|
+
var OACSchema = import_zod6.z.object({
|
|
2243
|
+
userId: import_zod6.z.string().min(1),
|
|
2244
|
+
deviceId: import_zod6.z.string().min(1),
|
|
2245
|
+
/** SubjectPublicKeyInfo DER, base64 (P-256). */
|
|
2246
|
+
devicePublicKey: Base64Std2,
|
|
2247
|
+
perTxCapKobo: import_zod6.z.number().int().nonnegative(),
|
|
2248
|
+
cumulativeCapKobo: import_zod6.z.number().int().nonnegative(),
|
|
2249
|
+
validFromMs: import_zod6.z.number().int().nonnegative(),
|
|
2250
|
+
validUntilMs: import_zod6.z.number().int().positive(),
|
|
2251
|
+
counterSeed: import_zod6.z.number().int().nonnegative(),
|
|
2252
|
+
nonce: import_zod6.z.string().min(1),
|
|
2253
|
+
/** ASN.1 DER ECDSA(SHA-256) signature, base64. */
|
|
2254
|
+
issuerSig: Base64Std2
|
|
1850
2255
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
1851
2256
|
message: "validUntilMs must be greater than validFromMs"
|
|
1852
2257
|
}).refine((v) => v.perTxCapKobo <= v.cumulativeCapKobo, {
|
|
1853
2258
|
message: "perTxCapKobo must not exceed cumulativeCapKobo"
|
|
1854
2259
|
});
|
|
1855
2260
|
function buildOAC(input) {
|
|
1856
|
-
const devicePublicKey = typeof input.devicePublicKey === "string" ? input.devicePublicKey : bytesToHex(input.devicePublicKey);
|
|
1857
2261
|
return {
|
|
1858
2262
|
userId: input.userId,
|
|
1859
2263
|
deviceId: input.deviceId,
|
|
1860
|
-
devicePublicKey,
|
|
2264
|
+
devicePublicKey: input.devicePublicKey,
|
|
1861
2265
|
perTxCapKobo: input.perTxCapKobo ?? OAC_DEFAULT_PER_TX_KOBO,
|
|
1862
2266
|
cumulativeCapKobo: input.cumulativeCapKobo ?? OAC_DEFAULT_CUMULATIVE_KOBO,
|
|
1863
2267
|
validFromMs: input.validFromMs,
|
|
@@ -1867,36 +2271,25 @@ function buildOAC(input) {
|
|
|
1867
2271
|
};
|
|
1868
2272
|
}
|
|
1869
2273
|
function signOAC(unsigned, issuerPrivateKey) {
|
|
1870
|
-
const issuerSig =
|
|
1871
|
-
|
|
2274
|
+
const issuerSig = signIssuerP256(
|
|
2275
|
+
canonicalJSONBytes(unsigned),
|
|
2276
|
+
issuerPrivateKey
|
|
1872
2277
|
);
|
|
1873
2278
|
return { ...unsigned, issuerSig };
|
|
1874
2279
|
}
|
|
1875
|
-
function verifyOAC(oac,
|
|
2280
|
+
function verifyOAC(oac, issuerPublicKeySpkiB64) {
|
|
1876
2281
|
try {
|
|
1877
2282
|
const parsed = OACSchema.parse(oac);
|
|
1878
2283
|
const { issuerSig, ...unsigned } = parsed;
|
|
1879
|
-
return
|
|
2284
|
+
return verifyIssuerP256(
|
|
1880
2285
|
canonicalJSONBytes(unsigned),
|
|
1881
|
-
|
|
1882
|
-
|
|
2286
|
+
issuerSig,
|
|
2287
|
+
issuerPublicKeySpkiB64
|
|
1883
2288
|
);
|
|
1884
2289
|
} catch {
|
|
1885
2290
|
return false;
|
|
1886
2291
|
}
|
|
1887
2292
|
}
|
|
1888
|
-
function bytesToHex(b) {
|
|
1889
|
-
let s = "";
|
|
1890
|
-
for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
|
|
1891
|
-
return s;
|
|
1892
|
-
}
|
|
1893
|
-
function hexToBytes(s) {
|
|
1894
|
-
if (s.length % 2 !== 0) throw new Error("hex: odd length");
|
|
1895
|
-
const out = new Uint8Array(s.length / 2);
|
|
1896
|
-
for (let i = 0; i < out.length; i++)
|
|
1897
|
-
out[i] = parseInt(s.slice(i * 2, i * 2 + 2), 16);
|
|
1898
|
-
return out;
|
|
1899
|
-
}
|
|
1900
2293
|
|
|
1901
2294
|
// src/offline/codec.ts
|
|
1902
2295
|
var ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
|
|
@@ -1952,20 +2345,20 @@ function decodeBase45(s) {
|
|
|
1952
2345
|
}
|
|
1953
2346
|
|
|
1954
2347
|
// src/offline/messages.ts
|
|
1955
|
-
var
|
|
1956
|
-
var
|
|
1957
|
-
var OfflinePaymentRequestSchema =
|
|
1958
|
-
reference:
|
|
1959
|
-
amountKobo:
|
|
2348
|
+
var import_zod7 = require("zod");
|
|
2349
|
+
var Base64Sig = import_zod7.z.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (standard) signature");
|
|
2350
|
+
var OfflinePaymentRequestSchema = import_zod7.z.object({
|
|
2351
|
+
reference: import_zod7.z.string().min(1),
|
|
2352
|
+
amountKobo: import_zod7.z.number().int().positive(),
|
|
1960
2353
|
merchantOAC: OACSchema,
|
|
1961
|
-
expiresAtMs:
|
|
1962
|
-
merchantSig:
|
|
2354
|
+
expiresAtMs: import_zod7.z.number().int().positive(),
|
|
2355
|
+
merchantSig: Base64Sig
|
|
1963
2356
|
});
|
|
1964
|
-
var OfflinePaymentAuthorizationSchema =
|
|
2357
|
+
var OfflinePaymentAuthorizationSchema = import_zod7.z.object({
|
|
1965
2358
|
request: OfflinePaymentRequestSchema,
|
|
1966
2359
|
payerOAC: OACSchema,
|
|
1967
|
-
payerCounter:
|
|
1968
|
-
payerSig:
|
|
2360
|
+
payerCounter: import_zod7.z.number().int().positive(),
|
|
2361
|
+
payerSig: Base64Sig
|
|
1969
2362
|
});
|
|
1970
2363
|
function buildPaymentRequest(input) {
|
|
1971
2364
|
if (!Number.isInteger(input.amountKobo) || input.amountKobo <= 0) {
|
|
@@ -1982,27 +2375,28 @@ function buildPaymentRequest(input) {
|
|
|
1982
2375
|
};
|
|
1983
2376
|
}
|
|
1984
2377
|
function signPaymentRequest(unsigned, merchantDevicePrivateKey) {
|
|
1985
|
-
const merchantSig =
|
|
1986
|
-
|
|
2378
|
+
const merchantSig = signIssuerP256(
|
|
2379
|
+
canonicalJSONBytes(unsigned),
|
|
2380
|
+
merchantDevicePrivateKey
|
|
1987
2381
|
);
|
|
1988
2382
|
return { ...unsigned, merchantSig };
|
|
1989
2383
|
}
|
|
1990
|
-
function verifyPaymentRequest(req,
|
|
2384
|
+
function verifyPaymentRequest(req, issuerPublicKeySpkiB64) {
|
|
1991
2385
|
try {
|
|
1992
2386
|
const parsed = OfflinePaymentRequestSchema.parse(req);
|
|
1993
2387
|
const { issuerSig: merchantOacSig, ...merchantOacUnsigned } = parsed.merchantOAC;
|
|
1994
|
-
if (!
|
|
2388
|
+
if (!verifyIssuerP256(
|
|
1995
2389
|
canonicalJSONBytes(merchantOacUnsigned),
|
|
1996
|
-
|
|
1997
|
-
|
|
2390
|
+
merchantOacSig,
|
|
2391
|
+
issuerPublicKeySpkiB64
|
|
1998
2392
|
)) {
|
|
1999
2393
|
return false;
|
|
2000
2394
|
}
|
|
2001
2395
|
const { merchantSig, ...unsigned } = parsed;
|
|
2002
|
-
return
|
|
2396
|
+
return verifyIssuerP256(
|
|
2003
2397
|
canonicalJSONBytes(unsigned),
|
|
2004
|
-
|
|
2005
|
-
|
|
2398
|
+
merchantSig,
|
|
2399
|
+
parsed.merchantOAC.devicePublicKey
|
|
2006
2400
|
);
|
|
2007
2401
|
} catch {
|
|
2008
2402
|
return false;
|
|
@@ -2022,28 +2416,30 @@ function buildAuthorization(input) {
|
|
|
2022
2416
|
};
|
|
2023
2417
|
}
|
|
2024
2418
|
function signAuthorization(unsigned, payerDevicePrivateKey) {
|
|
2025
|
-
const payerSig =
|
|
2026
|
-
|
|
2419
|
+
const payerSig = signIssuerP256(
|
|
2420
|
+
canonicalJSONBytes(unsigned),
|
|
2421
|
+
payerDevicePrivateKey
|
|
2027
2422
|
);
|
|
2028
2423
|
return { ...unsigned, payerSig };
|
|
2029
2424
|
}
|
|
2030
|
-
function verifyAuthorization(auth,
|
|
2425
|
+
function verifyAuthorization(auth, issuerPublicKeySpkiB64) {
|
|
2031
2426
|
try {
|
|
2032
2427
|
const parsed = OfflinePaymentAuthorizationSchema.parse(auth);
|
|
2033
|
-
if (!verifyPaymentRequest(parsed.request,
|
|
2428
|
+
if (!verifyPaymentRequest(parsed.request, issuerPublicKeySpkiB64))
|
|
2429
|
+
return false;
|
|
2034
2430
|
const { issuerSig: payerOacSig, ...payerOacUnsigned } = parsed.payerOAC;
|
|
2035
|
-
if (!
|
|
2431
|
+
if (!verifyIssuerP256(
|
|
2036
2432
|
canonicalJSONBytes(payerOacUnsigned),
|
|
2037
|
-
|
|
2038
|
-
|
|
2433
|
+
payerOacSig,
|
|
2434
|
+
issuerPublicKeySpkiB64
|
|
2039
2435
|
)) {
|
|
2040
2436
|
return false;
|
|
2041
2437
|
}
|
|
2042
2438
|
const { payerSig, ...unsigned } = parsed;
|
|
2043
|
-
return
|
|
2439
|
+
return verifyIssuerP256(
|
|
2044
2440
|
canonicalJSONBytes(unsigned),
|
|
2045
|
-
|
|
2046
|
-
|
|
2441
|
+
payerSig,
|
|
2442
|
+
parsed.payerOAC.devicePublicKey
|
|
2047
2443
|
);
|
|
2048
2444
|
} catch {
|
|
2049
2445
|
return false;
|
|
@@ -2073,56 +2469,60 @@ function decodeAuthorizationQR(s) {
|
|
|
2073
2469
|
}
|
|
2074
2470
|
|
|
2075
2471
|
// src/offline/settlements.ts
|
|
2076
|
-
var
|
|
2472
|
+
var import_zod8 = require("zod");
|
|
2077
2473
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
2078
2474
|
var import_utils = require("@noble/hashes/utils");
|
|
2079
|
-
var OfflineTokenSchema =
|
|
2080
|
-
tokenId:
|
|
2081
|
-
tokenSerial:
|
|
2082
|
-
issuerAccountId:
|
|
2083
|
-
payerUserId:
|
|
2084
|
-
maxAmountKobo:
|
|
2085
|
-
currency:
|
|
2086
|
-
issuedAtMs:
|
|
2087
|
-
expiresAtMs:
|
|
2088
|
-
issuerSig:
|
|
2475
|
+
var OfflineTokenSchema = import_zod8.z.object({
|
|
2476
|
+
tokenId: import_zod8.z.string().uuid(),
|
|
2477
|
+
tokenSerial: import_zod8.z.string(),
|
|
2478
|
+
issuerAccountId: import_zod8.z.string().uuid(),
|
|
2479
|
+
payerUserId: import_zod8.z.string().uuid(),
|
|
2480
|
+
maxAmountKobo: import_zod8.z.number().int().positive(),
|
|
2481
|
+
currency: import_zod8.z.string().length(3),
|
|
2482
|
+
issuedAtMs: import_zod8.z.number().int().nonnegative(),
|
|
2483
|
+
expiresAtMs: import_zod8.z.number().int().nonnegative(),
|
|
2484
|
+
issuerSig: import_zod8.z.string()
|
|
2089
2485
|
});
|
|
2090
|
-
var PaymentClaimSchema =
|
|
2091
|
-
encounterId:
|
|
2092
|
-
tokenSerial:
|
|
2093
|
-
payerUserId:
|
|
2094
|
-
payeeUserId:
|
|
2095
|
-
payerNonce:
|
|
2096
|
-
payeeNonce:
|
|
2097
|
-
amountKobo:
|
|
2098
|
-
currency:
|
|
2099
|
-
occurredAtMs:
|
|
2100
|
-
completedAtMs:
|
|
2101
|
-
contextId:
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2486
|
+
var PaymentClaimSchema = import_zod8.z.object({
|
|
2487
|
+
encounterId: import_zod8.z.string().regex(/^[0-9a-f]{64}$/i).optional(),
|
|
2488
|
+
tokenSerial: import_zod8.z.string(),
|
|
2489
|
+
payerUserId: import_zod8.z.string().uuid(),
|
|
2490
|
+
payeeUserId: import_zod8.z.string().uuid(),
|
|
2491
|
+
payerNonce: import_zod8.z.string(),
|
|
2492
|
+
payeeNonce: import_zod8.z.string(),
|
|
2493
|
+
amountKobo: import_zod8.z.number().int().positive(),
|
|
2494
|
+
currency: import_zod8.z.string().length(3).default("NGN"),
|
|
2495
|
+
occurredAtMs: import_zod8.z.number().int().nonnegative(),
|
|
2496
|
+
completedAtMs: import_zod8.z.number().int().nonnegative().optional(),
|
|
2497
|
+
contextId: import_zod8.z.string().optional(),
|
|
2498
|
+
// Stage 2c: P-256 device keys are now SubjectPublicKeyInfo DER, base64.
|
|
2499
|
+
// Signatures are ASN.1 DER ECDSA(SHA-256), base64. Backwards-incompatible
|
|
2500
|
+
// wire change; the backend has the matching widening in offline-settlements
|
|
2501
|
+
// service + zod schema.
|
|
2502
|
+
payerPubkey: import_zod8.z.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2503
|
+
payerSignature: import_zod8.z.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2504
|
+
payeePubkey: import_zod8.z.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional(),
|
|
2505
|
+
payeeSignature: import_zod8.z.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional()
|
|
2106
2506
|
});
|
|
2107
|
-
var SettlementSchema =
|
|
2108
|
-
settlementId:
|
|
2109
|
-
settlementKey:
|
|
2110
|
-
encounterId:
|
|
2111
|
-
issuerAccountId:
|
|
2112
|
-
tokenSerial:
|
|
2113
|
-
payerUserId:
|
|
2114
|
-
payeeUserId:
|
|
2115
|
-
amountKobo:
|
|
2116
|
-
currency:
|
|
2117
|
-
receiptId:
|
|
2118
|
-
status:
|
|
2119
|
-
issuerSig:
|
|
2120
|
-
createdAtMs:
|
|
2507
|
+
var SettlementSchema = import_zod8.z.object({
|
|
2508
|
+
settlementId: import_zod8.z.string().uuid(),
|
|
2509
|
+
settlementKey: import_zod8.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
2510
|
+
encounterId: import_zod8.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
2511
|
+
issuerAccountId: import_zod8.z.string().uuid(),
|
|
2512
|
+
tokenSerial: import_zod8.z.string(),
|
|
2513
|
+
payerUserId: import_zod8.z.string().uuid(),
|
|
2514
|
+
payeeUserId: import_zod8.z.string().uuid(),
|
|
2515
|
+
amountKobo: import_zod8.z.number().int().nonnegative(),
|
|
2516
|
+
currency: import_zod8.z.string().length(3),
|
|
2517
|
+
receiptId: import_zod8.z.string().nullable(),
|
|
2518
|
+
status: import_zod8.z.enum(["SETTLED", "REVIEW", "REJECTED"]),
|
|
2519
|
+
issuerSig: import_zod8.z.string(),
|
|
2520
|
+
createdAtMs: import_zod8.z.number().int().nonnegative()
|
|
2121
2521
|
});
|
|
2122
|
-
var SettleResponseSchema =
|
|
2522
|
+
var SettleResponseSchema = import_zod8.z.object({
|
|
2123
2523
|
settlement: SettlementSchema,
|
|
2124
|
-
encounterId:
|
|
2125
|
-
replayed:
|
|
2524
|
+
encounterId: import_zod8.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
2525
|
+
replayed: import_zod8.z.boolean()
|
|
2126
2526
|
});
|
|
2127
2527
|
var ENCOUNTER_DOMAIN = "offline:v1:encounter";
|
|
2128
2528
|
async function sha256Hex(input) {
|
|
@@ -2296,7 +2696,7 @@ function createHmacFetch(opts) {
|
|
|
2296
2696
|
}
|
|
2297
2697
|
|
|
2298
2698
|
// src/partner/client.ts
|
|
2299
|
-
var
|
|
2699
|
+
var import_zod9 = require("zod");
|
|
2300
2700
|
var import_sha2563 = require("@noble/hashes/sha256");
|
|
2301
2701
|
var import_hmac4 = require("@noble/hashes/hmac");
|
|
2302
2702
|
var import_utils2 = require("@noble/hashes/utils");
|
|
@@ -2320,18 +2720,18 @@ var PARTNER_SCOPES = [
|
|
|
2320
2720
|
"partner:payout:write",
|
|
2321
2721
|
"partner:reconciliation:read"
|
|
2322
2722
|
];
|
|
2323
|
-
var ApiCredentialPublicSchema =
|
|
2324
|
-
id:
|
|
2325
|
-
accountId:
|
|
2326
|
-
keyId:
|
|
2327
|
-
scopes:
|
|
2328
|
-
label:
|
|
2329
|
-
createdAtMs:
|
|
2330
|
-
lastUsedAtMs:
|
|
2331
|
-
revokedAtMs:
|
|
2723
|
+
var ApiCredentialPublicSchema = import_zod9.z.object({
|
|
2724
|
+
id: import_zod9.z.string().uuid(),
|
|
2725
|
+
accountId: import_zod9.z.string().uuid(),
|
|
2726
|
+
keyId: import_zod9.z.string(),
|
|
2727
|
+
scopes: import_zod9.z.array(import_zod9.z.enum(PARTNER_SCOPES)),
|
|
2728
|
+
label: import_zod9.z.string().nullable(),
|
|
2729
|
+
createdAtMs: import_zod9.z.number().int().nonnegative(),
|
|
2730
|
+
lastUsedAtMs: import_zod9.z.number().int().nonnegative().nullable(),
|
|
2731
|
+
revokedAtMs: import_zod9.z.number().int().nonnegative().nullable()
|
|
2332
2732
|
});
|
|
2333
2733
|
var MintedApiCredentialSchema = ApiCredentialPublicSchema.extend({
|
|
2334
|
-
secret:
|
|
2734
|
+
secret: import_zod9.z.string().min(1)
|
|
2335
2735
|
});
|
|
2336
2736
|
var enc = new TextEncoder();
|
|
2337
2737
|
async function sha256Hex2(input) {
|
|
@@ -2488,8 +2888,8 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2488
2888
|
}
|
|
2489
2889
|
return parser(raw);
|
|
2490
2890
|
}
|
|
2491
|
-
const listSchema =
|
|
2492
|
-
items:
|
|
2891
|
+
const listSchema = import_zod9.z.object({
|
|
2892
|
+
items: import_zod9.z.array(ApiCredentialPublicSchema)
|
|
2493
2893
|
});
|
|
2494
2894
|
return {
|
|
2495
2895
|
list: (accountId) => call(
|
|
@@ -2514,7 +2914,7 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2514
2914
|
}
|
|
2515
2915
|
|
|
2516
2916
|
// src/passes/pass.ts
|
|
2517
|
-
var
|
|
2917
|
+
var import_zod10 = require("zod");
|
|
2518
2918
|
var PASS_KINDS = [
|
|
2519
2919
|
"ride-ticket",
|
|
2520
2920
|
"transit-pass",
|
|
@@ -2530,42 +2930,39 @@ var PASS_STATES = [
|
|
|
2530
2930
|
"expired",
|
|
2531
2931
|
"revoked"
|
|
2532
2932
|
];
|
|
2533
|
-
var
|
|
2534
|
-
|
|
2535
|
-
`expected ${length}-byte hex string`
|
|
2536
|
-
);
|
|
2537
|
-
var PassMetadataSchema = import_zod9.z.record(
|
|
2538
|
-
import_zod9.z.union([import_zod9.z.string(), import_zod9.z.number(), import_zod9.z.boolean(), import_zod9.z.null()])
|
|
2933
|
+
var PassMetadataSchema = import_zod10.z.record(
|
|
2934
|
+
import_zod10.z.union([import_zod10.z.string(), import_zod10.z.number(), import_zod10.z.boolean(), import_zod10.z.null()])
|
|
2539
2935
|
);
|
|
2540
|
-
var PassSchema =
|
|
2541
|
-
passId:
|
|
2936
|
+
var PassSchema = import_zod10.z.object({
|
|
2937
|
+
passId: import_zod10.z.string().min(1),
|
|
2542
2938
|
/** Optional client/template grouping id (server may omit). */
|
|
2543
|
-
templateId:
|
|
2939
|
+
templateId: import_zod10.z.string().min(1).optional(),
|
|
2544
2940
|
/** Optional human-facing holder identity (server may omit). The cryptographic binding
|
|
2545
2941
|
* is `holderDevicePubkey` below. */
|
|
2546
|
-
holderUserId:
|
|
2547
|
-
kind:
|
|
2548
|
-
issuerId:
|
|
2549
|
-
issuedAtMs:
|
|
2550
|
-
validFromMs:
|
|
2551
|
-
validUntilMs:
|
|
2552
|
-
state:
|
|
2942
|
+
holderUserId: import_zod10.z.string().min(1).optional(),
|
|
2943
|
+
kind: import_zod10.z.enum(PASS_KINDS),
|
|
2944
|
+
issuerId: import_zod10.z.string().min(1),
|
|
2945
|
+
issuedAtMs: import_zod10.z.number().int().nonnegative(),
|
|
2946
|
+
validFromMs: import_zod10.z.number().int().nonnegative(),
|
|
2947
|
+
validUntilMs: import_zod10.z.number().int().positive(),
|
|
2948
|
+
state: import_zod10.z.enum(PASS_STATES),
|
|
2553
2949
|
metadata: PassMetadataSchema,
|
|
2554
|
-
nonce:
|
|
2950
|
+
nonce: import_zod10.z.string().min(1),
|
|
2555
2951
|
/** Device id this pass is bound to (FK to backend `device_keys`). */
|
|
2556
|
-
holderDeviceId:
|
|
2557
|
-
/**
|
|
2558
|
-
* is verified against this key — it is the security-critical binding. */
|
|
2559
|
-
holderDevicePubkey:
|
|
2952
|
+
holderDeviceId: import_zod10.z.string().min(1),
|
|
2953
|
+
/** SubjectPublicKeyInfo DER (P-256) of the bound device, base64. The redemption
|
|
2954
|
+
* signature is verified against this key — it is the security-critical binding. */
|
|
2955
|
+
holderDevicePubkey: import_zod10.z.string().min(64).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2560
2956
|
/** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
|
|
2561
|
-
amountKobo:
|
|
2957
|
+
amountKobo: import_zod10.z.number().int().nonnegative().optional(),
|
|
2562
2958
|
/** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
|
|
2563
|
-
currency:
|
|
2959
|
+
currency: import_zod10.z.string().min(3).max(8),
|
|
2564
2960
|
/** Monotonic redemption counter floor. Redemption.counter MUST be > counterSeed. */
|
|
2565
|
-
counterSeed:
|
|
2961
|
+
counterSeed: import_zod10.z.number().int().nonnegative(),
|
|
2566
2962
|
/** Optional cumulative spend cap in kobo across all redemptions of this pass. */
|
|
2567
|
-
cumulativeCapKobo:
|
|
2568
|
-
|
|
2963
|
+
cumulativeCapKobo: import_zod10.z.number().int().nonnegative().optional(),
|
|
2964
|
+
/** ASN.1 DER ECDSA P-256 signature, base64. */
|
|
2965
|
+
issuerSig: import_zod10.z.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
|
|
2569
2966
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
2570
2967
|
message: "validUntilMs must be greater than validFromMs"
|
|
2571
2968
|
});
|
|
@@ -2598,19 +2995,20 @@ function buildPass(input) {
|
|
|
2598
2995
|
return out;
|
|
2599
2996
|
}
|
|
2600
2997
|
function signPass(unsigned, issuerPrivateKey) {
|
|
2601
|
-
const issuerSig =
|
|
2602
|
-
|
|
2998
|
+
const issuerSig = signIssuerP256(
|
|
2999
|
+
canonicalJSONBytes(unsigned),
|
|
3000
|
+
issuerPrivateKey
|
|
2603
3001
|
);
|
|
2604
3002
|
return { ...unsigned, issuerSig };
|
|
2605
3003
|
}
|
|
2606
|
-
function verifyPass(pass,
|
|
3004
|
+
function verifyPass(pass, issuerPublicKeySpkiB64) {
|
|
2607
3005
|
try {
|
|
2608
3006
|
const parsed = PassSchema.parse(pass);
|
|
2609
3007
|
const { issuerSig, ...unsigned } = parsed;
|
|
2610
|
-
return
|
|
3008
|
+
return verifyIssuerP256(
|
|
2611
3009
|
canonicalJSONBytes(unsigned),
|
|
2612
|
-
|
|
2613
|
-
|
|
3010
|
+
issuerSig,
|
|
3011
|
+
issuerPublicKeySpkiB64
|
|
2614
3012
|
);
|
|
2615
3013
|
} catch {
|
|
2616
3014
|
return false;
|
|
@@ -2621,19 +3019,20 @@ function isPassWithinValidity(pass, nowMs) {
|
|
|
2621
3019
|
}
|
|
2622
3020
|
|
|
2623
3021
|
// src/passes/redemption.ts
|
|
2624
|
-
var
|
|
2625
|
-
var
|
|
2626
|
-
var RedemptionSchema =
|
|
3022
|
+
var import_zod11 = require("zod");
|
|
3023
|
+
var Base64Std3 = import_zod11.z.string().min(16).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (std)");
|
|
3024
|
+
var RedemptionSchema = import_zod11.z.object({
|
|
2627
3025
|
pass: PassSchema,
|
|
2628
|
-
redeemerId:
|
|
2629
|
-
redeemedAtMs:
|
|
3026
|
+
redeemerId: import_zod11.z.string().min(1),
|
|
3027
|
+
redeemedAtMs: import_zod11.z.number().int().nonnegative(),
|
|
2630
3028
|
/** Strictly monotonic counter scoped to a single pass. Must be > pass.counterSeed
|
|
2631
3029
|
* and > the redeemer's lastSeenCounter for this pass. */
|
|
2632
|
-
counter:
|
|
3030
|
+
counter: import_zod11.z.number().int().positive(),
|
|
2633
3031
|
/** Amount being redeemed in kobo (0 for non-monetary passes like ride tickets). */
|
|
2634
|
-
amountKobo:
|
|
2635
|
-
nonce:
|
|
2636
|
-
|
|
3032
|
+
amountKobo: import_zod11.z.number().int().nonnegative(),
|
|
3033
|
+
nonce: import_zod11.z.string().min(1),
|
|
3034
|
+
/** ASN.1 DER ECDSA P-256 signature over canonicalJSONBytes(unsigned), base64. */
|
|
3035
|
+
holderSig: Base64Std3
|
|
2637
3036
|
});
|
|
2638
3037
|
var REDEEMABLE_STATES = /* @__PURE__ */ new Set(["issued", "active"]);
|
|
2639
3038
|
function buildRedemption(input) {
|
|
@@ -2687,74 +3086,68 @@ function buildRedemption(input) {
|
|
|
2687
3086
|
};
|
|
2688
3087
|
}
|
|
2689
3088
|
function signRedemption(unsigned, holderDevicePrivateKey) {
|
|
2690
|
-
const holderSig =
|
|
2691
|
-
|
|
3089
|
+
const holderSig = signIssuerP256(
|
|
3090
|
+
canonicalJSONBytes(unsigned),
|
|
3091
|
+
holderDevicePrivateKey
|
|
2692
3092
|
);
|
|
2693
3093
|
return { ...unsigned, holderSig };
|
|
2694
3094
|
}
|
|
2695
|
-
function verifyRedemption(r,
|
|
3095
|
+
function verifyRedemption(r, issuerPublicKeySpkiB64) {
|
|
2696
3096
|
try {
|
|
2697
3097
|
const parsed = RedemptionSchema.parse(r);
|
|
2698
3098
|
if (parsed.counter <= parsed.pass.counterSeed) return false;
|
|
2699
3099
|
const { issuerSig, ...passUnsigned } = parsed.pass;
|
|
2700
|
-
if (!
|
|
3100
|
+
if (!verifyIssuerP256(
|
|
2701
3101
|
canonicalJSONBytes(passUnsigned),
|
|
2702
|
-
|
|
2703
|
-
|
|
3102
|
+
issuerSig,
|
|
3103
|
+
issuerPublicKeySpkiB64
|
|
2704
3104
|
)) {
|
|
2705
3105
|
return false;
|
|
2706
3106
|
}
|
|
2707
|
-
const
|
|
2708
|
-
if (typeof
|
|
3107
|
+
const holderPub = parsed.pass.holderDevicePubkey;
|
|
3108
|
+
if (typeof holderPub !== "string") return false;
|
|
2709
3109
|
const { holderSig, ...unsigned } = parsed;
|
|
2710
|
-
return
|
|
2711
|
-
canonicalJSONBytes(unsigned),
|
|
2712
|
-
hexToBytes(holderSig),
|
|
2713
|
-
hexToBytes(holderHex)
|
|
2714
|
-
);
|
|
3110
|
+
return verifyIssuerP256(canonicalJSONBytes(unsigned), holderSig, holderPub);
|
|
2715
3111
|
} catch {
|
|
2716
3112
|
return false;
|
|
2717
3113
|
}
|
|
2718
3114
|
}
|
|
2719
3115
|
|
|
2720
3116
|
// src/receipts/receipt.ts
|
|
2721
|
-
var
|
|
3117
|
+
var import_zod12 = require("zod");
|
|
2722
3118
|
var RECEIPT_CHANNELS = ["cash", "pass"];
|
|
2723
3119
|
var RECEIPT_KINDS = RECEIPT_CHANNELS;
|
|
2724
|
-
var
|
|
2725
|
-
|
|
2726
|
-
`expected ${length}-byte hex string`
|
|
2727
|
-
);
|
|
2728
|
-
var ReceiptPayloadSchema = import_zod11.z.record(
|
|
2729
|
-
import_zod11.z.union([import_zod11.z.string(), import_zod11.z.number(), import_zod11.z.boolean(), import_zod11.z.null()])
|
|
3120
|
+
var ReceiptPayloadSchema = import_zod12.z.record(
|
|
3121
|
+
import_zod12.z.union([import_zod12.z.string(), import_zod12.z.number(), import_zod12.z.boolean(), import_zod12.z.null()])
|
|
2730
3122
|
);
|
|
2731
|
-
var ReceiptSchema =
|
|
2732
|
-
receiptId:
|
|
2733
|
-
channel:
|
|
3123
|
+
var ReceiptSchema = import_zod12.z.object({
|
|
3124
|
+
receiptId: import_zod12.z.string().min(1),
|
|
3125
|
+
channel: import_zod12.z.enum(RECEIPT_CHANNELS),
|
|
2734
3126
|
/** Cash-channel: send_intents.id. Required when channel === 'cash'. */
|
|
2735
|
-
intentId:
|
|
3127
|
+
intentId: import_zod12.z.string().min(1).optional(),
|
|
2736
3128
|
/** Pass-channel: pass_redemptions.id. Required when channel === 'pass'. */
|
|
2737
|
-
passRedemptionId:
|
|
2738
|
-
payerUserId:
|
|
2739
|
-
payeeUserId:
|
|
2740
|
-
amountKobo:
|
|
2741
|
-
currency:
|
|
2742
|
-
issuedAtMs:
|
|
2743
|
-
issuerId:
|
|
3129
|
+
passRedemptionId: import_zod12.z.string().min(1).optional(),
|
|
3130
|
+
payerUserId: import_zod12.z.string().min(1),
|
|
3131
|
+
payeeUserId: import_zod12.z.string().min(1),
|
|
3132
|
+
amountKobo: import_zod12.z.number().int().nonnegative(),
|
|
3133
|
+
currency: import_zod12.z.string().min(3).max(8),
|
|
3134
|
+
issuedAtMs: import_zod12.z.number().int().nonnegative(),
|
|
3135
|
+
issuerId: import_zod12.z.string().min(1),
|
|
2744
3136
|
payload: ReceiptPayloadSchema,
|
|
2745
|
-
|
|
3137
|
+
/** ASN.1 DER ECDSA P-256 signature, base64. */
|
|
3138
|
+
issuerSig: import_zod12.z.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
|
|
2746
3139
|
}).superRefine((v, ctx) => {
|
|
2747
3140
|
if (v.channel === "cash") {
|
|
2748
3141
|
if (!v.intentId) {
|
|
2749
3142
|
ctx.addIssue({
|
|
2750
|
-
code:
|
|
3143
|
+
code: import_zod12.z.ZodIssueCode.custom,
|
|
2751
3144
|
message: "cash receipts require intentId",
|
|
2752
3145
|
path: ["intentId"]
|
|
2753
3146
|
});
|
|
2754
3147
|
}
|
|
2755
3148
|
if (v.passRedemptionId) {
|
|
2756
3149
|
ctx.addIssue({
|
|
2757
|
-
code:
|
|
3150
|
+
code: import_zod12.z.ZodIssueCode.custom,
|
|
2758
3151
|
message: "cash receipts must not carry passRedemptionId",
|
|
2759
3152
|
path: ["passRedemptionId"]
|
|
2760
3153
|
});
|
|
@@ -2762,14 +3155,14 @@ var ReceiptSchema = import_zod11.z.object({
|
|
|
2762
3155
|
} else if (v.channel === "pass") {
|
|
2763
3156
|
if (!v.passRedemptionId) {
|
|
2764
3157
|
ctx.addIssue({
|
|
2765
|
-
code:
|
|
3158
|
+
code: import_zod12.z.ZodIssueCode.custom,
|
|
2766
3159
|
message: "pass receipts require passRedemptionId",
|
|
2767
3160
|
path: ["passRedemptionId"]
|
|
2768
3161
|
});
|
|
2769
3162
|
}
|
|
2770
3163
|
if (v.intentId) {
|
|
2771
3164
|
ctx.addIssue({
|
|
2772
|
-
code:
|
|
3165
|
+
code: import_zod12.z.ZodIssueCode.custom,
|
|
2773
3166
|
message: "pass receipts must not carry intentId",
|
|
2774
3167
|
path: ["intentId"]
|
|
2775
3168
|
});
|
|
@@ -2808,19 +3201,20 @@ function buildReceipt(input) {
|
|
|
2808
3201
|
return out;
|
|
2809
3202
|
}
|
|
2810
3203
|
function signReceipt(unsigned, issuerPrivateKey) {
|
|
2811
|
-
const issuerSig =
|
|
2812
|
-
|
|
3204
|
+
const issuerSig = signIssuerP256(
|
|
3205
|
+
canonicalJSONBytes(unsigned),
|
|
3206
|
+
issuerPrivateKey
|
|
2813
3207
|
);
|
|
2814
3208
|
return { ...unsigned, issuerSig };
|
|
2815
3209
|
}
|
|
2816
|
-
function verifyReceipt(r,
|
|
3210
|
+
function verifyReceipt(r, issuerPublicKeySpkiB64) {
|
|
2817
3211
|
try {
|
|
2818
3212
|
const parsed = ReceiptSchema.parse(r);
|
|
2819
3213
|
const { issuerSig, ...unsigned } = parsed;
|
|
2820
|
-
return
|
|
3214
|
+
return verifyIssuerP256(
|
|
2821
3215
|
canonicalJSONBytes(unsigned),
|
|
2822
|
-
|
|
2823
|
-
|
|
3216
|
+
issuerSig,
|
|
3217
|
+
issuerPublicKeySpkiB64
|
|
2824
3218
|
);
|
|
2825
3219
|
} catch {
|
|
2826
3220
|
return false;
|
|
@@ -3070,23 +3464,23 @@ function init(opts) {
|
|
|
3070
3464
|
}
|
|
3071
3465
|
|
|
3072
3466
|
// src/accounts/client.ts
|
|
3073
|
-
var
|
|
3467
|
+
var import_zod13 = require("zod");
|
|
3074
3468
|
var ACCOUNT_TYPES = ["personal", "business", "partner"];
|
|
3075
3469
|
var ACCOUNT_STATUSES = ["active", "suspended", "closed"];
|
|
3076
3470
|
var MEMBERSHIP_ROLES = ["owner", "admin", "driver", "staff"];
|
|
3077
|
-
var AccountSchema =
|
|
3078
|
-
accountId:
|
|
3079
|
-
type:
|
|
3080
|
-
displayName:
|
|
3081
|
-
status:
|
|
3082
|
-
ownerUserId:
|
|
3083
|
-
createdAtMs:
|
|
3471
|
+
var AccountSchema = import_zod13.z.object({
|
|
3472
|
+
accountId: import_zod13.z.string().uuid(),
|
|
3473
|
+
type: import_zod13.z.enum(ACCOUNT_TYPES),
|
|
3474
|
+
displayName: import_zod13.z.string().min(1),
|
|
3475
|
+
status: import_zod13.z.enum(ACCOUNT_STATUSES),
|
|
3476
|
+
ownerUserId: import_zod13.z.string().uuid().nullable(),
|
|
3477
|
+
createdAtMs: import_zod13.z.number().int().nonnegative()
|
|
3084
3478
|
});
|
|
3085
|
-
var AccountMembershipSchema =
|
|
3086
|
-
accountId:
|
|
3087
|
-
userId:
|
|
3088
|
-
role:
|
|
3089
|
-
createdAtMs:
|
|
3479
|
+
var AccountMembershipSchema = import_zod13.z.object({
|
|
3480
|
+
accountId: import_zod13.z.string().uuid(),
|
|
3481
|
+
userId: import_zod13.z.string().uuid(),
|
|
3482
|
+
role: import_zod13.z.enum(MEMBERSHIP_ROLES),
|
|
3483
|
+
createdAtMs: import_zod13.z.number().int().nonnegative()
|
|
3090
3484
|
});
|
|
3091
3485
|
function createAccountsClient(opts) {
|
|
3092
3486
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -3119,9 +3513,9 @@ function createAccountsClient(opts) {
|
|
|
3119
3513
|
}
|
|
3120
3514
|
return parser(raw);
|
|
3121
3515
|
}
|
|
3122
|
-
const itemsSchema =
|
|
3123
|
-
const memberItemsSchema =
|
|
3124
|
-
items:
|
|
3516
|
+
const itemsSchema = import_zod13.z.object({ items: import_zod13.z.array(AccountSchema) });
|
|
3517
|
+
const memberItemsSchema = import_zod13.z.object({
|
|
3518
|
+
items: import_zod13.z.array(AccountMembershipSchema)
|
|
3125
3519
|
});
|
|
3126
3520
|
return {
|
|
3127
3521
|
listMyAccounts: () => call(
|
|
@@ -3152,345 +3546,8 @@ function createAccountsClient(opts) {
|
|
|
3152
3546
|
};
|
|
3153
3547
|
}
|
|
3154
3548
|
|
|
3155
|
-
// src/me-offline/client.ts
|
|
3156
|
-
var import_zod13 = require("zod");
|
|
3157
|
-
var Hex64 = import_zod13.z.string().regex(/^[0-9a-f]{64}$/i);
|
|
3158
|
-
var HexAny = import_zod13.z.string().regex(/^[0-9a-f]+$/i);
|
|
3159
|
-
var Sha256Hex = import_zod13.z.string().regex(/^[0-9a-f]{64}$/i);
|
|
3160
|
-
var Base64Std = import_zod13.z.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
3161
|
-
var RegisterDeviceKeyInputSchema = import_zod13.z.object({
|
|
3162
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3163
|
-
publicKeyHex: Hex64
|
|
3164
|
-
});
|
|
3165
|
-
var AttestationSecurityLevelSchema = import_zod13.z.enum([
|
|
3166
|
-
"STRONGBOX",
|
|
3167
|
-
"TEE",
|
|
3168
|
-
"SECURE_ENCLAVE",
|
|
3169
|
-
"SOFTWARE"
|
|
3170
|
-
]);
|
|
3171
|
-
var DeviceKeyAlgSchema = import_zod13.z.enum(["ed25519", "p256"]);
|
|
3172
|
-
var RegisterDeviceKeyP256InputSchema = import_zod13.z.object({
|
|
3173
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3174
|
-
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
3175
|
-
publicKeySpkiB64: Base64Std.min(64).max(4096),
|
|
3176
|
-
/** Base64 of the server-issued enrollment challenge string. */
|
|
3177
|
-
challengeB64: Base64Std.min(8).max(1024),
|
|
3178
|
-
/** iOS App Attest payload or Android X.509 Key Attestation chain. */
|
|
3179
|
-
attestationChainB64: import_zod13.z.array(Base64Std.min(16).max(16384)).min(1).max(16),
|
|
3180
|
-
securityLevel: AttestationSecurityLevelSchema
|
|
3181
|
-
});
|
|
3182
|
-
var P256EnrollmentChallengeInputSchema = import_zod13.z.object({
|
|
3183
|
-
deviceId: import_zod13.z.string().min(1).max(128)
|
|
3184
|
-
});
|
|
3185
|
-
var P256EnrollmentChallengeResultSchema = import_zod13.z.object({
|
|
3186
|
-
challenge: import_zod13.z.string().min(16),
|
|
3187
|
-
expiresAtMs: import_zod13.z.number().int().positive()
|
|
3188
|
-
});
|
|
3189
|
-
var DeviceKeyRecordSchema = import_zod13.z.object({
|
|
3190
|
-
id: import_zod13.z.string().uuid(),
|
|
3191
|
-
userId: import_zod13.z.string().uuid(),
|
|
3192
|
-
deviceId: import_zod13.z.string(),
|
|
3193
|
-
alg: DeviceKeyAlgSchema.default("ed25519"),
|
|
3194
|
-
publicKeyHex: Hex64.nullable().default(null),
|
|
3195
|
-
publicKeySpkiB64: Base64Std.nullable().default(null),
|
|
3196
|
-
securityLevel: AttestationSecurityLevelSchema.nullable().default(null),
|
|
3197
|
-
hardwareBacked: import_zod13.z.boolean().default(false),
|
|
3198
|
-
attestedAtMs: import_zod13.z.number().int().nonnegative().nullable().default(null),
|
|
3199
|
-
createdAtMs: import_zod13.z.number().int().nonnegative(),
|
|
3200
|
-
revokedAtMs: import_zod13.z.number().int().nonnegative().nullable()
|
|
3201
|
-
});
|
|
3202
|
-
var ConsumerOACSchema = import_zod13.z.object({
|
|
3203
|
-
oacId: import_zod13.z.string().uuid(),
|
|
3204
|
-
issuerId: import_zod13.z.string().min(1).max(64),
|
|
3205
|
-
userId: import_zod13.z.string().uuid(),
|
|
3206
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3207
|
-
alg: import_zod13.z.enum(["ed25519", "p256"]).optional(),
|
|
3208
|
-
devicePubkeyHex: Hex64.optional(),
|
|
3209
|
-
devicePubkeySpkiB64: Base64Std.min(64).max(4096).optional(),
|
|
3210
|
-
perTxCapKobo: import_zod13.z.number().int().positive(),
|
|
3211
|
-
cumulativeCapKobo: import_zod13.z.number().int().positive(),
|
|
3212
|
-
currency: import_zod13.z.string().length(3),
|
|
3213
|
-
validFromMs: import_zod13.z.number().int().nonnegative(),
|
|
3214
|
-
validUntilMs: import_zod13.z.number().int().nonnegative(),
|
|
3215
|
-
counterSeed: import_zod13.z.number().int().nonnegative(),
|
|
3216
|
-
issuedAtMs: import_zod13.z.number().int().nonnegative()
|
|
3217
|
-
}).refine(
|
|
3218
|
-
(o) => {
|
|
3219
|
-
const alg = o.alg ?? "ed25519";
|
|
3220
|
-
if (alg === "ed25519") {
|
|
3221
|
-
return Boolean(o.devicePubkeyHex) && !o.devicePubkeySpkiB64;
|
|
3222
|
-
}
|
|
3223
|
-
return Boolean(o.devicePubkeySpkiB64) && !o.devicePubkeyHex;
|
|
3224
|
-
},
|
|
3225
|
-
{ message: "OAC device pubkey shape must match alg" }
|
|
3226
|
-
);
|
|
3227
|
-
var SignedConsumerOACSchema = import_zod13.z.object({
|
|
3228
|
-
oac: ConsumerOACSchema,
|
|
3229
|
-
issuerSig: HexAny,
|
|
3230
|
-
issuerPublicKeyHex: Hex64
|
|
3231
|
-
});
|
|
3232
|
-
var OACRecordSchema = SignedConsumerOACSchema.extend({
|
|
3233
|
-
currentOfflineSpentKobo: import_zod13.z.number().int().nonnegative(),
|
|
3234
|
-
status: import_zod13.z.enum([
|
|
3235
|
-
"active",
|
|
3236
|
-
"superseded",
|
|
3237
|
-
"expired",
|
|
3238
|
-
"revoked",
|
|
3239
|
-
"disabling",
|
|
3240
|
-
"draining",
|
|
3241
|
-
"closed"
|
|
3242
|
-
]),
|
|
3243
|
-
supersededAtMs: import_zod13.z.number().int().nonnegative().nullable(),
|
|
3244
|
-
revokedAtMs: import_zod13.z.number().int().nonnegative().nullable(),
|
|
3245
|
-
holdId: import_zod13.z.string().uuid().nullable().optional()
|
|
3246
|
-
});
|
|
3247
|
-
var IssueOACInputSchema = import_zod13.z.object({
|
|
3248
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3249
|
-
perTxCapKobo: import_zod13.z.number().int().positive().optional(),
|
|
3250
|
-
cumulativeCapKobo: import_zod13.z.number().int().positive().optional(),
|
|
3251
|
-
ttlMs: import_zod13.z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
3252
|
-
spendableOnlineKobo: import_zod13.z.number().int().nonnegative().optional()
|
|
3253
|
-
});
|
|
3254
|
-
var EnableOfflineInputSchema = import_zod13.z.object({
|
|
3255
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3256
|
-
amountKobo: import_zod13.z.number().int().positive(),
|
|
3257
|
-
perTxCapKobo: import_zod13.z.number().int().positive().optional(),
|
|
3258
|
-
ttlMs: import_zod13.z.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
3259
|
-
installId: import_zod13.z.string().min(1).max(128),
|
|
3260
|
-
partnerId: import_zod13.z.string().min(1).max(64).optional()
|
|
3261
|
-
});
|
|
3262
|
-
var ProvisionOfflineAllowanceInputSchema = EnableOfflineInputSchema;
|
|
3263
|
-
var DisableOfflineInputSchema = import_zod13.z.object({
|
|
3264
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3265
|
-
installId: import_zod13.z.string().min(1).max(128).optional(),
|
|
3266
|
-
claims: import_zod13.z.array(import_zod13.z.unknown()).max(256).optional()
|
|
3267
|
-
});
|
|
3268
|
-
var OfflineHoldRecordSchema = import_zod13.z.object({
|
|
3269
|
-
holdId: import_zod13.z.string().uuid(),
|
|
3270
|
-
userId: import_zod13.z.string().uuid(),
|
|
3271
|
-
deviceId: import_zod13.z.string(),
|
|
3272
|
-
partnerId: import_zod13.z.string(),
|
|
3273
|
-
adapterKind: import_zod13.z.string(),
|
|
3274
|
-
externalHoldRef: import_zod13.z.string().nullable(),
|
|
3275
|
-
amountKobo: import_zod13.z.number().int().nonnegative(),
|
|
3276
|
-
capturedKobo: import_zod13.z.number().int().nonnegative(),
|
|
3277
|
-
releasedKobo: import_zod13.z.number().int().nonnegative(),
|
|
3278
|
-
remainingKobo: import_zod13.z.number().int().nonnegative(),
|
|
3279
|
-
currency: import_zod13.z.string().length(3),
|
|
3280
|
-
status: import_zod13.z.enum([
|
|
3281
|
-
"placing",
|
|
3282
|
-
"active",
|
|
3283
|
-
"disabling",
|
|
3284
|
-
"draining",
|
|
3285
|
-
"closed",
|
|
3286
|
-
"revoked",
|
|
3287
|
-
"failed"
|
|
3288
|
-
]),
|
|
3289
|
-
installId: import_zod13.z.string().nullable(),
|
|
3290
|
-
drainDeadlineMs: import_zod13.z.number().int().nonnegative(),
|
|
3291
|
-
disableRequestedAtMs: import_zod13.z.number().int().nonnegative().nullable(),
|
|
3292
|
-
createdAtMs: import_zod13.z.number().int().nonnegative(),
|
|
3293
|
-
closedAtMs: import_zod13.z.number().int().nonnegative().nullable(),
|
|
3294
|
-
isTrusted: import_zod13.z.boolean().optional()
|
|
3295
|
-
});
|
|
3296
|
-
var EnableOfflineResultSchema = import_zod13.z.object({
|
|
3297
|
-
hold: OfflineHoldRecordSchema,
|
|
3298
|
-
oac: OACRecordSchema
|
|
3299
|
-
});
|
|
3300
|
-
var ProvisionOfflineAllowanceResultSchema = EnableOfflineResultSchema;
|
|
3301
|
-
var DisableOfflineResultSchema = import_zod13.z.object({
|
|
3302
|
-
hold: OfflineHoldRecordSchema,
|
|
3303
|
-
trusted: import_zod13.z.boolean(),
|
|
3304
|
-
settledClaims: import_zod13.z.number().int().nonnegative()
|
|
3305
|
-
});
|
|
3306
|
-
var OfflineStatusResultSchema = import_zod13.z.object({
|
|
3307
|
-
hold: OfflineHoldRecordSchema.nullable(),
|
|
3308
|
-
active: OACRecordSchema.nullable()
|
|
3309
|
-
});
|
|
3310
|
-
var OfflineStateResultSchema = import_zod13.z.object({
|
|
3311
|
-
active: OACRecordSchema.nullable()
|
|
3312
|
-
});
|
|
3313
|
-
var ClaimAlgSchema = import_zod13.z.enum(["ed25519", "p256"]);
|
|
3314
|
-
var ConsumerPaymentClaimSchema = import_zod13.z.object({
|
|
3315
|
-
/** Algorithm discriminator. Omit / 'ed25519' for V1 clients. */
|
|
3316
|
-
alg: ClaimAlgSchema.optional(),
|
|
3317
|
-
oacId: import_zod13.z.string().uuid(),
|
|
3318
|
-
encounterId: Sha256Hex.optional(),
|
|
3319
|
-
payerUserId: import_zod13.z.string().uuid(),
|
|
3320
|
-
payeeUserId: import_zod13.z.string().uuid(),
|
|
3321
|
-
payerDeviceId: import_zod13.z.string().min(1).max(128),
|
|
3322
|
-
payerNonce: import_zod13.z.string().min(8).max(128),
|
|
3323
|
-
payeeNonce: import_zod13.z.string().min(8).max(128),
|
|
3324
|
-
amountKobo: import_zod13.z.number().int().positive(),
|
|
3325
|
-
currency: import_zod13.z.string().length(3).default("NGN"),
|
|
3326
|
-
occurredAtMs: import_zod13.z.number().int().nonnegative(),
|
|
3327
|
-
completedAtMs: import_zod13.z.number().int().nonnegative().optional(),
|
|
3328
|
-
contextId: import_zod13.z.string().max(128).optional(),
|
|
3329
|
-
// ed25519 path
|
|
3330
|
-
payerPubkeyHex: Hex64.optional(),
|
|
3331
|
-
payerSignature: HexAny.optional(),
|
|
3332
|
-
payeePubkeyHex: Hex64.optional(),
|
|
3333
|
-
payeeSignature: HexAny.optional(),
|
|
3334
|
-
// p256 path
|
|
3335
|
-
payerPubkeySpkiB64: import_zod13.z.string().min(64).max(4096).optional(),
|
|
3336
|
-
payerSignatureDerB64: import_zod13.z.string().min(16).max(2048).optional(),
|
|
3337
|
-
payeePubkeySpkiB64: import_zod13.z.string().min(64).max(4096).optional(),
|
|
3338
|
-
payeeSignatureDerB64: import_zod13.z.string().min(16).max(2048).optional()
|
|
3339
|
-
}).refine(
|
|
3340
|
-
(c) => {
|
|
3341
|
-
const alg = c.alg ?? "ed25519";
|
|
3342
|
-
if (alg === "ed25519") {
|
|
3343
|
-
return Boolean(c.payerPubkeyHex) && Boolean(c.payerSignature);
|
|
3344
|
-
}
|
|
3345
|
-
return Boolean(c.payerPubkeySpkiB64) && Boolean(c.payerSignatureDerB64);
|
|
3346
|
-
},
|
|
3347
|
-
{
|
|
3348
|
-
message: "payer key/signature fields must match alg (ed25519: hex; p256: SPKI+DER b64)"
|
|
3349
|
-
}
|
|
3350
|
-
);
|
|
3351
|
-
var ConsumerSettlementSchema = import_zod13.z.object({
|
|
3352
|
-
settlementId: import_zod13.z.string().uuid(),
|
|
3353
|
-
settlementKey: Sha256Hex,
|
|
3354
|
-
encounterId: Sha256Hex,
|
|
3355
|
-
oacId: import_zod13.z.string().uuid(),
|
|
3356
|
-
payerUserId: import_zod13.z.string().uuid(),
|
|
3357
|
-
payeeUserId: import_zod13.z.string().uuid(),
|
|
3358
|
-
amountKobo: import_zod13.z.number().int().positive(),
|
|
3359
|
-
currency: import_zod13.z.string().length(3),
|
|
3360
|
-
status: import_zod13.z.enum(["SETTLED", "REVIEW"]),
|
|
3361
|
-
reviewReason: import_zod13.z.string().nullable(),
|
|
3362
|
-
ledgerRef: import_zod13.z.string().nullable(),
|
|
3363
|
-
issuerSig: HexAny,
|
|
3364
|
-
createdAtMs: import_zod13.z.number().int().nonnegative()
|
|
3365
|
-
});
|
|
3366
|
-
var ConsumerSettleResultSchema = import_zod13.z.object({
|
|
3367
|
-
settlement: ConsumerSettlementSchema,
|
|
3368
|
-
encounterId: Sha256Hex,
|
|
3369
|
-
replayed: import_zod13.z.boolean()
|
|
3370
|
-
});
|
|
3371
|
-
var RevokeDeviceKeyInputSchema = import_zod13.z.object({
|
|
3372
|
-
deviceId: import_zod13.z.string().min(1).max(128),
|
|
3373
|
-
/** Step-up token from /api/v1/auth/send/verify with purpose='offline_revoke'. */
|
|
3374
|
-
sendAuthToken: import_zod13.z.string().min(16)
|
|
3375
|
-
});
|
|
3376
|
-
function createMeOfflineClient(opts) {
|
|
3377
|
-
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
3378
|
-
if (!fetchImpl) {
|
|
3379
|
-
throw new Error("createMeOfflineClient: no fetch implementation available");
|
|
3380
|
-
}
|
|
3381
|
-
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
3382
|
-
async function call(method, path, body, parser) {
|
|
3383
|
-
const init2 = {
|
|
3384
|
-
method,
|
|
3385
|
-
headers: {
|
|
3386
|
-
"content-type": "application/json",
|
|
3387
|
-
accept: "application/json"
|
|
3388
|
-
}
|
|
3389
|
-
};
|
|
3390
|
-
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
3391
|
-
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
3392
|
-
const text = await resp.text();
|
|
3393
|
-
let raw = void 0;
|
|
3394
|
-
if (text) {
|
|
3395
|
-
try {
|
|
3396
|
-
raw = JSON.parse(text);
|
|
3397
|
-
} catch {
|
|
3398
|
-
}
|
|
3399
|
-
}
|
|
3400
|
-
if (!resp.ok) {
|
|
3401
|
-
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
3402
|
-
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
3403
|
-
throw new FlurApiError(resp.status, code, message, raw);
|
|
3404
|
-
}
|
|
3405
|
-
return parser(raw);
|
|
3406
|
-
}
|
|
3407
|
-
const deviceKeyItems = import_zod13.z.object({ items: import_zod13.z.array(DeviceKeyRecordSchema) });
|
|
3408
|
-
return {
|
|
3409
|
-
registerDeviceKey: (input) => call(
|
|
3410
|
-
"POST",
|
|
3411
|
-
"/v1/me/offline/keys",
|
|
3412
|
-
RegisterDeviceKeyInputSchema.parse(input),
|
|
3413
|
-
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
3414
|
-
),
|
|
3415
|
-
issueP256EnrollmentChallenge: (input) => call(
|
|
3416
|
-
"POST",
|
|
3417
|
-
"/v1/me/offline/keys/p256/challenge",
|
|
3418
|
-
P256EnrollmentChallengeInputSchema.parse(input),
|
|
3419
|
-
(raw) => P256EnrollmentChallengeResultSchema.parse(raw)
|
|
3420
|
-
),
|
|
3421
|
-
registerDeviceKeyP256: (input) => call(
|
|
3422
|
-
"POST",
|
|
3423
|
-
"/v1/me/offline/keys/p256",
|
|
3424
|
-
RegisterDeviceKeyP256InputSchema.parse(input),
|
|
3425
|
-
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
3426
|
-
),
|
|
3427
|
-
listDeviceKeys: () => call(
|
|
3428
|
-
"GET",
|
|
3429
|
-
"/v1/me/offline/keys",
|
|
3430
|
-
void 0,
|
|
3431
|
-
(raw) => deviceKeyItems.parse(raw)
|
|
3432
|
-
),
|
|
3433
|
-
revokeDeviceKey: (input) => call(
|
|
3434
|
-
"POST",
|
|
3435
|
-
"/v1/me/offline/keys/revoke",
|
|
3436
|
-
RevokeDeviceKeyInputSchema.parse(input),
|
|
3437
|
-
() => void 0
|
|
3438
|
-
),
|
|
3439
|
-
provisionAllowance: (input) => call(
|
|
3440
|
-
"POST",
|
|
3441
|
-
"/v1/me/offline/allowance",
|
|
3442
|
-
ProvisionOfflineAllowanceInputSchema.parse(input),
|
|
3443
|
-
(raw) => ProvisionOfflineAllowanceResultSchema.parse(raw)
|
|
3444
|
-
),
|
|
3445
|
-
enable: (input) => call(
|
|
3446
|
-
"POST",
|
|
3447
|
-
"/v1/me/offline/enable",
|
|
3448
|
-
EnableOfflineInputSchema.parse(input),
|
|
3449
|
-
(raw) => EnableOfflineResultSchema.parse(raw)
|
|
3450
|
-
),
|
|
3451
|
-
refresh: (input) => call(
|
|
3452
|
-
"POST",
|
|
3453
|
-
"/v1/me/offline/refresh",
|
|
3454
|
-
IssueOACInputSchema.parse(input),
|
|
3455
|
-
(raw) => OACRecordSchema.parse(raw)
|
|
3456
|
-
),
|
|
3457
|
-
disable: (input) => call(
|
|
3458
|
-
"POST",
|
|
3459
|
-
"/v1/me/offline/disable",
|
|
3460
|
-
DisableOfflineInputSchema.parse(input),
|
|
3461
|
-
(raw) => DisableOfflineResultSchema.parse(raw)
|
|
3462
|
-
),
|
|
3463
|
-
getStatus: (deviceId) => {
|
|
3464
|
-
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
3465
|
-
return call(
|
|
3466
|
-
"GET",
|
|
3467
|
-
`/v1/me/offline/status${qs}`,
|
|
3468
|
-
void 0,
|
|
3469
|
-
(raw) => OfflineStatusResultSchema.parse(raw)
|
|
3470
|
-
);
|
|
3471
|
-
},
|
|
3472
|
-
getState: (deviceId) => {
|
|
3473
|
-
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
3474
|
-
return call(
|
|
3475
|
-
"GET",
|
|
3476
|
-
`/v1/me/offline/state${qs}`,
|
|
3477
|
-
void 0,
|
|
3478
|
-
(raw) => OfflineStateResultSchema.parse(raw)
|
|
3479
|
-
);
|
|
3480
|
-
},
|
|
3481
|
-
submitClaim: (claim) => call(
|
|
3482
|
-
"POST",
|
|
3483
|
-
"/v1/me/offline/claims",
|
|
3484
|
-
ConsumerPaymentClaimSchema.parse(claim),
|
|
3485
|
-
(raw) => ConsumerSettleResultSchema.parse(raw)
|
|
3486
|
-
)
|
|
3487
|
-
};
|
|
3488
|
-
}
|
|
3489
|
-
|
|
3490
3549
|
// src/me-offline/signer.ts
|
|
3491
|
-
var
|
|
3492
|
-
var import_nist = require("@noble/curves/nist");
|
|
3493
|
-
var import_utils3 = require("@noble/hashes/utils");
|
|
3550
|
+
var import_nist2 = require("@noble/curves/nist");
|
|
3494
3551
|
var CLAIM_DOMAIN_V2 = "flur:consumer-offline:v2:claim";
|
|
3495
3552
|
function canonicalClaimSigningPayload(claim) {
|
|
3496
3553
|
return {
|
|
@@ -3512,7 +3569,7 @@ function canonicalClaimSigningPayload(claim) {
|
|
|
3512
3569
|
function canonicalClaimSigningBytes(claim) {
|
|
3513
3570
|
return canonicalJSONBytes(canonicalClaimSigningPayload(claim));
|
|
3514
3571
|
}
|
|
3515
|
-
function
|
|
3572
|
+
function bytesToBase642(bytes) {
|
|
3516
3573
|
if (typeof Buffer !== "undefined") {
|
|
3517
3574
|
return Buffer.from(bytes).toString("base64");
|
|
3518
3575
|
}
|
|
@@ -3520,7 +3577,7 @@ function bytesToBase64(bytes) {
|
|
|
3520
3577
|
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
3521
3578
|
return btoa(bin);
|
|
3522
3579
|
}
|
|
3523
|
-
function
|
|
3580
|
+
function base64ToBytes2(b64) {
|
|
3524
3581
|
if (typeof Buffer !== "undefined") {
|
|
3525
3582
|
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
3526
3583
|
}
|
|
@@ -3529,7 +3586,7 @@ function base64ToBytes(b64) {
|
|
|
3529
3586
|
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
3530
3587
|
return out;
|
|
3531
3588
|
}
|
|
3532
|
-
var
|
|
3589
|
+
var P256_SPKI_HEADER2 = new Uint8Array([
|
|
3533
3590
|
48,
|
|
3534
3591
|
89,
|
|
3535
3592
|
48,
|
|
@@ -3561,38 +3618,25 @@ function p256PublicKeyToSpkiB64(rawUncompressed) {
|
|
|
3561
3618
|
if (rawUncompressed.length !== 65 || rawUncompressed[0] !== 4) {
|
|
3562
3619
|
throw new Error("p256: expected 65-byte uncompressed point");
|
|
3563
3620
|
}
|
|
3564
|
-
const out = new Uint8Array(
|
|
3565
|
-
out.set(
|
|
3566
|
-
out.set(rawUncompressed,
|
|
3567
|
-
return
|
|
3621
|
+
const out = new Uint8Array(P256_SPKI_HEADER2.length + rawUncompressed.length);
|
|
3622
|
+
out.set(P256_SPKI_HEADER2, 0);
|
|
3623
|
+
out.set(rawUncompressed, P256_SPKI_HEADER2.length);
|
|
3624
|
+
return bytesToBase642(out);
|
|
3568
3625
|
}
|
|
3569
3626
|
function p256SpkiB64ToPublicKey(spkiB64) {
|
|
3570
|
-
const spki =
|
|
3571
|
-
if (spki.length !==
|
|
3627
|
+
const spki = base64ToBytes2(spkiB64);
|
|
3628
|
+
if (spki.length !== P256_SPKI_HEADER2.length + 65) {
|
|
3572
3629
|
throw new Error("p256: invalid SPKI length");
|
|
3573
3630
|
}
|
|
3574
|
-
for (let i = 0; i <
|
|
3575
|
-
if (spki[i] !==
|
|
3631
|
+
for (let i = 0; i < P256_SPKI_HEADER2.length; i++) {
|
|
3632
|
+
if (spki[i] !== P256_SPKI_HEADER2[i]) {
|
|
3576
3633
|
throw new Error("p256: invalid SPKI header");
|
|
3577
3634
|
}
|
|
3578
3635
|
}
|
|
3579
|
-
return spki.slice(
|
|
3580
|
-
}
|
|
3581
|
-
function createSoftwareEd25519Signer(privateKey) {
|
|
3582
|
-
const pub = import_ed255198.ed25519.getPublicKey(privateKey);
|
|
3583
|
-
return {
|
|
3584
|
-
alg: "ed25519",
|
|
3585
|
-
async getPublicKey() {
|
|
3586
|
-
return { alg: "ed25519", publicKey: (0, import_utils3.bytesToHex)(pub) };
|
|
3587
|
-
},
|
|
3588
|
-
async sign(bytes) {
|
|
3589
|
-
const sig = import_ed255198.ed25519.sign(bytes, privateKey);
|
|
3590
|
-
return { alg: "ed25519", signature: (0, import_utils3.bytesToHex)(sig) };
|
|
3591
|
-
}
|
|
3592
|
-
};
|
|
3636
|
+
return spki.slice(P256_SPKI_HEADER2.length);
|
|
3593
3637
|
}
|
|
3594
3638
|
function createSoftwareP256Signer(privateKey) {
|
|
3595
|
-
const raw =
|
|
3639
|
+
const raw = import_nist2.p256.getPublicKey(privateKey, false);
|
|
3596
3640
|
const spkiB64 = p256PublicKeyToSpkiB64(raw);
|
|
3597
3641
|
return {
|
|
3598
3642
|
alg: "p256",
|
|
@@ -3600,35 +3644,87 @@ function createSoftwareP256Signer(privateKey) {
|
|
|
3600
3644
|
return { alg: "p256", publicKey: spkiB64 };
|
|
3601
3645
|
},
|
|
3602
3646
|
async sign(bytes) {
|
|
3603
|
-
const sig =
|
|
3647
|
+
const sig = import_nist2.p256.sign(bytes, privateKey, { prehash: true });
|
|
3604
3648
|
const der = sig.toBytes("der");
|
|
3605
|
-
return { alg: "p256", signature:
|
|
3649
|
+
return { alg: "p256", signature: bytesToBase642(der) };
|
|
3606
3650
|
}
|
|
3607
3651
|
};
|
|
3608
3652
|
}
|
|
3609
3653
|
function verifyClaimSignature(input) {
|
|
3610
3654
|
try {
|
|
3611
|
-
if (input.alg
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
}
|
|
3618
|
-
if (input.alg === "p256") {
|
|
3619
|
-
const sigDer = base64ToBytes(input.signature);
|
|
3620
|
-
const pub = p256SpkiB64ToPublicKey(input.publicKey);
|
|
3621
|
-
return import_nist.p256.verify(sigDer, input.bytes, pub, {
|
|
3622
|
-
prehash: true,
|
|
3623
|
-
format: "der"
|
|
3624
|
-
});
|
|
3625
|
-
}
|
|
3626
|
-
return false;
|
|
3655
|
+
if (input.alg !== "p256") return false;
|
|
3656
|
+
const sigDer = base64ToBytes2(input.signature);
|
|
3657
|
+
const pub = p256SpkiB64ToPublicKey(input.publicKey);
|
|
3658
|
+
return import_nist2.p256.verify(sigDer, input.bytes, pub, {
|
|
3659
|
+
prehash: true,
|
|
3660
|
+
format: "der"
|
|
3661
|
+
});
|
|
3627
3662
|
} catch {
|
|
3628
3663
|
return false;
|
|
3629
3664
|
}
|
|
3630
3665
|
}
|
|
3631
3666
|
|
|
3667
|
+
// src/me-offline/sms.ts
|
|
3668
|
+
var OFFLINE_CLAIM_SMS_PREFIX = "FLURC1.";
|
|
3669
|
+
var TOKEN_RE = /(?:^|\s)(FLURC1\.[A-Za-z0-9_-]+={0,2})(?:\s|$)/;
|
|
3670
|
+
function encodeOfflineClaimSmsMessage(claim) {
|
|
3671
|
+
const parsed = ConsumerPaymentClaimSchema.parse(claim);
|
|
3672
|
+
const json = JSON.stringify(parsed);
|
|
3673
|
+
return `${OFFLINE_CLAIM_SMS_PREFIX}${base64UrlEncodeUtf8(json)}`;
|
|
3674
|
+
}
|
|
3675
|
+
function decodeOfflineClaimSmsMessage(message) {
|
|
3676
|
+
const token = extractOfflineClaimSmsToken(message);
|
|
3677
|
+
if (!token) {
|
|
3678
|
+
throw new Error("offline claim SMS token not found");
|
|
3679
|
+
}
|
|
3680
|
+
const encoded = token.slice(OFFLINE_CLAIM_SMS_PREFIX.length);
|
|
3681
|
+
let raw;
|
|
3682
|
+
try {
|
|
3683
|
+
raw = JSON.parse(base64UrlDecodeUtf8(encoded));
|
|
3684
|
+
} catch {
|
|
3685
|
+
throw new Error("offline claim SMS token is malformed");
|
|
3686
|
+
}
|
|
3687
|
+
const parsed = ConsumerPaymentClaimSchema.safeParse(raw);
|
|
3688
|
+
if (!parsed.success) {
|
|
3689
|
+
throw new Error("offline claim SMS token is invalid");
|
|
3690
|
+
}
|
|
3691
|
+
return parsed.data;
|
|
3692
|
+
}
|
|
3693
|
+
function extractOfflineClaimSmsToken(message) {
|
|
3694
|
+
const trimmed = message.trim();
|
|
3695
|
+
if (trimmed.startsWith(OFFLINE_CLAIM_SMS_PREFIX)) {
|
|
3696
|
+
return trimmed.split(/\s+/, 1)[0] ?? null;
|
|
3697
|
+
}
|
|
3698
|
+
return TOKEN_RE.exec(message)?.[1] ?? null;
|
|
3699
|
+
}
|
|
3700
|
+
function base64UrlEncodeUtf8(input) {
|
|
3701
|
+
const bytes = new TextEncoder().encode(input);
|
|
3702
|
+
let binary = "";
|
|
3703
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
3704
|
+
const base64 = typeof btoa === "function" ? btoa(binary) : typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : void 0;
|
|
3705
|
+
if (!base64) throw new Error("base64 encoder unavailable");
|
|
3706
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
3707
|
+
}
|
|
3708
|
+
function base64UrlDecodeUtf8(input) {
|
|
3709
|
+
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
3710
|
+
const padded = base64.padEnd(
|
|
3711
|
+
base64.length + (4 - base64.length % 4) % 4,
|
|
3712
|
+
"="
|
|
3713
|
+
);
|
|
3714
|
+
if (typeof atob === "function") {
|
|
3715
|
+
const binary = atob(padded);
|
|
3716
|
+
const bytes = new Uint8Array(binary.length);
|
|
3717
|
+
for (let index = 0; index < binary.length; index++) {
|
|
3718
|
+
bytes[index] = binary.charCodeAt(index);
|
|
3719
|
+
}
|
|
3720
|
+
return new TextDecoder().decode(bytes);
|
|
3721
|
+
}
|
|
3722
|
+
if (typeof Buffer !== "undefined") {
|
|
3723
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
3724
|
+
}
|
|
3725
|
+
throw new Error("base64 decoder unavailable");
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3632
3728
|
// src/partner-funding/client.ts
|
|
3633
3729
|
var import_zod14 = require("zod");
|
|
3634
3730
|
var MinorString = import_zod14.z.string().regex(/^-?\d+$/);
|
|
@@ -4004,14 +4100,15 @@ function buildArtifactBody(input) {
|
|
|
4004
4100
|
return { ...header, data: input.data };
|
|
4005
4101
|
}
|
|
4006
4102
|
function signArtifact(body, privateKey) {
|
|
4007
|
-
const sig =
|
|
4103
|
+
const sig = signIssuerP256(canonicalJSONBytes(body), privateKey);
|
|
4008
4104
|
return { body, sig };
|
|
4009
4105
|
}
|
|
4010
4106
|
function encodeArtifactUri(signed) {
|
|
4011
4107
|
const bodyBytes = canonicalJSONBytes(signed.body);
|
|
4012
4108
|
const bodyB64 = base64UrlEncode(bodyBytes);
|
|
4013
|
-
const
|
|
4014
|
-
|
|
4109
|
+
const sigBytes = base64UrlDecode(signed.sig);
|
|
4110
|
+
const sigB64Url = base64UrlEncode(sigBytes);
|
|
4111
|
+
return `${FLUR_ARTIFACT_URI_PREFIX}${signed.body.t}/${bodyB64}.${sigB64Url}`;
|
|
4015
4112
|
}
|
|
4016
4113
|
function decodeArtifactUri(uri) {
|
|
4017
4114
|
if (!uri.startsWith(FLUR_ARTIFACT_URI_PREFIX)) {
|
|
@@ -4042,9 +4139,9 @@ function decodeArtifactUri(uri) {
|
|
|
4042
4139
|
}
|
|
4043
4140
|
const bodyBytes = base64UrlDecode(payload.slice(0, dot));
|
|
4044
4141
|
const sigBytes = base64UrlDecode(payload.slice(dot + 1));
|
|
4045
|
-
if (sigBytes.length
|
|
4142
|
+
if (sigBytes.length < 64 || sigBytes.length > 80) {
|
|
4046
4143
|
throw new FlurArtifactError(
|
|
4047
|
-
`Signature
|
|
4144
|
+
`Signature length out of range: ${sigBytes.length}`,
|
|
4048
4145
|
"INVALID_SIGNATURE"
|
|
4049
4146
|
);
|
|
4050
4147
|
}
|
|
@@ -4071,17 +4168,26 @@ function decodeArtifactUri(uri) {
|
|
|
4071
4168
|
type,
|
|
4072
4169
|
bodyBytes,
|
|
4073
4170
|
body: bodyJson,
|
|
4074
|
-
|
|
4171
|
+
// Encode as standard base64 (not url-safe) for sig field consistency.
|
|
4172
|
+
sig: encodeStdBase64(sigBytes)
|
|
4075
4173
|
};
|
|
4076
4174
|
}
|
|
4077
|
-
function
|
|
4175
|
+
function encodeStdBase64(bytes) {
|
|
4176
|
+
if (typeof Buffer !== "undefined") {
|
|
4177
|
+
return Buffer.from(bytes).toString("base64");
|
|
4178
|
+
}
|
|
4179
|
+
let bin = "";
|
|
4180
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
4181
|
+
return typeof btoa === "function" ? btoa(bin) : "";
|
|
4182
|
+
}
|
|
4183
|
+
function verifyArtifactSignature(decoded, publicKeySpkiB64, options = {}) {
|
|
4078
4184
|
if (options.enforceExpiry !== false && decoded.body.exp !== void 0) {
|
|
4079
4185
|
const now = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
4080
4186
|
if (decoded.body.exp < now) {
|
|
4081
4187
|
throw new FlurArtifactError("Artifact has expired", "EXPIRED");
|
|
4082
4188
|
}
|
|
4083
4189
|
}
|
|
4084
|
-
return
|
|
4190
|
+
return verifyIssuerP256(decoded.bodyBytes, decoded.sig, publicKeySpkiB64);
|
|
4085
4191
|
}
|
|
4086
4192
|
|
|
4087
4193
|
// src/artifacts/types.ts
|
|
@@ -4100,7 +4206,7 @@ var ARTIFACT_TYPES = {
|
|
|
4100
4206
|
PASS: "pass",
|
|
4101
4207
|
IDENTITY: "identity"
|
|
4102
4208
|
};
|
|
4103
|
-
var
|
|
4209
|
+
var HexString = (length) => import_zod16.z.string().regex(
|
|
4104
4210
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
4105
4211
|
`expected ${length}-byte hex string`
|
|
4106
4212
|
);
|
|
@@ -4118,7 +4224,7 @@ var ReceiptArtifactSchema = import_zod16.z.object({
|
|
|
4118
4224
|
settledAtMs: import_zod16.z.number().int().positive(),
|
|
4119
4225
|
ledgerTxnId: import_zod16.z.string().min(1).max(64).optional(),
|
|
4120
4226
|
memo: import_zod16.z.string().max(140).optional(),
|
|
4121
|
-
hashChainPrev:
|
|
4227
|
+
hashChainPrev: HexString(32).optional()
|
|
4122
4228
|
});
|
|
4123
4229
|
var ShortId = import_zod16.z.string().min(1).max(64);
|
|
4124
4230
|
var PositiveInt = import_zod16.z.number().int().positive();
|
|
@@ -4200,7 +4306,7 @@ var StatementArtifactSchema = import_zod16.z.object({
|
|
|
4200
4306
|
closingBalanceKobo: import_zod16.z.number().int(),
|
|
4201
4307
|
transactionCount: NonNegativeInt,
|
|
4202
4308
|
currency: Currency2,
|
|
4203
|
-
hashChainPrev:
|
|
4309
|
+
hashChainPrev: HexString(32).optional()
|
|
4204
4310
|
}).refine((v) => v.periodEndMs > v.periodStartMs, {
|
|
4205
4311
|
message: "periodEndMs must be greater than periodStartMs",
|
|
4206
4312
|
path: ["periodEndMs"]
|
|
@@ -4233,7 +4339,7 @@ var IdentityArtifactSchema = import_zod16.z.object({
|
|
|
4233
4339
|
"kyc_tier",
|
|
4234
4340
|
"age_band"
|
|
4235
4341
|
]),
|
|
4236
|
-
claimValueHash:
|
|
4342
|
+
claimValueHash: HexString(32),
|
|
4237
4343
|
attestedAtMs: PositiveInt
|
|
4238
4344
|
});
|
|
4239
4345
|
var ARTIFACT_BODY_SCHEMAS = {
|
|
@@ -4297,7 +4403,7 @@ function createArtifactUri(input) {
|
|
|
4297
4403
|
const signed = signArtifact(body, input.privateKey);
|
|
4298
4404
|
return { uri: encodeArtifactUri(signed), signed };
|
|
4299
4405
|
}
|
|
4300
|
-
function verifyArtifactUri(uri,
|
|
4406
|
+
function verifyArtifactUri(uri, publicKeySpkiB64, options = {}) {
|
|
4301
4407
|
const decoded = decodeArtifactUri(uri);
|
|
4302
4408
|
if (!isKnownArtifactType(decoded.type)) {
|
|
4303
4409
|
throw new FlurArtifactError(
|
|
@@ -4319,7 +4425,7 @@ function verifyArtifactUri(uri, publicKey, options = {}) {
|
|
|
4319
4425
|
"INVALID_BODY"
|
|
4320
4426
|
);
|
|
4321
4427
|
}
|
|
4322
|
-
const ok = verifyArtifactSignature(decoded,
|
|
4428
|
+
const ok = verifyArtifactSignature(decoded, publicKeySpkiB64, options);
|
|
4323
4429
|
if (!ok) {
|
|
4324
4430
|
throw new FlurArtifactError(
|
|
4325
4431
|
"Artifact signature verification failed",
|
|
@@ -4396,6 +4502,7 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4396
4502
|
HARDENED_ARTIFACT_TYPES,
|
|
4397
4503
|
IdentityArtifactSchema,
|
|
4398
4504
|
IngestFundingResultSchema,
|
|
4505
|
+
IssueAccountOacInputSchema,
|
|
4399
4506
|
IssueOACInputSchema,
|
|
4400
4507
|
LedgerJournalEntryArtifactSchema,
|
|
4401
4508
|
ListPayoutDestinationsResultSchema,
|
|
@@ -4413,6 +4520,7 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4413
4520
|
OAC_DEFAULT_CUMULATIVE_KOBO,
|
|
4414
4521
|
OAC_DEFAULT_PER_TX_KOBO,
|
|
4415
4522
|
OAC_DEFAULT_VALIDITY_MS,
|
|
4523
|
+
OFFLINE_CLAIM_SMS_PREFIX,
|
|
4416
4524
|
OfflineClaimArtifactSchema,
|
|
4417
4525
|
OfflineHoldRecordSchema,
|
|
4418
4526
|
OfflinePaymentAuthorizationArtifactSchema,
|
|
@@ -4508,20 +4616,21 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4508
4616
|
createPassesClient,
|
|
4509
4617
|
createReceiptArtifactUri,
|
|
4510
4618
|
createReceiptsClient,
|
|
4511
|
-
createSoftwareEd25519Signer,
|
|
4512
4619
|
createSoftwareP256Signer,
|
|
4513
4620
|
decodeArtifactUri,
|
|
4514
4621
|
decodeAuthorizationQR,
|
|
4515
4622
|
decodeBase45,
|
|
4623
|
+
decodeOfflineClaimSmsMessage,
|
|
4516
4624
|
decodePaymentRequestQR,
|
|
4517
4625
|
encodeArtifactUri,
|
|
4518
4626
|
encodeAuthorizationQR,
|
|
4519
4627
|
encodeBase45,
|
|
4520
4628
|
encodeNQR,
|
|
4629
|
+
encodeOfflineClaimSmsMessage,
|
|
4521
4630
|
encodePaymentRequestQR,
|
|
4631
|
+
extractOfflineClaimSmsToken,
|
|
4522
4632
|
formatAmount,
|
|
4523
4633
|
generateDynamicQR,
|
|
4524
|
-
generateKeyPair,
|
|
4525
4634
|
generateStaticQR,
|
|
4526
4635
|
init,
|
|
4527
4636
|
isHardenedArtifactType,
|
|
@@ -4532,13 +4641,10 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4532
4641
|
parseAmountInput,
|
|
4533
4642
|
parseNQR,
|
|
4534
4643
|
parseQR,
|
|
4535
|
-
publicKeyFromPrivate,
|
|
4536
4644
|
readTLV,
|
|
4537
4645
|
routingHint,
|
|
4538
|
-
sign,
|
|
4539
4646
|
signArtifact,
|
|
4540
4647
|
signAuthorization,
|
|
4541
|
-
signCanonical,
|
|
4542
4648
|
signOAC,
|
|
4543
4649
|
signPartnerRequest,
|
|
4544
4650
|
signPass,
|
|
@@ -4546,11 +4652,9 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4546
4652
|
signReceipt,
|
|
4547
4653
|
signRedemption,
|
|
4548
4654
|
signRequestHMAC,
|
|
4549
|
-
verify,
|
|
4550
4655
|
verifyArtifactSignature,
|
|
4551
4656
|
verifyArtifactUri,
|
|
4552
4657
|
verifyAuthorization,
|
|
4553
|
-
verifyCanonical,
|
|
4554
4658
|
verifyClaimSignature,
|
|
4555
4659
|
verifyOAC,
|
|
4556
4660
|
verifyPass,
|