@nokinc-flur/sdk 1.0.5 → 1.0.6
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 +1404 -634
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1682 -146
- package/dist/index.d.ts +1682 -146
- package/dist/index.js +1358 -634
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
|
-
import { z as
|
|
2
|
+
import { z as z4 } from "zod";
|
|
3
3
|
|
|
4
4
|
// src/contracts.ts
|
|
5
5
|
import { z } from "zod";
|
|
@@ -387,6 +387,341 @@ function moneyMinorToNumber(amountMinor) {
|
|
|
387
387
|
return asNumber;
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
// src/collections/client.ts
|
|
391
|
+
import { z as z3 } from "zod";
|
|
392
|
+
var MERCHANT_PROFILE_STATUSES = [
|
|
393
|
+
"pending",
|
|
394
|
+
"active",
|
|
395
|
+
"suspended",
|
|
396
|
+
"closed"
|
|
397
|
+
];
|
|
398
|
+
var SETTLEMENT_SCHEDULES = ["manual", "daily", "t1"];
|
|
399
|
+
var COLLECTION_INTENT_STATUSES = [
|
|
400
|
+
"created",
|
|
401
|
+
"pending",
|
|
402
|
+
"paid",
|
|
403
|
+
"expired",
|
|
404
|
+
"cancelled",
|
|
405
|
+
"failed",
|
|
406
|
+
"reversed"
|
|
407
|
+
];
|
|
408
|
+
var COLLECTION_PAYMENT_STATUSES = [
|
|
409
|
+
"pending",
|
|
410
|
+
"paid",
|
|
411
|
+
"failed",
|
|
412
|
+
"reversed"
|
|
413
|
+
];
|
|
414
|
+
var MERCHANT_PAYOUT_STATUSES = [
|
|
415
|
+
"requested",
|
|
416
|
+
"processing",
|
|
417
|
+
"paid",
|
|
418
|
+
"failed",
|
|
419
|
+
"cancelled"
|
|
420
|
+
];
|
|
421
|
+
var MoneyKoboSchema = z3.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
422
|
+
var MetadataSchema = z3.record(
|
|
423
|
+
z3.union([z3.string(), z3.number(), z3.boolean(), z3.null()])
|
|
424
|
+
);
|
|
425
|
+
var CurrencySchema2 = z3.string().trim().length(3).transform((value) => value.toUpperCase());
|
|
426
|
+
var ReferenceSchema = z3.string().trim().min(6).max(128).regex(/^[A-Za-z0-9._:-]+$/);
|
|
427
|
+
var MerchantProfileSchema = z3.object({
|
|
428
|
+
accountId: z3.string().uuid(),
|
|
429
|
+
legalName: z3.string(),
|
|
430
|
+
tradingName: z3.string(),
|
|
431
|
+
merchantCategoryCode: z3.string().regex(/^\d{4}$/),
|
|
432
|
+
nqrMerchantId: z3.string(),
|
|
433
|
+
settlementBankCode: z3.string(),
|
|
434
|
+
settlementAccountNumber: z3.string(),
|
|
435
|
+
settlementAccountName: z3.string(),
|
|
436
|
+
settlementSchedule: z3.enum(SETTLEMENT_SCHEDULES),
|
|
437
|
+
status: z3.enum(MERCHANT_PROFILE_STATUSES),
|
|
438
|
+
offlineEnabled: z3.boolean(),
|
|
439
|
+
perTxLimitKobo: MoneyKoboSchema,
|
|
440
|
+
dailyLimitKobo: MoneyKoboSchema,
|
|
441
|
+
metadata: MetadataSchema,
|
|
442
|
+
createdAtMs: z3.number().int().nonnegative(),
|
|
443
|
+
updatedAtMs: z3.number().int().nonnegative()
|
|
444
|
+
});
|
|
445
|
+
var UpsertMerchantProfileInputSchema = z3.object({
|
|
446
|
+
legalName: z3.string().trim().min(1).max(200),
|
|
447
|
+
tradingName: z3.string().trim().min(1).max(25),
|
|
448
|
+
merchantCategoryCode: z3.string().trim().regex(/^\d{4}$/),
|
|
449
|
+
nqrMerchantId: z3.string().trim().min(3).max(64),
|
|
450
|
+
settlementBankCode: z3.string().trim().min(2).max(16),
|
|
451
|
+
settlementAccountNumber: z3.string().trim().min(5).max(32),
|
|
452
|
+
settlementAccountName: z3.string().trim().min(1).max(200),
|
|
453
|
+
settlementSchedule: z3.enum(SETTLEMENT_SCHEDULES).optional(),
|
|
454
|
+
status: z3.enum(MERCHANT_PROFILE_STATUSES).optional(),
|
|
455
|
+
offlineEnabled: z3.boolean().optional(),
|
|
456
|
+
perTxLimitKobo: MoneyKoboSchema.optional(),
|
|
457
|
+
dailyLimitKobo: MoneyKoboSchema.optional(),
|
|
458
|
+
metadata: MetadataSchema.optional()
|
|
459
|
+
});
|
|
460
|
+
var CollectionIntentSchema = z3.object({
|
|
461
|
+
intentId: z3.string().uuid(),
|
|
462
|
+
accountId: z3.string().uuid(),
|
|
463
|
+
terminalId: z3.string().uuid().nullable(),
|
|
464
|
+
reference: z3.string(),
|
|
465
|
+
amountKobo: z3.number().int().positive().nullable(),
|
|
466
|
+
currency: z3.string().length(3),
|
|
467
|
+
status: z3.enum(COLLECTION_INTENT_STATUSES),
|
|
468
|
+
description: z3.string().nullable(),
|
|
469
|
+
nqrPayload: z3.string(),
|
|
470
|
+
provider: z3.string(),
|
|
471
|
+
providerReference: z3.string().nullable(),
|
|
472
|
+
metadata: MetadataSchema,
|
|
473
|
+
expiresAtMs: z3.number().int().nonnegative().nullable(),
|
|
474
|
+
paidAtMs: z3.number().int().nonnegative().nullable(),
|
|
475
|
+
createdAtMs: z3.number().int().nonnegative(),
|
|
476
|
+
updatedAtMs: z3.number().int().nonnegative()
|
|
477
|
+
});
|
|
478
|
+
var CreateCollectionIntentInputSchema = z3.object({
|
|
479
|
+
reference: ReferenceSchema.optional(),
|
|
480
|
+
amountKobo: MoneyKoboSchema.optional(),
|
|
481
|
+
currency: CurrencySchema2.optional(),
|
|
482
|
+
terminalId: z3.string().uuid().optional(),
|
|
483
|
+
terminalLabel: z3.string().trim().min(1).max(25).optional(),
|
|
484
|
+
merchantCity: z3.string().trim().min(1).max(15).optional(),
|
|
485
|
+
description: z3.string().trim().min(1).max(280).optional(),
|
|
486
|
+
expiresAtMs: z3.number().int().positive().optional(),
|
|
487
|
+
provider: z3.string().trim().min(1).max(40).optional(),
|
|
488
|
+
metadata: MetadataSchema.optional()
|
|
489
|
+
});
|
|
490
|
+
var PublicCollectionIntentSchema = z3.object({
|
|
491
|
+
intentId: z3.string().uuid(),
|
|
492
|
+
reference: z3.string(),
|
|
493
|
+
amountKobo: z3.number().int().positive().nullable(),
|
|
494
|
+
currency: z3.string().length(3),
|
|
495
|
+
status: z3.enum(COLLECTION_INTENT_STATUSES),
|
|
496
|
+
merchantAccountId: z3.string().uuid(),
|
|
497
|
+
merchantName: z3.string(),
|
|
498
|
+
merchantCategoryCode: z3.string(),
|
|
499
|
+
description: z3.string().nullable(),
|
|
500
|
+
expiresAtMs: z3.number().int().nonnegative().nullable()
|
|
501
|
+
});
|
|
502
|
+
var PayCollectionInputSchema = z3.object({
|
|
503
|
+
reference: ReferenceSchema,
|
|
504
|
+
amountKobo: MoneyKoboSchema.optional(),
|
|
505
|
+
currency: CurrencySchema2.optional(),
|
|
506
|
+
idempotencyKey: z3.string().trim().min(8).max(160).optional()
|
|
507
|
+
});
|
|
508
|
+
var CollectionPaymentSchema = z3.object({
|
|
509
|
+
paymentId: z3.string().uuid(),
|
|
510
|
+
intentId: z3.string().uuid(),
|
|
511
|
+
accountId: z3.string().uuid(),
|
|
512
|
+
payerUserId: z3.string().uuid().nullable(),
|
|
513
|
+
merchantOwnerUserId: z3.string().uuid(),
|
|
514
|
+
amountKobo: MoneyKoboSchema,
|
|
515
|
+
currency: z3.string().length(3),
|
|
516
|
+
status: z3.enum(COLLECTION_PAYMENT_STATUSES),
|
|
517
|
+
provider: z3.string(),
|
|
518
|
+
providerReference: z3.string().nullable(),
|
|
519
|
+
idempotencyKey: z3.string().nullable(),
|
|
520
|
+
ledgerRef: z3.string(),
|
|
521
|
+
failureCode: z3.string().nullable(),
|
|
522
|
+
failureMessage: z3.string().nullable(),
|
|
523
|
+
paidAtMs: z3.number().int().nonnegative().nullable(),
|
|
524
|
+
createdAtMs: z3.number().int().nonnegative(),
|
|
525
|
+
updatedAtMs: z3.number().int().nonnegative()
|
|
526
|
+
});
|
|
527
|
+
var CollectionPaymentResultSchema = z3.object({
|
|
528
|
+
payment: CollectionPaymentSchema,
|
|
529
|
+
intent: CollectionIntentSchema,
|
|
530
|
+
receipt: z3.unknown().optional(),
|
|
531
|
+
replayed: z3.boolean()
|
|
532
|
+
});
|
|
533
|
+
var CollectionReportSummarySchema = z3.object({
|
|
534
|
+
accountId: z3.string().uuid(),
|
|
535
|
+
fromMs: z3.number().int().nonnegative(),
|
|
536
|
+
toMs: z3.number().int().nonnegative(),
|
|
537
|
+
currency: z3.string().length(3),
|
|
538
|
+
paidCount: z3.number().int().nonnegative(),
|
|
539
|
+
paidAmountKobo: z3.number().int().nonnegative(),
|
|
540
|
+
pendingCount: z3.number().int().nonnegative(),
|
|
541
|
+
failedCount: z3.number().int().nonnegative(),
|
|
542
|
+
reversedCount: z3.number().int().nonnegative(),
|
|
543
|
+
availableBalanceKobo: z3.number().int().nonnegative()
|
|
544
|
+
});
|
|
545
|
+
var CollectionStatementSchema = z3.object({
|
|
546
|
+
accountId: z3.string().uuid(),
|
|
547
|
+
year: z3.number().int(),
|
|
548
|
+
month: z3.number().int().min(1).max(12),
|
|
549
|
+
currency: z3.string().length(3),
|
|
550
|
+
totalPaidKobo: z3.number().int().nonnegative(),
|
|
551
|
+
items: z3.array(CollectionPaymentSchema)
|
|
552
|
+
});
|
|
553
|
+
var CreatePayoutInputSchema = z3.object({
|
|
554
|
+
amountKobo: MoneyKoboSchema,
|
|
555
|
+
currency: CurrencySchema2.optional(),
|
|
556
|
+
idempotencyKey: z3.string().trim().min(8).max(160)
|
|
557
|
+
});
|
|
558
|
+
var MerchantPayoutSchema = z3.object({
|
|
559
|
+
payoutId: z3.string().uuid(),
|
|
560
|
+
accountId: z3.string().uuid(),
|
|
561
|
+
amountKobo: MoneyKoboSchema,
|
|
562
|
+
currency: z3.string().length(3),
|
|
563
|
+
status: z3.enum(MERCHANT_PAYOUT_STATUSES),
|
|
564
|
+
idempotencyKey: z3.string().nullable(),
|
|
565
|
+
ledgerRef: z3.string(),
|
|
566
|
+
providerReference: z3.string().nullable(),
|
|
567
|
+
requestedByUserId: z3.string().uuid().nullable(),
|
|
568
|
+
failureCode: z3.string().nullable(),
|
|
569
|
+
failureMessage: z3.string().nullable(),
|
|
570
|
+
createdAtMs: z3.number().int().nonnegative(),
|
|
571
|
+
updatedAtMs: z3.number().int().nonnegative()
|
|
572
|
+
});
|
|
573
|
+
var ProviderEventInputSchema = z3.object({
|
|
574
|
+
provider: z3.string().trim().min(1).max(80),
|
|
575
|
+
eventId: z3.string().trim().min(1).max(160),
|
|
576
|
+
eventType: z3.string().trim().min(1).max(120),
|
|
577
|
+
payload: z3.record(z3.unknown()).optional()
|
|
578
|
+
});
|
|
579
|
+
var ProviderEventRecordSchema = z3.object({
|
|
580
|
+
id: z3.string().uuid(),
|
|
581
|
+
provider: z3.string(),
|
|
582
|
+
eventId: z3.string(),
|
|
583
|
+
eventType: z3.string(),
|
|
584
|
+
signatureVerified: z3.boolean(),
|
|
585
|
+
receivedAtMs: z3.number().int().nonnegative(),
|
|
586
|
+
processedAtMs: z3.number().int().nonnegative().nullable()
|
|
587
|
+
});
|
|
588
|
+
function createCollectionsClient(opts) {
|
|
589
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
590
|
+
if (!fetchImpl) {
|
|
591
|
+
throw new Error(
|
|
592
|
+
"createCollectionsClient: no fetch implementation available"
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
596
|
+
async function call(method, path, body, parser) {
|
|
597
|
+
const init2 = {
|
|
598
|
+
method,
|
|
599
|
+
headers: { accept: "application/json" }
|
|
600
|
+
};
|
|
601
|
+
if (body !== void 0) {
|
|
602
|
+
init2.body = JSON.stringify(body);
|
|
603
|
+
init2.headers = { ...init2.headers, "content-type": "application/json" };
|
|
604
|
+
}
|
|
605
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
606
|
+
const text = await resp.text();
|
|
607
|
+
let raw;
|
|
608
|
+
if (text) {
|
|
609
|
+
try {
|
|
610
|
+
raw = JSON.parse(text);
|
|
611
|
+
} catch {
|
|
612
|
+
raw = text;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (!resp.ok) {
|
|
616
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
617
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
618
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
619
|
+
}
|
|
620
|
+
return parser(raw);
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
upsertMerchantProfile: (accountId, input) => call(
|
|
624
|
+
"PUT",
|
|
625
|
+
`/v1/accounts/${encodeURIComponent(accountId)}/merchant-profile`,
|
|
626
|
+
UpsertMerchantProfileInputSchema.parse(input),
|
|
627
|
+
(raw) => MerchantProfileSchema.parse(raw)
|
|
628
|
+
),
|
|
629
|
+
getMerchantProfile: (accountId) => call(
|
|
630
|
+
"GET",
|
|
631
|
+
`/v1/accounts/${encodeURIComponent(accountId)}/merchant-profile`,
|
|
632
|
+
void 0,
|
|
633
|
+
(raw) => MerchantProfileSchema.parse(raw)
|
|
634
|
+
),
|
|
635
|
+
createIntent: (input) => call(
|
|
636
|
+
"POST",
|
|
637
|
+
"/v1/collections/intents",
|
|
638
|
+
CreateCollectionIntentInputSchema.parse(input),
|
|
639
|
+
(raw) => CollectionIntentSchema.parse(raw)
|
|
640
|
+
),
|
|
641
|
+
getIntent: (intentId) => call(
|
|
642
|
+
"GET",
|
|
643
|
+
`/v1/collections/intents/${encodeURIComponent(intentId)}`,
|
|
644
|
+
void 0,
|
|
645
|
+
(raw) => CollectionIntentSchema.parse(raw)
|
|
646
|
+
),
|
|
647
|
+
resolveIntent: (reference) => call(
|
|
648
|
+
"GET",
|
|
649
|
+
`/v1/collections/resolve/${encodeURIComponent(reference)}`,
|
|
650
|
+
void 0,
|
|
651
|
+
(raw) => PublicCollectionIntentSchema.parse(raw)
|
|
652
|
+
),
|
|
653
|
+
pay: (input) => call(
|
|
654
|
+
"POST",
|
|
655
|
+
"/v1/collections/pay",
|
|
656
|
+
PayCollectionInputSchema.parse(input),
|
|
657
|
+
(raw) => CollectionPaymentResultSchema.parse(raw)
|
|
658
|
+
),
|
|
659
|
+
reportSummary: (input) => {
|
|
660
|
+
const qs = new URLSearchParams({
|
|
661
|
+
fromMs: String(input.fromMs),
|
|
662
|
+
toMs: String(input.toMs)
|
|
663
|
+
});
|
|
664
|
+
if (input.currency) qs.set("currency", input.currency);
|
|
665
|
+
return call(
|
|
666
|
+
"GET",
|
|
667
|
+
`/v1/collections/reports/summary?${qs.toString()}`,
|
|
668
|
+
void 0,
|
|
669
|
+
(raw) => CollectionReportSummarySchema.parse(raw)
|
|
670
|
+
);
|
|
671
|
+
},
|
|
672
|
+
monthlyStatement: (input) => {
|
|
673
|
+
const qs = new URLSearchParams({
|
|
674
|
+
year: String(input.year),
|
|
675
|
+
month: String(input.month)
|
|
676
|
+
});
|
|
677
|
+
if (input.currency) qs.set("currency", input.currency);
|
|
678
|
+
return call(
|
|
679
|
+
"GET",
|
|
680
|
+
`/v1/collections/statements/monthly?${qs.toString()}`,
|
|
681
|
+
void 0,
|
|
682
|
+
(raw) => CollectionStatementSchema.parse(raw)
|
|
683
|
+
);
|
|
684
|
+
},
|
|
685
|
+
createPayout: (input) => call(
|
|
686
|
+
"POST",
|
|
687
|
+
"/v1/collections/payouts",
|
|
688
|
+
CreatePayoutInputSchema.parse(input),
|
|
689
|
+
(raw) => MerchantPayoutSchema.parse(raw)
|
|
690
|
+
),
|
|
691
|
+
getPayout: (payoutId) => call(
|
|
692
|
+
"GET",
|
|
693
|
+
`/v1/collections/payouts/${encodeURIComponent(payoutId)}`,
|
|
694
|
+
void 0,
|
|
695
|
+
(raw) => MerchantPayoutSchema.parse(raw)
|
|
696
|
+
),
|
|
697
|
+
recordProviderEvent: (input) => call(
|
|
698
|
+
"POST",
|
|
699
|
+
"/v1/collections/provider-events",
|
|
700
|
+
ProviderEventInputSchema.parse(input),
|
|
701
|
+
(raw) => ProviderEventRecordSchema.parse(raw)
|
|
702
|
+
)
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function createPartnerCollectionsClient(opts) {
|
|
706
|
+
const client = createCollectionsClient(opts);
|
|
707
|
+
return {
|
|
708
|
+
createIntent: client.createIntent,
|
|
709
|
+
getIntent: client.getIntent,
|
|
710
|
+
reportSummary: client.reportSummary,
|
|
711
|
+
monthlyStatement: client.monthlyStatement,
|
|
712
|
+
createPayout: client.createPayout,
|
|
713
|
+
getPayout: client.getPayout,
|
|
714
|
+
recordProviderEvent: client.recordProviderEvent
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function createConsumerCollectionsClient(opts) {
|
|
718
|
+
const client = createCollectionsClient(opts);
|
|
719
|
+
return {
|
|
720
|
+
resolveIntent: client.resolveIntent,
|
|
721
|
+
pay: client.pay
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
390
725
|
// src/client.ts
|
|
391
726
|
var FlurClient = class {
|
|
392
727
|
baseUrl;
|
|
@@ -635,6 +970,36 @@ var FlurClient = class {
|
|
|
635
970
|
}
|
|
636
971
|
);
|
|
637
972
|
}
|
|
973
|
+
async resolveCollection(reference, options) {
|
|
974
|
+
return this.requestJson(
|
|
975
|
+
`/v1/collections/resolve/${encodeURIComponent(reference)}`,
|
|
976
|
+
{
|
|
977
|
+
method: "GET",
|
|
978
|
+
headers: {
|
|
979
|
+
authorization: `Bearer ${options.accessToken}`
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
void 0,
|
|
983
|
+
PublicCollectionIntentSchema
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
async payCollection(input, options) {
|
|
987
|
+
const idempotencyKey = input.idempotencyKey ?? options.idempotencyKey ?? getSecureRandomUuid();
|
|
988
|
+
return this.requestJson(
|
|
989
|
+
"/v1/collections/pay",
|
|
990
|
+
{
|
|
991
|
+
method: "POST",
|
|
992
|
+
headers: {
|
|
993
|
+
"content-type": "application/json",
|
|
994
|
+
authorization: `Bearer ${options.accessToken}`,
|
|
995
|
+
"x-idempotency-key": idempotencyKey
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
PayCollectionInputSchema,
|
|
999
|
+
CollectionPaymentResultSchema,
|
|
1000
|
+
{ ...input, idempotencyKey }
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
638
1003
|
async accountSummary(options) {
|
|
639
1004
|
return this.requestJson(
|
|
640
1005
|
"/api/v1/account/summary",
|
|
@@ -733,7 +1098,7 @@ var FlurClient = class {
|
|
|
733
1098
|
try {
|
|
734
1099
|
body = requestSchema ? JSON.stringify(requestSchema.parse(input)) : init2.body;
|
|
735
1100
|
} catch (err) {
|
|
736
|
-
if (err instanceof
|
|
1101
|
+
if (err instanceof z4.ZodError) {
|
|
737
1102
|
throw new FlurError("Invalid request payload", "INVALID_REQUEST", {
|
|
738
1103
|
details: err.flatten()
|
|
739
1104
|
});
|
|
@@ -761,7 +1126,7 @@ var FlurClient = class {
|
|
|
761
1126
|
try {
|
|
762
1127
|
return responseSchema.parse(payload);
|
|
763
1128
|
} catch (err) {
|
|
764
|
-
if (err instanceof
|
|
1129
|
+
if (err instanceof z4.ZodError) {
|
|
765
1130
|
throw new FlurError(
|
|
766
1131
|
"SDK contract validation failed",
|
|
767
1132
|
"INVALID_REQUEST",
|
|
@@ -1219,24 +1584,24 @@ function verifyCanonical(value, signature, publicKey) {
|
|
|
1219
1584
|
}
|
|
1220
1585
|
|
|
1221
1586
|
// src/offline/oac.ts
|
|
1222
|
-
import { z as
|
|
1587
|
+
import { z as z5 } from "zod";
|
|
1223
1588
|
var OAC_DEFAULT_PER_TX_KOBO = 5e5;
|
|
1224
1589
|
var OAC_DEFAULT_CUMULATIVE_KOBO = 2e6;
|
|
1225
1590
|
var OAC_DEFAULT_VALIDITY_MS = 24 * 60 * 60 * 1e3;
|
|
1226
|
-
var HexString = (length) =>
|
|
1591
|
+
var HexString = (length) => z5.string().regex(
|
|
1227
1592
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
1228
1593
|
`expected ${length}-byte hex string`
|
|
1229
1594
|
);
|
|
1230
|
-
var OACSchema =
|
|
1231
|
-
userId:
|
|
1232
|
-
deviceId:
|
|
1595
|
+
var OACSchema = z5.object({
|
|
1596
|
+
userId: z5.string().min(1),
|
|
1597
|
+
deviceId: z5.string().min(1),
|
|
1233
1598
|
devicePublicKey: HexString(32),
|
|
1234
|
-
perTxCapKobo:
|
|
1235
|
-
cumulativeCapKobo:
|
|
1236
|
-
validFromMs:
|
|
1237
|
-
validUntilMs:
|
|
1238
|
-
counterSeed:
|
|
1239
|
-
nonce:
|
|
1599
|
+
perTxCapKobo: z5.number().int().nonnegative(),
|
|
1600
|
+
cumulativeCapKobo: z5.number().int().nonnegative(),
|
|
1601
|
+
validFromMs: z5.number().int().nonnegative(),
|
|
1602
|
+
validUntilMs: z5.number().int().positive(),
|
|
1603
|
+
counterSeed: z5.number().int().nonnegative(),
|
|
1604
|
+
nonce: z5.string().min(1),
|
|
1240
1605
|
issuerSig: HexString(64)
|
|
1241
1606
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
1242
1607
|
message: "validUntilMs must be greater than validFromMs"
|
|
@@ -1343,19 +1708,19 @@ function decodeBase45(s) {
|
|
|
1343
1708
|
}
|
|
1344
1709
|
|
|
1345
1710
|
// src/offline/messages.ts
|
|
1346
|
-
import { z as
|
|
1347
|
-
var HexSig =
|
|
1348
|
-
var OfflinePaymentRequestSchema =
|
|
1349
|
-
reference:
|
|
1350
|
-
amountKobo:
|
|
1711
|
+
import { z as z6 } from "zod";
|
|
1712
|
+
var HexSig = z6.string().regex(/^[0-9a-fA-F]{128}$/, "expected 64-byte hex signature");
|
|
1713
|
+
var OfflinePaymentRequestSchema = z6.object({
|
|
1714
|
+
reference: z6.string().min(1),
|
|
1715
|
+
amountKobo: z6.number().int().positive(),
|
|
1351
1716
|
merchantOAC: OACSchema,
|
|
1352
|
-
expiresAtMs:
|
|
1717
|
+
expiresAtMs: z6.number().int().positive(),
|
|
1353
1718
|
merchantSig: HexSig
|
|
1354
1719
|
});
|
|
1355
|
-
var OfflinePaymentAuthorizationSchema =
|
|
1720
|
+
var OfflinePaymentAuthorizationSchema = z6.object({
|
|
1356
1721
|
request: OfflinePaymentRequestSchema,
|
|
1357
1722
|
payerOAC: OACSchema,
|
|
1358
|
-
payerCounter:
|
|
1723
|
+
payerCounter: z6.number().int().positive(),
|
|
1359
1724
|
payerSig: HexSig
|
|
1360
1725
|
});
|
|
1361
1726
|
function buildPaymentRequest(input) {
|
|
@@ -1464,56 +1829,56 @@ function decodeAuthorizationQR(s) {
|
|
|
1464
1829
|
}
|
|
1465
1830
|
|
|
1466
1831
|
// src/offline/settlements.ts
|
|
1467
|
-
import { z as
|
|
1832
|
+
import { z as z7 } from "zod";
|
|
1468
1833
|
import { sha256 } from "@noble/hashes/sha256";
|
|
1469
1834
|
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
|
1470
|
-
var OfflineTokenSchema =
|
|
1471
|
-
tokenId:
|
|
1472
|
-
tokenSerial:
|
|
1473
|
-
issuerAccountId:
|
|
1474
|
-
payerUserId:
|
|
1475
|
-
maxAmountKobo:
|
|
1476
|
-
currency:
|
|
1477
|
-
issuedAtMs:
|
|
1478
|
-
expiresAtMs:
|
|
1479
|
-
issuerSig:
|
|
1480
|
-
});
|
|
1481
|
-
var PaymentClaimSchema =
|
|
1482
|
-
encounterId:
|
|
1483
|
-
tokenSerial:
|
|
1484
|
-
payerUserId:
|
|
1485
|
-
payeeUserId:
|
|
1486
|
-
payerNonce:
|
|
1487
|
-
payeeNonce:
|
|
1488
|
-
amountKobo:
|
|
1489
|
-
currency:
|
|
1490
|
-
occurredAtMs:
|
|
1491
|
-
completedAtMs:
|
|
1492
|
-
contextId:
|
|
1493
|
-
payerPubkey:
|
|
1494
|
-
payerSignature:
|
|
1495
|
-
payeePubkey:
|
|
1496
|
-
payeeSignature:
|
|
1497
|
-
});
|
|
1498
|
-
var SettlementSchema =
|
|
1499
|
-
settlementId:
|
|
1500
|
-
settlementKey:
|
|
1501
|
-
encounterId:
|
|
1502
|
-
issuerAccountId:
|
|
1503
|
-
tokenSerial:
|
|
1504
|
-
payerUserId:
|
|
1505
|
-
payeeUserId:
|
|
1506
|
-
amountKobo:
|
|
1507
|
-
currency:
|
|
1508
|
-
receiptId:
|
|
1509
|
-
status:
|
|
1510
|
-
issuerSig:
|
|
1511
|
-
createdAtMs:
|
|
1512
|
-
});
|
|
1513
|
-
var SettleResponseSchema =
|
|
1835
|
+
var OfflineTokenSchema = z7.object({
|
|
1836
|
+
tokenId: z7.string().uuid(),
|
|
1837
|
+
tokenSerial: z7.string(),
|
|
1838
|
+
issuerAccountId: z7.string().uuid(),
|
|
1839
|
+
payerUserId: z7.string().uuid(),
|
|
1840
|
+
maxAmountKobo: z7.number().int().positive(),
|
|
1841
|
+
currency: z7.string().length(3),
|
|
1842
|
+
issuedAtMs: z7.number().int().nonnegative(),
|
|
1843
|
+
expiresAtMs: z7.number().int().nonnegative(),
|
|
1844
|
+
issuerSig: z7.string()
|
|
1845
|
+
});
|
|
1846
|
+
var PaymentClaimSchema = z7.object({
|
|
1847
|
+
encounterId: z7.string().regex(/^[0-9a-f]{64}$/i).optional(),
|
|
1848
|
+
tokenSerial: z7.string(),
|
|
1849
|
+
payerUserId: z7.string().uuid(),
|
|
1850
|
+
payeeUserId: z7.string().uuid(),
|
|
1851
|
+
payerNonce: z7.string(),
|
|
1852
|
+
payeeNonce: z7.string(),
|
|
1853
|
+
amountKobo: z7.number().int().positive(),
|
|
1854
|
+
currency: z7.string().length(3).default("NGN"),
|
|
1855
|
+
occurredAtMs: z7.number().int().nonnegative(),
|
|
1856
|
+
completedAtMs: z7.number().int().nonnegative().optional(),
|
|
1857
|
+
contextId: z7.string().optional(),
|
|
1858
|
+
payerPubkey: z7.string().regex(/^[0-9a-f]{64}$/i),
|
|
1859
|
+
payerSignature: z7.string().regex(/^[0-9a-f]+$/i),
|
|
1860
|
+
payeePubkey: z7.string().regex(/^[0-9a-f]{64}$/i).optional(),
|
|
1861
|
+
payeeSignature: z7.string().regex(/^[0-9a-f]+$/i).optional()
|
|
1862
|
+
});
|
|
1863
|
+
var SettlementSchema = z7.object({
|
|
1864
|
+
settlementId: z7.string().uuid(),
|
|
1865
|
+
settlementKey: z7.string().regex(/^[0-9a-f]{64}$/i),
|
|
1866
|
+
encounterId: z7.string().regex(/^[0-9a-f]{64}$/i),
|
|
1867
|
+
issuerAccountId: z7.string().uuid(),
|
|
1868
|
+
tokenSerial: z7.string(),
|
|
1869
|
+
payerUserId: z7.string().uuid(),
|
|
1870
|
+
payeeUserId: z7.string().uuid(),
|
|
1871
|
+
amountKobo: z7.number().int().nonnegative(),
|
|
1872
|
+
currency: z7.string().length(3),
|
|
1873
|
+
receiptId: z7.string().nullable(),
|
|
1874
|
+
status: z7.enum(["SETTLED", "REVIEW", "REJECTED"]),
|
|
1875
|
+
issuerSig: z7.string(),
|
|
1876
|
+
createdAtMs: z7.number().int().nonnegative()
|
|
1877
|
+
});
|
|
1878
|
+
var SettleResponseSchema = z7.object({
|
|
1514
1879
|
settlement: SettlementSchema,
|
|
1515
|
-
encounterId:
|
|
1516
|
-
replayed:
|
|
1880
|
+
encounterId: z7.string().regex(/^[0-9a-f]{64}$/i),
|
|
1881
|
+
replayed: z7.boolean()
|
|
1517
1882
|
});
|
|
1518
1883
|
var ENCOUNTER_DOMAIN = "offline:v1:encounter";
|
|
1519
1884
|
async function sha256Hex(input) {
|
|
@@ -1686,134 +2051,352 @@ function createHmacFetch(opts) {
|
|
|
1686
2051
|
});
|
|
1687
2052
|
}
|
|
1688
2053
|
|
|
1689
|
-
// src/
|
|
1690
|
-
import { z as
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
"
|
|
1696
|
-
"
|
|
1697
|
-
"
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
"
|
|
1701
|
-
"
|
|
1702
|
-
"
|
|
1703
|
-
"
|
|
1704
|
-
"
|
|
2054
|
+
// src/partner/client.ts
|
|
2055
|
+
import { z as z8 } from "zod";
|
|
2056
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha256";
|
|
2057
|
+
import { hmac as hmac2 } from "@noble/hashes/hmac";
|
|
2058
|
+
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils";
|
|
2059
|
+
var PARTNER_SCOPES = [
|
|
2060
|
+
"passes:write",
|
|
2061
|
+
"passes:read",
|
|
2062
|
+
"passes:redeem",
|
|
2063
|
+
"receipts:write",
|
|
2064
|
+
"receipts:read",
|
|
2065
|
+
"offline:issue",
|
|
2066
|
+
"offline:settle",
|
|
2067
|
+
"offline:read",
|
|
2068
|
+
"collections:write",
|
|
2069
|
+
"collections:read",
|
|
2070
|
+
"collections:pay",
|
|
2071
|
+
"collections:webhook",
|
|
2072
|
+
"reports:read",
|
|
2073
|
+
"payouts:write",
|
|
2074
|
+
"payouts:read",
|
|
2075
|
+
"partner:funding:write",
|
|
2076
|
+
"partner:payout:write",
|
|
2077
|
+
"partner:reconciliation:read"
|
|
1705
2078
|
];
|
|
1706
|
-
var
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
)
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
)
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
/** Optional client/template grouping id (server may omit). */
|
|
1716
|
-
templateId: z7.string().min(1).optional(),
|
|
1717
|
-
/** Optional human-facing holder identity (server may omit). The cryptographic binding
|
|
1718
|
-
* is `holderDevicePubkey` below. */
|
|
1719
|
-
holderUserId: z7.string().min(1).optional(),
|
|
1720
|
-
kind: z7.enum(PASS_KINDS),
|
|
1721
|
-
issuerId: z7.string().min(1),
|
|
1722
|
-
issuedAtMs: z7.number().int().nonnegative(),
|
|
1723
|
-
validFromMs: z7.number().int().nonnegative(),
|
|
1724
|
-
validUntilMs: z7.number().int().positive(),
|
|
1725
|
-
state: z7.enum(PASS_STATES),
|
|
1726
|
-
metadata: PassMetadataSchema,
|
|
1727
|
-
nonce: z7.string().min(1),
|
|
1728
|
-
/** Device id this pass is bound to (FK to backend `device_keys`). */
|
|
1729
|
-
holderDeviceId: z7.string().min(1),
|
|
1730
|
-
/** 32-byte hex Ed25519 public key of the bound device. The redemption signature
|
|
1731
|
-
* is verified against this key — it is the security-critical binding. */
|
|
1732
|
-
holderDevicePubkey: HexString2(32),
|
|
1733
|
-
/** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
|
|
1734
|
-
amountKobo: z7.number().int().nonnegative().optional(),
|
|
1735
|
-
/** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
|
|
1736
|
-
currency: z7.string().min(3).max(8),
|
|
1737
|
-
/** Monotonic redemption counter floor. Redemption.counter MUST be > counterSeed. */
|
|
1738
|
-
counterSeed: z7.number().int().nonnegative(),
|
|
1739
|
-
/** Optional cumulative spend cap in kobo across all redemptions of this pass. */
|
|
1740
|
-
cumulativeCapKobo: z7.number().int().nonnegative().optional(),
|
|
1741
|
-
issuerSig: HexString2(64)
|
|
1742
|
-
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
1743
|
-
message: "validUntilMs must be greater than validFromMs"
|
|
2079
|
+
var ApiCredentialPublicSchema = z8.object({
|
|
2080
|
+
id: z8.string().uuid(),
|
|
2081
|
+
accountId: z8.string().uuid(),
|
|
2082
|
+
keyId: z8.string(),
|
|
2083
|
+
scopes: z8.array(z8.enum(PARTNER_SCOPES)),
|
|
2084
|
+
label: z8.string().nullable(),
|
|
2085
|
+
createdAtMs: z8.number().int().nonnegative(),
|
|
2086
|
+
lastUsedAtMs: z8.number().int().nonnegative().nullable(),
|
|
2087
|
+
revokedAtMs: z8.number().int().nonnegative().nullable()
|
|
1744
2088
|
});
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
issuerId: input.issuerId,
|
|
1753
|
-
issuedAtMs: input.issuedAtMs,
|
|
1754
|
-
validFromMs: input.validFromMs,
|
|
1755
|
-
validUntilMs: input.validUntilMs,
|
|
1756
|
-
state: input.state,
|
|
1757
|
-
metadata: input.metadata,
|
|
1758
|
-
nonce: input.nonce,
|
|
1759
|
-
holderDeviceId: input.holderDeviceId,
|
|
1760
|
-
holderDevicePubkey: input.holderDevicePubkey,
|
|
1761
|
-
currency: input.currency ?? "NGN",
|
|
1762
|
-
counterSeed: input.counterSeed
|
|
1763
|
-
};
|
|
1764
|
-
if (typeof input.templateId === "string") out.templateId = input.templateId;
|
|
1765
|
-
if (typeof input.holderUserId === "string")
|
|
1766
|
-
out.holderUserId = input.holderUserId;
|
|
1767
|
-
if (typeof input.amountKobo === "number") out.amountKobo = input.amountKobo;
|
|
1768
|
-
if (typeof input.cumulativeCapKobo === "number") {
|
|
1769
|
-
out.cumulativeCapKobo = input.cumulativeCapKobo;
|
|
1770
|
-
}
|
|
1771
|
-
return out;
|
|
2089
|
+
var MintedApiCredentialSchema = ApiCredentialPublicSchema.extend({
|
|
2090
|
+
secret: z8.string().min(1)
|
|
2091
|
+
});
|
|
2092
|
+
var enc = new TextEncoder();
|
|
2093
|
+
async function sha256Hex2(input) {
|
|
2094
|
+
const data = typeof input === "string" ? enc.encode(input) : input;
|
|
2095
|
+
return bytesToHex4(sha2563(data));
|
|
1772
2096
|
}
|
|
1773
|
-
function
|
|
1774
|
-
|
|
1775
|
-
sign(canonicalJSONBytes(unsigned), issuerPrivateKey)
|
|
1776
|
-
);
|
|
1777
|
-
return { ...unsigned, issuerSig };
|
|
2097
|
+
async function hmacSha256Hex(keyHex, message) {
|
|
2098
|
+
return bytesToHex4(hmac2(sha2563, enc.encode(keyHex), enc.encode(message)));
|
|
1778
2099
|
}
|
|
1779
|
-
function
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
return verify(
|
|
1784
|
-
canonicalJSONBytes(unsigned),
|
|
1785
|
-
hexToBytes(issuerSig),
|
|
1786
|
-
issuerPublicKey
|
|
1787
|
-
);
|
|
1788
|
-
} catch {
|
|
1789
|
-
return false;
|
|
1790
|
-
}
|
|
2100
|
+
function defaultNonce2() {
|
|
2101
|
+
const c = globalThis.crypto;
|
|
2102
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
2103
|
+
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
|
|
1791
2104
|
}
|
|
1792
|
-
function
|
|
1793
|
-
return
|
|
2105
|
+
function canonical(params) {
|
|
2106
|
+
return [
|
|
2107
|
+
params.method.toUpperCase(),
|
|
2108
|
+
params.path,
|
|
2109
|
+
params.ts,
|
|
2110
|
+
params.nonce,
|
|
2111
|
+
params.bodyHash
|
|
2112
|
+
].join("\n");
|
|
1794
2113
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
2114
|
+
async function signPartnerRequest(params) {
|
|
2115
|
+
const ts = String(Math.floor((params.nowMs ?? Date.now()) / 1e3));
|
|
2116
|
+
const nonce = params.nonce ?? defaultNonce2();
|
|
2117
|
+
const bodyData = typeof params.body === "string" ? enc.encode(params.body) : params.body ?? new Uint8Array();
|
|
2118
|
+
const bodyHash = await sha256Hex2(bodyData);
|
|
2119
|
+
const message = canonical({
|
|
2120
|
+
method: params.method,
|
|
2121
|
+
path: params.path,
|
|
2122
|
+
ts,
|
|
2123
|
+
nonce,
|
|
2124
|
+
bodyHash
|
|
2125
|
+
});
|
|
2126
|
+
const signingKey = await sha256Hex2(params.secret);
|
|
2127
|
+
const sig = await hmacSha256Hex(signingKey, message);
|
|
2128
|
+
return {
|
|
2129
|
+
authorization: `Flur-Hmac key=${params.keyId}, ts=${ts}, nonce=${nonce}, sig=${sig}`,
|
|
2130
|
+
ts,
|
|
2131
|
+
nonce,
|
|
2132
|
+
bodyHash
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
function createFlurPartnerClient(opts) {
|
|
2136
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2137
|
+
if (!fetchImpl) {
|
|
2138
|
+
throw new Error(
|
|
2139
|
+
"createFlurPartnerClient: no fetch implementation available"
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
2142
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2143
|
+
const scopeHeader = normalizeScopeHeader(opts.scope);
|
|
2144
|
+
async function makeSignedInit(method, path, body) {
|
|
2145
|
+
const sig = await signPartnerRequest({
|
|
2146
|
+
keyId: opts.keyId,
|
|
2147
|
+
secret: opts.secret,
|
|
2148
|
+
method,
|
|
2149
|
+
path,
|
|
2150
|
+
body: body ?? "",
|
|
2151
|
+
nowMs: opts.nowMs?.(),
|
|
2152
|
+
nonce: opts.nonce?.()
|
|
2153
|
+
});
|
|
2154
|
+
const headers = {
|
|
2155
|
+
authorization: sig.authorization,
|
|
2156
|
+
accept: "application/json"
|
|
2157
|
+
};
|
|
2158
|
+
if (scopeHeader) headers["x-flur-scope"] = scopeHeader;
|
|
2159
|
+
if (body !== void 0) headers["content-type"] = "application/json";
|
|
2160
|
+
const init2 = { method, headers };
|
|
2161
|
+
if (body !== void 0) init2.body = body;
|
|
2162
|
+
return init2;
|
|
2163
|
+
}
|
|
2164
|
+
async function request(opts2) {
|
|
2165
|
+
const bodyStr = opts2.body === void 0 ? void 0 : JSON.stringify(opts2.body);
|
|
2166
|
+
const init2 = await makeSignedInit(opts2.method, opts2.path, bodyStr);
|
|
2167
|
+
const resp = await fetchImpl(`${baseUrl}${opts2.path}`, init2);
|
|
2168
|
+
const text = await resp.text();
|
|
2169
|
+
let raw;
|
|
2170
|
+
if (text) {
|
|
2171
|
+
try {
|
|
2172
|
+
raw = JSON.parse(text);
|
|
2173
|
+
} catch {
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
if (!resp.ok) {
|
|
2177
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2178
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2179
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
2180
|
+
}
|
|
2181
|
+
return raw;
|
|
2182
|
+
}
|
|
2183
|
+
const fetchLike = async (input, init2) => {
|
|
2184
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
2185
|
+
const u = new URL(url, baseUrl);
|
|
2186
|
+
const path = `${u.pathname}${u.search}`;
|
|
2187
|
+
const method = (init2?.method ?? "GET").toUpperCase();
|
|
2188
|
+
let bodyStr;
|
|
2189
|
+
if (init2?.body) {
|
|
2190
|
+
bodyStr = typeof init2.body === "string" ? init2.body : init2.body.toString();
|
|
2191
|
+
}
|
|
2192
|
+
const signed = await makeSignedInit(method, path, bodyStr);
|
|
2193
|
+
return fetchImpl(`${baseUrl}${path}`, {
|
|
2194
|
+
...init2,
|
|
2195
|
+
...signed,
|
|
2196
|
+
headers: {
|
|
2197
|
+
...init2?.headers,
|
|
2198
|
+
...signed.headers
|
|
2199
|
+
}
|
|
2200
|
+
});
|
|
2201
|
+
};
|
|
2202
|
+
return { request, fetch: fetchLike };
|
|
2203
|
+
}
|
|
2204
|
+
function normalizeScopeHeader(scope) {
|
|
2205
|
+
if (!scope || scope.length === 0) return void 0;
|
|
2206
|
+
const unique = Array.from(new Set(scope));
|
|
2207
|
+
for (const item of unique) {
|
|
2208
|
+
if (!PARTNER_SCOPES.includes(item)) {
|
|
2209
|
+
throw new Error(`unsupported partner scope: ${item}`);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return unique.join(" ");
|
|
2213
|
+
}
|
|
2214
|
+
function createApiCredentialsAdminClient(opts) {
|
|
2215
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2216
|
+
if (!fetchImpl) {
|
|
2217
|
+
throw new Error(
|
|
2218
|
+
"createApiCredentialsAdminClient: no fetch implementation available"
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2222
|
+
async function call(method, path, body, parser) {
|
|
2223
|
+
const init2 = {
|
|
2224
|
+
method,
|
|
2225
|
+
headers: {
|
|
2226
|
+
"content-type": "application/json",
|
|
2227
|
+
accept: "application/json"
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
2231
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
2232
|
+
const text = await resp.text();
|
|
2233
|
+
let raw;
|
|
2234
|
+
if (text) {
|
|
2235
|
+
try {
|
|
2236
|
+
raw = JSON.parse(text);
|
|
2237
|
+
} catch {
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
if (!resp.ok) {
|
|
2241
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2242
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2243
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
2244
|
+
}
|
|
2245
|
+
return parser(raw);
|
|
2246
|
+
}
|
|
2247
|
+
const listSchema = z8.object({
|
|
2248
|
+
items: z8.array(ApiCredentialPublicSchema)
|
|
2249
|
+
});
|
|
2250
|
+
return {
|
|
2251
|
+
list: (accountId) => call(
|
|
2252
|
+
"GET",
|
|
2253
|
+
`/v1/accounts/${accountId}/api-credentials`,
|
|
2254
|
+
void 0,
|
|
2255
|
+
(raw) => listSchema.parse(raw)
|
|
2256
|
+
),
|
|
2257
|
+
mint: (accountId, input) => call(
|
|
2258
|
+
"POST",
|
|
2259
|
+
`/v1/accounts/${accountId}/api-credentials`,
|
|
2260
|
+
input,
|
|
2261
|
+
(raw) => MintedApiCredentialSchema.parse(raw)
|
|
2262
|
+
),
|
|
2263
|
+
revoke: (accountId, credentialId) => call(
|
|
2264
|
+
"DELETE",
|
|
2265
|
+
`/v1/accounts/${accountId}/api-credentials/${credentialId}`,
|
|
2266
|
+
void 0,
|
|
2267
|
+
(raw) => ApiCredentialPublicSchema.parse(raw)
|
|
2268
|
+
)
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// src/passes/pass.ts
|
|
2273
|
+
import { z as z9 } from "zod";
|
|
2274
|
+
var PASS_KINDS = [
|
|
2275
|
+
"ride-ticket",
|
|
2276
|
+
"transit-pass",
|
|
2277
|
+
"event-ticket",
|
|
2278
|
+
"voucher",
|
|
2279
|
+
"loyalty",
|
|
2280
|
+
"receipt-link"
|
|
2281
|
+
];
|
|
2282
|
+
var PASS_STATES = [
|
|
2283
|
+
"issued",
|
|
2284
|
+
"active",
|
|
2285
|
+
"redeemed",
|
|
2286
|
+
"expired",
|
|
2287
|
+
"revoked"
|
|
2288
|
+
];
|
|
2289
|
+
var HexString2 = (length) => z9.string().regex(
|
|
2290
|
+
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
2291
|
+
`expected ${length}-byte hex string`
|
|
2292
|
+
);
|
|
2293
|
+
var PassMetadataSchema = z9.record(
|
|
2294
|
+
z9.union([z9.string(), z9.number(), z9.boolean(), z9.null()])
|
|
2295
|
+
);
|
|
2296
|
+
var PassSchema = z9.object({
|
|
2297
|
+
passId: z9.string().min(1),
|
|
2298
|
+
/** Optional client/template grouping id (server may omit). */
|
|
2299
|
+
templateId: z9.string().min(1).optional(),
|
|
2300
|
+
/** Optional human-facing holder identity (server may omit). The cryptographic binding
|
|
2301
|
+
* is `holderDevicePubkey` below. */
|
|
2302
|
+
holderUserId: z9.string().min(1).optional(),
|
|
2303
|
+
kind: z9.enum(PASS_KINDS),
|
|
2304
|
+
issuerId: z9.string().min(1),
|
|
2305
|
+
issuedAtMs: z9.number().int().nonnegative(),
|
|
2306
|
+
validFromMs: z9.number().int().nonnegative(),
|
|
2307
|
+
validUntilMs: z9.number().int().positive(),
|
|
2308
|
+
state: z9.enum(PASS_STATES),
|
|
2309
|
+
metadata: PassMetadataSchema,
|
|
2310
|
+
nonce: z9.string().min(1),
|
|
2311
|
+
/** Device id this pass is bound to (FK to backend `device_keys`). */
|
|
2312
|
+
holderDeviceId: z9.string().min(1),
|
|
2313
|
+
/** 32-byte hex Ed25519 public key of the bound device. The redemption signature
|
|
2314
|
+
* is verified against this key — it is the security-critical binding. */
|
|
2315
|
+
holderDevicePubkey: HexString2(32),
|
|
2316
|
+
/** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
|
|
2317
|
+
amountKobo: z9.number().int().nonnegative().optional(),
|
|
2318
|
+
/** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
|
|
2319
|
+
currency: z9.string().min(3).max(8),
|
|
2320
|
+
/** Monotonic redemption counter floor. Redemption.counter MUST be > counterSeed. */
|
|
2321
|
+
counterSeed: z9.number().int().nonnegative(),
|
|
2322
|
+
/** Optional cumulative spend cap in kobo across all redemptions of this pass. */
|
|
2323
|
+
cumulativeCapKobo: z9.number().int().nonnegative().optional(),
|
|
2324
|
+
issuerSig: HexString2(64)
|
|
2325
|
+
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
2326
|
+
message: "validUntilMs must be greater than validFromMs"
|
|
2327
|
+
});
|
|
2328
|
+
function buildPass(input) {
|
|
2329
|
+
if (input.validUntilMs <= input.validFromMs) {
|
|
2330
|
+
throw new Error("validUntilMs must be greater than validFromMs");
|
|
2331
|
+
}
|
|
2332
|
+
const out = {
|
|
2333
|
+
passId: input.passId,
|
|
2334
|
+
kind: input.kind,
|
|
2335
|
+
issuerId: input.issuerId,
|
|
2336
|
+
issuedAtMs: input.issuedAtMs,
|
|
2337
|
+
validFromMs: input.validFromMs,
|
|
2338
|
+
validUntilMs: input.validUntilMs,
|
|
2339
|
+
state: input.state,
|
|
2340
|
+
metadata: input.metadata,
|
|
2341
|
+
nonce: input.nonce,
|
|
2342
|
+
holderDeviceId: input.holderDeviceId,
|
|
2343
|
+
holderDevicePubkey: input.holderDevicePubkey,
|
|
2344
|
+
currency: input.currency ?? "NGN",
|
|
2345
|
+
counterSeed: input.counterSeed
|
|
2346
|
+
};
|
|
2347
|
+
if (typeof input.templateId === "string") out.templateId = input.templateId;
|
|
2348
|
+
if (typeof input.holderUserId === "string")
|
|
2349
|
+
out.holderUserId = input.holderUserId;
|
|
2350
|
+
if (typeof input.amountKobo === "number") out.amountKobo = input.amountKobo;
|
|
2351
|
+
if (typeof input.cumulativeCapKobo === "number") {
|
|
2352
|
+
out.cumulativeCapKobo = input.cumulativeCapKobo;
|
|
2353
|
+
}
|
|
2354
|
+
return out;
|
|
2355
|
+
}
|
|
2356
|
+
function signPass(unsigned, issuerPrivateKey) {
|
|
2357
|
+
const issuerSig = bytesToHex(
|
|
2358
|
+
sign(canonicalJSONBytes(unsigned), issuerPrivateKey)
|
|
2359
|
+
);
|
|
2360
|
+
return { ...unsigned, issuerSig };
|
|
2361
|
+
}
|
|
2362
|
+
function verifyPass(pass, issuerPublicKey) {
|
|
2363
|
+
try {
|
|
2364
|
+
const parsed = PassSchema.parse(pass);
|
|
2365
|
+
const { issuerSig, ...unsigned } = parsed;
|
|
2366
|
+
return verify(
|
|
2367
|
+
canonicalJSONBytes(unsigned),
|
|
2368
|
+
hexToBytes(issuerSig),
|
|
2369
|
+
issuerPublicKey
|
|
2370
|
+
);
|
|
2371
|
+
} catch {
|
|
2372
|
+
return false;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
function isPassWithinValidity(pass, nowMs) {
|
|
2376
|
+
return nowMs >= pass.validFromMs && nowMs < pass.validUntilMs;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
// src/passes/redemption.ts
|
|
2380
|
+
import { z as z10 } from "zod";
|
|
2381
|
+
var HexSig2 = z10.string().regex(/^[0-9a-fA-F]{128}$/, "expected 64-byte hex signature");
|
|
2382
|
+
var RedemptionSchema = z10.object({
|
|
2383
|
+
pass: PassSchema,
|
|
2384
|
+
redeemerId: z10.string().min(1),
|
|
2385
|
+
redeemedAtMs: z10.number().int().nonnegative(),
|
|
2386
|
+
/** Strictly monotonic counter scoped to a single pass. Must be > pass.counterSeed
|
|
2387
|
+
* and > the redeemer's lastSeenCounter for this pass. */
|
|
2388
|
+
counter: z10.number().int().positive(),
|
|
2389
|
+
/** Amount being redeemed in kobo (0 for non-monetary passes like ride tickets). */
|
|
2390
|
+
amountKobo: z10.number().int().nonnegative(),
|
|
2391
|
+
nonce: z10.string().min(1),
|
|
2392
|
+
holderSig: HexSig2
|
|
2393
|
+
});
|
|
2394
|
+
var REDEEMABLE_STATES = /* @__PURE__ */ new Set(["issued", "active"]);
|
|
2395
|
+
function buildRedemption(input) {
|
|
2396
|
+
if (!REDEEMABLE_STATES.has(input.pass.state)) {
|
|
2397
|
+
throw new Error(`pass not in redeemable state: ${input.pass.state}`);
|
|
2398
|
+
}
|
|
2399
|
+
if (input.redeemedAtMs < input.pass.validFromMs) {
|
|
1817
2400
|
throw new Error("redeemedAtMs is before pass validFromMs");
|
|
1818
2401
|
}
|
|
1819
2402
|
if (input.redeemedAtMs >= input.pass.validUntilMs) {
|
|
@@ -1891,43 +2474,43 @@ function verifyRedemption(r, issuerPublicKey) {
|
|
|
1891
2474
|
}
|
|
1892
2475
|
|
|
1893
2476
|
// src/receipts/receipt.ts
|
|
1894
|
-
import { z as
|
|
2477
|
+
import { z as z11 } from "zod";
|
|
1895
2478
|
var RECEIPT_CHANNELS = ["cash", "pass"];
|
|
1896
2479
|
var RECEIPT_KINDS = RECEIPT_CHANNELS;
|
|
1897
|
-
var HexString3 = (length) =>
|
|
2480
|
+
var HexString3 = (length) => z11.string().regex(
|
|
1898
2481
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
1899
2482
|
`expected ${length}-byte hex string`
|
|
1900
2483
|
);
|
|
1901
|
-
var ReceiptPayloadSchema =
|
|
1902
|
-
|
|
2484
|
+
var ReceiptPayloadSchema = z11.record(
|
|
2485
|
+
z11.union([z11.string(), z11.number(), z11.boolean(), z11.null()])
|
|
1903
2486
|
);
|
|
1904
|
-
var ReceiptSchema =
|
|
1905
|
-
receiptId:
|
|
1906
|
-
channel:
|
|
2487
|
+
var ReceiptSchema = z11.object({
|
|
2488
|
+
receiptId: z11.string().min(1),
|
|
2489
|
+
channel: z11.enum(RECEIPT_CHANNELS),
|
|
1907
2490
|
/** Cash-channel: send_intents.id. Required when channel === 'cash'. */
|
|
1908
|
-
intentId:
|
|
2491
|
+
intentId: z11.string().min(1).optional(),
|
|
1909
2492
|
/** Pass-channel: pass_redemptions.id. Required when channel === 'pass'. */
|
|
1910
|
-
passRedemptionId:
|
|
1911
|
-
payerUserId:
|
|
1912
|
-
payeeUserId:
|
|
1913
|
-
amountKobo:
|
|
1914
|
-
currency:
|
|
1915
|
-
issuedAtMs:
|
|
1916
|
-
issuerId:
|
|
2493
|
+
passRedemptionId: z11.string().min(1).optional(),
|
|
2494
|
+
payerUserId: z11.string().min(1),
|
|
2495
|
+
payeeUserId: z11.string().min(1),
|
|
2496
|
+
amountKobo: z11.number().int().nonnegative(),
|
|
2497
|
+
currency: z11.string().min(3).max(8),
|
|
2498
|
+
issuedAtMs: z11.number().int().nonnegative(),
|
|
2499
|
+
issuerId: z11.string().min(1),
|
|
1917
2500
|
payload: ReceiptPayloadSchema,
|
|
1918
2501
|
issuerSig: HexString3(64)
|
|
1919
2502
|
}).superRefine((v, ctx) => {
|
|
1920
2503
|
if (v.channel === "cash") {
|
|
1921
2504
|
if (!v.intentId) {
|
|
1922
2505
|
ctx.addIssue({
|
|
1923
|
-
code:
|
|
2506
|
+
code: z11.ZodIssueCode.custom,
|
|
1924
2507
|
message: "cash receipts require intentId",
|
|
1925
2508
|
path: ["intentId"]
|
|
1926
2509
|
});
|
|
1927
2510
|
}
|
|
1928
2511
|
if (v.passRedemptionId) {
|
|
1929
2512
|
ctx.addIssue({
|
|
1930
|
-
code:
|
|
2513
|
+
code: z11.ZodIssueCode.custom,
|
|
1931
2514
|
message: "cash receipts must not carry passRedemptionId",
|
|
1932
2515
|
path: ["passRedemptionId"]
|
|
1933
2516
|
});
|
|
@@ -1935,14 +2518,14 @@ var ReceiptSchema = z9.object({
|
|
|
1935
2518
|
} else if (v.channel === "pass") {
|
|
1936
2519
|
if (!v.passRedemptionId) {
|
|
1937
2520
|
ctx.addIssue({
|
|
1938
|
-
code:
|
|
2521
|
+
code: z11.ZodIssueCode.custom,
|
|
1939
2522
|
message: "pass receipts require passRedemptionId",
|
|
1940
2523
|
path: ["passRedemptionId"]
|
|
1941
2524
|
});
|
|
1942
2525
|
}
|
|
1943
2526
|
if (v.intentId) {
|
|
1944
2527
|
ctx.addIssue({
|
|
1945
|
-
code:
|
|
2528
|
+
code: z11.ZodIssueCode.custom,
|
|
1946
2529
|
message: "pass receipts must not carry intentId",
|
|
1947
2530
|
path: ["intentId"]
|
|
1948
2531
|
});
|
|
@@ -2181,317 +2764,90 @@ function createReceiptsClient(opts) {
|
|
|
2181
2764
|
verifyReceipt: (receipt, issuerPublicKey) => verifyReceipt(receipt, issuerPublicKey)
|
|
2182
2765
|
};
|
|
2183
2766
|
}
|
|
2184
|
-
|
|
2185
|
-
// src/client/flur.ts
|
|
2186
|
-
function generateStaticQR(input) {
|
|
2187
|
-
return encodeNQR({ ...input, pointOfInitiation: "static" });
|
|
2188
|
-
}
|
|
2189
|
-
function generateDynamicQR(input) {
|
|
2190
|
-
return encodeNQR({ ...input, pointOfInitiation: "dynamic" });
|
|
2191
|
-
}
|
|
2192
|
-
function parseQR(payload) {
|
|
2193
|
-
return parseNQR(payload);
|
|
2194
|
-
}
|
|
2195
|
-
function init(opts) {
|
|
2196
|
-
const signedFetch = createHmacFetch({
|
|
2197
|
-
apiKey: opts.apiKey,
|
|
2198
|
-
apiSecret: opts.apiSecret,
|
|
2199
|
-
fetchImpl: opts.fetchImpl,
|
|
2200
|
-
scope: opts.scope
|
|
2201
|
-
});
|
|
2202
|
-
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2203
|
-
function subscribeToPayments(s) {
|
|
2204
|
-
const controller = new AbortController();
|
|
2205
|
-
let cancelled = false;
|
|
2206
|
-
(async () => {
|
|
2207
|
-
try {
|
|
2208
|
-
const url = `${baseUrl}/v1/payments/subscribe?reference=${encodeURIComponent(s.reference)}`;
|
|
2209
|
-
const resp = await signedFetch(url, {
|
|
2210
|
-
method: "GET",
|
|
2211
|
-
headers: { accept: "text/event-stream" },
|
|
2212
|
-
signal: controller.signal
|
|
2213
|
-
});
|
|
2214
|
-
if (!resp.body) return;
|
|
2215
|
-
const reader = resp.body.getReader();
|
|
2216
|
-
const decoder = new TextDecoder();
|
|
2217
|
-
let buffer = "";
|
|
2218
|
-
while (!cancelled) {
|
|
2219
|
-
const { value, done } = await reader.read();
|
|
2220
|
-
if (done) return;
|
|
2221
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2222
|
-
let idx;
|
|
2223
|
-
while ((idx = buffer.indexOf("\n\n")) >= 0) {
|
|
2224
|
-
const chunk = buffer.slice(0, idx);
|
|
2225
|
-
buffer = buffer.slice(idx + 2);
|
|
2226
|
-
for (const line of chunk.split("\n")) {
|
|
2227
|
-
if (!line.startsWith("data:")) continue;
|
|
2228
|
-
const json = line.slice(5).trim();
|
|
2229
|
-
if (!json) continue;
|
|
2230
|
-
try {
|
|
2231
|
-
s.onEvent(JSON.parse(json));
|
|
2232
|
-
} catch (err) {
|
|
2233
|
-
s.onError?.(err);
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
} catch (err) {
|
|
2239
|
-
if (!cancelled) s.onError?.(err);
|
|
2240
|
-
}
|
|
2241
|
-
})();
|
|
2242
|
-
return () => {
|
|
2243
|
-
cancelled = true;
|
|
2244
|
-
controller.abort();
|
|
2245
|
-
};
|
|
2246
|
-
}
|
|
2247
|
-
const cash = {
|
|
2248
|
-
generateStaticQR,
|
|
2249
|
-
generateDynamicQR,
|
|
2250
|
-
parseQR,
|
|
2251
|
-
subscribeToPayments
|
|
2252
|
-
};
|
|
2253
|
-
const passes = createPassesClient({ baseUrl, fetchImpl: signedFetch });
|
|
2254
|
-
const receipts = createReceiptsClient({ baseUrl, fetchImpl: signedFetch });
|
|
2255
|
-
return {
|
|
2256
|
-
// top-level back-compat surface
|
|
2257
|
-
generateStaticQR,
|
|
2258
|
-
generateDynamicQR,
|
|
2259
|
-
parseQR,
|
|
2260
|
-
subscribeToPayments,
|
|
2261
|
-
// namespaces
|
|
2262
|
-
cash,
|
|
2263
|
-
passes,
|
|
2264
|
-
receipts
|
|
2265
|
-
};
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
// src/accounts/client.ts
|
|
2269
|
-
import { z as z10 } from "zod";
|
|
2270
|
-
var ACCOUNT_TYPES = ["personal", "business", "partner"];
|
|
2271
|
-
var ACCOUNT_STATUSES = ["active", "suspended", "closed"];
|
|
2272
|
-
var MEMBERSHIP_ROLES = ["owner", "admin", "driver", "staff"];
|
|
2273
|
-
var AccountSchema = z10.object({
|
|
2274
|
-
accountId: z10.string().uuid(),
|
|
2275
|
-
type: z10.enum(ACCOUNT_TYPES),
|
|
2276
|
-
displayName: z10.string().min(1),
|
|
2277
|
-
status: z10.enum(ACCOUNT_STATUSES),
|
|
2278
|
-
ownerUserId: z10.string().uuid().nullable(),
|
|
2279
|
-
createdAtMs: z10.number().int().nonnegative()
|
|
2280
|
-
});
|
|
2281
|
-
var AccountMembershipSchema = z10.object({
|
|
2282
|
-
accountId: z10.string().uuid(),
|
|
2283
|
-
userId: z10.string().uuid(),
|
|
2284
|
-
role: z10.enum(MEMBERSHIP_ROLES),
|
|
2285
|
-
createdAtMs: z10.number().int().nonnegative()
|
|
2286
|
-
});
|
|
2287
|
-
function createAccountsClient(opts) {
|
|
2288
|
-
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2289
|
-
if (!fetchImpl) {
|
|
2290
|
-
throw new Error("createAccountsClient: no fetch implementation available");
|
|
2291
|
-
}
|
|
2292
|
-
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2293
|
-
async function call(method, path, body, parser) {
|
|
2294
|
-
const init2 = {
|
|
2295
|
-
method,
|
|
2296
|
-
headers: {
|
|
2297
|
-
"content-type": "application/json",
|
|
2298
|
-
accept: "application/json"
|
|
2299
|
-
}
|
|
2300
|
-
};
|
|
2301
|
-
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
2302
|
-
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
2303
|
-
const text = await resp.text();
|
|
2304
|
-
let raw = void 0;
|
|
2305
|
-
if (text) {
|
|
2306
|
-
try {
|
|
2307
|
-
raw = JSON.parse(text);
|
|
2308
|
-
} catch {
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
if (!resp.ok) {
|
|
2312
|
-
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2313
|
-
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2314
|
-
throw new FlurApiError(resp.status, code, message, raw);
|
|
2315
|
-
}
|
|
2316
|
-
return parser(raw);
|
|
2317
|
-
}
|
|
2318
|
-
const itemsSchema = z10.object({ items: z10.array(AccountSchema) });
|
|
2319
|
-
const memberItemsSchema = z10.object({
|
|
2320
|
-
items: z10.array(AccountMembershipSchema)
|
|
2321
|
-
});
|
|
2322
|
-
return {
|
|
2323
|
-
listMyAccounts: () => call(
|
|
2324
|
-
"GET",
|
|
2325
|
-
"/v1/accounts/me",
|
|
2326
|
-
void 0,
|
|
2327
|
-
(raw) => itemsSchema.parse(raw)
|
|
2328
|
-
),
|
|
2329
|
-
getAccount: (accountId) => call(
|
|
2330
|
-
"GET",
|
|
2331
|
-
`/v1/accounts/${encodeURIComponent(accountId)}`,
|
|
2332
|
-
void 0,
|
|
2333
|
-
(raw) => AccountSchema.parse(raw)
|
|
2334
|
-
),
|
|
2335
|
-
listMembers: (accountId) => call(
|
|
2336
|
-
"GET",
|
|
2337
|
-
`/v1/accounts/${encodeURIComponent(accountId)}/members`,
|
|
2338
|
-
void 0,
|
|
2339
|
-
(raw) => memberItemsSchema.parse(raw)
|
|
2340
|
-
),
|
|
2341
|
-
createBusinessAccount: (input) => call("POST", "/v1/accounts", input, (raw) => AccountSchema.parse(raw)),
|
|
2342
|
-
addMember: (accountId, input) => call(
|
|
2343
|
-
"POST",
|
|
2344
|
-
`/v1/accounts/${encodeURIComponent(accountId)}/members`,
|
|
2345
|
-
input,
|
|
2346
|
-
(raw) => AccountMembershipSchema.parse(raw)
|
|
2347
|
-
)
|
|
2348
|
-
};
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
// src/partner/client.ts
|
|
2352
|
-
import { z as z11 } from "zod";
|
|
2353
|
-
import { sha256 as sha2563 } from "@noble/hashes/sha256";
|
|
2354
|
-
import { hmac as hmac2 } from "@noble/hashes/hmac";
|
|
2355
|
-
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils";
|
|
2356
|
-
var PARTNER_SCOPES = [
|
|
2357
|
-
"passes:write",
|
|
2358
|
-
"passes:read",
|
|
2359
|
-
"passes:redeem",
|
|
2360
|
-
"receipts:write",
|
|
2361
|
-
"receipts:read",
|
|
2362
|
-
"offline:issue",
|
|
2363
|
-
"offline:settle",
|
|
2364
|
-
"offline:read"
|
|
2365
|
-
];
|
|
2366
|
-
var ApiCredentialPublicSchema = z11.object({
|
|
2367
|
-
id: z11.string().uuid(),
|
|
2368
|
-
accountId: z11.string().uuid(),
|
|
2369
|
-
keyId: z11.string(),
|
|
2370
|
-
scopes: z11.array(z11.enum(PARTNER_SCOPES)),
|
|
2371
|
-
label: z11.string().nullable(),
|
|
2372
|
-
createdAtMs: z11.number().int().nonnegative(),
|
|
2373
|
-
lastUsedAtMs: z11.number().int().nonnegative().nullable(),
|
|
2374
|
-
revokedAtMs: z11.number().int().nonnegative().nullable()
|
|
2375
|
-
});
|
|
2376
|
-
var MintedApiCredentialSchema = ApiCredentialPublicSchema.extend({
|
|
2377
|
-
secret: z11.string().min(1)
|
|
2378
|
-
});
|
|
2379
|
-
var enc = new TextEncoder();
|
|
2380
|
-
async function sha256Hex2(input) {
|
|
2381
|
-
const data = typeof input === "string" ? enc.encode(input) : input;
|
|
2382
|
-
return bytesToHex4(sha2563(data));
|
|
2383
|
-
}
|
|
2384
|
-
async function hmacSha256Hex(keyHex, message) {
|
|
2385
|
-
return bytesToHex4(hmac2(sha2563, enc.encode(keyHex), enc.encode(message)));
|
|
2386
|
-
}
|
|
2387
|
-
function defaultNonce2() {
|
|
2388
|
-
const c = globalThis.crypto;
|
|
2389
|
-
if (c?.randomUUID) return c.randomUUID();
|
|
2390
|
-
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
|
|
2391
|
-
}
|
|
2392
|
-
function canonical(params) {
|
|
2393
|
-
return [
|
|
2394
|
-
params.method.toUpperCase(),
|
|
2395
|
-
params.path,
|
|
2396
|
-
params.ts,
|
|
2397
|
-
params.nonce,
|
|
2398
|
-
params.bodyHash
|
|
2399
|
-
].join("\n");
|
|
2400
|
-
}
|
|
2401
|
-
async function signPartnerRequest(params) {
|
|
2402
|
-
const ts = String(Math.floor((params.nowMs ?? Date.now()) / 1e3));
|
|
2403
|
-
const nonce = params.nonce ?? defaultNonce2();
|
|
2404
|
-
const bodyData = typeof params.body === "string" ? enc.encode(params.body) : params.body ?? new Uint8Array();
|
|
2405
|
-
const bodyHash = await sha256Hex2(bodyData);
|
|
2406
|
-
const message = canonical({
|
|
2407
|
-
method: params.method,
|
|
2408
|
-
path: params.path,
|
|
2409
|
-
ts,
|
|
2410
|
-
nonce,
|
|
2411
|
-
bodyHash
|
|
2412
|
-
});
|
|
2413
|
-
const signingKey = await sha256Hex2(params.secret);
|
|
2414
|
-
const sig = await hmacSha256Hex(signingKey, message);
|
|
2415
|
-
return {
|
|
2416
|
-
authorization: `Flur-Hmac key=${params.keyId}, ts=${ts}, nonce=${nonce}, sig=${sig}`,
|
|
2417
|
-
ts,
|
|
2418
|
-
nonce,
|
|
2419
|
-
bodyHash
|
|
2420
|
-
};
|
|
2767
|
+
|
|
2768
|
+
// src/client/flur.ts
|
|
2769
|
+
function generateStaticQR(input) {
|
|
2770
|
+
return encodeNQR({ ...input, pointOfInitiation: "static" });
|
|
2421
2771
|
}
|
|
2422
|
-
function
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2772
|
+
function generateDynamicQR(input) {
|
|
2773
|
+
return encodeNQR({ ...input, pointOfInitiation: "dynamic" });
|
|
2774
|
+
}
|
|
2775
|
+
function parseQR(payload) {
|
|
2776
|
+
return parseNQR(payload);
|
|
2777
|
+
}
|
|
2778
|
+
function init(opts) {
|
|
2779
|
+
const partner = createFlurPartnerClient({
|
|
2780
|
+
baseUrl: opts.baseUrl,
|
|
2781
|
+
keyId: opts.apiKey,
|
|
2782
|
+
secret: opts.apiSecret,
|
|
2783
|
+
fetchImpl: opts.fetchImpl,
|
|
2784
|
+
scope: opts.scope
|
|
2785
|
+
});
|
|
2786
|
+
const signedFetch = partner.fetch;
|
|
2429
2787
|
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2788
|
+
function subscribeToPayments(s) {
|
|
2789
|
+
let cancelled = false;
|
|
2790
|
+
queueMicrotask(() => {
|
|
2791
|
+
if (cancelled) return;
|
|
2792
|
+
s.onError?.(
|
|
2793
|
+
new Error(
|
|
2794
|
+
"cash.subscribeToPayments is not available on this backend release; use collections reports or provider webhooks for payment status."
|
|
2795
|
+
)
|
|
2796
|
+
);
|
|
2439
2797
|
});
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
accept: "application/json"
|
|
2798
|
+
return () => {
|
|
2799
|
+
cancelled = true;
|
|
2443
2800
|
};
|
|
2444
|
-
if (body !== void 0) headers["content-type"] = "application/json";
|
|
2445
|
-
const init2 = { method, headers };
|
|
2446
|
-
if (body !== void 0) init2.body = body;
|
|
2447
|
-
return init2;
|
|
2448
|
-
}
|
|
2449
|
-
async function request(opts2) {
|
|
2450
|
-
const bodyStr = opts2.body === void 0 ? void 0 : JSON.stringify(opts2.body);
|
|
2451
|
-
const init2 = await makeSignedInit(opts2.method, opts2.path, bodyStr);
|
|
2452
|
-
const resp = await fetchImpl(`${baseUrl}${opts2.path}`, init2);
|
|
2453
|
-
const text = await resp.text();
|
|
2454
|
-
let raw;
|
|
2455
|
-
if (text) {
|
|
2456
|
-
try {
|
|
2457
|
-
raw = JSON.parse(text);
|
|
2458
|
-
} catch {
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
if (!resp.ok) {
|
|
2462
|
-
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2463
|
-
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2464
|
-
throw new FlurApiError(resp.status, code, message, raw);
|
|
2465
|
-
}
|
|
2466
|
-
return raw;
|
|
2467
2801
|
}
|
|
2468
|
-
const
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2802
|
+
const cash = {
|
|
2803
|
+
generateStaticQR,
|
|
2804
|
+
generateDynamicQR,
|
|
2805
|
+
parseQR,
|
|
2806
|
+
subscribeToPayments
|
|
2807
|
+
};
|
|
2808
|
+
const passes = createPassesClient({ baseUrl, fetchImpl: signedFetch });
|
|
2809
|
+
const receipts = createReceiptsClient({ baseUrl, fetchImpl: signedFetch });
|
|
2810
|
+
const collections = createPartnerCollectionsClient({
|
|
2811
|
+
baseUrl,
|
|
2812
|
+
fetchImpl: signedFetch
|
|
2813
|
+
});
|
|
2814
|
+
return {
|
|
2815
|
+
// top-level back-compat surface
|
|
2816
|
+
generateStaticQR,
|
|
2817
|
+
generateDynamicQR,
|
|
2818
|
+
parseQR,
|
|
2819
|
+
subscribeToPayments,
|
|
2820
|
+
// namespaces
|
|
2821
|
+
cash,
|
|
2822
|
+
collections,
|
|
2823
|
+
passes,
|
|
2824
|
+
receipts
|
|
2486
2825
|
};
|
|
2487
|
-
return { request, fetch: fetchLike };
|
|
2488
2826
|
}
|
|
2489
|
-
|
|
2827
|
+
|
|
2828
|
+
// src/accounts/client.ts
|
|
2829
|
+
import { z as z12 } from "zod";
|
|
2830
|
+
var ACCOUNT_TYPES = ["personal", "business", "partner"];
|
|
2831
|
+
var ACCOUNT_STATUSES = ["active", "suspended", "closed"];
|
|
2832
|
+
var MEMBERSHIP_ROLES = ["owner", "admin", "driver", "staff"];
|
|
2833
|
+
var AccountSchema = z12.object({
|
|
2834
|
+
accountId: z12.string().uuid(),
|
|
2835
|
+
type: z12.enum(ACCOUNT_TYPES),
|
|
2836
|
+
displayName: z12.string().min(1),
|
|
2837
|
+
status: z12.enum(ACCOUNT_STATUSES),
|
|
2838
|
+
ownerUserId: z12.string().uuid().nullable(),
|
|
2839
|
+
createdAtMs: z12.number().int().nonnegative()
|
|
2840
|
+
});
|
|
2841
|
+
var AccountMembershipSchema = z12.object({
|
|
2842
|
+
accountId: z12.string().uuid(),
|
|
2843
|
+
userId: z12.string().uuid(),
|
|
2844
|
+
role: z12.enum(MEMBERSHIP_ROLES),
|
|
2845
|
+
createdAtMs: z12.number().int().nonnegative()
|
|
2846
|
+
});
|
|
2847
|
+
function createAccountsClient(opts) {
|
|
2490
2848
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2491
2849
|
if (!fetchImpl) {
|
|
2492
|
-
throw new Error(
|
|
2493
|
-
"createApiCredentialsAdminClient: no fetch implementation available"
|
|
2494
|
-
);
|
|
2850
|
+
throw new Error("createAccountsClient: no fetch implementation available");
|
|
2495
2851
|
}
|
|
2496
2852
|
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2497
2853
|
async function call(method, path, body, parser) {
|
|
@@ -2505,7 +2861,7 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2505
2861
|
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
2506
2862
|
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
2507
2863
|
const text = await resp.text();
|
|
2508
|
-
let raw;
|
|
2864
|
+
let raw = void 0;
|
|
2509
2865
|
if (text) {
|
|
2510
2866
|
try {
|
|
2511
2867
|
raw = JSON.parse(text);
|
|
@@ -2519,70 +2875,78 @@ function createApiCredentialsAdminClient(opts) {
|
|
|
2519
2875
|
}
|
|
2520
2876
|
return parser(raw);
|
|
2521
2877
|
}
|
|
2522
|
-
const
|
|
2523
|
-
|
|
2878
|
+
const itemsSchema = z12.object({ items: z12.array(AccountSchema) });
|
|
2879
|
+
const memberItemsSchema = z12.object({
|
|
2880
|
+
items: z12.array(AccountMembershipSchema)
|
|
2524
2881
|
});
|
|
2525
2882
|
return {
|
|
2526
|
-
|
|
2883
|
+
listMyAccounts: () => call(
|
|
2527
2884
|
"GET",
|
|
2528
|
-
|
|
2885
|
+
"/v1/accounts/me",
|
|
2529
2886
|
void 0,
|
|
2530
|
-
(raw) =>
|
|
2887
|
+
(raw) => itemsSchema.parse(raw)
|
|
2531
2888
|
),
|
|
2532
|
-
|
|
2533
|
-
"
|
|
2534
|
-
`/v1/accounts/${accountId}
|
|
2535
|
-
|
|
2536
|
-
(raw) =>
|
|
2889
|
+
getAccount: (accountId) => call(
|
|
2890
|
+
"GET",
|
|
2891
|
+
`/v1/accounts/${encodeURIComponent(accountId)}`,
|
|
2892
|
+
void 0,
|
|
2893
|
+
(raw) => AccountSchema.parse(raw)
|
|
2537
2894
|
),
|
|
2538
|
-
|
|
2539
|
-
"
|
|
2540
|
-
`/v1/accounts/${accountId}/
|
|
2895
|
+
listMembers: (accountId) => call(
|
|
2896
|
+
"GET",
|
|
2897
|
+
`/v1/accounts/${encodeURIComponent(accountId)}/members`,
|
|
2541
2898
|
void 0,
|
|
2542
|
-
(raw) =>
|
|
2899
|
+
(raw) => memberItemsSchema.parse(raw)
|
|
2900
|
+
),
|
|
2901
|
+
createBusinessAccount: (input) => call("POST", "/v1/accounts", input, (raw) => AccountSchema.parse(raw)),
|
|
2902
|
+
addMember: (accountId, input) => call(
|
|
2903
|
+
"POST",
|
|
2904
|
+
`/v1/accounts/${encodeURIComponent(accountId)}/members`,
|
|
2905
|
+
input,
|
|
2906
|
+
(raw) => AccountMembershipSchema.parse(raw)
|
|
2543
2907
|
)
|
|
2544
2908
|
};
|
|
2545
2909
|
}
|
|
2546
2910
|
|
|
2547
2911
|
// src/me-offline/client.ts
|
|
2548
|
-
import { z as
|
|
2549
|
-
var Hex64 =
|
|
2550
|
-
var HexAny =
|
|
2551
|
-
var Sha256Hex =
|
|
2552
|
-
var RegisterDeviceKeyInputSchema =
|
|
2553
|
-
deviceId:
|
|
2912
|
+
import { z as z13 } from "zod";
|
|
2913
|
+
var Hex64 = z13.string().regex(/^[0-9a-f]{64}$/i);
|
|
2914
|
+
var HexAny = z13.string().regex(/^[0-9a-f]+$/i);
|
|
2915
|
+
var Sha256Hex = z13.string().regex(/^[0-9a-f]{64}$/i);
|
|
2916
|
+
var RegisterDeviceKeyInputSchema = z13.object({
|
|
2917
|
+
deviceId: z13.string().min(1).max(128),
|
|
2554
2918
|
publicKeyHex: Hex64
|
|
2555
2919
|
});
|
|
2556
|
-
var DeviceKeyRecordSchema =
|
|
2557
|
-
id:
|
|
2558
|
-
userId:
|
|
2559
|
-
deviceId:
|
|
2920
|
+
var DeviceKeyRecordSchema = z13.object({
|
|
2921
|
+
id: z13.string().uuid(),
|
|
2922
|
+
userId: z13.string().uuid(),
|
|
2923
|
+
deviceId: z13.string(),
|
|
2560
2924
|
publicKeyHex: Hex64,
|
|
2561
|
-
createdAtMs:
|
|
2562
|
-
revokedAtMs:
|
|
2925
|
+
createdAtMs: z13.number().int().nonnegative(),
|
|
2926
|
+
revokedAtMs: z13.number().int().nonnegative().nullable()
|
|
2563
2927
|
});
|
|
2564
|
-
var ConsumerOACSchema =
|
|
2565
|
-
oacId:
|
|
2566
|
-
issuerId:
|
|
2567
|
-
userId:
|
|
2568
|
-
deviceId:
|
|
2928
|
+
var ConsumerOACSchema = z13.object({
|
|
2929
|
+
oacId: z13.string().uuid(),
|
|
2930
|
+
issuerId: z13.string().min(1).max(64),
|
|
2931
|
+
userId: z13.string().uuid(),
|
|
2932
|
+
deviceId: z13.string().min(1).max(128),
|
|
2569
2933
|
devicePubkeyHex: Hex64,
|
|
2570
|
-
perTxCapKobo:
|
|
2571
|
-
cumulativeCapKobo:
|
|
2572
|
-
currency:
|
|
2573
|
-
validFromMs:
|
|
2574
|
-
validUntilMs:
|
|
2575
|
-
counterSeed:
|
|
2576
|
-
issuedAtMs:
|
|
2577
|
-
});
|
|
2578
|
-
var SignedConsumerOACSchema =
|
|
2934
|
+
perTxCapKobo: z13.number().int().positive(),
|
|
2935
|
+
cumulativeCapKobo: z13.number().int().positive(),
|
|
2936
|
+
currency: z13.string().length(3),
|
|
2937
|
+
validFromMs: z13.number().int().nonnegative(),
|
|
2938
|
+
validUntilMs: z13.number().int().nonnegative(),
|
|
2939
|
+
counterSeed: z13.number().int().nonnegative(),
|
|
2940
|
+
issuedAtMs: z13.number().int().nonnegative()
|
|
2941
|
+
});
|
|
2942
|
+
var SignedConsumerOACSchema = z13.object({
|
|
2579
2943
|
oac: ConsumerOACSchema,
|
|
2580
2944
|
issuerSig: HexAny,
|
|
2581
2945
|
issuerPublicKeyHex: Hex64
|
|
2582
2946
|
});
|
|
2583
2947
|
var OACRecordSchema = SignedConsumerOACSchema.extend({
|
|
2584
|
-
currentOfflineSpentKobo:
|
|
2585
|
-
status:
|
|
2948
|
+
currentOfflineSpentKobo: z13.number().int().nonnegative(),
|
|
2949
|
+
status: z13.enum([
|
|
2586
2950
|
"active",
|
|
2587
2951
|
"superseded",
|
|
2588
2952
|
"expired",
|
|
@@ -2591,43 +2955,43 @@ var OACRecordSchema = SignedConsumerOACSchema.extend({
|
|
|
2591
2955
|
"draining",
|
|
2592
2956
|
"closed"
|
|
2593
2957
|
]),
|
|
2594
|
-
supersededAtMs:
|
|
2595
|
-
revokedAtMs:
|
|
2596
|
-
holdId:
|
|
2597
|
-
});
|
|
2598
|
-
var IssueOACInputSchema =
|
|
2599
|
-
deviceId:
|
|
2600
|
-
perTxCapKobo:
|
|
2601
|
-
cumulativeCapKobo:
|
|
2602
|
-
ttlMs:
|
|
2603
|
-
spendableOnlineKobo:
|
|
2604
|
-
});
|
|
2605
|
-
var EnableOfflineInputSchema =
|
|
2606
|
-
deviceId:
|
|
2607
|
-
amountKobo:
|
|
2608
|
-
perTxCapKobo:
|
|
2609
|
-
ttlMs:
|
|
2610
|
-
installId:
|
|
2611
|
-
partnerId:
|
|
2612
|
-
});
|
|
2613
|
-
var DisableOfflineInputSchema =
|
|
2614
|
-
deviceId:
|
|
2615
|
-
installId:
|
|
2616
|
-
claims:
|
|
2617
|
-
});
|
|
2618
|
-
var OfflineHoldRecordSchema =
|
|
2619
|
-
holdId:
|
|
2620
|
-
userId:
|
|
2621
|
-
deviceId:
|
|
2622
|
-
partnerId:
|
|
2623
|
-
adapterKind:
|
|
2624
|
-
externalHoldRef:
|
|
2625
|
-
amountKobo:
|
|
2626
|
-
capturedKobo:
|
|
2627
|
-
releasedKobo:
|
|
2628
|
-
remainingKobo:
|
|
2629
|
-
currency:
|
|
2630
|
-
status:
|
|
2958
|
+
supersededAtMs: z13.number().int().nonnegative().nullable(),
|
|
2959
|
+
revokedAtMs: z13.number().int().nonnegative().nullable(),
|
|
2960
|
+
holdId: z13.string().uuid().nullable().optional()
|
|
2961
|
+
});
|
|
2962
|
+
var IssueOACInputSchema = z13.object({
|
|
2963
|
+
deviceId: z13.string().min(1).max(128),
|
|
2964
|
+
perTxCapKobo: z13.number().int().positive().optional(),
|
|
2965
|
+
cumulativeCapKobo: z13.number().int().positive().optional(),
|
|
2966
|
+
ttlMs: z13.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
2967
|
+
spendableOnlineKobo: z13.number().int().nonnegative().optional()
|
|
2968
|
+
});
|
|
2969
|
+
var EnableOfflineInputSchema = z13.object({
|
|
2970
|
+
deviceId: z13.string().min(1).max(128),
|
|
2971
|
+
amountKobo: z13.number().int().positive(),
|
|
2972
|
+
perTxCapKobo: z13.number().int().positive().optional(),
|
|
2973
|
+
ttlMs: z13.number().int().min(6e4).max(1e3 * 60 * 60 * 24 * 7).optional(),
|
|
2974
|
+
installId: z13.string().min(1).max(128),
|
|
2975
|
+
partnerId: z13.string().min(1).max(64).optional()
|
|
2976
|
+
});
|
|
2977
|
+
var DisableOfflineInputSchema = z13.object({
|
|
2978
|
+
deviceId: z13.string().min(1).max(128),
|
|
2979
|
+
installId: z13.string().min(1).max(128).optional(),
|
|
2980
|
+
claims: z13.array(z13.unknown()).max(256).optional()
|
|
2981
|
+
});
|
|
2982
|
+
var OfflineHoldRecordSchema = z13.object({
|
|
2983
|
+
holdId: z13.string().uuid(),
|
|
2984
|
+
userId: z13.string().uuid(),
|
|
2985
|
+
deviceId: z13.string(),
|
|
2986
|
+
partnerId: z13.string(),
|
|
2987
|
+
adapterKind: z13.string(),
|
|
2988
|
+
externalHoldRef: z13.string().nullable(),
|
|
2989
|
+
amountKobo: z13.number().int().nonnegative(),
|
|
2990
|
+
capturedKobo: z13.number().int().nonnegative(),
|
|
2991
|
+
releasedKobo: z13.number().int().nonnegative(),
|
|
2992
|
+
remainingKobo: z13.number().int().nonnegative(),
|
|
2993
|
+
currency: z13.string().length(3),
|
|
2994
|
+
status: z13.enum([
|
|
2631
2995
|
"placing",
|
|
2632
2996
|
"active",
|
|
2633
2997
|
"disabling",
|
|
@@ -2636,71 +3000,71 @@ var OfflineHoldRecordSchema = z12.object({
|
|
|
2636
3000
|
"revoked",
|
|
2637
3001
|
"failed"
|
|
2638
3002
|
]),
|
|
2639
|
-
installId:
|
|
2640
|
-
drainDeadlineMs:
|
|
2641
|
-
disableRequestedAtMs:
|
|
2642
|
-
createdAtMs:
|
|
2643
|
-
closedAtMs:
|
|
2644
|
-
isTrusted:
|
|
2645
|
-
});
|
|
2646
|
-
var EnableOfflineResultSchema =
|
|
3003
|
+
installId: z13.string().nullable(),
|
|
3004
|
+
drainDeadlineMs: z13.number().int().nonnegative(),
|
|
3005
|
+
disableRequestedAtMs: z13.number().int().nonnegative().nullable(),
|
|
3006
|
+
createdAtMs: z13.number().int().nonnegative(),
|
|
3007
|
+
closedAtMs: z13.number().int().nonnegative().nullable(),
|
|
3008
|
+
isTrusted: z13.boolean().optional()
|
|
3009
|
+
});
|
|
3010
|
+
var EnableOfflineResultSchema = z13.object({
|
|
2647
3011
|
hold: OfflineHoldRecordSchema,
|
|
2648
3012
|
oac: OACRecordSchema
|
|
2649
3013
|
});
|
|
2650
|
-
var DisableOfflineResultSchema =
|
|
3014
|
+
var DisableOfflineResultSchema = z13.object({
|
|
2651
3015
|
hold: OfflineHoldRecordSchema,
|
|
2652
|
-
trusted:
|
|
2653
|
-
settledClaims:
|
|
3016
|
+
trusted: z13.boolean(),
|
|
3017
|
+
settledClaims: z13.number().int().nonnegative()
|
|
2654
3018
|
});
|
|
2655
|
-
var OfflineStatusResultSchema =
|
|
3019
|
+
var OfflineStatusResultSchema = z13.object({
|
|
2656
3020
|
hold: OfflineHoldRecordSchema.nullable(),
|
|
2657
3021
|
active: OACRecordSchema.nullable()
|
|
2658
3022
|
});
|
|
2659
|
-
var OfflineStateResultSchema =
|
|
3023
|
+
var OfflineStateResultSchema = z13.object({
|
|
2660
3024
|
active: OACRecordSchema.nullable()
|
|
2661
3025
|
});
|
|
2662
|
-
var ConsumerPaymentClaimSchema =
|
|
2663
|
-
oacId:
|
|
3026
|
+
var ConsumerPaymentClaimSchema = z13.object({
|
|
3027
|
+
oacId: z13.string().uuid(),
|
|
2664
3028
|
encounterId: Sha256Hex.optional(),
|
|
2665
|
-
payerUserId:
|
|
2666
|
-
payeeUserId:
|
|
2667
|
-
payerDeviceId:
|
|
2668
|
-
payerNonce:
|
|
2669
|
-
payeeNonce:
|
|
2670
|
-
amountKobo:
|
|
2671
|
-
currency:
|
|
2672
|
-
occurredAtMs:
|
|
2673
|
-
completedAtMs:
|
|
2674
|
-
contextId:
|
|
3029
|
+
payerUserId: z13.string().uuid(),
|
|
3030
|
+
payeeUserId: z13.string().uuid(),
|
|
3031
|
+
payerDeviceId: z13.string().min(1).max(128),
|
|
3032
|
+
payerNonce: z13.string().min(8).max(128),
|
|
3033
|
+
payeeNonce: z13.string().min(8).max(128),
|
|
3034
|
+
amountKobo: z13.number().int().positive(),
|
|
3035
|
+
currency: z13.string().length(3).default("NGN"),
|
|
3036
|
+
occurredAtMs: z13.number().int().nonnegative(),
|
|
3037
|
+
completedAtMs: z13.number().int().nonnegative().optional(),
|
|
3038
|
+
contextId: z13.string().max(128).optional(),
|
|
2675
3039
|
payerPubkeyHex: Hex64,
|
|
2676
3040
|
payerSignature: HexAny,
|
|
2677
3041
|
payeePubkeyHex: Hex64.optional(),
|
|
2678
3042
|
payeeSignature: HexAny.optional()
|
|
2679
3043
|
});
|
|
2680
|
-
var ConsumerSettlementSchema =
|
|
2681
|
-
settlementId:
|
|
3044
|
+
var ConsumerSettlementSchema = z13.object({
|
|
3045
|
+
settlementId: z13.string().uuid(),
|
|
2682
3046
|
settlementKey: Sha256Hex,
|
|
2683
3047
|
encounterId: Sha256Hex,
|
|
2684
|
-
oacId:
|
|
2685
|
-
payerUserId:
|
|
2686
|
-
payeeUserId:
|
|
2687
|
-
amountKobo:
|
|
2688
|
-
currency:
|
|
2689
|
-
status:
|
|
2690
|
-
reviewReason:
|
|
2691
|
-
ledgerRef:
|
|
3048
|
+
oacId: z13.string().uuid(),
|
|
3049
|
+
payerUserId: z13.string().uuid(),
|
|
3050
|
+
payeeUserId: z13.string().uuid(),
|
|
3051
|
+
amountKobo: z13.number().int().positive(),
|
|
3052
|
+
currency: z13.string().length(3),
|
|
3053
|
+
status: z13.enum(["SETTLED", "REVIEW"]),
|
|
3054
|
+
reviewReason: z13.string().nullable(),
|
|
3055
|
+
ledgerRef: z13.string().nullable(),
|
|
2692
3056
|
issuerSig: HexAny,
|
|
2693
|
-
createdAtMs:
|
|
3057
|
+
createdAtMs: z13.number().int().nonnegative()
|
|
2694
3058
|
});
|
|
2695
|
-
var ConsumerSettleResultSchema =
|
|
3059
|
+
var ConsumerSettleResultSchema = z13.object({
|
|
2696
3060
|
settlement: ConsumerSettlementSchema,
|
|
2697
3061
|
encounterId: Sha256Hex,
|
|
2698
|
-
replayed:
|
|
3062
|
+
replayed: z13.boolean()
|
|
2699
3063
|
});
|
|
2700
|
-
var RevokeDeviceKeyInputSchema =
|
|
2701
|
-
deviceId:
|
|
3064
|
+
var RevokeDeviceKeyInputSchema = z13.object({
|
|
3065
|
+
deviceId: z13.string().min(1).max(128),
|
|
2702
3066
|
/** Step-up token from /api/v1/auth/send/verify with purpose='offline_revoke'. */
|
|
2703
|
-
sendAuthToken:
|
|
3067
|
+
sendAuthToken: z13.string().min(16)
|
|
2704
3068
|
});
|
|
2705
3069
|
function createMeOfflineClient(opts) {
|
|
2706
3070
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -2733,7 +3097,7 @@ function createMeOfflineClient(opts) {
|
|
|
2733
3097
|
}
|
|
2734
3098
|
return parser(raw);
|
|
2735
3099
|
}
|
|
2736
|
-
const deviceKeyItems =
|
|
3100
|
+
const deviceKeyItems = z13.object({ items: z13.array(DeviceKeyRecordSchema) });
|
|
2737
3101
|
return {
|
|
2738
3102
|
registerDeviceKey: (input) => call(
|
|
2739
3103
|
"POST",
|
|
@@ -2797,6 +3161,320 @@ function createMeOfflineClient(opts) {
|
|
|
2797
3161
|
)
|
|
2798
3162
|
};
|
|
2799
3163
|
}
|
|
3164
|
+
|
|
3165
|
+
// src/partner-funding/client.ts
|
|
3166
|
+
import { z as z14 } from "zod";
|
|
3167
|
+
var MinorString = z14.string().regex(/^-?\d+$/);
|
|
3168
|
+
var PositiveMinor = z14.union([
|
|
3169
|
+
z14.number().int().positive(),
|
|
3170
|
+
z14.string().regex(/^[1-9]\d{0,18}$/)
|
|
3171
|
+
]);
|
|
3172
|
+
var Currency = z14.string().trim().length(3).transform((v) => v.toUpperCase());
|
|
3173
|
+
var Metadata = z14.record(z14.unknown());
|
|
3174
|
+
var PARTNER_KINDS = ["bank", "merchant"];
|
|
3175
|
+
var CUSTODIAL_MODES = ["agent_of_bank", "flur_virtual_pool"];
|
|
3176
|
+
var PARTNER_PROFILE_STATUSES = [
|
|
3177
|
+
"active",
|
|
3178
|
+
"suspended",
|
|
3179
|
+
"closed"
|
|
3180
|
+
];
|
|
3181
|
+
var PARTNER_FUNDING_DIRECTIONS = ["credit", "debit"];
|
|
3182
|
+
var PARTNER_FUNDING_STATUSES = [
|
|
3183
|
+
"pending",
|
|
3184
|
+
"posted",
|
|
3185
|
+
"failed"
|
|
3186
|
+
];
|
|
3187
|
+
var PAYOUT_DESTINATION_STATUSES = [
|
|
3188
|
+
"pending",
|
|
3189
|
+
"verified",
|
|
3190
|
+
"disabled"
|
|
3191
|
+
];
|
|
3192
|
+
var WITHDRAWAL_STATES = [
|
|
3193
|
+
"requested",
|
|
3194
|
+
"submitted",
|
|
3195
|
+
"processing",
|
|
3196
|
+
"paid",
|
|
3197
|
+
"failed",
|
|
3198
|
+
"reversed"
|
|
3199
|
+
];
|
|
3200
|
+
var PartnerProfileSchema = z14.object({
|
|
3201
|
+
partnerAccountId: z14.string().uuid(),
|
|
3202
|
+
kind: z14.enum(PARTNER_KINDS),
|
|
3203
|
+
custodialMode: z14.enum(CUSTODIAL_MODES),
|
|
3204
|
+
displayName: z14.string(),
|
|
3205
|
+
bankCode: z14.string().nullable(),
|
|
3206
|
+
poolAccountNumber: z14.string().nullable(),
|
|
3207
|
+
status: z14.enum(PARTNER_PROFILE_STATUSES),
|
|
3208
|
+
metadata: Metadata,
|
|
3209
|
+
createdAtMs: z14.number().int().nonnegative(),
|
|
3210
|
+
updatedAtMs: z14.number().int().nonnegative()
|
|
3211
|
+
});
|
|
3212
|
+
var UpsertPartnerProfileInputSchema = z14.object({
|
|
3213
|
+
kind: z14.enum(PARTNER_KINDS),
|
|
3214
|
+
custodialMode: z14.enum(CUSTODIAL_MODES),
|
|
3215
|
+
displayName: z14.string().trim().min(1).max(200),
|
|
3216
|
+
bankCode: z14.string().trim().min(1).max(64).optional(),
|
|
3217
|
+
poolAccountNumber: z14.string().trim().min(1).max(64).optional(),
|
|
3218
|
+
metadata: Metadata.optional()
|
|
3219
|
+
});
|
|
3220
|
+
var PartnerFundingEventInputSchema = z14.object({
|
|
3221
|
+
externalRef: z14.string().trim().min(8).max(128),
|
|
3222
|
+
direction: z14.enum(PARTNER_FUNDING_DIRECTIONS).optional(),
|
|
3223
|
+
userId: z14.string().uuid().optional(),
|
|
3224
|
+
accountId: z14.string().uuid().optional(),
|
|
3225
|
+
amountMinor: PositiveMinor,
|
|
3226
|
+
currency: Currency,
|
|
3227
|
+
fundingSource: z14.string().trim().min(1).max(64).optional(),
|
|
3228
|
+
providerMetadata: Metadata.optional()
|
|
3229
|
+
});
|
|
3230
|
+
var PartnerFundingSchema = z14.object({
|
|
3231
|
+
fundingId: z14.string().uuid(),
|
|
3232
|
+
partnerId: z14.string().uuid(),
|
|
3233
|
+
accountId: z14.string().uuid(),
|
|
3234
|
+
userId: z14.string().uuid().nullable(),
|
|
3235
|
+
direction: z14.enum(PARTNER_FUNDING_DIRECTIONS),
|
|
3236
|
+
currency: z14.string(),
|
|
3237
|
+
amountMinor: MinorString,
|
|
3238
|
+
externalRef: z14.string(),
|
|
3239
|
+
status: z14.enum(PARTNER_FUNDING_STATUSES),
|
|
3240
|
+
fundingSource: z14.string(),
|
|
3241
|
+
ledgerRef: z14.string(),
|
|
3242
|
+
providerMetadata: Metadata,
|
|
3243
|
+
createdAtMs: z14.number().int().nonnegative(),
|
|
3244
|
+
updatedAtMs: z14.number().int().nonnegative()
|
|
3245
|
+
});
|
|
3246
|
+
var IngestFundingResultSchema = z14.object({
|
|
3247
|
+
funding: PartnerFundingSchema,
|
|
3248
|
+
replayed: z14.boolean()
|
|
3249
|
+
});
|
|
3250
|
+
var PayoutDestinationSchema = z14.object({
|
|
3251
|
+
destinationId: z14.string().uuid(),
|
|
3252
|
+
accountId: z14.string().uuid(),
|
|
3253
|
+
partnerId: z14.string().uuid(),
|
|
3254
|
+
bankCode: z14.string(),
|
|
3255
|
+
accountNumber: z14.string(),
|
|
3256
|
+
accountName: z14.string(),
|
|
3257
|
+
status: z14.enum(PAYOUT_DESTINATION_STATUSES),
|
|
3258
|
+
verifiedAtMs: z14.number().int().nonnegative().nullable(),
|
|
3259
|
+
metadata: Metadata,
|
|
3260
|
+
createdAtMs: z14.number().int().nonnegative(),
|
|
3261
|
+
updatedAtMs: z14.number().int().nonnegative()
|
|
3262
|
+
});
|
|
3263
|
+
var CreatePayoutDestinationInputSchema = z14.object({
|
|
3264
|
+
partnerId: z14.string().uuid(),
|
|
3265
|
+
bankCode: z14.string().trim().min(1).max(32),
|
|
3266
|
+
accountNumber: z14.string().trim().min(4).max(64),
|
|
3267
|
+
accountName: z14.string().trim().min(1).max(200),
|
|
3268
|
+
metadata: Metadata.optional()
|
|
3269
|
+
});
|
|
3270
|
+
var ListPayoutDestinationsResultSchema = z14.object({
|
|
3271
|
+
items: z14.array(PayoutDestinationSchema)
|
|
3272
|
+
});
|
|
3273
|
+
var WithdrawalSchema = z14.object({
|
|
3274
|
+
withdrawalId: z14.string().uuid(),
|
|
3275
|
+
accountId: z14.string().uuid(),
|
|
3276
|
+
userId: z14.string().uuid(),
|
|
3277
|
+
partnerId: z14.string().uuid(),
|
|
3278
|
+
destinationId: z14.string().uuid(),
|
|
3279
|
+
currency: z14.string(),
|
|
3280
|
+
amountMinor: MinorString,
|
|
3281
|
+
state: z14.enum(WITHDRAWAL_STATES),
|
|
3282
|
+
idempotencyKey: z14.string(),
|
|
3283
|
+
providerRef: z14.string().nullable(),
|
|
3284
|
+
lastError: z14.string().nullable(),
|
|
3285
|
+
ledgerRef: z14.string(),
|
|
3286
|
+
reverseLedgerRef: z14.string().nullable(),
|
|
3287
|
+
metadata: Metadata,
|
|
3288
|
+
createdAtMs: z14.number().int().nonnegative(),
|
|
3289
|
+
updatedAtMs: z14.number().int().nonnegative()
|
|
3290
|
+
});
|
|
3291
|
+
var CreateWithdrawalInputSchema = z14.object({
|
|
3292
|
+
destinationId: z14.string().uuid(),
|
|
3293
|
+
amountMinor: PositiveMinor,
|
|
3294
|
+
currency: Currency,
|
|
3295
|
+
idempotencyKey: z14.string().trim().min(8).max(128),
|
|
3296
|
+
metadata: Metadata.optional()
|
|
3297
|
+
});
|
|
3298
|
+
var CreateWithdrawalResultSchema = z14.object({
|
|
3299
|
+
withdrawal: WithdrawalSchema,
|
|
3300
|
+
replayed: z14.boolean()
|
|
3301
|
+
});
|
|
3302
|
+
var PayoutEventInputSchema = z14.object({
|
|
3303
|
+
externalRef: z14.string().trim().min(8).max(128),
|
|
3304
|
+
withdrawalId: z14.string().uuid().optional(),
|
|
3305
|
+
state: z14.enum(["submitted", "processing", "paid", "failed"]),
|
|
3306
|
+
providerRef: z14.string().trim().min(1).max(128).optional(),
|
|
3307
|
+
failureCode: z14.string().trim().max(64).optional(),
|
|
3308
|
+
failureMessage: z14.string().trim().max(512).optional(),
|
|
3309
|
+
providerMetadata: Metadata.optional()
|
|
3310
|
+
});
|
|
3311
|
+
var RecordPayoutEventResultSchema = z14.object({
|
|
3312
|
+
withdrawal: WithdrawalSchema,
|
|
3313
|
+
replayed: z14.boolean()
|
|
3314
|
+
});
|
|
3315
|
+
var ReconciliationReportSchema = z14.object({
|
|
3316
|
+
partnerId: z14.string().uuid(),
|
|
3317
|
+
currency: z14.string(),
|
|
3318
|
+
fromMs: z14.number().int().nonnegative(),
|
|
3319
|
+
toMs: z14.number().int().nonnegative(),
|
|
3320
|
+
fundingsCreditMinor: MinorString,
|
|
3321
|
+
fundingsDebitMinor: MinorString,
|
|
3322
|
+
withdrawalsPaidMinor: MinorString,
|
|
3323
|
+
withdrawalsReversedMinor: MinorString,
|
|
3324
|
+
withdrawalsInFlightMinor: MinorString,
|
|
3325
|
+
expectedReserveBalanceMinor: MinorString,
|
|
3326
|
+
actualReserveBalanceMinor: MinorString,
|
|
3327
|
+
imbalanceMinor: MinorString,
|
|
3328
|
+
generatedAtMs: z14.number().int().nonnegative()
|
|
3329
|
+
});
|
|
3330
|
+
function createPartnerFundingClient(partner) {
|
|
3331
|
+
return {
|
|
3332
|
+
ingestFunding: async (input) => {
|
|
3333
|
+
const body = PartnerFundingEventInputSchema.parse(input);
|
|
3334
|
+
const raw = await partner.request({
|
|
3335
|
+
method: "POST",
|
|
3336
|
+
path: "/v1/partners/fundings",
|
|
3337
|
+
body
|
|
3338
|
+
});
|
|
3339
|
+
return IngestFundingResultSchema.parse(raw);
|
|
3340
|
+
},
|
|
3341
|
+
recordPayoutEvent: async (input) => {
|
|
3342
|
+
const body = PayoutEventInputSchema.parse(input);
|
|
3343
|
+
const raw = await partner.request({
|
|
3344
|
+
method: "POST",
|
|
3345
|
+
path: "/v1/partners/payouts/events",
|
|
3346
|
+
body
|
|
3347
|
+
});
|
|
3348
|
+
return RecordPayoutEventResultSchema.parse(raw);
|
|
3349
|
+
},
|
|
3350
|
+
reconciliation: async (input) => {
|
|
3351
|
+
const qs = new URLSearchParams({
|
|
3352
|
+
currency: input.currency
|
|
3353
|
+
});
|
|
3354
|
+
if (typeof input.fromMs === "number")
|
|
3355
|
+
qs.set("fromMs", String(input.fromMs));
|
|
3356
|
+
if (typeof input.toMs === "number") qs.set("toMs", String(input.toMs));
|
|
3357
|
+
const raw = await partner.request({
|
|
3358
|
+
method: "GET",
|
|
3359
|
+
path: `/v1/partners/reconciliation/daily?${qs.toString()}`
|
|
3360
|
+
});
|
|
3361
|
+
return ReconciliationReportSchema.parse(raw);
|
|
3362
|
+
}
|
|
3363
|
+
};
|
|
3364
|
+
}
|
|
3365
|
+
function createConsumerWithdrawalsClient(opts) {
|
|
3366
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
3367
|
+
if (!fetchImpl) {
|
|
3368
|
+
throw new Error(
|
|
3369
|
+
"createConsumerWithdrawalsClient: no fetch implementation available"
|
|
3370
|
+
);
|
|
3371
|
+
}
|
|
3372
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
3373
|
+
async function call(method, path, body, parser) {
|
|
3374
|
+
const init2 = {
|
|
3375
|
+
method,
|
|
3376
|
+
headers: { accept: "application/json" }
|
|
3377
|
+
};
|
|
3378
|
+
if (body !== void 0) {
|
|
3379
|
+
init2.body = JSON.stringify(body);
|
|
3380
|
+
init2.headers = { ...init2.headers, "content-type": "application/json" };
|
|
3381
|
+
}
|
|
3382
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
3383
|
+
const text = await resp.text();
|
|
3384
|
+
let raw;
|
|
3385
|
+
if (text) {
|
|
3386
|
+
try {
|
|
3387
|
+
raw = JSON.parse(text);
|
|
3388
|
+
} catch {
|
|
3389
|
+
raw = text;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
if (!resp.ok) {
|
|
3393
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
3394
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
3395
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
3396
|
+
}
|
|
3397
|
+
return parser(raw);
|
|
3398
|
+
}
|
|
3399
|
+
return {
|
|
3400
|
+
listDestinations: () => call(
|
|
3401
|
+
"GET",
|
|
3402
|
+
"/v1/me/payout-destinations",
|
|
3403
|
+
void 0,
|
|
3404
|
+
(raw) => ListPayoutDestinationsResultSchema.parse(raw)
|
|
3405
|
+
),
|
|
3406
|
+
createDestination: (input) => call(
|
|
3407
|
+
"POST",
|
|
3408
|
+
"/v1/me/payout-destinations",
|
|
3409
|
+
CreatePayoutDestinationInputSchema.parse(input),
|
|
3410
|
+
(raw) => PayoutDestinationSchema.parse(raw)
|
|
3411
|
+
),
|
|
3412
|
+
verifyDestination: (destinationId) => call(
|
|
3413
|
+
"POST",
|
|
3414
|
+
`/v1/me/payout-destinations/${encodeURIComponent(destinationId)}/verify`,
|
|
3415
|
+
{},
|
|
3416
|
+
(raw) => PayoutDestinationSchema.parse(raw)
|
|
3417
|
+
),
|
|
3418
|
+
disableDestination: (destinationId) => call(
|
|
3419
|
+
"POST",
|
|
3420
|
+
`/v1/me/payout-destinations/${encodeURIComponent(destinationId)}/disable`,
|
|
3421
|
+
{},
|
|
3422
|
+
(raw) => PayoutDestinationSchema.parse(raw)
|
|
3423
|
+
),
|
|
3424
|
+
createWithdrawal: (input) => call(
|
|
3425
|
+
"POST",
|
|
3426
|
+
"/v1/me/withdrawals",
|
|
3427
|
+
CreateWithdrawalInputSchema.parse(input),
|
|
3428
|
+
(raw) => CreateWithdrawalResultSchema.parse(raw)
|
|
3429
|
+
),
|
|
3430
|
+
getWithdrawal: (withdrawalId) => call(
|
|
3431
|
+
"GET",
|
|
3432
|
+
`/v1/me/withdrawals/${encodeURIComponent(withdrawalId)}`,
|
|
3433
|
+
void 0,
|
|
3434
|
+
(raw) => WithdrawalSchema.parse(raw)
|
|
3435
|
+
)
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
function createPartnerProfileAdminClient(opts) {
|
|
3439
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
3440
|
+
if (!fetchImpl) {
|
|
3441
|
+
throw new Error(
|
|
3442
|
+
"createPartnerProfileAdminClient: no fetch implementation available"
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
3446
|
+
return {
|
|
3447
|
+
upsertProfile: async (partnerAccountId, input) => {
|
|
3448
|
+
const body = {
|
|
3449
|
+
partnerAccountId,
|
|
3450
|
+
...UpsertPartnerProfileInputSchema.parse(input)
|
|
3451
|
+
};
|
|
3452
|
+
const resp = await fetchImpl(`${baseUrl}/v1/partners/profile`, {
|
|
3453
|
+
method: "POST",
|
|
3454
|
+
headers: {
|
|
3455
|
+
"content-type": "application/json",
|
|
3456
|
+
accept: "application/json"
|
|
3457
|
+
},
|
|
3458
|
+
body: JSON.stringify(body)
|
|
3459
|
+
});
|
|
3460
|
+
const text = await resp.text();
|
|
3461
|
+
let raw;
|
|
3462
|
+
if (text) {
|
|
3463
|
+
try {
|
|
3464
|
+
raw = JSON.parse(text);
|
|
3465
|
+
} catch {
|
|
3466
|
+
raw = text;
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
if (!resp.ok) {
|
|
3470
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
3471
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
3472
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
3473
|
+
}
|
|
3474
|
+
return PartnerProfileSchema.parse(raw);
|
|
3475
|
+
}
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
2800
3478
|
export {
|
|
2801
3479
|
ACCOUNT_STATUSES,
|
|
2802
3480
|
ACCOUNT_TYPES,
|
|
@@ -2804,11 +3482,24 @@ export {
|
|
|
2804
3482
|
AccountMembershipSchema,
|
|
2805
3483
|
AccountSchema,
|
|
2806
3484
|
ApiCredentialPublicSchema,
|
|
3485
|
+
COLLECTION_INTENT_STATUSES,
|
|
3486
|
+
COLLECTION_PAYMENT_STATUSES,
|
|
3487
|
+
CUSTODIAL_MODES,
|
|
3488
|
+
CollectionIntentSchema,
|
|
3489
|
+
CollectionPaymentResultSchema,
|
|
3490
|
+
CollectionPaymentSchema,
|
|
3491
|
+
CollectionReportSummarySchema,
|
|
3492
|
+
CollectionStatementSchema,
|
|
2807
3493
|
OACRecordSchema as ConsumerOACRecordSchema,
|
|
2808
3494
|
ConsumerOACSchema,
|
|
2809
3495
|
ConsumerPaymentClaimSchema,
|
|
2810
3496
|
ConsumerSettleResultSchema,
|
|
2811
3497
|
ConsumerSettlementSchema,
|
|
3498
|
+
CreateCollectionIntentInputSchema,
|
|
3499
|
+
CreatePayoutDestinationInputSchema,
|
|
3500
|
+
CreatePayoutInputSchema,
|
|
3501
|
+
CreateWithdrawalInputSchema,
|
|
3502
|
+
CreateWithdrawalResultSchema,
|
|
2812
3503
|
DeviceKeyRecordSchema,
|
|
2813
3504
|
DisableOfflineInputSchema,
|
|
2814
3505
|
DisableOfflineResultSchema,
|
|
@@ -2821,8 +3512,14 @@ export {
|
|
|
2821
3512
|
FlurError,
|
|
2822
3513
|
FlurExpiredError,
|
|
2823
3514
|
FlurReplayError,
|
|
3515
|
+
IngestFundingResultSchema,
|
|
2824
3516
|
IssueOACInputSchema,
|
|
3517
|
+
ListPayoutDestinationsResultSchema,
|
|
2825
3518
|
MEMBERSHIP_ROLES,
|
|
3519
|
+
MERCHANT_PAYOUT_STATUSES,
|
|
3520
|
+
MERCHANT_PROFILE_STATUSES,
|
|
3521
|
+
MerchantPayoutSchema,
|
|
3522
|
+
MerchantProfileSchema,
|
|
2826
3523
|
MintedApiCredentialSchema,
|
|
2827
3524
|
NGN_CURRENCY_CODE,
|
|
2828
3525
|
NG_COUNTRY_CODE,
|
|
@@ -2837,25 +3534,46 @@ export {
|
|
|
2837
3534
|
OfflineStateResultSchema,
|
|
2838
3535
|
OfflineStatusResultSchema,
|
|
2839
3536
|
OfflineTokenSchema,
|
|
3537
|
+
PARTNER_FUNDING_DIRECTIONS,
|
|
3538
|
+
PARTNER_FUNDING_STATUSES,
|
|
3539
|
+
PARTNER_KINDS,
|
|
3540
|
+
PARTNER_PROFILE_STATUSES,
|
|
2840
3541
|
PARTNER_SCOPES,
|
|
2841
3542
|
PASS_KINDS,
|
|
2842
3543
|
PASS_STATES,
|
|
2843
3544
|
PAYLOAD_FORMAT_INDICATOR_VALUE,
|
|
3545
|
+
PAYOUT_DESTINATION_STATUSES,
|
|
2844
3546
|
POINT_OF_INITIATION,
|
|
3547
|
+
PartnerFundingEventInputSchema,
|
|
3548
|
+
PartnerFundingSchema,
|
|
3549
|
+
PartnerProfileSchema,
|
|
2845
3550
|
PassMetadataSchema,
|
|
2846
3551
|
PassSchema,
|
|
3552
|
+
PayCollectionInputSchema,
|
|
2847
3553
|
PaymentClaimSchema,
|
|
3554
|
+
PayoutDestinationSchema,
|
|
3555
|
+
PayoutEventInputSchema,
|
|
3556
|
+
ProviderEventInputSchema,
|
|
3557
|
+
ProviderEventRecordSchema,
|
|
3558
|
+
PublicCollectionIntentSchema,
|
|
2848
3559
|
RECEIPT_CHANNELS,
|
|
2849
3560
|
RECEIPT_KINDS,
|
|
2850
3561
|
REPLAY_WINDOW_MS,
|
|
2851
3562
|
ReceiptPayloadSchema,
|
|
2852
3563
|
ReceiptSchema,
|
|
3564
|
+
ReconciliationReportSchema,
|
|
3565
|
+
RecordPayoutEventResultSchema,
|
|
2853
3566
|
RedemptionSchema,
|
|
2854
3567
|
RegisterDeviceKeyInputSchema,
|
|
2855
3568
|
RevokeDeviceKeyInputSchema,
|
|
3569
|
+
SETTLEMENT_SCHEDULES,
|
|
2856
3570
|
SettleResponseSchema,
|
|
2857
3571
|
SettlementSchema,
|
|
2858
3572
|
SignedConsumerOACSchema,
|
|
3573
|
+
UpsertMerchantProfileInputSchema,
|
|
3574
|
+
UpsertPartnerProfileInputSchema,
|
|
3575
|
+
WITHDRAWAL_STATES,
|
|
3576
|
+
WithdrawalSchema,
|
|
2859
3577
|
bodySha256Hex,
|
|
2860
3578
|
buildAuthorization,
|
|
2861
3579
|
buildOAC,
|
|
@@ -2872,10 +3590,16 @@ export {
|
|
|
2872
3590
|
crc16ccittHex,
|
|
2873
3591
|
createAccountsClient,
|
|
2874
3592
|
createApiCredentialsAdminClient,
|
|
3593
|
+
createCollectionsClient,
|
|
3594
|
+
createConsumerCollectionsClient,
|
|
3595
|
+
createConsumerWithdrawalsClient,
|
|
2875
3596
|
createFlurPartnerClient,
|
|
2876
3597
|
createHmacFetch,
|
|
2877
3598
|
createMeOfflineClient,
|
|
2878
3599
|
createOfflineSettlementsClient,
|
|
3600
|
+
createPartnerCollectionsClient,
|
|
3601
|
+
createPartnerFundingClient,
|
|
3602
|
+
createPartnerProfileAdminClient,
|
|
2879
3603
|
createPassesClient,
|
|
2880
3604
|
createReceiptsClient,
|
|
2881
3605
|
decodeAuthorizationQR,
|