@nokinc-flur/sdk 2.0.0 → 2.1.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.js CHANGED
@@ -2958,6 +2958,9 @@ function createAccountsClient(opts) {
2958
2958
  import { z as z13 } from "zod";
2959
2959
  var Sha256Hex = z13.string().regex(/^[0-9a-f]{64}$/);
2960
2960
  var Base64Std3 = z13.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
2961
+ var ClaimNonce = z13.string().min(8).max(128).refine((value) => !value.includes("|"), {
2962
+ message: "nonce must not contain |"
2963
+ });
2961
2964
  var ACCOUNT_FUNDED_OAC_MAX_TTL_MS = 1e3 * 60 * 60 * 24 * 7;
2962
2965
  var CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS = 1e3 * 60 * 60 * 24;
2963
2966
  var AttestationSecurityLevelSchema = z13.enum([
@@ -3050,13 +3053,25 @@ var ConsumerPaymentClaimSchema = z13.object({
3050
3053
  payerUserId: z13.string().uuid(),
3051
3054
  payeeUserId: z13.string().uuid(),
3052
3055
  payerDeviceId: z13.string().min(1).max(128),
3053
- payerNonce: z13.string().min(8).max(128),
3054
- payeeNonce: z13.string().min(8).max(128),
3056
+ payerNonce: ClaimNonce,
3057
+ payeeNonce: ClaimNonce,
3055
3058
  amountKobo: z13.number().int().positive(),
3056
3059
  currency: z13.string().length(3).default("NGN"),
3057
3060
  occurredAtMs: z13.number().int().nonnegative(),
3058
3061
  completedAtMs: z13.number().int().nonnegative().optional(),
3059
3062
  contextId: z13.string().max(128).optional(),
3063
+ requestId: z13.string().uuid().optional(),
3064
+ requestMode: z13.enum(["fixed", "editable"]).optional(),
3065
+ requestTakerUserId: z13.string().uuid().optional(),
3066
+ requestAmountKobo: z13.number().int().positive().optional(),
3067
+ requestCurrency: z13.string().length(3).optional(),
3068
+ requestReference: z13.string().max(128).nullable().optional(),
3069
+ requestCreatedAtMs: z13.number().int().nonnegative().optional(),
3070
+ requestExpiresAtMs: z13.number().int().positive().optional(),
3071
+ requestNonce: z13.string().min(8).max(128).optional(),
3072
+ requestTakerDeviceId: z13.string().min(1).max(128).nullable().optional(),
3073
+ requestTakerPubkeySpkiB64: Base64Std3.min(64).max(4096).optional(),
3074
+ requestTakerSignatureDerB64: Base64Std3.min(16).max(2048).optional(),
3060
3075
  payerPubkeySpkiB64: Base64Std3.min(64).max(4096),
3061
3076
  payerSignatureDerB64: Base64Std3.min(16).max(2048),
3062
3077
  payeePubkeySpkiB64: Base64Std3.min(64).max(4096).optional(),
@@ -3076,7 +3091,10 @@ var ConsumerSettlementSchema = z13.object({
3076
3091
  ledgerRef: z13.string().nullable(),
3077
3092
  /** ASN.1 DER ECDSA P-256 issuer signature, base64. */
3078
3093
  issuerSig: Base64Std3.min(16).max(2048),
3079
- createdAtMs: z13.number().int().nonnegative()
3094
+ /** Canonical millisecond timestamp signed into the settlement receipt. */
3095
+ issuedAtMs: z13.number().int().nonnegative(),
3096
+ /** Compatibility alias for API consumers that predate issuedAtMs. */
3097
+ createdAtMs: z13.number().int().nonnegative().optional()
3080
3098
  });
3081
3099
  var ConsumerSettleResultSchema = z13.object({
3082
3100
  settlement: ConsumerSettlementSchema,
@@ -3165,13 +3183,21 @@ function createMeOfflineClient(opts) {
3165
3183
  "/v1/me/offline/claims",
3166
3184
  ConsumerPaymentClaimSchema.parse(claim),
3167
3185
  (raw) => ConsumerSettleResultSchema.parse(raw)
3186
+ ),
3187
+ getSettlement: (idOrKey) => call(
3188
+ "GET",
3189
+ `/v1/me/offline/settlements/${encodeURIComponent(idOrKey)}`,
3190
+ void 0,
3191
+ (raw) => ConsumerSettlementSchema.parse(raw)
3168
3192
  )
3169
3193
  };
3170
3194
  }
3171
3195
 
3172
3196
  // src/me-offline/signer.ts
3173
3197
  import { p256 as p2562 } from "@noble/curves/nist";
3198
+ import { sha256 as sha2564 } from "@noble/hashes/sha2";
3174
3199
  var CLAIM_DOMAIN_V2 = "flur:consumer-offline:v2:claim";
3200
+ var ENCOUNTER_DOMAIN2 = "flur:consumer-offline:v1:encounter";
3175
3201
  function canonicalClaimSigningPayload(claim) {
3176
3202
  return {
3177
3203
  domain: CLAIM_DOMAIN_V2,
@@ -3192,6 +3218,27 @@ function canonicalClaimSigningPayload(claim) {
3192
3218
  function canonicalClaimSigningBytes(claim) {
3193
3219
  return canonicalJSONBytes(canonicalClaimSigningPayload(claim));
3194
3220
  }
3221
+ function computeConsumerClaimEncounterId(input) {
3222
+ const material = `${ENCOUNTER_DOMAIN2}|${[
3223
+ assertEncounterPart("oacId", input.oacId),
3224
+ assertEncounterPart("payerUserId", input.payerUserId),
3225
+ assertEncounterPart("payeeUserId", input.payeeUserId),
3226
+ assertEncounterPart("payerNonce", input.payerNonce),
3227
+ assertEncounterPart("payeeNonce", input.payeeNonce)
3228
+ ].join("|")}`;
3229
+ return bytesToHex5(sha2564(new TextEncoder().encode(material)));
3230
+ }
3231
+ function assertEncounterPart(field, value) {
3232
+ if (value.includes("|")) {
3233
+ throw new Error(`consumer encounter id ${field} must not contain |`);
3234
+ }
3235
+ return value;
3236
+ }
3237
+ function bytesToHex5(bytes) {
3238
+ let out = "";
3239
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
3240
+ return out;
3241
+ }
3195
3242
  function bytesToBase642(bytes) {
3196
3243
  if (typeof Buffer !== "undefined") {
3197
3244
  return Buffer.from(bytes).toString("base64");
@@ -3287,38 +3334,192 @@ function verifyClaimSignature(input) {
3287
3334
  }
3288
3335
  }
3289
3336
 
3290
- // src/me-offline/sms.ts
3291
- var OFFLINE_CLAIM_SMS_PREFIX = "FLURC1.";
3292
- var TOKEN_RE = /(?:^|\s)(FLURC1\.[A-Za-z0-9_-]+={0,2})(?:\s|$)/;
3293
- function encodeOfflineClaimSmsMessage(claim) {
3294
- const parsed = ConsumerPaymentClaimSchema.parse(claim);
3295
- const json = JSON.stringify(parsed);
3296
- return `${OFFLINE_CLAIM_SMS_PREFIX}${base64UrlEncodeUtf8(json)}`;
3337
+ // src/me-offline/request.ts
3338
+ import { z as z14 } from "zod";
3339
+ var Base64Std4 = z14.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
3340
+ var CONSUMER_PAYMENT_REQUEST_DOMAIN = "flur:consumer-offline:v1:request";
3341
+ var ConsumerPaymentRequestEnvelopeSchema = z14.object({
3342
+ requestId: z14.string().uuid(),
3343
+ mode: z14.enum(["fixed", "editable"]),
3344
+ takerUserId: z14.string().uuid(),
3345
+ amountKobo: z14.number().int().positive(),
3346
+ currency: z14.string().length(3).default("NGN"),
3347
+ reference: z14.string().max(128).nullable().default(null),
3348
+ createdAtMs: z14.number().int().nonnegative(),
3349
+ expiresAtMs: z14.number().int().positive(),
3350
+ nonce: z14.string().min(8).max(128),
3351
+ takerDeviceId: z14.string().min(1).max(128).nullable().default(null),
3352
+ takerPubkeySpkiB64: Base64Std4.min(64).max(4096).optional(),
3353
+ takerSignatureDerB64: Base64Std4.min(16).max(2048).optional()
3354
+ }).superRefine((value, ctx) => {
3355
+ if (value.expiresAtMs <= value.createdAtMs) {
3356
+ ctx.addIssue({
3357
+ code: z14.ZodIssueCode.custom,
3358
+ path: ["expiresAtMs"],
3359
+ message: "expiresAtMs must be greater than createdAtMs"
3360
+ });
3361
+ }
3362
+ const hasSignature = Boolean(
3363
+ value.takerPubkeySpkiB64 || value.takerSignatureDerB64
3364
+ );
3365
+ if (value.mode === "fixed" || hasSignature) {
3366
+ if (!value.takerDeviceId) {
3367
+ ctx.addIssue({
3368
+ code: z14.ZodIssueCode.custom,
3369
+ path: ["takerDeviceId"],
3370
+ message: "signed requests require takerDeviceId"
3371
+ });
3372
+ }
3373
+ if (!value.takerPubkeySpkiB64) {
3374
+ ctx.addIssue({
3375
+ code: z14.ZodIssueCode.custom,
3376
+ path: ["takerPubkeySpkiB64"],
3377
+ message: "signed requests require takerPubkeySpkiB64"
3378
+ });
3379
+ }
3380
+ if (!value.takerSignatureDerB64) {
3381
+ ctx.addIssue({
3382
+ code: z14.ZodIssueCode.custom,
3383
+ path: ["takerSignatureDerB64"],
3384
+ message: "signed requests require takerSignatureDerB64"
3385
+ });
3386
+ }
3387
+ }
3388
+ });
3389
+ function buildConsumerPaymentRequest(input) {
3390
+ const unsigned = {
3391
+ requestId: input.requestId,
3392
+ mode: input.mode,
3393
+ takerUserId: input.takerUserId,
3394
+ amountKobo: input.amountKobo,
3395
+ currency: input.currency ?? "NGN",
3396
+ reference: input.reference ?? null,
3397
+ createdAtMs: input.createdAtMs,
3398
+ expiresAtMs: input.expiresAtMs,
3399
+ nonce: input.nonce,
3400
+ takerDeviceId: input.takerDeviceId ?? null
3401
+ };
3402
+ if (unsigned.mode === "fixed" && !unsigned.takerDeviceId) {
3403
+ throw new Error("fixed requests require takerDeviceId");
3404
+ }
3405
+ if (unsigned.expiresAtMs <= unsigned.createdAtMs) {
3406
+ throw new Error("expiresAtMs must be greater than createdAtMs");
3407
+ }
3408
+ return unsigned;
3297
3409
  }
3298
- function decodeOfflineClaimSmsMessage(message) {
3299
- const token = extractOfflineClaimSmsToken(message);
3300
- if (!token) {
3301
- throw new Error("offline claim SMS token not found");
3410
+ function consumerPaymentRequestSigningPayload(request) {
3411
+ return {
3412
+ domain: CONSUMER_PAYMENT_REQUEST_DOMAIN,
3413
+ version: 1,
3414
+ requestId: request.requestId,
3415
+ mode: request.mode,
3416
+ takerUserId: request.takerUserId,
3417
+ amountKobo: request.amountKobo,
3418
+ currency: request.currency,
3419
+ reference: request.reference ?? null,
3420
+ createdAtMs: request.createdAtMs,
3421
+ expiresAtMs: request.expiresAtMs,
3422
+ nonce: request.nonce,
3423
+ takerDeviceId: request.takerDeviceId ?? null
3424
+ };
3425
+ }
3426
+ function consumerPaymentRequestSigningBytes(request) {
3427
+ return canonicalJSONBytes(consumerPaymentRequestSigningPayload(request));
3428
+ }
3429
+ async function signConsumerPaymentRequest(unsigned, signer) {
3430
+ if (signer.alg !== "p256") {
3431
+ throw new Error("consumer payment requests require p256 signer");
3302
3432
  }
3303
- const encoded = token.slice(OFFLINE_CLAIM_SMS_PREFIX.length);
3433
+ const publicKey = await signer.getPublicKey();
3434
+ if (publicKey.alg !== "p256") {
3435
+ throw new Error("consumer payment requests require p256 public key");
3436
+ }
3437
+ const signature = await signer.sign(
3438
+ consumerPaymentRequestSigningBytes(unsigned)
3439
+ );
3440
+ return ConsumerPaymentRequestEnvelopeSchema.parse({
3441
+ ...unsigned,
3442
+ takerPubkeySpkiB64: publicKey.publicKey,
3443
+ takerSignatureDerB64: signature.signature
3444
+ });
3445
+ }
3446
+ function verifyConsumerPaymentRequest(request) {
3447
+ const parsed = ConsumerPaymentRequestEnvelopeSchema.safeParse(request);
3448
+ if (!parsed.success) return false;
3449
+ const value = parsed.data;
3450
+ if (!value.takerPubkeySpkiB64 || !value.takerSignatureDerB64) return false;
3451
+ return verifyClaimSignature({
3452
+ alg: "p256",
3453
+ bytes: consumerPaymentRequestSigningBytes(value),
3454
+ signature: value.takerSignatureDerB64,
3455
+ publicKey: value.takerPubkeySpkiB64
3456
+ });
3457
+ }
3458
+ function isConsumerPaymentRequestExpired(request, nowMs = Date.now()) {
3459
+ const parsed = ConsumerPaymentRequestEnvelopeSchema.parse(request);
3460
+ return parsed.expiresAtMs <= nowMs;
3461
+ }
3462
+
3463
+ // src/me-offline/settlement.ts
3464
+ var CONSUMER_SETTLEMENT_DOMAIN = "flur:consumer-offline:v1:settlement";
3465
+ var CONSUMER_SETTLEMENT_RECEIPT_QR_PREFIX = "FLURSR1.";
3466
+ function consumerSettlementSigningPayload(settlement) {
3467
+ return {
3468
+ domain: CONSUMER_SETTLEMENT_DOMAIN,
3469
+ settlementId: settlement.settlementId,
3470
+ settlementKey: settlement.settlementKey,
3471
+ encounterId: settlement.encounterId,
3472
+ oacId: settlement.oacId,
3473
+ payerUserId: settlement.payerUserId,
3474
+ payeeUserId: settlement.payeeUserId,
3475
+ amountKobo: settlement.amountKobo,
3476
+ currency: settlement.currency,
3477
+ status: settlement.status,
3478
+ reviewReason: settlement.reviewReason,
3479
+ ledgerRef: settlement.ledgerRef,
3480
+ issuedAtMs: settlement.issuedAtMs
3481
+ };
3482
+ }
3483
+ function verifyConsumerSettlement(settlement, issuerPublicKeySpkiB64) {
3484
+ const parsed = ConsumerSettlementSchema.safeParse(settlement);
3485
+ if (!parsed.success) return false;
3486
+ return verifyIssuerP256(
3487
+ canonicalJSONBytes(consumerSettlementSigningPayload(parsed.data)),
3488
+ parsed.data.issuerSig,
3489
+ issuerPublicKeySpkiB64
3490
+ );
3491
+ }
3492
+ function encodeConsumerSettlementReceiptQR(settlement) {
3493
+ const parsed = ConsumerSettlementSchema.parse(settlement);
3494
+ return `${CONSUMER_SETTLEMENT_RECEIPT_QR_PREFIX}${base64UrlEncodeUtf8(
3495
+ JSON.stringify(parsed)
3496
+ )}`;
3497
+ }
3498
+ function decodeUnverifiedConsumerSettlementReceiptQR(value) {
3499
+ if (!value.startsWith(CONSUMER_SETTLEMENT_RECEIPT_QR_PREFIX)) {
3500
+ throw new Error("not a Flur consumer settlement receipt QR");
3501
+ }
3502
+ const encoded = value.slice(CONSUMER_SETTLEMENT_RECEIPT_QR_PREFIX.length);
3304
3503
  let raw;
3305
3504
  try {
3306
3505
  raw = JSON.parse(base64UrlDecodeUtf8(encoded));
3307
3506
  } catch {
3308
- throw new Error("offline claim SMS token is malformed");
3507
+ throw new Error("consumer settlement receipt QR is malformed");
3309
3508
  }
3310
- const parsed = ConsumerPaymentClaimSchema.safeParse(raw);
3311
- if (!parsed.success) {
3312
- throw new Error("offline claim SMS token is invalid");
3509
+ return ConsumerSettlementSchema.parse(raw);
3510
+ }
3511
+ function verifyConsumerSettlementReceiptQR(value, issuerPublicKeySpkiB64) {
3512
+ const settlement = decodeUnverifiedConsumerSettlementReceiptQR(value);
3513
+ if (!verifyConsumerSettlement(settlement, issuerPublicKeySpkiB64)) {
3514
+ throw new Error("consumer settlement receipt QR signature invalid");
3313
3515
  }
3314
- return parsed.data;
3516
+ return settlement;
3315
3517
  }
3316
- function extractOfflineClaimSmsToken(message) {
3317
- const trimmed = message.trim();
3318
- if (trimmed.startsWith(OFFLINE_CLAIM_SMS_PREFIX)) {
3319
- return trimmed.split(/\s+/, 1)[0] ?? null;
3518
+ function decodeConsumerSettlementReceiptQR(value, issuerPublicKeySpkiB64) {
3519
+ if (!issuerPublicKeySpkiB64) {
3520
+ return decodeUnverifiedConsumerSettlementReceiptQR(value);
3320
3521
  }
3321
- return TOKEN_RE.exec(message)?.[1] ?? null;
3522
+ return verifyConsumerSettlementReceiptQR(value, issuerPublicKeySpkiB64);
3322
3523
  }
3323
3524
  function base64UrlEncodeUtf8(input) {
3324
3525
  const bytes = new TextEncoder().encode(input);
@@ -3348,15 +3549,281 @@ function base64UrlDecodeUtf8(input) {
3348
3549
  throw new Error("base64 decoder unavailable");
3349
3550
  }
3350
3551
 
3552
+ // src/me-offline/sms.ts
3553
+ import { p256 as p2563 } from "@noble/curves/nist";
3554
+ var OFFLINE_CLAIM_SMS_PREFIX = "FLURC1.";
3555
+ var CLAIM_TOKEN_RE = /(?:^|\s)(FLURC1\.[A-Za-z0-9_-]+={0,2})(?:\s|$)/;
3556
+ function encodeOfflineClaimSmsMessage(claim) {
3557
+ const parsed = ConsumerPaymentClaimSchema.parse(claim);
3558
+ return `${OFFLINE_CLAIM_SMS_PREFIX}${base64UrlEncodeUtf82(
3559
+ JSON.stringify(parsed)
3560
+ )}`;
3561
+ }
3562
+ function decodeOfflineClaimSmsMessage(message) {
3563
+ const token = extractOfflineClaimSmsToken(message);
3564
+ if (!token) throw new Error("offline claim QR token not found");
3565
+ const encoded = token.slice(OFFLINE_CLAIM_SMS_PREFIX.length);
3566
+ let raw;
3567
+ try {
3568
+ raw = JSON.parse(base64UrlDecodeUtf82(encoded));
3569
+ } catch {
3570
+ throw new Error("offline claim QR token is malformed");
3571
+ }
3572
+ const parsed = ConsumerPaymentClaimSchema.safeParse(raw);
3573
+ if (!parsed.success) throw new Error("offline claim QR token is invalid");
3574
+ return parsed.data;
3575
+ }
3576
+ function extractOfflineClaimSmsToken(message) {
3577
+ const trimmed = message.trim();
3578
+ if (trimmed.startsWith(OFFLINE_CLAIM_SMS_PREFIX)) {
3579
+ return trimmed.split(/\s+/, 1)[0] ?? null;
3580
+ }
3581
+ return CLAIM_TOKEN_RE.exec(message)?.[1] ?? null;
3582
+ }
3583
+ var OFFLINE_SMS_SETTLE_PREFIX = "FLURA1.";
3584
+ var OFFLINE_SMS_SETTLE_DOMAIN = "flur:consumer-offline:v1:attest";
3585
+ var OFFLINE_SMS_SETTLE_TOKEN_BYTES = 112;
3586
+ var OFFLINE_SMS_SETTLE_HEADER_BYTES = 48;
3587
+ var OFFLINE_SMS_SETTLE_SIGNATURE_BYTES = 64;
3588
+ var OFFLINE_SMS_SETTLE_VERSION = 1;
3589
+ var TOKEN_RE = /(?:^|[\s,;:()<>"'])(FLURA1\.[A-Za-z0-9_-]{150})/;
3590
+ async function encodeOfflineSmsSettleToken(input, signer) {
3591
+ const header = await buildSmsSettleHeader(input);
3592
+ const sig = await signer.signRaw(domainTag(header));
3593
+ if (sig.length !== OFFLINE_SMS_SETTLE_SIGNATURE_BYTES) {
3594
+ throw new Error(
3595
+ `FLURA1: signer returned ${sig.length}-byte sig; expected ${OFFLINE_SMS_SETTLE_SIGNATURE_BYTES}`
3596
+ );
3597
+ }
3598
+ const out = new Uint8Array(OFFLINE_SMS_SETTLE_TOKEN_BYTES);
3599
+ out.set(header, 0);
3600
+ out.set(sig, OFFLINE_SMS_SETTLE_HEADER_BYTES);
3601
+ return `${OFFLINE_SMS_SETTLE_PREFIX}${bytesToBase64Url(out)}`;
3602
+ }
3603
+ async function buildSmsSettleHeader(input) {
3604
+ assertSafeUint64(input.amountKobo, "amountKobo");
3605
+ if (input.amountKobo <= 0) {
3606
+ throw new Error("FLURA1: amountKobo must be greater than zero");
3607
+ }
3608
+ assertSafeUint48(input.occurredAtMs, "occurredAtMs");
3609
+ const encounterPrefix = (await sha2565(utf8(input.encounterId))).slice(0, 16);
3610
+ const payerPrefix = uuidToBytes(input.payerUserId).slice(0, 8);
3611
+ const payeePrefix = uuidToBytes(input.payeeUserId).slice(0, 8);
3612
+ const header = new Uint8Array(OFFLINE_SMS_SETTLE_HEADER_BYTES);
3613
+ const dv = new DataView(header.buffer);
3614
+ header[0] = OFFLINE_SMS_SETTLE_VERSION;
3615
+ header[1] = 0;
3616
+ header.set(encounterPrefix, 2);
3617
+ header.set(payerPrefix, 18);
3618
+ header.set(payeePrefix, 26);
3619
+ writeUint64BE(dv, 34, input.amountKobo);
3620
+ writeUint48BE(dv, 42, input.occurredAtMs);
3621
+ return header;
3622
+ }
3623
+ function domainTag(header) {
3624
+ if (header.length !== OFFLINE_SMS_SETTLE_HEADER_BYTES) {
3625
+ throw new Error(
3626
+ `FLURA1: header must be ${OFFLINE_SMS_SETTLE_HEADER_BYTES} bytes`
3627
+ );
3628
+ }
3629
+ const domain = utf8(OFFLINE_SMS_SETTLE_DOMAIN);
3630
+ const out = new Uint8Array(domain.length + header.length);
3631
+ out.set(domain, 0);
3632
+ out.set(header, domain.length);
3633
+ return out;
3634
+ }
3635
+ function decodeOfflineSmsSettleToken(message) {
3636
+ const token = extractOfflineSmsSettleToken(message);
3637
+ if (!token) throw new Error("FLURA1: token not found");
3638
+ const encoded = token.slice(OFFLINE_SMS_SETTLE_PREFIX.length);
3639
+ let bytes;
3640
+ try {
3641
+ bytes = base64UrlToBytes(encoded);
3642
+ } catch {
3643
+ throw new Error("FLURA1: token base64url is malformed");
3644
+ }
3645
+ if (bytesToBase64Url(bytes) !== encoded) {
3646
+ throw new Error("FLURA1: token base64url is malformed");
3647
+ }
3648
+ if (bytes.length !== OFFLINE_SMS_SETTLE_TOKEN_BYTES) {
3649
+ throw new Error(
3650
+ `FLURA1: expected ${OFFLINE_SMS_SETTLE_TOKEN_BYTES} bytes, got ${bytes.length}`
3651
+ );
3652
+ }
3653
+ const version = bytes[0];
3654
+ const flags = bytes[1];
3655
+ if (version !== OFFLINE_SMS_SETTLE_VERSION) {
3656
+ throw new Error(`FLURA1: unsupported version ${version}`);
3657
+ }
3658
+ if (flags !== 0) {
3659
+ throw new Error(`FLURA1: reserved flags must be 0, got ${flags}`);
3660
+ }
3661
+ const header = bytes.slice(0, OFFLINE_SMS_SETTLE_HEADER_BYTES);
3662
+ const signature = bytes.slice(OFFLINE_SMS_SETTLE_HEADER_BYTES);
3663
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3664
+ const amountKobo = readUint64BE(dv, 34);
3665
+ if (amountKobo <= 0) {
3666
+ throw new Error("FLURA1: amountKobo must be greater than zero");
3667
+ }
3668
+ return {
3669
+ version,
3670
+ flags,
3671
+ encounterIdPrefixHex: bytesToHex6(bytes.slice(2, 18)),
3672
+ payerUserIdPrefixHex: bytesToHex6(bytes.slice(18, 26)),
3673
+ payeeUserIdPrefixHex: bytesToHex6(bytes.slice(26, 34)),
3674
+ amountKobo,
3675
+ occurredAtMs: readUint48BE(dv, 42),
3676
+ signature,
3677
+ header,
3678
+ signedBytes: domainTag(header)
3679
+ };
3680
+ }
3681
+ function extractOfflineSmsSettleToken(message) {
3682
+ const trimmed = message.trim();
3683
+ if (trimmed.startsWith(OFFLINE_SMS_SETTLE_PREFIX)) {
3684
+ return trimmed.split(/\s+/, 1)[0] ?? null;
3685
+ }
3686
+ return TOKEN_RE.exec(message)?.[1] ?? null;
3687
+ }
3688
+ function verifyOfflineSmsSettleToken(decoded, payerPubkeySpkiB64) {
3689
+ try {
3690
+ const pubRaw = p256SpkiB64ToRaw(payerPubkeySpkiB64);
3691
+ return p2563.verify(decoded.signature, decoded.signedBytes, pubRaw, {
3692
+ prehash: true,
3693
+ format: "compact"
3694
+ });
3695
+ } catch {
3696
+ return false;
3697
+ }
3698
+ }
3699
+ function derToRawP256Signature(derBytes) {
3700
+ const sig = p2563.Signature.fromBytes(derBytes, "der");
3701
+ const raw = sig.toBytes("compact");
3702
+ if (raw.length !== OFFLINE_SMS_SETTLE_SIGNATURE_BYTES) {
3703
+ throw new Error(
3704
+ `FLURA1: DER\u2192raw produced ${raw.length} bytes; expected ${OFFLINE_SMS_SETTLE_SIGNATURE_BYTES}`
3705
+ );
3706
+ }
3707
+ return raw;
3708
+ }
3709
+ function utf8(s) {
3710
+ return new TextEncoder().encode(s);
3711
+ }
3712
+ async function sha2565(bytes) {
3713
+ const subtle = typeof globalThis !== "undefined" && globalThis.crypto?.subtle || void 0;
3714
+ if (subtle) {
3715
+ const digest = await subtle.digest("SHA-256", bytes);
3716
+ return new Uint8Array(digest);
3717
+ }
3718
+ const { sha256: nobleSha256 } = await import("@noble/hashes/sha2");
3719
+ return nobleSha256(bytes);
3720
+ }
3721
+ function uuidToBytes(uuid) {
3722
+ const hex = uuid.replace(/-/g, "").toLowerCase();
3723
+ if (hex.length !== 32 || !/^[0-9a-f]{32}$/.test(hex)) {
3724
+ throw new Error(`FLURA1: invalid UUID: ${uuid}`);
3725
+ }
3726
+ const out = new Uint8Array(16);
3727
+ for (let i = 0; i < 16; i++) {
3728
+ out[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
3729
+ }
3730
+ return out;
3731
+ }
3732
+ function assertSafeUint64(value, field) {
3733
+ if (!Number.isInteger(value) || value < 0) {
3734
+ throw new Error(`FLURA1: ${field} must be a non-negative integer`);
3735
+ }
3736
+ if (value > Number.MAX_SAFE_INTEGER) {
3737
+ throw new Error(`FLURA1: ${field} exceeds Number.MAX_SAFE_INTEGER`);
3738
+ }
3739
+ }
3740
+ function assertSafeUint48(value, field) {
3741
+ assertSafeUint64(value, field);
3742
+ if (value > 281474976710655) {
3743
+ throw new Error(`FLURA1: ${field} exceeds uint48 range`);
3744
+ }
3745
+ }
3746
+ function writeUint64BE(dv, offset, value) {
3747
+ const high = Math.floor(value / 4294967296);
3748
+ const low = value >>> 0;
3749
+ dv.setUint32(offset, high, false);
3750
+ dv.setUint32(offset + 4, low, false);
3751
+ }
3752
+ function readUint64BE(dv, offset) {
3753
+ const high = dv.getUint32(offset, false);
3754
+ const low = dv.getUint32(offset + 4, false);
3755
+ if (high > 2097151) {
3756
+ throw new Error("FLURA1: amountKobo exceeds Number.MAX_SAFE_INTEGER");
3757
+ }
3758
+ return high * 4294967296 + low;
3759
+ }
3760
+ function writeUint48BE(dv, offset, value) {
3761
+ const high = Math.floor(value / 65536);
3762
+ const low = value & 65535;
3763
+ dv.setUint32(offset, high, false);
3764
+ dv.setUint16(offset + 4, low, false);
3765
+ }
3766
+ function readUint48BE(dv, offset) {
3767
+ const high = dv.getUint32(offset, false);
3768
+ const low = dv.getUint16(offset + 4, false);
3769
+ return high * 65536 + low;
3770
+ }
3771
+ function bytesToHex6(bytes) {
3772
+ let out = "";
3773
+ for (let i = 0; i < bytes.length; i++) {
3774
+ out += bytes[i].toString(16).padStart(2, "0");
3775
+ }
3776
+ return out;
3777
+ }
3778
+ function bytesToBase64Url(bytes) {
3779
+ let base64;
3780
+ if (typeof Buffer !== "undefined") {
3781
+ base64 = Buffer.from(bytes).toString("base64");
3782
+ } else {
3783
+ let binary = "";
3784
+ for (let i = 0; i < bytes.length; i++) {
3785
+ binary += String.fromCharCode(bytes[i]);
3786
+ }
3787
+ if (typeof btoa !== "function") {
3788
+ throw new Error("FLURA1: base64 encoder unavailable");
3789
+ }
3790
+ base64 = btoa(binary);
3791
+ }
3792
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
3793
+ }
3794
+ function base64UrlEncodeUtf82(input) {
3795
+ return bytesToBase64Url(utf8(input));
3796
+ }
3797
+ function base64UrlDecodeUtf82(input) {
3798
+ return new TextDecoder().decode(base64UrlToBytes(input));
3799
+ }
3800
+ function base64UrlToBytes(input) {
3801
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
3802
+ const padded = base64.padEnd(
3803
+ base64.length + (4 - base64.length % 4) % 4,
3804
+ "="
3805
+ );
3806
+ if (typeof Buffer !== "undefined") {
3807
+ return new Uint8Array(Buffer.from(padded, "base64"));
3808
+ }
3809
+ if (typeof atob !== "function") {
3810
+ throw new Error("FLURA1: base64 decoder unavailable");
3811
+ }
3812
+ const binary = atob(padded);
3813
+ const out = new Uint8Array(binary.length);
3814
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
3815
+ return out;
3816
+ }
3817
+
3351
3818
  // src/partner-funding/client.ts
3352
- import { z as z14 } from "zod";
3353
- var MinorString = z14.string().regex(/^-?\d+$/);
3354
- var PositiveMinor = z14.union([
3355
- z14.number().int().positive(),
3356
- z14.string().regex(/^[1-9]\d{0,18}$/)
3819
+ import { z as z15 } from "zod";
3820
+ var MinorString = z15.string().regex(/^-?\d+$/);
3821
+ var PositiveMinor = z15.union([
3822
+ z15.number().int().positive(),
3823
+ z15.string().regex(/^[1-9]\d{0,18}$/)
3357
3824
  ]);
3358
- var Currency = z14.string().trim().length(3).transform((v) => v.toUpperCase());
3359
- var Metadata = z14.record(z14.unknown());
3825
+ var Currency = z15.string().trim().length(3).transform((v) => v.toUpperCase());
3826
+ var Metadata = z15.record(z15.unknown());
3360
3827
  var PARTNER_KINDS = ["bank", "merchant"];
3361
3828
  var CUSTODIAL_MODES = ["agent_of_bank", "flur_virtual_pool"];
3362
3829
  var PARTNER_PROFILE_STATUSES = [
@@ -3383,126 +3850,126 @@ var WITHDRAWAL_STATES = [
3383
3850
  "failed",
3384
3851
  "reversed"
3385
3852
  ];
3386
- var PartnerProfileSchema = z14.object({
3387
- partnerAccountId: z14.string().uuid(),
3388
- kind: z14.enum(PARTNER_KINDS),
3389
- custodialMode: z14.enum(CUSTODIAL_MODES),
3390
- displayName: z14.string(),
3391
- bankCode: z14.string().nullable(),
3392
- poolAccountNumber: z14.string().nullable(),
3393
- status: z14.enum(PARTNER_PROFILE_STATUSES),
3853
+ var PartnerProfileSchema = z15.object({
3854
+ partnerAccountId: z15.string().uuid(),
3855
+ kind: z15.enum(PARTNER_KINDS),
3856
+ custodialMode: z15.enum(CUSTODIAL_MODES),
3857
+ displayName: z15.string(),
3858
+ bankCode: z15.string().nullable(),
3859
+ poolAccountNumber: z15.string().nullable(),
3860
+ status: z15.enum(PARTNER_PROFILE_STATUSES),
3394
3861
  metadata: Metadata,
3395
- createdAtMs: z14.number().int().nonnegative(),
3396
- updatedAtMs: z14.number().int().nonnegative()
3862
+ createdAtMs: z15.number().int().nonnegative(),
3863
+ updatedAtMs: z15.number().int().nonnegative()
3397
3864
  });
3398
- var UpsertPartnerProfileInputSchema = z14.object({
3399
- kind: z14.enum(PARTNER_KINDS),
3400
- custodialMode: z14.enum(CUSTODIAL_MODES),
3401
- displayName: z14.string().trim().min(1).max(200),
3402
- bankCode: z14.string().trim().min(1).max(64).optional(),
3403
- poolAccountNumber: z14.string().trim().min(1).max(64).optional(),
3865
+ var UpsertPartnerProfileInputSchema = z15.object({
3866
+ kind: z15.enum(PARTNER_KINDS),
3867
+ custodialMode: z15.enum(CUSTODIAL_MODES),
3868
+ displayName: z15.string().trim().min(1).max(200),
3869
+ bankCode: z15.string().trim().min(1).max(64).optional(),
3870
+ poolAccountNumber: z15.string().trim().min(1).max(64).optional(),
3404
3871
  metadata: Metadata.optional()
3405
3872
  });
3406
- var PartnerFundingEventInputSchema = z14.object({
3407
- externalRef: z14.string().trim().min(8).max(128),
3408
- direction: z14.enum(PARTNER_FUNDING_DIRECTIONS).optional(),
3409
- userId: z14.string().uuid().optional(),
3410
- accountId: z14.string().uuid().optional(),
3873
+ var PartnerFundingEventInputSchema = z15.object({
3874
+ externalRef: z15.string().trim().min(8).max(128),
3875
+ direction: z15.enum(PARTNER_FUNDING_DIRECTIONS).optional(),
3876
+ userId: z15.string().uuid().optional(),
3877
+ accountId: z15.string().uuid().optional(),
3411
3878
  amountMinor: PositiveMinor,
3412
3879
  currency: Currency,
3413
- fundingSource: z14.string().trim().min(1).max(64).optional(),
3880
+ fundingSource: z15.string().trim().min(1).max(64).optional(),
3414
3881
  providerMetadata: Metadata.optional()
3415
3882
  });
3416
- var PartnerFundingSchema = z14.object({
3417
- fundingId: z14.string().uuid(),
3418
- partnerId: z14.string().uuid(),
3419
- accountId: z14.string().uuid(),
3420
- userId: z14.string().uuid().nullable(),
3421
- direction: z14.enum(PARTNER_FUNDING_DIRECTIONS),
3422
- currency: z14.string(),
3883
+ var PartnerFundingSchema = z15.object({
3884
+ fundingId: z15.string().uuid(),
3885
+ partnerId: z15.string().uuid(),
3886
+ accountId: z15.string().uuid(),
3887
+ userId: z15.string().uuid().nullable(),
3888
+ direction: z15.enum(PARTNER_FUNDING_DIRECTIONS),
3889
+ currency: z15.string(),
3423
3890
  amountMinor: MinorString,
3424
- externalRef: z14.string(),
3425
- status: z14.enum(PARTNER_FUNDING_STATUSES),
3426
- fundingSource: z14.string(),
3427
- ledgerRef: z14.string(),
3891
+ externalRef: z15.string(),
3892
+ status: z15.enum(PARTNER_FUNDING_STATUSES),
3893
+ fundingSource: z15.string(),
3894
+ ledgerRef: z15.string(),
3428
3895
  providerMetadata: Metadata,
3429
- createdAtMs: z14.number().int().nonnegative(),
3430
- updatedAtMs: z14.number().int().nonnegative()
3896
+ createdAtMs: z15.number().int().nonnegative(),
3897
+ updatedAtMs: z15.number().int().nonnegative()
3431
3898
  });
3432
- var IngestFundingResultSchema = z14.object({
3899
+ var IngestFundingResultSchema = z15.object({
3433
3900
  funding: PartnerFundingSchema,
3434
- replayed: z14.boolean()
3901
+ replayed: z15.boolean()
3435
3902
  });
3436
- var PayoutDestinationSchema = z14.object({
3437
- destinationId: z14.string().uuid(),
3438
- accountId: z14.string().uuid(),
3439
- partnerId: z14.string().uuid(),
3440
- bankCode: z14.string(),
3441
- accountNumber: z14.string(),
3442
- accountName: z14.string(),
3443
- status: z14.enum(PAYOUT_DESTINATION_STATUSES),
3444
- verifiedAtMs: z14.number().int().nonnegative().nullable(),
3903
+ var PayoutDestinationSchema = z15.object({
3904
+ destinationId: z15.string().uuid(),
3905
+ accountId: z15.string().uuid(),
3906
+ partnerId: z15.string().uuid(),
3907
+ bankCode: z15.string(),
3908
+ accountNumber: z15.string(),
3909
+ accountName: z15.string(),
3910
+ status: z15.enum(PAYOUT_DESTINATION_STATUSES),
3911
+ verifiedAtMs: z15.number().int().nonnegative().nullable(),
3445
3912
  metadata: Metadata,
3446
- createdAtMs: z14.number().int().nonnegative(),
3447
- updatedAtMs: z14.number().int().nonnegative()
3913
+ createdAtMs: z15.number().int().nonnegative(),
3914
+ updatedAtMs: z15.number().int().nonnegative()
3448
3915
  });
3449
- var CreatePayoutDestinationInputSchema = z14.object({
3450
- partnerId: z14.string().uuid(),
3451
- bankCode: z14.string().trim().min(1).max(32),
3452
- accountNumber: z14.string().trim().min(4).max(64),
3453
- accountName: z14.string().trim().min(1).max(200),
3916
+ var CreatePayoutDestinationInputSchema = z15.object({
3917
+ partnerId: z15.string().uuid(),
3918
+ bankCode: z15.string().trim().min(1).max(32),
3919
+ accountNumber: z15.string().trim().min(4).max(64),
3920
+ accountName: z15.string().trim().min(1).max(200),
3454
3921
  metadata: Metadata.optional()
3455
3922
  });
3456
- var ListPayoutDestinationsResultSchema = z14.object({
3457
- items: z14.array(PayoutDestinationSchema)
3923
+ var ListPayoutDestinationsResultSchema = z15.object({
3924
+ items: z15.array(PayoutDestinationSchema)
3458
3925
  });
3459
- var WithdrawalSchema = z14.object({
3460
- withdrawalId: z14.string().uuid(),
3461
- accountId: z14.string().uuid(),
3462
- userId: z14.string().uuid(),
3463
- partnerId: z14.string().uuid(),
3464
- destinationId: z14.string().uuid(),
3465
- currency: z14.string(),
3926
+ var WithdrawalSchema = z15.object({
3927
+ withdrawalId: z15.string().uuid(),
3928
+ accountId: z15.string().uuid(),
3929
+ userId: z15.string().uuid(),
3930
+ partnerId: z15.string().uuid(),
3931
+ destinationId: z15.string().uuid(),
3932
+ currency: z15.string(),
3466
3933
  amountMinor: MinorString,
3467
- state: z14.enum(WITHDRAWAL_STATES),
3468
- idempotencyKey: z14.string(),
3469
- providerRef: z14.string().nullable(),
3470
- lastError: z14.string().nullable(),
3471
- ledgerRef: z14.string(),
3472
- reverseLedgerRef: z14.string().nullable(),
3934
+ state: z15.enum(WITHDRAWAL_STATES),
3935
+ idempotencyKey: z15.string(),
3936
+ providerRef: z15.string().nullable(),
3937
+ lastError: z15.string().nullable(),
3938
+ ledgerRef: z15.string(),
3939
+ reverseLedgerRef: z15.string().nullable(),
3473
3940
  metadata: Metadata,
3474
- createdAtMs: z14.number().int().nonnegative(),
3475
- updatedAtMs: z14.number().int().nonnegative()
3941
+ createdAtMs: z15.number().int().nonnegative(),
3942
+ updatedAtMs: z15.number().int().nonnegative()
3476
3943
  });
3477
- var CreateWithdrawalInputSchema = z14.object({
3478
- destinationId: z14.string().uuid(),
3944
+ var CreateWithdrawalInputSchema = z15.object({
3945
+ destinationId: z15.string().uuid(),
3479
3946
  amountMinor: PositiveMinor,
3480
3947
  currency: Currency,
3481
- idempotencyKey: z14.string().trim().min(8).max(128),
3948
+ idempotencyKey: z15.string().trim().min(8).max(128),
3482
3949
  metadata: Metadata.optional()
3483
3950
  });
3484
- var CreateWithdrawalResultSchema = z14.object({
3951
+ var CreateWithdrawalResultSchema = z15.object({
3485
3952
  withdrawal: WithdrawalSchema,
3486
- replayed: z14.boolean()
3953
+ replayed: z15.boolean()
3487
3954
  });
3488
- var PayoutEventInputSchema = z14.object({
3489
- externalRef: z14.string().trim().min(8).max(128),
3490
- withdrawalId: z14.string().uuid().optional(),
3491
- state: z14.enum(["submitted", "processing", "paid", "failed"]),
3492
- providerRef: z14.string().trim().min(1).max(128).optional(),
3493
- failureCode: z14.string().trim().max(64).optional(),
3494
- failureMessage: z14.string().trim().max(512).optional(),
3955
+ var PayoutEventInputSchema = z15.object({
3956
+ externalRef: z15.string().trim().min(8).max(128),
3957
+ withdrawalId: z15.string().uuid().optional(),
3958
+ state: z15.enum(["submitted", "processing", "paid", "failed"]),
3959
+ providerRef: z15.string().trim().min(1).max(128).optional(),
3960
+ failureCode: z15.string().trim().max(64).optional(),
3961
+ failureMessage: z15.string().trim().max(512).optional(),
3495
3962
  providerMetadata: Metadata.optional()
3496
3963
  });
3497
- var RecordPayoutEventResultSchema = z14.object({
3964
+ var RecordPayoutEventResultSchema = z15.object({
3498
3965
  withdrawal: WithdrawalSchema,
3499
- replayed: z14.boolean()
3966
+ replayed: z15.boolean()
3500
3967
  });
3501
- var ReconciliationReportSchema = z14.object({
3502
- partnerId: z14.string().uuid(),
3503
- currency: z14.string(),
3504
- fromMs: z14.number().int().nonnegative(),
3505
- toMs: z14.number().int().nonnegative(),
3968
+ var ReconciliationReportSchema = z15.object({
3969
+ partnerId: z15.string().uuid(),
3970
+ currency: z15.string(),
3971
+ fromMs: z15.number().int().nonnegative(),
3972
+ toMs: z15.number().int().nonnegative(),
3506
3973
  fundingsCreditMinor: MinorString,
3507
3974
  fundingsDebitMinor: MinorString,
3508
3975
  withdrawalsPaidMinor: MinorString,
@@ -3511,7 +3978,7 @@ var ReconciliationReportSchema = z14.object({
3511
3978
  expectedReserveBalanceMinor: MinorString,
3512
3979
  actualReserveBalanceMinor: MinorString,
3513
3980
  imbalanceMinor: MinorString,
3514
- generatedAtMs: z14.number().int().nonnegative()
3981
+ generatedAtMs: z15.number().int().nonnegative()
3515
3982
  });
3516
3983
  function createPartnerFundingClient(partner) {
3517
3984
  return {
@@ -3663,19 +4130,19 @@ function createPartnerProfileAdminClient(opts) {
3663
4130
  }
3664
4131
 
3665
4132
  // src/artifacts/envelope.ts
3666
- import { z as z15 } from "zod";
4133
+ import { z as z16 } from "zod";
3667
4134
  var FLUR_ARTIFACT_URI_SCHEME = "flur";
3668
4135
  var FLUR_ARTIFACT_VERSION = 1;
3669
4136
  var FLUR_ARTIFACT_URI_PREFIX = `${FLUR_ARTIFACT_URI_SCHEME}://v${FLUR_ARTIFACT_VERSION}/`;
3670
4137
  var ArtifactTypeRe = /^[a-z][a-z0-9_]{1,63}$/;
3671
- var ArtifactHeaderSchema = z15.object({
3672
- v: z15.literal(FLUR_ARTIFACT_VERSION),
3673
- t: z15.string().regex(ArtifactTypeRe, "invalid artifact type"),
3674
- iss: z15.string().min(1).max(128),
3675
- kid: z15.string().min(1).max(128),
3676
- iat: z15.number().int().nonnegative(),
3677
- exp: z15.number().int().positive().optional(),
3678
- nonce: z15.string().min(8).max(64).regex(/^[A-Za-z0-9_-]+$/, "nonce must be url-safe")
4138
+ var ArtifactHeaderSchema = z16.object({
4139
+ v: z16.literal(FLUR_ARTIFACT_VERSION),
4140
+ t: z16.string().regex(ArtifactTypeRe, "invalid artifact type"),
4141
+ iss: z16.string().min(1).max(128),
4142
+ kid: z16.string().min(1).max(128),
4143
+ iat: z16.number().int().nonnegative(),
4144
+ exp: z16.number().int().positive().optional(),
4145
+ nonce: z16.string().min(8).max(64).regex(/^[A-Za-z0-9_-]+$/, "nonce must be url-safe")
3679
4146
  });
3680
4147
  var FlurArtifactError = class extends Error {
3681
4148
  constructor(message, code) {
@@ -3814,7 +4281,7 @@ function verifyArtifactSignature(decoded, publicKeySpkiB64, options = {}) {
3814
4281
  }
3815
4282
 
3816
4283
  // src/artifacts/types.ts
3817
- import { z as z16 } from "zod";
4284
+ import { z as z17 } from "zod";
3818
4285
  var ARTIFACT_TYPES = {
3819
4286
  OFFLINE_PAYMENT_AUTHORIZATION: "offline_payment_authorization",
3820
4287
  RECEIPT: "receipt",
@@ -3829,32 +4296,32 @@ var ARTIFACT_TYPES = {
3829
4296
  PASS: "pass",
3830
4297
  IDENTITY: "identity"
3831
4298
  };
3832
- var HexString = (length) => z16.string().regex(
4299
+ var HexString = (length) => z17.string().regex(
3833
4300
  new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
3834
4301
  `expected ${length}-byte hex string`
3835
4302
  );
3836
- var OfflinePaymentAuthorizationArtifactSchema = z16.object({
4303
+ var OfflinePaymentAuthorizationArtifactSchema = z17.object({
3837
4304
  authorization: OfflinePaymentAuthorizationSchema
3838
4305
  });
3839
- var ReceiptArtifactSchema = z16.object({
3840
- receiptId: z16.string().min(1).max(64),
3841
- paymentReference: z16.string().min(1).max(64),
3842
- payerUserId: z16.string().min(1).max(64).optional(),
3843
- payeeUserId: z16.string().min(1).max(64),
3844
- amountKobo: z16.number().int().positive(),
3845
- currency: z16.literal("NGN"),
3846
- channel: z16.enum(["online", "offline_reconciled", "pay_link", "nqr"]),
3847
- settledAtMs: z16.number().int().positive(),
3848
- ledgerTxnId: z16.string().min(1).max(64).optional(),
3849
- memo: z16.string().max(140).optional(),
4306
+ var ReceiptArtifactSchema = z17.object({
4307
+ receiptId: z17.string().min(1).max(64),
4308
+ paymentReference: z17.string().min(1).max(64),
4309
+ payerUserId: z17.string().min(1).max(64).optional(),
4310
+ payeeUserId: z17.string().min(1).max(64),
4311
+ amountKobo: z17.number().int().positive(),
4312
+ currency: z17.literal("NGN"),
4313
+ channel: z17.enum(["online", "offline_reconciled", "pay_link", "nqr"]),
4314
+ settledAtMs: z17.number().int().positive(),
4315
+ ledgerTxnId: z17.string().min(1).max(64).optional(),
4316
+ memo: z17.string().max(140).optional(),
3850
4317
  hashChainPrev: HexString(32).optional()
3851
4318
  });
3852
- var ShortId = z16.string().min(1).max(64);
3853
- var PositiveInt = z16.number().int().positive();
3854
- var NonNegativeInt = z16.number().int().nonnegative();
3855
- var Currency2 = z16.literal("NGN");
3856
- var Memo = z16.string().max(140);
3857
- var NqrPaymentRequestArtifactSchema = z16.object({
4319
+ var ShortId = z17.string().min(1).max(64);
4320
+ var PositiveInt = z17.number().int().positive();
4321
+ var NonNegativeInt = z17.number().int().nonnegative();
4322
+ var Currency2 = z17.literal("NGN");
4323
+ var Memo = z17.string().max(140);
4324
+ var NqrPaymentRequestArtifactSchema = z17.object({
3858
4325
  requestId: ShortId,
3859
4326
  payeeUserId: ShortId,
3860
4327
  amountKobo: PositiveInt.optional(),
@@ -3862,7 +4329,7 @@ var NqrPaymentRequestArtifactSchema = z16.object({
3862
4329
  memo: Memo.optional(),
3863
4330
  expiresAtMs: PositiveInt.optional()
3864
4331
  });
3865
- var PaymentIntentArtifactSchema = z16.object({
4332
+ var PaymentIntentArtifactSchema = z17.object({
3866
4333
  intentId: ShortId,
3867
4334
  payerUserId: ShortId,
3868
4335
  payeeUserId: ShortId,
@@ -3871,7 +4338,7 @@ var PaymentIntentArtifactSchema = z16.object({
3871
4338
  idempotencyKey: ShortId,
3872
4339
  createdAtMs: PositiveInt
3873
4340
  });
3874
- var OfflineClaimArtifactSchema = z16.object({
4341
+ var OfflineClaimArtifactSchema = z17.object({
3875
4342
  claimId: ShortId,
3876
4343
  authorizationId: ShortId,
3877
4344
  payeeUserId: ShortId,
@@ -3880,10 +4347,10 @@ var OfflineClaimArtifactSchema = z16.object({
3880
4347
  claimedAtMs: PositiveInt,
3881
4348
  paymentReference: ShortId.optional()
3882
4349
  });
3883
- var SettlementRecordArtifactSchema = z16.object({
4350
+ var SettlementRecordArtifactSchema = z17.object({
3884
4351
  settlementId: ShortId,
3885
4352
  ledgerTxnId: ShortId,
3886
- sourceRefType: z16.enum([
4353
+ sourceRefType: z17.enum([
3887
4354
  "offline_authorization",
3888
4355
  "offline_claim",
3889
4356
  "transfer",
@@ -3894,12 +4361,12 @@ var SettlementRecordArtifactSchema = z16.object({
3894
4361
  currency: Currency2,
3895
4362
  settledAtMs: PositiveInt
3896
4363
  });
3897
- var ReversalRecordArtifactSchema = z16.object({
4364
+ var ReversalRecordArtifactSchema = z17.object({
3898
4365
  reversalId: ShortId,
3899
4366
  originalTxnId: ShortId,
3900
4367
  amountKobo: PositiveInt,
3901
4368
  currency: Currency2,
3902
- reason: z16.enum([
4369
+ reason: z17.enum([
3903
4370
  "user_dispute",
3904
4371
  "fraud",
3905
4372
  "duplicate",
@@ -3909,7 +4376,7 @@ var ReversalRecordArtifactSchema = z16.object({
3909
4376
  reversedAtMs: PositiveInt,
3910
4377
  memo: Memo.optional()
3911
4378
  });
3912
- var LedgerJournalEntryArtifactSchema = z16.object({
4379
+ var LedgerJournalEntryArtifactSchema = z17.object({
3913
4380
  entryId: ShortId,
3914
4381
  journalId: ShortId,
3915
4382
  debitAccountId: ShortId,
@@ -3920,13 +4387,13 @@ var LedgerJournalEntryArtifactSchema = z16.object({
3920
4387
  refType: ShortId.optional(),
3921
4388
  refId: ShortId.optional()
3922
4389
  });
3923
- var StatementArtifactSchema = z16.object({
4390
+ var StatementArtifactSchema = z17.object({
3924
4391
  statementId: ShortId,
3925
4392
  userId: ShortId,
3926
4393
  periodStartMs: PositiveInt,
3927
4394
  periodEndMs: PositiveInt,
3928
- openingBalanceKobo: z16.number().int(),
3929
- closingBalanceKobo: z16.number().int(),
4395
+ openingBalanceKobo: z17.number().int(),
4396
+ closingBalanceKobo: z17.number().int(),
3930
4397
  transactionCount: NonNegativeInt,
3931
4398
  currency: Currency2,
3932
4399
  hashChainPrev: HexString(32).optional()
@@ -3934,16 +4401,16 @@ var StatementArtifactSchema = z16.object({
3934
4401
  message: "periodEndMs must be greater than periodStartMs",
3935
4402
  path: ["periodEndMs"]
3936
4403
  });
3937
- var PassArtifactSchema = z16.object({
4404
+ var PassArtifactSchema = z17.object({
3938
4405
  passId: ShortId,
3939
4406
  holderId: ShortId,
3940
- category: z16.enum(["membership", "ticket", "loyalty", "access", "voucher"]),
3941
- title: z16.string().min(1).max(120),
4407
+ category: z17.enum(["membership", "ticket", "loyalty", "access", "voucher"]),
4408
+ title: z17.string().min(1).max(120),
3942
4409
  validFromMs: PositiveInt,
3943
4410
  validUntilMs: PositiveInt.optional(),
3944
- metadata: z16.record(
3945
- z16.string().min(1).max(64),
3946
- z16.union([z16.string().max(280), z16.number(), z16.boolean()])
4411
+ metadata: z17.record(
4412
+ z17.string().min(1).max(64),
4413
+ z17.union([z17.string().max(280), z17.number(), z17.boolean()])
3947
4414
  ).optional()
3948
4415
  }).refine(
3949
4416
  (v) => v.validUntilMs === void 0 || v.validUntilMs > v.validFromMs,
@@ -3952,10 +4419,10 @@ var PassArtifactSchema = z16.object({
3952
4419
  path: ["validUntilMs"]
3953
4420
  }
3954
4421
  );
3955
- var IdentityArtifactSchema = z16.object({
4422
+ var IdentityArtifactSchema = z17.object({
3956
4423
  attestationId: ShortId,
3957
4424
  subjectId: ShortId,
3958
- claimType: z16.enum([
4425
+ claimType: z17.enum([
3959
4426
  "phone_verified",
3960
4427
  "email_verified",
3961
4428
  "bvn_verified",
@@ -4090,6 +4557,9 @@ export {
4090
4557
  COLLECTION_INTENT_STATUSES,
4091
4558
  COLLECTION_PAYMENT_STATUSES,
4092
4559
  CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS,
4560
+ CONSUMER_PAYMENT_REQUEST_DOMAIN,
4561
+ CONSUMER_SETTLEMENT_DOMAIN,
4562
+ CONSUMER_SETTLEMENT_RECEIPT_QR_PREFIX,
4093
4563
  CUSTODIAL_MODES,
4094
4564
  CollectionIntentSchema,
4095
4565
  CollectionPaymentResultSchema,
@@ -4099,6 +4569,7 @@ export {
4099
4569
  OACRecordSchema as ConsumerOACRecordSchema,
4100
4570
  ConsumerOACSchema,
4101
4571
  ConsumerPaymentClaimSchema,
4572
+ ConsumerPaymentRequestEnvelopeSchema,
4102
4573
  ConsumerSettleResultSchema,
4103
4574
  ConsumerSettlementSchema,
4104
4575
  CreateCollectionIntentInputSchema,
@@ -4140,6 +4611,12 @@ export {
4140
4611
  OAC_DEFAULT_PER_TX_KOBO,
4141
4612
  OAC_DEFAULT_VALIDITY_MS,
4142
4613
  OFFLINE_CLAIM_SMS_PREFIX,
4614
+ OFFLINE_SMS_SETTLE_DOMAIN,
4615
+ OFFLINE_SMS_SETTLE_HEADER_BYTES,
4616
+ OFFLINE_SMS_SETTLE_PREFIX,
4617
+ OFFLINE_SMS_SETTLE_SIGNATURE_BYTES,
4618
+ OFFLINE_SMS_SETTLE_TOKEN_BYTES,
4619
+ OFFLINE_SMS_SETTLE_VERSION,
4143
4620
  OfflineClaimArtifactSchema,
4144
4621
  OfflinePaymentAuthorizationArtifactSchema,
4145
4622
  OfflinePaymentAuthorizationSchema,
@@ -4199,18 +4676,25 @@ export {
4199
4676
  bodySha256Hex,
4200
4677
  buildArtifactBody,
4201
4678
  buildAuthorization,
4679
+ buildConsumerPaymentRequest,
4202
4680
  buildOAC,
4203
4681
  buildPass,
4204
4682
  buildPaymentRequest,
4205
4683
  buildReceipt,
4206
4684
  buildRedemption,
4685
+ buildSmsSettleHeader,
4686
+ domainTag as buildSmsSettleSignedBytes,
4207
4687
  canonicalClaimSigningBytes,
4208
4688
  canonicalClaimSigningPayload,
4209
4689
  canonicalJSONBytes,
4210
4690
  canonicalJSONStringify,
4211
4691
  canonicalRequestString,
4692
+ computeConsumerClaimEncounterId,
4212
4693
  computeEncounterId,
4213
4694
  constantTimeEqual,
4695
+ consumerPaymentRequestSigningBytes,
4696
+ consumerPaymentRequestSigningPayload,
4697
+ consumerSettlementSigningPayload,
4214
4698
  crc16ccitt,
4215
4699
  crc16ccittHex,
4216
4700
  createAccountsClient,
@@ -4234,19 +4718,27 @@ export {
4234
4718
  decodeArtifactUri,
4235
4719
  decodeAuthorizationQR,
4236
4720
  decodeBase45,
4721
+ decodeConsumerSettlementReceiptQR,
4237
4722
  decodeOfflineClaimSmsMessage,
4723
+ decodeOfflineSmsSettleToken,
4238
4724
  decodePaymentRequestQR,
4725
+ decodeUnverifiedConsumerSettlementReceiptQR,
4726
+ derToRawP256Signature,
4239
4727
  encodeArtifactUri,
4240
4728
  encodeAuthorizationQR,
4241
4729
  encodeBase45,
4730
+ encodeConsumerSettlementReceiptQR,
4242
4731
  encodeNQR,
4243
4732
  encodeOfflineClaimSmsMessage,
4733
+ encodeOfflineSmsSettleToken,
4244
4734
  encodePaymentRequestQR,
4245
4735
  extractOfflineClaimSmsToken,
4736
+ extractOfflineSmsSettleToken,
4246
4737
  formatAmount,
4247
4738
  generateDynamicQR,
4248
4739
  generateStaticQR,
4249
4740
  init,
4741
+ isConsumerPaymentRequestExpired,
4250
4742
  isHardenedArtifactType,
4251
4743
  isKnownArtifactType,
4252
4744
  isPassWithinValidity,
@@ -4259,6 +4751,7 @@ export {
4259
4751
  routingHint,
4260
4752
  signArtifact,
4261
4753
  signAuthorization,
4754
+ signConsumerPaymentRequest,
4262
4755
  signOAC,
4263
4756
  signPartnerRequest,
4264
4757
  signPass,
@@ -4270,7 +4763,11 @@ export {
4270
4763
  verifyArtifactUri,
4271
4764
  verifyAuthorization,
4272
4765
  verifyClaimSignature,
4766
+ verifyConsumerPaymentRequest,
4767
+ verifyConsumerSettlement,
4768
+ verifyConsumerSettlementReceiptQR,
4273
4769
  verifyOAC,
4770
+ verifyOfflineSmsSettleToken,
4274
4771
  verifyPass,
4275
4772
  verifyPaymentRequest,
4276
4773
  verifyReceipt,