@nokinc-flur/sdk 1.1.4 → 1.1.5

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