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