@nokinc-flur/sdk 1.1.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +693 -851
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +874 -2092
- package/dist/index.d.ts +874 -2092
- package/dist/index.js +691 -841
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,201 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
|
-
import { z as
|
|
2
|
+
import { z as z4 } from "zod";
|
|
3
3
|
|
|
4
4
|
// src/contracts.ts
|
|
5
|
-
import { z as z2 } from "zod";
|
|
6
|
-
|
|
7
|
-
// src/me-offline/client.ts
|
|
8
5
|
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
|
+
});
|
|
9
199
|
|
|
10
200
|
// src/errors.ts
|
|
11
201
|
var backendErrorCodeSet = /* @__PURE__ */ new Set([
|
|
@@ -109,556 +299,9 @@ async function mapToFlurError(res) {
|
|
|
109
299
|
});
|
|
110
300
|
}
|
|
111
301
|
|
|
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
|
-
|
|
659
302
|
// src/primitives.ts
|
|
660
|
-
import { z as
|
|
661
|
-
var CurrencyCodeSchema =
|
|
303
|
+
import { z as z2 } from "zod";
|
|
304
|
+
var CurrencyCodeSchema = z2.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
662
305
|
var currencyFractionDigits = {
|
|
663
306
|
NGN: 2,
|
|
664
307
|
USD: 2,
|
|
@@ -752,7 +395,7 @@ function moneyMinorToNumber(amountMinor) {
|
|
|
752
395
|
}
|
|
753
396
|
|
|
754
397
|
// src/collections/client.ts
|
|
755
|
-
import { z as
|
|
398
|
+
import { z as z3 } from "zod";
|
|
756
399
|
var MERCHANT_PROFILE_STATUSES = [
|
|
757
400
|
"pending",
|
|
758
401
|
"active",
|
|
@@ -782,172 +425,172 @@ var MERCHANT_PAYOUT_STATUSES = [
|
|
|
782
425
|
"failed",
|
|
783
426
|
"cancelled"
|
|
784
427
|
];
|
|
785
|
-
var MoneyKoboSchema =
|
|
786
|
-
var MetadataSchema =
|
|
787
|
-
|
|
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()])
|
|
788
431
|
);
|
|
789
|
-
var CurrencySchema2 =
|
|
790
|
-
var ReferenceSchema =
|
|
791
|
-
var MerchantProfileSchema =
|
|
792
|
-
accountId:
|
|
793
|
-
legalName:
|
|
794
|
-
tradingName:
|
|
795
|
-
merchantCategoryCode:
|
|
796
|
-
nqrMerchantId:
|
|
797
|
-
settlementBankCode:
|
|
798
|
-
settlementAccountNumber:
|
|
799
|
-
settlementAccountName:
|
|
800
|
-
settlementSchedule:
|
|
801
|
-
status:
|
|
802
|
-
offlineEnabled:
|
|
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(),
|
|
803
446
|
perTxLimitKobo: MoneyKoboSchema,
|
|
804
447
|
dailyLimitKobo: MoneyKoboSchema,
|
|
805
448
|
metadata: MetadataSchema,
|
|
806
|
-
createdAtMs:
|
|
807
|
-
updatedAtMs:
|
|
449
|
+
createdAtMs: z3.number().int().nonnegative(),
|
|
450
|
+
updatedAtMs: z3.number().int().nonnegative()
|
|
808
451
|
});
|
|
809
|
-
var UpsertMerchantProfileInputSchema =
|
|
810
|
-
legalName:
|
|
811
|
-
tradingName:
|
|
812
|
-
merchantCategoryCode:
|
|
813
|
-
nqrMerchantId:
|
|
814
|
-
settlementBankCode:
|
|
815
|
-
settlementAccountNumber:
|
|
816
|
-
settlementAccountName:
|
|
817
|
-
settlementSchedule:
|
|
818
|
-
status:
|
|
819
|
-
offlineEnabled:
|
|
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(),
|
|
820
463
|
perTxLimitKobo: MoneyKoboSchema.optional(),
|
|
821
464
|
dailyLimitKobo: MoneyKoboSchema.optional(),
|
|
822
465
|
metadata: MetadataSchema.optional()
|
|
823
466
|
});
|
|
824
|
-
var CollectionIntentSchema =
|
|
825
|
-
intentId:
|
|
826
|
-
accountId:
|
|
827
|
-
terminalId:
|
|
828
|
-
reference:
|
|
829
|
-
amountKobo:
|
|
830
|
-
currency:
|
|
831
|
-
status:
|
|
832
|
-
description:
|
|
833
|
-
nqrPayload:
|
|
834
|
-
provider:
|
|
835
|
-
providerReference:
|
|
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(),
|
|
836
479
|
metadata: MetadataSchema,
|
|
837
|
-
expiresAtMs:
|
|
838
|
-
paidAtMs:
|
|
839
|
-
createdAtMs:
|
|
840
|
-
updatedAtMs:
|
|
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()
|
|
841
484
|
});
|
|
842
|
-
var CreateCollectionIntentInputSchema =
|
|
485
|
+
var CreateCollectionIntentInputSchema = z3.object({
|
|
843
486
|
reference: ReferenceSchema.optional(),
|
|
844
487
|
amountKobo: MoneyKoboSchema.optional(),
|
|
845
488
|
currency: CurrencySchema2.optional(),
|
|
846
|
-
terminalId:
|
|
847
|
-
terminalLabel:
|
|
848
|
-
merchantCity:
|
|
849
|
-
description:
|
|
850
|
-
expiresAtMs:
|
|
851
|
-
provider:
|
|
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(),
|
|
852
495
|
metadata: MetadataSchema.optional()
|
|
853
496
|
});
|
|
854
|
-
var PublicCollectionIntentSchema =
|
|
855
|
-
intentId:
|
|
856
|
-
reference:
|
|
857
|
-
amountKobo:
|
|
858
|
-
currency:
|
|
859
|
-
status:
|
|
860
|
-
merchantAccountId:
|
|
861
|
-
merchantName:
|
|
862
|
-
merchantCategoryCode:
|
|
863
|
-
description:
|
|
864
|
-
expiresAtMs:
|
|
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()
|
|
865
508
|
});
|
|
866
|
-
var PayCollectionInputSchema =
|
|
509
|
+
var PayCollectionInputSchema = z3.object({
|
|
867
510
|
reference: ReferenceSchema,
|
|
868
511
|
amountKobo: MoneyKoboSchema.optional(),
|
|
869
512
|
currency: CurrencySchema2.optional(),
|
|
870
|
-
idempotencyKey:
|
|
513
|
+
idempotencyKey: z3.string().trim().min(8).max(160).optional()
|
|
871
514
|
});
|
|
872
|
-
var CollectionPaymentSchema =
|
|
873
|
-
paymentId:
|
|
874
|
-
intentId:
|
|
875
|
-
accountId:
|
|
876
|
-
payerUserId:
|
|
877
|
-
merchantOwnerUserId:
|
|
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(),
|
|
878
521
|
amountKobo: MoneyKoboSchema,
|
|
879
|
-
currency:
|
|
880
|
-
status:
|
|
881
|
-
provider:
|
|
882
|
-
providerReference:
|
|
883
|
-
idempotencyKey:
|
|
884
|
-
ledgerRef:
|
|
885
|
-
failureCode:
|
|
886
|
-
failureMessage:
|
|
887
|
-
paidAtMs:
|
|
888
|
-
createdAtMs:
|
|
889
|
-
updatedAtMs:
|
|
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()
|
|
890
533
|
});
|
|
891
|
-
var CollectionPaymentResultSchema =
|
|
534
|
+
var CollectionPaymentResultSchema = z3.object({
|
|
892
535
|
payment: CollectionPaymentSchema,
|
|
893
536
|
intent: CollectionIntentSchema,
|
|
894
|
-
receipt:
|
|
895
|
-
replayed:
|
|
537
|
+
receipt: z3.unknown().optional(),
|
|
538
|
+
replayed: z3.boolean()
|
|
896
539
|
});
|
|
897
|
-
var CollectionReportSummarySchema =
|
|
898
|
-
accountId:
|
|
899
|
-
fromMs:
|
|
900
|
-
toMs:
|
|
901
|
-
currency:
|
|
902
|
-
paidCount:
|
|
903
|
-
paidAmountKobo:
|
|
904
|
-
pendingCount:
|
|
905
|
-
failedCount:
|
|
906
|
-
reversedCount:
|
|
907
|
-
availableBalanceKobo:
|
|
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()
|
|
908
551
|
});
|
|
909
|
-
var CollectionStatementSchema =
|
|
910
|
-
accountId:
|
|
911
|
-
year:
|
|
912
|
-
month:
|
|
913
|
-
currency:
|
|
914
|
-
totalPaidKobo:
|
|
915
|
-
items:
|
|
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)
|
|
916
559
|
});
|
|
917
|
-
var CreatePayoutInputSchema =
|
|
560
|
+
var CreatePayoutInputSchema = z3.object({
|
|
918
561
|
amountKobo: MoneyKoboSchema,
|
|
919
562
|
currency: CurrencySchema2.optional(),
|
|
920
|
-
idempotencyKey:
|
|
563
|
+
idempotencyKey: z3.string().trim().min(8).max(160)
|
|
921
564
|
});
|
|
922
|
-
var MerchantPayoutSchema =
|
|
923
|
-
payoutId:
|
|
924
|
-
accountId:
|
|
565
|
+
var MerchantPayoutSchema = z3.object({
|
|
566
|
+
payoutId: z3.string().uuid(),
|
|
567
|
+
accountId: z3.string().uuid(),
|
|
925
568
|
amountKobo: MoneyKoboSchema,
|
|
926
|
-
currency:
|
|
927
|
-
status:
|
|
928
|
-
idempotencyKey:
|
|
929
|
-
ledgerRef:
|
|
930
|
-
providerReference:
|
|
931
|
-
requestedByUserId:
|
|
932
|
-
failureCode:
|
|
933
|
-
failureMessage:
|
|
934
|
-
createdAtMs:
|
|
935
|
-
updatedAtMs:
|
|
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()
|
|
936
579
|
});
|
|
937
|
-
var ProviderEventInputSchema =
|
|
938
|
-
provider:
|
|
939
|
-
eventId:
|
|
940
|
-
eventType:
|
|
941
|
-
payload:
|
|
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()
|
|
942
585
|
});
|
|
943
|
-
var ProviderEventRecordSchema =
|
|
944
|
-
id:
|
|
945
|
-
provider:
|
|
946
|
-
eventId:
|
|
947
|
-
eventType:
|
|
948
|
-
signatureVerified:
|
|
949
|
-
receivedAtMs:
|
|
950
|
-
processedAtMs:
|
|
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()
|
|
951
594
|
});
|
|
952
595
|
function createCollectionsClient(opts) {
|
|
953
596
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -1462,7 +1105,7 @@ var FlurClient = class {
|
|
|
1462
1105
|
try {
|
|
1463
1106
|
body = requestSchema ? JSON.stringify(requestSchema.parse(input)) : init2.body;
|
|
1464
1107
|
} catch (err) {
|
|
1465
|
-
if (err instanceof
|
|
1108
|
+
if (err instanceof z4.ZodError) {
|
|
1466
1109
|
throw new FlurError("Invalid request payload", "INVALID_REQUEST", {
|
|
1467
1110
|
details: err.flatten()
|
|
1468
1111
|
});
|
|
@@ -1490,7 +1133,7 @@ var FlurClient = class {
|
|
|
1490
1133
|
try {
|
|
1491
1134
|
return responseSchema.parse(payload);
|
|
1492
1135
|
} catch (err) {
|
|
1493
|
-
if (err instanceof
|
|
1136
|
+
if (err instanceof z4.ZodError) {
|
|
1494
1137
|
throw new FlurError(
|
|
1495
1138
|
"SDK contract validation failed",
|
|
1496
1139
|
"INVALID_REQUEST",
|
|
@@ -1921,7 +1564,7 @@ function constantTimeEqual(a, b) {
|
|
|
1921
1564
|
}
|
|
1922
1565
|
|
|
1923
1566
|
// src/offline/oac.ts
|
|
1924
|
-
import { z as
|
|
1567
|
+
import { z as z5 } from "zod";
|
|
1925
1568
|
|
|
1926
1569
|
// src/crypto/p256-issuer.ts
|
|
1927
1570
|
import { p256 } from "@noble/curves/nist";
|
|
@@ -2003,20 +1646,20 @@ function verifyIssuerP256(bytes, signatureB64, issuerPublicKeySpkiB64) {
|
|
|
2003
1646
|
var OAC_DEFAULT_PER_TX_KOBO = 5e5;
|
|
2004
1647
|
var OAC_DEFAULT_CUMULATIVE_KOBO = 2e6;
|
|
2005
1648
|
var OAC_DEFAULT_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
2006
|
-
var
|
|
2007
|
-
var OACSchema =
|
|
2008
|
-
userId:
|
|
2009
|
-
deviceId:
|
|
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),
|
|
2010
1653
|
/** SubjectPublicKeyInfo DER, base64 (P-256). */
|
|
2011
|
-
devicePublicKey:
|
|
2012
|
-
perTxCapKobo:
|
|
2013
|
-
cumulativeCapKobo:
|
|
2014
|
-
validFromMs:
|
|
2015
|
-
validUntilMs:
|
|
2016
|
-
counterSeed:
|
|
2017
|
-
nonce:
|
|
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),
|
|
2018
1661
|
/** ASN.1 DER ECDSA(SHA-256) signature, base64. */
|
|
2019
|
-
issuerSig:
|
|
1662
|
+
issuerSig: Base64Std
|
|
2020
1663
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
2021
1664
|
message: "validUntilMs must be greater than validFromMs"
|
|
2022
1665
|
}).refine((v) => v.perTxCapKobo <= v.cumulativeCapKobo, {
|
|
@@ -2110,19 +1753,19 @@ function decodeBase45(s) {
|
|
|
2110
1753
|
}
|
|
2111
1754
|
|
|
2112
1755
|
// src/offline/messages.ts
|
|
2113
|
-
import { z as
|
|
2114
|
-
var Base64Sig =
|
|
2115
|
-
var OfflinePaymentRequestSchema =
|
|
2116
|
-
reference:
|
|
2117
|
-
amountKobo:
|
|
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(),
|
|
2118
1761
|
merchantOAC: OACSchema,
|
|
2119
|
-
expiresAtMs:
|
|
1762
|
+
expiresAtMs: z6.number().int().positive(),
|
|
2120
1763
|
merchantSig: Base64Sig
|
|
2121
1764
|
});
|
|
2122
|
-
var OfflinePaymentAuthorizationSchema =
|
|
1765
|
+
var OfflinePaymentAuthorizationSchema = z6.object({
|
|
2123
1766
|
request: OfflinePaymentRequestSchema,
|
|
2124
1767
|
payerOAC: OACSchema,
|
|
2125
|
-
payerCounter:
|
|
1768
|
+
payerCounter: z6.number().int().positive(),
|
|
2126
1769
|
payerSig: Base64Sig
|
|
2127
1770
|
});
|
|
2128
1771
|
function buildPaymentRequest(input) {
|
|
@@ -2234,60 +1877,60 @@ function decodeAuthorizationQR(s) {
|
|
|
2234
1877
|
}
|
|
2235
1878
|
|
|
2236
1879
|
// src/offline/settlements.ts
|
|
2237
|
-
import { z as
|
|
1880
|
+
import { z as z7 } from "zod";
|
|
2238
1881
|
import { sha256 } from "@noble/hashes/sha256";
|
|
2239
1882
|
import { bytesToHex } from "@noble/hashes/utils";
|
|
2240
|
-
var OfflineTokenSchema =
|
|
2241
|
-
tokenId:
|
|
2242
|
-
tokenSerial:
|
|
2243
|
-
issuerAccountId:
|
|
2244
|
-
payerUserId:
|
|
2245
|
-
maxAmountKobo:
|
|
2246
|
-
currency:
|
|
2247
|
-
issuedAtMs:
|
|
2248
|
-
expiresAtMs:
|
|
2249
|
-
issuerSig:
|
|
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()
|
|
2250
1893
|
});
|
|
2251
|
-
var PaymentClaimSchema =
|
|
2252
|
-
encounterId:
|
|
2253
|
-
tokenSerial:
|
|
2254
|
-
payerUserId:
|
|
2255
|
-
payeeUserId:
|
|
2256
|
-
payerNonce:
|
|
2257
|
-
payeeNonce:
|
|
2258
|
-
amountKobo:
|
|
2259
|
-
currency:
|
|
2260
|
-
occurredAtMs:
|
|
2261
|
-
completedAtMs:
|
|
2262
|
-
contextId:
|
|
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(),
|
|
2263
1906
|
// Stage 2c: P-256 device keys are now SubjectPublicKeyInfo DER, base64.
|
|
2264
1907
|
// Signatures are ASN.1 DER ECDSA(SHA-256), base64. Backwards-incompatible
|
|
2265
1908
|
// wire change; the backend has the matching widening in offline-settlements
|
|
2266
1909
|
// service + zod schema.
|
|
2267
|
-
payerPubkey:
|
|
2268
|
-
payerSignature:
|
|
2269
|
-
payeePubkey:
|
|
2270
|
-
payeeSignature:
|
|
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()
|
|
2271
1914
|
});
|
|
2272
|
-
var SettlementSchema =
|
|
2273
|
-
settlementId:
|
|
2274
|
-
settlementKey:
|
|
2275
|
-
encounterId:
|
|
2276
|
-
issuerAccountId:
|
|
2277
|
-
tokenSerial:
|
|
2278
|
-
payerUserId:
|
|
2279
|
-
payeeUserId:
|
|
2280
|
-
amountKobo:
|
|
2281
|
-
currency:
|
|
2282
|
-
receiptId:
|
|
2283
|
-
status:
|
|
2284
|
-
issuerSig:
|
|
2285
|
-
createdAtMs:
|
|
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()
|
|
2286
1929
|
});
|
|
2287
|
-
var SettleResponseSchema =
|
|
1930
|
+
var SettleResponseSchema = z7.object({
|
|
2288
1931
|
settlement: SettlementSchema,
|
|
2289
|
-
encounterId:
|
|
2290
|
-
replayed:
|
|
1932
|
+
encounterId: z7.string().regex(/^[0-9a-f]{64}$/i),
|
|
1933
|
+
replayed: z7.boolean()
|
|
2291
1934
|
});
|
|
2292
1935
|
var ENCOUNTER_DOMAIN = "offline:v1:encounter";
|
|
2293
1936
|
async function sha256Hex(input) {
|
|
@@ -2461,7 +2104,7 @@ function createHmacFetch(opts) {
|
|
|
2461
2104
|
}
|
|
2462
2105
|
|
|
2463
2106
|
// src/partner/client.ts
|
|
2464
|
-
import { z as
|
|
2107
|
+
import { z as z8 } from "zod";
|
|
2465
2108
|
import { sha256 as sha2563 } from "@noble/hashes/sha256";
|
|
2466
2109
|
import { hmac as hmac2 } from "@noble/hashes/hmac";
|
|
2467
2110
|
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils";
|
|
@@ -2485,18 +2128,18 @@ var PARTNER_SCOPES = [
|
|
|
2485
2128
|
"partner:payout:write",
|
|
2486
2129
|
"partner:reconciliation:read"
|
|
2487
2130
|
];
|
|
2488
|
-
var ApiCredentialPublicSchema =
|
|
2489
|
-
id:
|
|
2490
|
-
accountId:
|
|
2491
|
-
keyId:
|
|
2492
|
-
scopes:
|
|
2493
|
-
label:
|
|
2494
|
-
createdAtMs:
|
|
2495
|
-
lastUsedAtMs:
|
|
2496
|
-
revokedAtMs:
|
|
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()
|
|
2497
2140
|
});
|
|
2498
2141
|
var MintedApiCredentialSchema = ApiCredentialPublicSchema.extend({
|
|
2499
|
-
secret:
|
|
2142
|
+
secret: z8.string().min(1)
|
|
2500
2143
|
});
|
|
2501
2144
|
var enc = new TextEncoder();
|
|
2502
2145
|
async function sha256Hex2(input) {
|
|
@@ -2653,8 +2296,8 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2653
2296
|
}
|
|
2654
2297
|
return parser(raw);
|
|
2655
2298
|
}
|
|
2656
|
-
const listSchema =
|
|
2657
|
-
items:
|
|
2299
|
+
const listSchema = z8.object({
|
|
2300
|
+
items: z8.array(ApiCredentialPublicSchema)
|
|
2658
2301
|
});
|
|
2659
2302
|
return {
|
|
2660
2303
|
list: (accountId) => call(
|
|
@@ -2679,7 +2322,7 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2679
2322
|
}
|
|
2680
2323
|
|
|
2681
2324
|
// src/passes/pass.ts
|
|
2682
|
-
import { z as
|
|
2325
|
+
import { z as z9 } from "zod";
|
|
2683
2326
|
var PASS_KINDS = [
|
|
2684
2327
|
"ride-ticket",
|
|
2685
2328
|
"transit-pass",
|
|
@@ -2695,39 +2338,39 @@ var PASS_STATES = [
|
|
|
2695
2338
|
"expired",
|
|
2696
2339
|
"revoked"
|
|
2697
2340
|
];
|
|
2698
|
-
var PassMetadataSchema =
|
|
2699
|
-
|
|
2341
|
+
var PassMetadataSchema = z9.record(
|
|
2342
|
+
z9.union([z9.string(), z9.number(), z9.boolean(), z9.null()])
|
|
2700
2343
|
);
|
|
2701
|
-
var PassSchema =
|
|
2702
|
-
passId:
|
|
2344
|
+
var PassSchema = z9.object({
|
|
2345
|
+
passId: z9.string().min(1),
|
|
2703
2346
|
/** Optional client/template grouping id (server may omit). */
|
|
2704
|
-
templateId:
|
|
2347
|
+
templateId: z9.string().min(1).optional(),
|
|
2705
2348
|
/** Optional human-facing holder identity (server may omit). The cryptographic binding
|
|
2706
2349
|
* is `holderDevicePubkey` below. */
|
|
2707
|
-
holderUserId:
|
|
2708
|
-
kind:
|
|
2709
|
-
issuerId:
|
|
2710
|
-
issuedAtMs:
|
|
2711
|
-
validFromMs:
|
|
2712
|
-
validUntilMs:
|
|
2713
|
-
state:
|
|
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),
|
|
2714
2357
|
metadata: PassMetadataSchema,
|
|
2715
|
-
nonce:
|
|
2358
|
+
nonce: z9.string().min(1),
|
|
2716
2359
|
/** Device id this pass is bound to (FK to backend `device_keys`). */
|
|
2717
|
-
holderDeviceId:
|
|
2360
|
+
holderDeviceId: z9.string().min(1),
|
|
2718
2361
|
/** SubjectPublicKeyInfo DER (P-256) of the bound device, base64. The redemption
|
|
2719
2362
|
* signature is verified against this key — it is the security-critical binding. */
|
|
2720
|
-
holderDevicePubkey:
|
|
2363
|
+
holderDevicePubkey: z9.string().min(64).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
|
|
2721
2364
|
/** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
|
|
2722
|
-
amountKobo:
|
|
2365
|
+
amountKobo: z9.number().int().nonnegative().optional(),
|
|
2723
2366
|
/** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
|
|
2724
|
-
currency:
|
|
2367
|
+
currency: z9.string().min(3).max(8),
|
|
2725
2368
|
/** Monotonic redemption counter floor. Redemption.counter MUST be > counterSeed. */
|
|
2726
|
-
counterSeed:
|
|
2369
|
+
counterSeed: z9.number().int().nonnegative(),
|
|
2727
2370
|
/** Optional cumulative spend cap in kobo across all redemptions of this pass. */
|
|
2728
|
-
cumulativeCapKobo:
|
|
2371
|
+
cumulativeCapKobo: z9.number().int().nonnegative().optional(),
|
|
2729
2372
|
/** ASN.1 DER ECDSA P-256 signature, base64. */
|
|
2730
|
-
issuerSig:
|
|
2373
|
+
issuerSig: z9.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
|
|
2731
2374
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
2732
2375
|
message: "validUntilMs must be greater than validFromMs"
|
|
2733
2376
|
});
|
|
@@ -2784,20 +2427,20 @@ function isPassWithinValidity(pass, nowMs) {
|
|
|
2784
2427
|
}
|
|
2785
2428
|
|
|
2786
2429
|
// src/passes/redemption.ts
|
|
2787
|
-
import { z as
|
|
2788
|
-
var
|
|
2789
|
-
var RedemptionSchema =
|
|
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({
|
|
2790
2433
|
pass: PassSchema,
|
|
2791
|
-
redeemerId:
|
|
2792
|
-
redeemedAtMs:
|
|
2434
|
+
redeemerId: z10.string().min(1),
|
|
2435
|
+
redeemedAtMs: z10.number().int().nonnegative(),
|
|
2793
2436
|
/** Strictly monotonic counter scoped to a single pass. Must be > pass.counterSeed
|
|
2794
2437
|
* and > the redeemer's lastSeenCounter for this pass. */
|
|
2795
|
-
counter:
|
|
2438
|
+
counter: z10.number().int().positive(),
|
|
2796
2439
|
/** Amount being redeemed in kobo (0 for non-monetary passes like ride tickets). */
|
|
2797
|
-
amountKobo:
|
|
2798
|
-
nonce:
|
|
2440
|
+
amountKobo: z10.number().int().nonnegative(),
|
|
2441
|
+
nonce: z10.string().min(1),
|
|
2799
2442
|
/** ASN.1 DER ECDSA P-256 signature over canonicalJSONBytes(unsigned), base64. */
|
|
2800
|
-
holderSig:
|
|
2443
|
+
holderSig: Base64Std2
|
|
2801
2444
|
});
|
|
2802
2445
|
var REDEEMABLE_STATES = /* @__PURE__ */ new Set(["issued", "active"]);
|
|
2803
2446
|
function buildRedemption(input) {
|
|
@@ -2879,40 +2522,40 @@ function verifyRedemption(r, issuerPublicKeySpkiB64) {
|
|
|
2879
2522
|
}
|
|
2880
2523
|
|
|
2881
2524
|
// src/receipts/receipt.ts
|
|
2882
|
-
import { z as
|
|
2525
|
+
import { z as z11 } from "zod";
|
|
2883
2526
|
var RECEIPT_CHANNELS = ["cash", "pass"];
|
|
2884
2527
|
var RECEIPT_KINDS = RECEIPT_CHANNELS;
|
|
2885
|
-
var ReceiptPayloadSchema =
|
|
2886
|
-
|
|
2528
|
+
var ReceiptPayloadSchema = z11.record(
|
|
2529
|
+
z11.union([z11.string(), z11.number(), z11.boolean(), z11.null()])
|
|
2887
2530
|
);
|
|
2888
|
-
var ReceiptSchema =
|
|
2889
|
-
receiptId:
|
|
2890
|
-
channel:
|
|
2531
|
+
var ReceiptSchema = z11.object({
|
|
2532
|
+
receiptId: z11.string().min(1),
|
|
2533
|
+
channel: z11.enum(RECEIPT_CHANNELS),
|
|
2891
2534
|
/** Cash-channel: send_intents.id. Required when channel === 'cash'. */
|
|
2892
|
-
intentId:
|
|
2535
|
+
intentId: z11.string().min(1).optional(),
|
|
2893
2536
|
/** Pass-channel: pass_redemptions.id. Required when channel === 'pass'. */
|
|
2894
|
-
passRedemptionId:
|
|
2895
|
-
payerUserId:
|
|
2896
|
-
payeeUserId:
|
|
2897
|
-
amountKobo:
|
|
2898
|
-
currency:
|
|
2899
|
-
issuedAtMs:
|
|
2900
|
-
issuerId:
|
|
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),
|
|
2901
2544
|
payload: ReceiptPayloadSchema,
|
|
2902
2545
|
/** ASN.1 DER ECDSA P-256 signature, base64. */
|
|
2903
|
-
issuerSig:
|
|
2546
|
+
issuerSig: z11.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
|
|
2904
2547
|
}).superRefine((v, ctx) => {
|
|
2905
2548
|
if (v.channel === "cash") {
|
|
2906
2549
|
if (!v.intentId) {
|
|
2907
2550
|
ctx.addIssue({
|
|
2908
|
-
code:
|
|
2551
|
+
code: z11.ZodIssueCode.custom,
|
|
2909
2552
|
message: "cash receipts require intentId",
|
|
2910
2553
|
path: ["intentId"]
|
|
2911
2554
|
});
|
|
2912
2555
|
}
|
|
2913
2556
|
if (v.passRedemptionId) {
|
|
2914
2557
|
ctx.addIssue({
|
|
2915
|
-
code:
|
|
2558
|
+
code: z11.ZodIssueCode.custom,
|
|
2916
2559
|
message: "cash receipts must not carry passRedemptionId",
|
|
2917
2560
|
path: ["passRedemptionId"]
|
|
2918
2561
|
});
|
|
@@ -2920,14 +2563,14 @@ var ReceiptSchema = z12.object({
|
|
|
2920
2563
|
} else if (v.channel === "pass") {
|
|
2921
2564
|
if (!v.passRedemptionId) {
|
|
2922
2565
|
ctx.addIssue({
|
|
2923
|
-
code:
|
|
2566
|
+
code: z11.ZodIssueCode.custom,
|
|
2924
2567
|
message: "pass receipts require passRedemptionId",
|
|
2925
2568
|
path: ["passRedemptionId"]
|
|
2926
2569
|
});
|
|
2927
2570
|
}
|
|
2928
2571
|
if (v.intentId) {
|
|
2929
2572
|
ctx.addIssue({
|
|
2930
|
-
code:
|
|
2573
|
+
code: z11.ZodIssueCode.custom,
|
|
2931
2574
|
message: "pass receipts must not carry intentId",
|
|
2932
2575
|
path: ["intentId"]
|
|
2933
2576
|
});
|
|
@@ -3229,23 +2872,23 @@ function init(opts) {
|
|
|
3229
2872
|
}
|
|
3230
2873
|
|
|
3231
2874
|
// src/accounts/client.ts
|
|
3232
|
-
import { z as
|
|
2875
|
+
import { z as z12 } from "zod";
|
|
3233
2876
|
var ACCOUNT_TYPES = ["personal", "business", "partner"];
|
|
3234
2877
|
var ACCOUNT_STATUSES = ["active", "suspended", "closed"];
|
|
3235
2878
|
var MEMBERSHIP_ROLES = ["owner", "admin", "driver", "staff"];
|
|
3236
|
-
var AccountSchema =
|
|
3237
|
-
accountId:
|
|
3238
|
-
type:
|
|
3239
|
-
displayName:
|
|
3240
|
-
status:
|
|
3241
|
-
ownerUserId:
|
|
3242
|
-
createdAtMs:
|
|
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()
|
|
3243
2886
|
});
|
|
3244
|
-
var AccountMembershipSchema =
|
|
3245
|
-
accountId:
|
|
3246
|
-
userId:
|
|
3247
|
-
role:
|
|
3248
|
-
createdAtMs:
|
|
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()
|
|
3249
2892
|
});
|
|
3250
2893
|
function createAccountsClient(opts) {
|
|
3251
2894
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -3278,9 +2921,9 @@ function createAccountsClient(opts) {
|
|
|
3278
2921
|
}
|
|
3279
2922
|
return parser(raw);
|
|
3280
2923
|
}
|
|
3281
|
-
const itemsSchema =
|
|
3282
|
-
const memberItemsSchema =
|
|
3283
|
-
items:
|
|
2924
|
+
const itemsSchema = z12.object({ items: z12.array(AccountSchema) });
|
|
2925
|
+
const memberItemsSchema = z12.object({
|
|
2926
|
+
items: z12.array(AccountMembershipSchema)
|
|
3284
2927
|
});
|
|
3285
2928
|
return {
|
|
3286
2929
|
listMyAccounts: () => call(
|
|
@@ -3311,6 +2954,221 @@ function createAccountsClient(opts) {
|
|
|
3311
2954
|
};
|
|
3312
2955
|
}
|
|
3313
2956
|
|
|
2957
|
+
// src/me-offline/client.ts
|
|
2958
|
+
import { z as z13 } from "zod";
|
|
2959
|
+
var Sha256Hex = z13.string().regex(/^[0-9a-f]{64}$/);
|
|
2960
|
+
var Base64Std3 = z13.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
2961
|
+
var ACCOUNT_FUNDED_OAC_MAX_TTL_MS = 1e3 * 60 * 60 * 24 * 7;
|
|
2962
|
+
var CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS = 1e3 * 60 * 60 * 24;
|
|
2963
|
+
var AttestationSecurityLevelSchema = z13.enum([
|
|
2964
|
+
"STRONGBOX",
|
|
2965
|
+
"TEE",
|
|
2966
|
+
"SECURE_ENCLAVE",
|
|
2967
|
+
"SOFTWARE"
|
|
2968
|
+
]);
|
|
2969
|
+
var DeviceKeyAlgSchema = z13.literal("p256");
|
|
2970
|
+
var RegisterDeviceKeyP256InputSchema = z13.object({
|
|
2971
|
+
deviceId: z13.string().min(1).max(128),
|
|
2972
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
2973
|
+
publicKeySpkiB64: Base64Std3.min(64).max(4096),
|
|
2974
|
+
/** Base64 of the server-issued enrollment challenge string. */
|
|
2975
|
+
challengeB64: Base64Std3.min(8).max(1024),
|
|
2976
|
+
/** iOS App Attest payload or Android X.509 Key Attestation chain. */
|
|
2977
|
+
attestationChainB64: z13.array(Base64Std3.min(16).max(16384)).min(1).max(16),
|
|
2978
|
+
securityLevel: AttestationSecurityLevelSchema
|
|
2979
|
+
});
|
|
2980
|
+
var P256EnrollmentChallengeInputSchema = z13.object({
|
|
2981
|
+
deviceId: z13.string().min(1).max(128)
|
|
2982
|
+
});
|
|
2983
|
+
var P256EnrollmentChallengeResultSchema = z13.object({
|
|
2984
|
+
challenge: z13.string().min(16),
|
|
2985
|
+
expiresAtMs: z13.number().int().positive()
|
|
2986
|
+
});
|
|
2987
|
+
var DeviceKeyRecordSchema = z13.object({
|
|
2988
|
+
id: z13.string().uuid(),
|
|
2989
|
+
userId: z13.string().uuid(),
|
|
2990
|
+
deviceId: z13.string(),
|
|
2991
|
+
/** Always 'p256' on the consumer offline rail. Field retained for forward-compat. */
|
|
2992
|
+
alg: DeviceKeyAlgSchema.default("p256"),
|
|
2993
|
+
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
2994
|
+
publicKeySpkiB64: Base64Std3.nullable().default(null),
|
|
2995
|
+
securityLevel: AttestationSecurityLevelSchema.nullable().default(null),
|
|
2996
|
+
hardwareBacked: z13.boolean().default(false),
|
|
2997
|
+
attestedAtMs: z13.number().int().nonnegative().nullable().default(null),
|
|
2998
|
+
createdAtMs: z13.number().int().nonnegative(),
|
|
2999
|
+
revokedAtMs: z13.number().int().nonnegative().nullable()
|
|
3000
|
+
});
|
|
3001
|
+
var ConsumerOACSchema = z13.object({
|
|
3002
|
+
oacId: z13.string().uuid(),
|
|
3003
|
+
issuerId: z13.string().min(1).max(64),
|
|
3004
|
+
userId: z13.string().uuid(),
|
|
3005
|
+
deviceId: z13.string().min(1).max(128),
|
|
3006
|
+
/**
|
|
3007
|
+
* Always 'p256'. Required on the wire (backend always emits it).
|
|
3008
|
+
* Kept as a literal so input/output infer identically and the schema
|
|
3009
|
+
* can be nested inside other response shapes without Zod input/output
|
|
3010
|
+
* divergence under tsup DTS bundling.
|
|
3011
|
+
*/
|
|
3012
|
+
alg: z13.literal("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(["active", "superseded", "expired", "revoked"]),
|
|
3033
|
+
supersededAtMs: z13.number().int().nonnegative().nullable(),
|
|
3034
|
+
revokedAtMs: z13.number().int().nonnegative().nullable()
|
|
3035
|
+
});
|
|
3036
|
+
var IssueAccountOacInputSchema = z13.object({
|
|
3037
|
+
deviceId: z13.string().min(1).max(128),
|
|
3038
|
+
perTxCapKobo: z13.number().int().positive().optional(),
|
|
3039
|
+
cumulativeCapKobo: z13.number().int().positive().optional(),
|
|
3040
|
+
ttlMs: z13.number().int().min(6e4).max(ACCOUNT_FUNDED_OAC_MAX_TTL_MS).optional()
|
|
3041
|
+
});
|
|
3042
|
+
var OfflineStatusResultSchema = z13.object({
|
|
3043
|
+
active: OACRecordSchema.nullable()
|
|
3044
|
+
});
|
|
3045
|
+
var ConsumerPaymentClaimSchema = z13.object({
|
|
3046
|
+
/** Always 'p256'. Retained for forward-compat and as an explicit domain marker. */
|
|
3047
|
+
alg: z13.literal("p256").default("p256"),
|
|
3048
|
+
oacId: z13.string().uuid(),
|
|
3049
|
+
encounterId: Sha256Hex.optional(),
|
|
3050
|
+
payerUserId: z13.string().uuid(),
|
|
3051
|
+
payeeUserId: z13.string().uuid(),
|
|
3052
|
+
payerDeviceId: z13.string().min(1).max(128),
|
|
3053
|
+
payerNonce: z13.string().min(8).max(128),
|
|
3054
|
+
payeeNonce: z13.string().min(8).max(128),
|
|
3055
|
+
amountKobo: z13.number().int().positive(),
|
|
3056
|
+
currency: z13.string().length(3).default("NGN"),
|
|
3057
|
+
occurredAtMs: z13.number().int().nonnegative(),
|
|
3058
|
+
completedAtMs: z13.number().int().nonnegative().optional(),
|
|
3059
|
+
contextId: z13.string().max(128).optional(),
|
|
3060
|
+
payerPubkeySpkiB64: Base64Std3.min(64).max(4096),
|
|
3061
|
+
payerSignatureDerB64: Base64Std3.min(16).max(2048),
|
|
3062
|
+
payeePubkeySpkiB64: Base64Std3.min(64).max(4096).optional(),
|
|
3063
|
+
payeeSignatureDerB64: Base64Std3.min(16).max(2048).optional()
|
|
3064
|
+
});
|
|
3065
|
+
var ConsumerSettlementSchema = z13.object({
|
|
3066
|
+
settlementId: z13.string().uuid(),
|
|
3067
|
+
settlementKey: Sha256Hex,
|
|
3068
|
+
encounterId: Sha256Hex,
|
|
3069
|
+
oacId: z13.string().uuid(),
|
|
3070
|
+
payerUserId: z13.string().uuid(),
|
|
3071
|
+
payeeUserId: z13.string().uuid(),
|
|
3072
|
+
amountKobo: z13.number().int().positive(),
|
|
3073
|
+
currency: z13.string().length(3),
|
|
3074
|
+
status: z13.enum(["SETTLED", "REVIEW"]),
|
|
3075
|
+
reviewReason: z13.string().nullable(),
|
|
3076
|
+
ledgerRef: z13.string().nullable(),
|
|
3077
|
+
/** ASN.1 DER ECDSA P-256 issuer signature, base64. */
|
|
3078
|
+
issuerSig: Base64Std3.min(16).max(2048),
|
|
3079
|
+
createdAtMs: z13.number().int().nonnegative()
|
|
3080
|
+
});
|
|
3081
|
+
var ConsumerSettleResultSchema = z13.object({
|
|
3082
|
+
settlement: ConsumerSettlementSchema,
|
|
3083
|
+
encounterId: Sha256Hex,
|
|
3084
|
+
replayed: z13.boolean()
|
|
3085
|
+
});
|
|
3086
|
+
var RevokeDeviceKeyInputSchema = z13.object({
|
|
3087
|
+
deviceId: z13.string().min(1).max(128),
|
|
3088
|
+
/** Step-up token from /api/v1/auth/send/verify with purpose='offline_revoke'. */
|
|
3089
|
+
sendAuthToken: z13.string().min(16)
|
|
3090
|
+
});
|
|
3091
|
+
function createMeOfflineClient(opts) {
|
|
3092
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
3093
|
+
if (!fetchImpl) {
|
|
3094
|
+
throw new Error("createMeOfflineClient: no fetch implementation available");
|
|
3095
|
+
}
|
|
3096
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
3097
|
+
async function call(method, path, body, parser) {
|
|
3098
|
+
const init2 = {
|
|
3099
|
+
method,
|
|
3100
|
+
headers: {
|
|
3101
|
+
"content-type": "application/json",
|
|
3102
|
+
accept: "application/json"
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
3106
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
3107
|
+
const text = await resp.text();
|
|
3108
|
+
let raw = void 0;
|
|
3109
|
+
if (text) {
|
|
3110
|
+
try {
|
|
3111
|
+
raw = JSON.parse(text);
|
|
3112
|
+
} catch {
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
if (!resp.ok) {
|
|
3116
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
3117
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
3118
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
3119
|
+
}
|
|
3120
|
+
return parser(raw);
|
|
3121
|
+
}
|
|
3122
|
+
const deviceKeyItems = z13.object({ items: z13.array(DeviceKeyRecordSchema) });
|
|
3123
|
+
return {
|
|
3124
|
+
issueP256EnrollmentChallenge: (input) => call(
|
|
3125
|
+
"POST",
|
|
3126
|
+
"/v1/me/offline/keys/p256/challenge",
|
|
3127
|
+
P256EnrollmentChallengeInputSchema.parse(input),
|
|
3128
|
+
(raw) => P256EnrollmentChallengeResultSchema.parse(raw)
|
|
3129
|
+
),
|
|
3130
|
+
registerDeviceKeyP256: (input) => call(
|
|
3131
|
+
"POST",
|
|
3132
|
+
"/v1/me/offline/keys/p256",
|
|
3133
|
+
RegisterDeviceKeyP256InputSchema.parse(input),
|
|
3134
|
+
(raw) => DeviceKeyRecordSchema.parse(raw)
|
|
3135
|
+
),
|
|
3136
|
+
listDeviceKeys: () => call(
|
|
3137
|
+
"GET",
|
|
3138
|
+
"/v1/me/offline/keys",
|
|
3139
|
+
void 0,
|
|
3140
|
+
(raw) => deviceKeyItems.parse(raw)
|
|
3141
|
+
),
|
|
3142
|
+
revokeDeviceKey: (input) => call(
|
|
3143
|
+
"POST",
|
|
3144
|
+
"/v1/me/offline/keys/revoke",
|
|
3145
|
+
RevokeDeviceKeyInputSchema.parse(input),
|
|
3146
|
+
() => void 0
|
|
3147
|
+
),
|
|
3148
|
+
issueAccountOac: (input) => call(
|
|
3149
|
+
"POST",
|
|
3150
|
+
"/v1/me/offline/oac",
|
|
3151
|
+
IssueAccountOacInputSchema.parse(input),
|
|
3152
|
+
(raw) => OACRecordSchema.parse(raw)
|
|
3153
|
+
),
|
|
3154
|
+
getStatus: (deviceId) => {
|
|
3155
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
3156
|
+
return call(
|
|
3157
|
+
"GET",
|
|
3158
|
+
`/v1/me/offline/status${qs}`,
|
|
3159
|
+
void 0,
|
|
3160
|
+
(raw) => OfflineStatusResultSchema.parse(raw)
|
|
3161
|
+
);
|
|
3162
|
+
},
|
|
3163
|
+
submitClaim: (claim) => call(
|
|
3164
|
+
"POST",
|
|
3165
|
+
"/v1/me/offline/claims",
|
|
3166
|
+
ConsumerPaymentClaimSchema.parse(claim),
|
|
3167
|
+
(raw) => ConsumerSettleResultSchema.parse(raw)
|
|
3168
|
+
)
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3314
3172
|
// src/me-offline/signer.ts
|
|
3315
3173
|
import { p256 as p2562 } from "@noble/curves/nist";
|
|
3316
3174
|
var CLAIM_DOMAIN_V2 = "flur:consumer-offline:v2:claim";
|
|
@@ -4217,6 +4075,7 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4217
4075
|
});
|
|
4218
4076
|
}
|
|
4219
4077
|
export {
|
|
4078
|
+
ACCOUNT_FUNDED_OAC_MAX_TTL_MS,
|
|
4220
4079
|
ACCOUNT_STATUSES,
|
|
4221
4080
|
ACCOUNT_TYPES,
|
|
4222
4081
|
ADDITIONAL_DATA_SUBFIELD,
|
|
@@ -4230,6 +4089,7 @@ export {
|
|
|
4230
4089
|
CLAIM_DOMAIN_V2,
|
|
4231
4090
|
COLLECTION_INTENT_STATUSES,
|
|
4232
4091
|
COLLECTION_PAYMENT_STATUSES,
|
|
4092
|
+
CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS,
|
|
4233
4093
|
CUSTODIAL_MODES,
|
|
4234
4094
|
CollectionIntentSchema,
|
|
4235
4095
|
CollectionPaymentResultSchema,
|
|
@@ -4248,10 +4108,6 @@ export {
|
|
|
4248
4108
|
CreateWithdrawalResultSchema,
|
|
4249
4109
|
DeviceKeyAlgSchema,
|
|
4250
4110
|
DeviceKeyRecordSchema,
|
|
4251
|
-
DisableOfflineInputSchema,
|
|
4252
|
-
DisableOfflineResultSchema,
|
|
4253
|
-
EnableOfflineInputSchema,
|
|
4254
|
-
EnableOfflineResultSchema,
|
|
4255
4111
|
FIELD,
|
|
4256
4112
|
FLUR_ARTIFACT_URI_PREFIX,
|
|
4257
4113
|
FLUR_ARTIFACT_URI_SCHEME,
|
|
@@ -4267,7 +4123,6 @@ export {
|
|
|
4267
4123
|
IdentityArtifactSchema,
|
|
4268
4124
|
IngestFundingResultSchema,
|
|
4269
4125
|
IssueAccountOacInputSchema,
|
|
4270
|
-
IssueOACInputSchema,
|
|
4271
4126
|
LedgerJournalEntryArtifactSchema,
|
|
4272
4127
|
ListPayoutDestinationsResultSchema,
|
|
4273
4128
|
MEMBERSHIP_ROLES,
|
|
@@ -4286,11 +4141,9 @@ export {
|
|
|
4286
4141
|
OAC_DEFAULT_VALIDITY_MS,
|
|
4287
4142
|
OFFLINE_CLAIM_SMS_PREFIX,
|
|
4288
4143
|
OfflineClaimArtifactSchema,
|
|
4289
|
-
OfflineHoldRecordSchema,
|
|
4290
4144
|
OfflinePaymentAuthorizationArtifactSchema,
|
|
4291
4145
|
OfflinePaymentAuthorizationSchema,
|
|
4292
4146
|
OfflinePaymentRequestSchema,
|
|
4293
|
-
OfflineStateResultSchema,
|
|
4294
4147
|
OfflineStatusResultSchema,
|
|
4295
4148
|
OfflineTokenSchema,
|
|
4296
4149
|
P256EnrollmentChallengeInputSchema,
|
|
@@ -4318,8 +4171,6 @@ export {
|
|
|
4318
4171
|
PayoutEventInputSchema,
|
|
4319
4172
|
ProviderEventInputSchema,
|
|
4320
4173
|
ProviderEventRecordSchema,
|
|
4321
|
-
ProvisionOfflineAllowanceInputSchema,
|
|
4322
|
-
ProvisionOfflineAllowanceResultSchema,
|
|
4323
4174
|
PublicCollectionIntentSchema,
|
|
4324
4175
|
RECEIPT_CHANNELS,
|
|
4325
4176
|
RECEIPT_KINDS,
|
|
@@ -4330,7 +4181,6 @@ export {
|
|
|
4330
4181
|
ReconciliationReportSchema,
|
|
4331
4182
|
RecordPayoutEventResultSchema,
|
|
4332
4183
|
RedemptionSchema,
|
|
4333
|
-
RegisterDeviceKeyInputSchema,
|
|
4334
4184
|
RegisterDeviceKeyP256InputSchema,
|
|
4335
4185
|
ReversalRecordArtifactSchema,
|
|
4336
4186
|
RevokeDeviceKeyInputSchema,
|