@nokinc-flur/sdk 2.2.0 → 2.4.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 +325 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +329 -191
- package/dist/index.d.ts +329 -191
- package/dist/index.js +314 -300
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2961,7 +2961,17 @@ var Base64Std3 = z13.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
|
2961
2961
|
var ClaimNonce = z13.string().min(8).max(128).refine((value) => !value.includes("|"), {
|
|
2962
2962
|
message: "nonce must not contain |"
|
|
2963
2963
|
});
|
|
2964
|
-
var ACCOUNT_FUNDED_OAC_MAX_TTL_MS = 1e3 * 60 * 60 * 24
|
|
2964
|
+
var ACCOUNT_FUNDED_OAC_MAX_TTL_MS = 1e3 * 60 * 60 * 24;
|
|
2965
|
+
var IssuerTrustKeySchema = z13.object({
|
|
2966
|
+
issuerId: z13.string().min(1).max(128),
|
|
2967
|
+
alg: z13.literal("p256"),
|
|
2968
|
+
publicKeySpkiB64: Base64Std3.min(64).max(4096),
|
|
2969
|
+
notBeforeMs: z13.number().int().nonnegative().optional(),
|
|
2970
|
+
notAfterMs: z13.number().int().positive().optional()
|
|
2971
|
+
});
|
|
2972
|
+
var IssuerTrustBundleSchema = z13.object({
|
|
2973
|
+
keys: z13.array(IssuerTrustKeySchema).min(1)
|
|
2974
|
+
});
|
|
2965
2975
|
var CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS = 1e3 * 60 * 60 * 24;
|
|
2966
2976
|
var AttestationSecurityLevelSchema = z13.enum([
|
|
2967
2977
|
"STRONGBOX",
|
|
@@ -3015,13 +3025,28 @@ var ConsumerOACSchema = z13.object({
|
|
|
3015
3025
|
alg: z13.literal("p256"),
|
|
3016
3026
|
/** P-256 SubjectPublicKeyInfo DER, base64. */
|
|
3017
3027
|
devicePubkeySpkiB64: Base64Std3.min(64).max(4096),
|
|
3018
|
-
|
|
3019
|
-
|
|
3028
|
+
/**
|
|
3029
|
+
* Per-transaction / cumulative offline spend ceilings. Zero is valid and
|
|
3030
|
+
* denotes an identity-only OAC: the credential proves who the holder is and
|
|
3031
|
+
* binds their device key for offline-verifiable first contact, but carries
|
|
3032
|
+
* no offline spend authority (every claim against it routes to REVIEW).
|
|
3033
|
+
* Issued to zero-balance wallets so they can still be paid offline.
|
|
3034
|
+
*/
|
|
3035
|
+
perTxCapKobo: z13.number().int().nonnegative(),
|
|
3036
|
+
cumulativeCapKobo: z13.number().int().nonnegative(),
|
|
3020
3037
|
currency: z13.string().length(3),
|
|
3021
3038
|
validFromMs: z13.number().int().nonnegative(),
|
|
3022
3039
|
validUntilMs: z13.number().int().nonnegative(),
|
|
3023
3040
|
counterSeed: z13.number().int().nonnegative(),
|
|
3024
|
-
issuedAtMs: z13.number().int().nonnegative()
|
|
3041
|
+
issuedAtMs: z13.number().int().nonnegative(),
|
|
3042
|
+
/**
|
|
3043
|
+
* Issuer-attested identity folded into the OAC so a single signed
|
|
3044
|
+
* credential serves both Tier B offline-verifiable identity and offline
|
|
3045
|
+
* spend authority. Verified offline against a pinned issuer key; the
|
|
3046
|
+
* backend remains authoritative at settlement.
|
|
3047
|
+
*/
|
|
3048
|
+
phoneE164: z13.string().regex(/^\+[1-9]\d{7,14}$/),
|
|
3049
|
+
displayName: z13.string().min(1).max(64)
|
|
3025
3050
|
});
|
|
3026
3051
|
var SignedConsumerOACSchema = z13.object({
|
|
3027
3052
|
oac: ConsumerOACSchema,
|
|
@@ -3189,6 +3214,12 @@ function createMeOfflineClient(opts) {
|
|
|
3189
3214
|
`/v1/me/offline/settlements/${encodeURIComponent(idOrKey)}`,
|
|
3190
3215
|
void 0,
|
|
3191
3216
|
(raw) => ConsumerSettlementSchema.parse(raw)
|
|
3217
|
+
),
|
|
3218
|
+
getIssuerKeys: () => call(
|
|
3219
|
+
"GET",
|
|
3220
|
+
"/v1/issuer/keys",
|
|
3221
|
+
void 0,
|
|
3222
|
+
(raw) => IssuerTrustBundleSchema.parse(raw)
|
|
3192
3223
|
)
|
|
3193
3224
|
};
|
|
3194
3225
|
}
|
|
@@ -3549,13 +3580,130 @@ function base64UrlDecodeUtf8(input) {
|
|
|
3549
3580
|
throw new Error("base64 decoder unavailable");
|
|
3550
3581
|
}
|
|
3551
3582
|
|
|
3583
|
+
// src/me-offline/oac.ts
|
|
3584
|
+
import { z as z15 } from "zod";
|
|
3585
|
+
var CONSUMER_OAC_DOMAIN = "flur:consumer-offline:v1:oac";
|
|
3586
|
+
function consumerOacSigningPayload(oac) {
|
|
3587
|
+
return { domain: CONSUMER_OAC_DOMAIN, ...oac };
|
|
3588
|
+
}
|
|
3589
|
+
function verifyOacOffline(signed, trustedKeys, options = {}) {
|
|
3590
|
+
const parsed = SignedConsumerOACSchema.safeParse(signed);
|
|
3591
|
+
if (!parsed.success) return { ok: false, reason: "malformed" };
|
|
3592
|
+
const oacParsed = ConsumerOACSchema.safeParse(parsed.data.oac);
|
|
3593
|
+
if (!oacParsed.success) return { ok: false, reason: "malformed" };
|
|
3594
|
+
const oac = oacParsed.data;
|
|
3595
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
3596
|
+
const pinned = trustedKeys.filter(
|
|
3597
|
+
(k) => k.issuerId === oac.issuerId && (k.notBeforeMs === void 0 || nowMs >= k.notBeforeMs) && (k.notAfterMs === void 0 || nowMs <= k.notAfterMs)
|
|
3598
|
+
);
|
|
3599
|
+
if (pinned.length === 0) return { ok: false, reason: "untrusted_issuer" };
|
|
3600
|
+
const signingBytes = canonicalJSONBytes(consumerOacSigningPayload(oac));
|
|
3601
|
+
const signatureOk = pinned.some(
|
|
3602
|
+
(k) => verifyIssuerP256(signingBytes, parsed.data.issuerSig, k.publicKeySpkiB64)
|
|
3603
|
+
);
|
|
3604
|
+
if (!signatureOk) return { ok: false, reason: "signature_invalid" };
|
|
3605
|
+
if (oac.validUntilMs - oac.validFromMs > ACCOUNT_FUNDED_OAC_MAX_TTL_MS) {
|
|
3606
|
+
return { ok: false, reason: "window_too_long" };
|
|
3607
|
+
}
|
|
3608
|
+
if (nowMs < oac.validFromMs) return { ok: false, reason: "not_yet_valid" };
|
|
3609
|
+
if (nowMs >= oac.validUntilMs) return { ok: false, reason: "expired" };
|
|
3610
|
+
return {
|
|
3611
|
+
ok: true,
|
|
3612
|
+
oac,
|
|
3613
|
+
identity: {
|
|
3614
|
+
oacId: oac.oacId,
|
|
3615
|
+
issuerId: oac.issuerId,
|
|
3616
|
+
userId: oac.userId,
|
|
3617
|
+
phoneE164: oac.phoneE164,
|
|
3618
|
+
displayName: oac.displayName,
|
|
3619
|
+
devicePubkeySpkiB64: oac.devicePubkeySpkiB64
|
|
3620
|
+
}
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
var CONSUMER_OAC_QR_PREFIX = "FLUROAC1.";
|
|
3624
|
+
function isConsumerOacQR(value) {
|
|
3625
|
+
return value.startsWith(CONSUMER_OAC_QR_PREFIX);
|
|
3626
|
+
}
|
|
3627
|
+
var OacPresentmentRequestSchema = z15.object({
|
|
3628
|
+
/** Requested amount in minor units (kobo). */
|
|
3629
|
+
amountMinor: z15.number().int().positive().max(1e12).optional(),
|
|
3630
|
+
/** Purpose/intent code (mirrors the NIBSS intent vocabulary). */
|
|
3631
|
+
intent: z15.string().min(1).max(32).optional(),
|
|
3632
|
+
/** Free-text reference / note. */
|
|
3633
|
+
reference: z15.string().min(1).max(64).optional()
|
|
3634
|
+
}).strict();
|
|
3635
|
+
function encodeConsumerOacQR(signed, request) {
|
|
3636
|
+
const parsed = SignedConsumerOACSchema.parse(signed);
|
|
3637
|
+
const base = `${CONSUMER_OAC_QR_PREFIX}${base64UrlEncodeUtf82(JSON.stringify(parsed))}`;
|
|
3638
|
+
if (request === void 0) return base;
|
|
3639
|
+
const parsedRequest = OacPresentmentRequestSchema.parse(request);
|
|
3640
|
+
if (Object.keys(parsedRequest).length === 0) return base;
|
|
3641
|
+
return `${base}.${base64UrlEncodeUtf82(JSON.stringify(parsedRequest))}`;
|
|
3642
|
+
}
|
|
3643
|
+
function decodeUnverifiedConsumerOacQR(value) {
|
|
3644
|
+
if (!value.startsWith(CONSUMER_OAC_QR_PREFIX)) {
|
|
3645
|
+
throw new Error("not a Flur consumer OAC QR");
|
|
3646
|
+
}
|
|
3647
|
+
const remainder = value.slice(CONSUMER_OAC_QR_PREFIX.length);
|
|
3648
|
+
const encoded = remainder.split(".", 1)[0];
|
|
3649
|
+
let raw;
|
|
3650
|
+
try {
|
|
3651
|
+
raw = JSON.parse(base64UrlDecodeUtf82(encoded));
|
|
3652
|
+
} catch {
|
|
3653
|
+
throw new Error("consumer OAC QR is malformed");
|
|
3654
|
+
}
|
|
3655
|
+
return SignedConsumerOACSchema.parse(raw);
|
|
3656
|
+
}
|
|
3657
|
+
function decodeConsumerOacRequest(value) {
|
|
3658
|
+
if (!value.startsWith(CONSUMER_OAC_QR_PREFIX)) return null;
|
|
3659
|
+
const remainder = value.slice(CONSUMER_OAC_QR_PREFIX.length);
|
|
3660
|
+
const dot = remainder.indexOf(".");
|
|
3661
|
+
if (dot < 0) return null;
|
|
3662
|
+
const suffix = remainder.slice(dot + 1);
|
|
3663
|
+
if (suffix.length === 0) return null;
|
|
3664
|
+
try {
|
|
3665
|
+
const raw = JSON.parse(base64UrlDecodeUtf82(suffix));
|
|
3666
|
+
const parsed = OacPresentmentRequestSchema.safeParse(raw);
|
|
3667
|
+
return parsed.success ? parsed.data : null;
|
|
3668
|
+
} catch {
|
|
3669
|
+
return null;
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
function base64UrlEncodeUtf82(input) {
|
|
3673
|
+
const bytes = new TextEncoder().encode(input);
|
|
3674
|
+
let binary = "";
|
|
3675
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
3676
|
+
const base64 = typeof btoa === "function" ? btoa(binary) : typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : void 0;
|
|
3677
|
+
if (!base64) throw new Error("base64 encoder unavailable");
|
|
3678
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
3679
|
+
}
|
|
3680
|
+
function base64UrlDecodeUtf82(input) {
|
|
3681
|
+
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
3682
|
+
const padded = base64.padEnd(
|
|
3683
|
+
base64.length + (4 - base64.length % 4) % 4,
|
|
3684
|
+
"="
|
|
3685
|
+
);
|
|
3686
|
+
if (typeof atob === "function") {
|
|
3687
|
+
const binary = atob(padded);
|
|
3688
|
+
const bytes = new Uint8Array(binary.length);
|
|
3689
|
+
for (let index = 0; index < binary.length; index++) {
|
|
3690
|
+
bytes[index] = binary.charCodeAt(index);
|
|
3691
|
+
}
|
|
3692
|
+
return new TextDecoder().decode(bytes);
|
|
3693
|
+
}
|
|
3694
|
+
if (typeof Buffer !== "undefined") {
|
|
3695
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
3696
|
+
}
|
|
3697
|
+
throw new Error("base64 decoder unavailable");
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3552
3700
|
// src/me-offline/sms.ts
|
|
3553
3701
|
import { p256 as p2563 } from "@noble/curves/nist";
|
|
3554
3702
|
var OFFLINE_CLAIM_SMS_PREFIX = "FLURC1.";
|
|
3555
3703
|
var CLAIM_TOKEN_RE = /(?:^|\s)(FLURC1\.[A-Za-z0-9_-]+={0,2})(?:\s|$)/;
|
|
3556
3704
|
function encodeOfflineClaimSmsMessage(claim) {
|
|
3557
3705
|
const parsed = ConsumerPaymentClaimSchema.parse(claim);
|
|
3558
|
-
return `${OFFLINE_CLAIM_SMS_PREFIX}${
|
|
3706
|
+
return `${OFFLINE_CLAIM_SMS_PREFIX}${base64UrlEncodeUtf83(
|
|
3559
3707
|
JSON.stringify(parsed)
|
|
3560
3708
|
)}`;
|
|
3561
3709
|
}
|
|
@@ -3565,7 +3713,7 @@ function decodeOfflineClaimSmsMessage(message) {
|
|
|
3565
3713
|
const encoded = token.slice(OFFLINE_CLAIM_SMS_PREFIX.length);
|
|
3566
3714
|
let raw;
|
|
3567
3715
|
try {
|
|
3568
|
-
raw = JSON.parse(
|
|
3716
|
+
raw = JSON.parse(base64UrlDecodeUtf83(encoded));
|
|
3569
3717
|
} catch {
|
|
3570
3718
|
throw new Error("offline claim QR token is malformed");
|
|
3571
3719
|
}
|
|
@@ -3791,10 +3939,10 @@ function bytesToBase64Url(bytes) {
|
|
|
3791
3939
|
}
|
|
3792
3940
|
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
3793
3941
|
}
|
|
3794
|
-
function
|
|
3942
|
+
function base64UrlEncodeUtf83(input) {
|
|
3795
3943
|
return bytesToBase64Url(utf8(input));
|
|
3796
3944
|
}
|
|
3797
|
-
function
|
|
3945
|
+
function base64UrlDecodeUtf83(input) {
|
|
3798
3946
|
return new TextDecoder().decode(base64UrlToBytes(input));
|
|
3799
3947
|
}
|
|
3800
3948
|
function base64UrlToBytes(input) {
|
|
@@ -3816,14 +3964,14 @@ function base64UrlToBytes(input) {
|
|
|
3816
3964
|
}
|
|
3817
3965
|
|
|
3818
3966
|
// src/partner-funding/client.ts
|
|
3819
|
-
import { z as
|
|
3820
|
-
var MinorString =
|
|
3821
|
-
var PositiveMinor =
|
|
3822
|
-
|
|
3823
|
-
|
|
3967
|
+
import { z as z16 } from "zod";
|
|
3968
|
+
var MinorString = z16.string().regex(/^-?\d+$/);
|
|
3969
|
+
var PositiveMinor = z16.union([
|
|
3970
|
+
z16.number().int().positive(),
|
|
3971
|
+
z16.string().regex(/^[1-9]\d{0,18}$/)
|
|
3824
3972
|
]);
|
|
3825
|
-
var Currency =
|
|
3826
|
-
var Metadata =
|
|
3973
|
+
var Currency = z16.string().trim().length(3).transform((v) => v.toUpperCase());
|
|
3974
|
+
var Metadata = z16.record(z16.unknown());
|
|
3827
3975
|
var PARTNER_KINDS = ["bank", "merchant"];
|
|
3828
3976
|
var CUSTODIAL_MODES = ["agent_of_bank", "flur_virtual_pool"];
|
|
3829
3977
|
var PARTNER_PROFILE_STATUSES = [
|
|
@@ -3850,126 +3998,126 @@ var WITHDRAWAL_STATES = [
|
|
|
3850
3998
|
"failed",
|
|
3851
3999
|
"reversed"
|
|
3852
4000
|
];
|
|
3853
|
-
var PartnerProfileSchema =
|
|
3854
|
-
partnerAccountId:
|
|
3855
|
-
kind:
|
|
3856
|
-
custodialMode:
|
|
3857
|
-
displayName:
|
|
3858
|
-
bankCode:
|
|
3859
|
-
poolAccountNumber:
|
|
3860
|
-
status:
|
|
4001
|
+
var PartnerProfileSchema = z16.object({
|
|
4002
|
+
partnerAccountId: z16.string().uuid(),
|
|
4003
|
+
kind: z16.enum(PARTNER_KINDS),
|
|
4004
|
+
custodialMode: z16.enum(CUSTODIAL_MODES),
|
|
4005
|
+
displayName: z16.string(),
|
|
4006
|
+
bankCode: z16.string().nullable(),
|
|
4007
|
+
poolAccountNumber: z16.string().nullable(),
|
|
4008
|
+
status: z16.enum(PARTNER_PROFILE_STATUSES),
|
|
3861
4009
|
metadata: Metadata,
|
|
3862
|
-
createdAtMs:
|
|
3863
|
-
updatedAtMs:
|
|
4010
|
+
createdAtMs: z16.number().int().nonnegative(),
|
|
4011
|
+
updatedAtMs: z16.number().int().nonnegative()
|
|
3864
4012
|
});
|
|
3865
|
-
var UpsertPartnerProfileInputSchema =
|
|
3866
|
-
kind:
|
|
3867
|
-
custodialMode:
|
|
3868
|
-
displayName:
|
|
3869
|
-
bankCode:
|
|
3870
|
-
poolAccountNumber:
|
|
4013
|
+
var UpsertPartnerProfileInputSchema = z16.object({
|
|
4014
|
+
kind: z16.enum(PARTNER_KINDS),
|
|
4015
|
+
custodialMode: z16.enum(CUSTODIAL_MODES),
|
|
4016
|
+
displayName: z16.string().trim().min(1).max(200),
|
|
4017
|
+
bankCode: z16.string().trim().min(1).max(64).optional(),
|
|
4018
|
+
poolAccountNumber: z16.string().trim().min(1).max(64).optional(),
|
|
3871
4019
|
metadata: Metadata.optional()
|
|
3872
4020
|
});
|
|
3873
|
-
var PartnerFundingEventInputSchema =
|
|
3874
|
-
externalRef:
|
|
3875
|
-
direction:
|
|
3876
|
-
userId:
|
|
3877
|
-
accountId:
|
|
4021
|
+
var PartnerFundingEventInputSchema = z16.object({
|
|
4022
|
+
externalRef: z16.string().trim().min(8).max(128),
|
|
4023
|
+
direction: z16.enum(PARTNER_FUNDING_DIRECTIONS).optional(),
|
|
4024
|
+
userId: z16.string().uuid().optional(),
|
|
4025
|
+
accountId: z16.string().uuid().optional(),
|
|
3878
4026
|
amountMinor: PositiveMinor,
|
|
3879
4027
|
currency: Currency,
|
|
3880
|
-
fundingSource:
|
|
4028
|
+
fundingSource: z16.string().trim().min(1).max(64).optional(),
|
|
3881
4029
|
providerMetadata: Metadata.optional()
|
|
3882
4030
|
});
|
|
3883
|
-
var PartnerFundingSchema =
|
|
3884
|
-
fundingId:
|
|
3885
|
-
partnerId:
|
|
3886
|
-
accountId:
|
|
3887
|
-
userId:
|
|
3888
|
-
direction:
|
|
3889
|
-
currency:
|
|
4031
|
+
var PartnerFundingSchema = z16.object({
|
|
4032
|
+
fundingId: z16.string().uuid(),
|
|
4033
|
+
partnerId: z16.string().uuid(),
|
|
4034
|
+
accountId: z16.string().uuid(),
|
|
4035
|
+
userId: z16.string().uuid().nullable(),
|
|
4036
|
+
direction: z16.enum(PARTNER_FUNDING_DIRECTIONS),
|
|
4037
|
+
currency: z16.string(),
|
|
3890
4038
|
amountMinor: MinorString,
|
|
3891
|
-
externalRef:
|
|
3892
|
-
status:
|
|
3893
|
-
fundingSource:
|
|
3894
|
-
ledgerRef:
|
|
4039
|
+
externalRef: z16.string(),
|
|
4040
|
+
status: z16.enum(PARTNER_FUNDING_STATUSES),
|
|
4041
|
+
fundingSource: z16.string(),
|
|
4042
|
+
ledgerRef: z16.string(),
|
|
3895
4043
|
providerMetadata: Metadata,
|
|
3896
|
-
createdAtMs:
|
|
3897
|
-
updatedAtMs:
|
|
4044
|
+
createdAtMs: z16.number().int().nonnegative(),
|
|
4045
|
+
updatedAtMs: z16.number().int().nonnegative()
|
|
3898
4046
|
});
|
|
3899
|
-
var IngestFundingResultSchema =
|
|
4047
|
+
var IngestFundingResultSchema = z16.object({
|
|
3900
4048
|
funding: PartnerFundingSchema,
|
|
3901
|
-
replayed:
|
|
4049
|
+
replayed: z16.boolean()
|
|
3902
4050
|
});
|
|
3903
|
-
var PayoutDestinationSchema =
|
|
3904
|
-
destinationId:
|
|
3905
|
-
accountId:
|
|
3906
|
-
partnerId:
|
|
3907
|
-
bankCode:
|
|
3908
|
-
accountNumber:
|
|
3909
|
-
accountName:
|
|
3910
|
-
status:
|
|
3911
|
-
verifiedAtMs:
|
|
4051
|
+
var PayoutDestinationSchema = z16.object({
|
|
4052
|
+
destinationId: z16.string().uuid(),
|
|
4053
|
+
accountId: z16.string().uuid(),
|
|
4054
|
+
partnerId: z16.string().uuid(),
|
|
4055
|
+
bankCode: z16.string(),
|
|
4056
|
+
accountNumber: z16.string(),
|
|
4057
|
+
accountName: z16.string(),
|
|
4058
|
+
status: z16.enum(PAYOUT_DESTINATION_STATUSES),
|
|
4059
|
+
verifiedAtMs: z16.number().int().nonnegative().nullable(),
|
|
3912
4060
|
metadata: Metadata,
|
|
3913
|
-
createdAtMs:
|
|
3914
|
-
updatedAtMs:
|
|
4061
|
+
createdAtMs: z16.number().int().nonnegative(),
|
|
4062
|
+
updatedAtMs: z16.number().int().nonnegative()
|
|
3915
4063
|
});
|
|
3916
|
-
var CreatePayoutDestinationInputSchema =
|
|
3917
|
-
partnerId:
|
|
3918
|
-
bankCode:
|
|
3919
|
-
accountNumber:
|
|
3920
|
-
accountName:
|
|
4064
|
+
var CreatePayoutDestinationInputSchema = z16.object({
|
|
4065
|
+
partnerId: z16.string().uuid(),
|
|
4066
|
+
bankCode: z16.string().trim().min(1).max(32),
|
|
4067
|
+
accountNumber: z16.string().trim().min(4).max(64),
|
|
4068
|
+
accountName: z16.string().trim().min(1).max(200),
|
|
3921
4069
|
metadata: Metadata.optional()
|
|
3922
4070
|
});
|
|
3923
|
-
var ListPayoutDestinationsResultSchema =
|
|
3924
|
-
items:
|
|
4071
|
+
var ListPayoutDestinationsResultSchema = z16.object({
|
|
4072
|
+
items: z16.array(PayoutDestinationSchema)
|
|
3925
4073
|
});
|
|
3926
|
-
var WithdrawalSchema =
|
|
3927
|
-
withdrawalId:
|
|
3928
|
-
accountId:
|
|
3929
|
-
userId:
|
|
3930
|
-
partnerId:
|
|
3931
|
-
destinationId:
|
|
3932
|
-
currency:
|
|
4074
|
+
var WithdrawalSchema = z16.object({
|
|
4075
|
+
withdrawalId: z16.string().uuid(),
|
|
4076
|
+
accountId: z16.string().uuid(),
|
|
4077
|
+
userId: z16.string().uuid(),
|
|
4078
|
+
partnerId: z16.string().uuid(),
|
|
4079
|
+
destinationId: z16.string().uuid(),
|
|
4080
|
+
currency: z16.string(),
|
|
3933
4081
|
amountMinor: MinorString,
|
|
3934
|
-
state:
|
|
3935
|
-
idempotencyKey:
|
|
3936
|
-
providerRef:
|
|
3937
|
-
lastError:
|
|
3938
|
-
ledgerRef:
|
|
3939
|
-
reverseLedgerRef:
|
|
4082
|
+
state: z16.enum(WITHDRAWAL_STATES),
|
|
4083
|
+
idempotencyKey: z16.string(),
|
|
4084
|
+
providerRef: z16.string().nullable(),
|
|
4085
|
+
lastError: z16.string().nullable(),
|
|
4086
|
+
ledgerRef: z16.string(),
|
|
4087
|
+
reverseLedgerRef: z16.string().nullable(),
|
|
3940
4088
|
metadata: Metadata,
|
|
3941
|
-
createdAtMs:
|
|
3942
|
-
updatedAtMs:
|
|
4089
|
+
createdAtMs: z16.number().int().nonnegative(),
|
|
4090
|
+
updatedAtMs: z16.number().int().nonnegative()
|
|
3943
4091
|
});
|
|
3944
|
-
var CreateWithdrawalInputSchema =
|
|
3945
|
-
destinationId:
|
|
4092
|
+
var CreateWithdrawalInputSchema = z16.object({
|
|
4093
|
+
destinationId: z16.string().uuid(),
|
|
3946
4094
|
amountMinor: PositiveMinor,
|
|
3947
4095
|
currency: Currency,
|
|
3948
|
-
idempotencyKey:
|
|
4096
|
+
idempotencyKey: z16.string().trim().min(8).max(128),
|
|
3949
4097
|
metadata: Metadata.optional()
|
|
3950
4098
|
});
|
|
3951
|
-
var CreateWithdrawalResultSchema =
|
|
4099
|
+
var CreateWithdrawalResultSchema = z16.object({
|
|
3952
4100
|
withdrawal: WithdrawalSchema,
|
|
3953
|
-
replayed:
|
|
4101
|
+
replayed: z16.boolean()
|
|
3954
4102
|
});
|
|
3955
|
-
var PayoutEventInputSchema =
|
|
3956
|
-
externalRef:
|
|
3957
|
-
withdrawalId:
|
|
3958
|
-
state:
|
|
3959
|
-
providerRef:
|
|
3960
|
-
failureCode:
|
|
3961
|
-
failureMessage:
|
|
4103
|
+
var PayoutEventInputSchema = z16.object({
|
|
4104
|
+
externalRef: z16.string().trim().min(8).max(128),
|
|
4105
|
+
withdrawalId: z16.string().uuid().optional(),
|
|
4106
|
+
state: z16.enum(["submitted", "processing", "paid", "failed"]),
|
|
4107
|
+
providerRef: z16.string().trim().min(1).max(128).optional(),
|
|
4108
|
+
failureCode: z16.string().trim().max(64).optional(),
|
|
4109
|
+
failureMessage: z16.string().trim().max(512).optional(),
|
|
3962
4110
|
providerMetadata: Metadata.optional()
|
|
3963
4111
|
});
|
|
3964
|
-
var RecordPayoutEventResultSchema =
|
|
4112
|
+
var RecordPayoutEventResultSchema = z16.object({
|
|
3965
4113
|
withdrawal: WithdrawalSchema,
|
|
3966
|
-
replayed:
|
|
4114
|
+
replayed: z16.boolean()
|
|
3967
4115
|
});
|
|
3968
|
-
var ReconciliationReportSchema =
|
|
3969
|
-
partnerId:
|
|
3970
|
-
currency:
|
|
3971
|
-
fromMs:
|
|
3972
|
-
toMs:
|
|
4116
|
+
var ReconciliationReportSchema = z16.object({
|
|
4117
|
+
partnerId: z16.string().uuid(),
|
|
4118
|
+
currency: z16.string(),
|
|
4119
|
+
fromMs: z16.number().int().nonnegative(),
|
|
4120
|
+
toMs: z16.number().int().nonnegative(),
|
|
3973
4121
|
fundingsCreditMinor: MinorString,
|
|
3974
4122
|
fundingsDebitMinor: MinorString,
|
|
3975
4123
|
withdrawalsPaidMinor: MinorString,
|
|
@@ -3978,7 +4126,7 @@ var ReconciliationReportSchema = z15.object({
|
|
|
3978
4126
|
expectedReserveBalanceMinor: MinorString,
|
|
3979
4127
|
actualReserveBalanceMinor: MinorString,
|
|
3980
4128
|
imbalanceMinor: MinorString,
|
|
3981
|
-
generatedAtMs:
|
|
4129
|
+
generatedAtMs: z16.number().int().nonnegative()
|
|
3982
4130
|
});
|
|
3983
4131
|
function createPartnerFundingClient(partner) {
|
|
3984
4132
|
return {
|
|
@@ -4130,19 +4278,19 @@ function createPartnerProfileAdminClient(opts) {
|
|
|
4130
4278
|
}
|
|
4131
4279
|
|
|
4132
4280
|
// src/artifacts/envelope.ts
|
|
4133
|
-
import { z as
|
|
4281
|
+
import { z as z17 } from "zod";
|
|
4134
4282
|
var FLUR_ARTIFACT_URI_SCHEME = "flur";
|
|
4135
4283
|
var FLUR_ARTIFACT_VERSION = 1;
|
|
4136
4284
|
var FLUR_ARTIFACT_URI_PREFIX = `${FLUR_ARTIFACT_URI_SCHEME}://v${FLUR_ARTIFACT_VERSION}/`;
|
|
4137
4285
|
var ArtifactTypeRe = /^[a-z][a-z0-9_]{1,63}$/;
|
|
4138
|
-
var ArtifactHeaderSchema =
|
|
4139
|
-
v:
|
|
4140
|
-
t:
|
|
4141
|
-
iss:
|
|
4142
|
-
kid:
|
|
4143
|
-
iat:
|
|
4144
|
-
exp:
|
|
4145
|
-
nonce:
|
|
4286
|
+
var ArtifactHeaderSchema = z17.object({
|
|
4287
|
+
v: z17.literal(FLUR_ARTIFACT_VERSION),
|
|
4288
|
+
t: z17.string().regex(ArtifactTypeRe, "invalid artifact type"),
|
|
4289
|
+
iss: z17.string().min(1).max(128),
|
|
4290
|
+
kid: z17.string().min(1).max(128),
|
|
4291
|
+
iat: z17.number().int().nonnegative(),
|
|
4292
|
+
exp: z17.number().int().positive().optional(),
|
|
4293
|
+
nonce: z17.string().min(8).max(64).regex(/^[A-Za-z0-9_-]+$/, "nonce must be url-safe")
|
|
4146
4294
|
});
|
|
4147
4295
|
var FlurArtifactError = class extends Error {
|
|
4148
4296
|
constructor(message, code) {
|
|
@@ -4281,7 +4429,7 @@ function verifyArtifactSignature(decoded, publicKeySpkiB64, options = {}) {
|
|
|
4281
4429
|
}
|
|
4282
4430
|
|
|
4283
4431
|
// src/artifacts/types.ts
|
|
4284
|
-
import { z as
|
|
4432
|
+
import { z as z18 } from "zod";
|
|
4285
4433
|
var ARTIFACT_TYPES = {
|
|
4286
4434
|
OFFLINE_PAYMENT_AUTHORIZATION: "offline_payment_authorization",
|
|
4287
4435
|
RECEIPT: "receipt",
|
|
@@ -4294,37 +4442,34 @@ var ARTIFACT_TYPES = {
|
|
|
4294
4442
|
LEDGER_JOURNAL_ENTRY: "ledger_journal_entry",
|
|
4295
4443
|
STATEMENT: "statement",
|
|
4296
4444
|
PASS: "pass",
|
|
4297
|
-
IDENTITY: "identity"
|
|
4298
|
-
// Tier B: holder-signed identity attestation for offline trust. The
|
|
4299
|
-
// envelope.iat / envelope.exp express the card's canonical lifetime.
|
|
4300
|
-
PAY_CARD: "pay_card"
|
|
4445
|
+
IDENTITY: "identity"
|
|
4301
4446
|
};
|
|
4302
|
-
var HexString = (length) =>
|
|
4447
|
+
var HexString = (length) => z18.string().regex(
|
|
4303
4448
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
4304
4449
|
`expected ${length}-byte hex string`
|
|
4305
4450
|
);
|
|
4306
|
-
var OfflinePaymentAuthorizationArtifactSchema =
|
|
4451
|
+
var OfflinePaymentAuthorizationArtifactSchema = z18.object({
|
|
4307
4452
|
authorization: OfflinePaymentAuthorizationSchema
|
|
4308
4453
|
});
|
|
4309
|
-
var ReceiptArtifactSchema =
|
|
4310
|
-
receiptId:
|
|
4311
|
-
paymentReference:
|
|
4312
|
-
payerUserId:
|
|
4313
|
-
payeeUserId:
|
|
4314
|
-
amountKobo:
|
|
4315
|
-
currency:
|
|
4316
|
-
channel:
|
|
4317
|
-
settledAtMs:
|
|
4318
|
-
ledgerTxnId:
|
|
4319
|
-
memo:
|
|
4454
|
+
var ReceiptArtifactSchema = z18.object({
|
|
4455
|
+
receiptId: z18.string().min(1).max(64),
|
|
4456
|
+
paymentReference: z18.string().min(1).max(64),
|
|
4457
|
+
payerUserId: z18.string().min(1).max(64).optional(),
|
|
4458
|
+
payeeUserId: z18.string().min(1).max(64),
|
|
4459
|
+
amountKobo: z18.number().int().positive(),
|
|
4460
|
+
currency: z18.literal("NGN"),
|
|
4461
|
+
channel: z18.enum(["online", "offline_reconciled", "pay_link", "nqr"]),
|
|
4462
|
+
settledAtMs: z18.number().int().positive(),
|
|
4463
|
+
ledgerTxnId: z18.string().min(1).max(64).optional(),
|
|
4464
|
+
memo: z18.string().max(140).optional(),
|
|
4320
4465
|
hashChainPrev: HexString(32).optional()
|
|
4321
4466
|
});
|
|
4322
|
-
var ShortId =
|
|
4323
|
-
var PositiveInt =
|
|
4324
|
-
var NonNegativeInt =
|
|
4325
|
-
var Currency2 =
|
|
4326
|
-
var Memo =
|
|
4327
|
-
var NqrPaymentRequestArtifactSchema =
|
|
4467
|
+
var ShortId = z18.string().min(1).max(64);
|
|
4468
|
+
var PositiveInt = z18.number().int().positive();
|
|
4469
|
+
var NonNegativeInt = z18.number().int().nonnegative();
|
|
4470
|
+
var Currency2 = z18.literal("NGN");
|
|
4471
|
+
var Memo = z18.string().max(140);
|
|
4472
|
+
var NqrPaymentRequestArtifactSchema = z18.object({
|
|
4328
4473
|
requestId: ShortId,
|
|
4329
4474
|
payeeUserId: ShortId,
|
|
4330
4475
|
amountKobo: PositiveInt.optional(),
|
|
@@ -4332,7 +4477,7 @@ var NqrPaymentRequestArtifactSchema = z17.object({
|
|
|
4332
4477
|
memo: Memo.optional(),
|
|
4333
4478
|
expiresAtMs: PositiveInt.optional()
|
|
4334
4479
|
});
|
|
4335
|
-
var PaymentIntentArtifactSchema =
|
|
4480
|
+
var PaymentIntentArtifactSchema = z18.object({
|
|
4336
4481
|
intentId: ShortId,
|
|
4337
4482
|
payerUserId: ShortId,
|
|
4338
4483
|
payeeUserId: ShortId,
|
|
@@ -4341,7 +4486,7 @@ var PaymentIntentArtifactSchema = z17.object({
|
|
|
4341
4486
|
idempotencyKey: ShortId,
|
|
4342
4487
|
createdAtMs: PositiveInt
|
|
4343
4488
|
});
|
|
4344
|
-
var OfflineClaimArtifactSchema =
|
|
4489
|
+
var OfflineClaimArtifactSchema = z18.object({
|
|
4345
4490
|
claimId: ShortId,
|
|
4346
4491
|
authorizationId: ShortId,
|
|
4347
4492
|
payeeUserId: ShortId,
|
|
@@ -4350,10 +4495,10 @@ var OfflineClaimArtifactSchema = z17.object({
|
|
|
4350
4495
|
claimedAtMs: PositiveInt,
|
|
4351
4496
|
paymentReference: ShortId.optional()
|
|
4352
4497
|
});
|
|
4353
|
-
var SettlementRecordArtifactSchema =
|
|
4498
|
+
var SettlementRecordArtifactSchema = z18.object({
|
|
4354
4499
|
settlementId: ShortId,
|
|
4355
4500
|
ledgerTxnId: ShortId,
|
|
4356
|
-
sourceRefType:
|
|
4501
|
+
sourceRefType: z18.enum([
|
|
4357
4502
|
"offline_authorization",
|
|
4358
4503
|
"offline_claim",
|
|
4359
4504
|
"transfer",
|
|
@@ -4364,12 +4509,12 @@ var SettlementRecordArtifactSchema = z17.object({
|
|
|
4364
4509
|
currency: Currency2,
|
|
4365
4510
|
settledAtMs: PositiveInt
|
|
4366
4511
|
});
|
|
4367
|
-
var ReversalRecordArtifactSchema =
|
|
4512
|
+
var ReversalRecordArtifactSchema = z18.object({
|
|
4368
4513
|
reversalId: ShortId,
|
|
4369
4514
|
originalTxnId: ShortId,
|
|
4370
4515
|
amountKobo: PositiveInt,
|
|
4371
4516
|
currency: Currency2,
|
|
4372
|
-
reason:
|
|
4517
|
+
reason: z18.enum([
|
|
4373
4518
|
"user_dispute",
|
|
4374
4519
|
"fraud",
|
|
4375
4520
|
"duplicate",
|
|
@@ -4379,7 +4524,7 @@ var ReversalRecordArtifactSchema = z17.object({
|
|
|
4379
4524
|
reversedAtMs: PositiveInt,
|
|
4380
4525
|
memo: Memo.optional()
|
|
4381
4526
|
});
|
|
4382
|
-
var LedgerJournalEntryArtifactSchema =
|
|
4527
|
+
var LedgerJournalEntryArtifactSchema = z18.object({
|
|
4383
4528
|
entryId: ShortId,
|
|
4384
4529
|
journalId: ShortId,
|
|
4385
4530
|
debitAccountId: ShortId,
|
|
@@ -4390,13 +4535,13 @@ var LedgerJournalEntryArtifactSchema = z17.object({
|
|
|
4390
4535
|
refType: ShortId.optional(),
|
|
4391
4536
|
refId: ShortId.optional()
|
|
4392
4537
|
});
|
|
4393
|
-
var StatementArtifactSchema =
|
|
4538
|
+
var StatementArtifactSchema = z18.object({
|
|
4394
4539
|
statementId: ShortId,
|
|
4395
4540
|
userId: ShortId,
|
|
4396
4541
|
periodStartMs: PositiveInt,
|
|
4397
4542
|
periodEndMs: PositiveInt,
|
|
4398
|
-
openingBalanceKobo:
|
|
4399
|
-
closingBalanceKobo:
|
|
4543
|
+
openingBalanceKobo: z18.number().int(),
|
|
4544
|
+
closingBalanceKobo: z18.number().int(),
|
|
4400
4545
|
transactionCount: NonNegativeInt,
|
|
4401
4546
|
currency: Currency2,
|
|
4402
4547
|
hashChainPrev: HexString(32).optional()
|
|
@@ -4404,16 +4549,16 @@ var StatementArtifactSchema = z17.object({
|
|
|
4404
4549
|
message: "periodEndMs must be greater than periodStartMs",
|
|
4405
4550
|
path: ["periodEndMs"]
|
|
4406
4551
|
});
|
|
4407
|
-
var PassArtifactSchema =
|
|
4552
|
+
var PassArtifactSchema = z18.object({
|
|
4408
4553
|
passId: ShortId,
|
|
4409
4554
|
holderId: ShortId,
|
|
4410
|
-
category:
|
|
4411
|
-
title:
|
|
4555
|
+
category: z18.enum(["membership", "ticket", "loyalty", "access", "voucher"]),
|
|
4556
|
+
title: z18.string().min(1).max(120),
|
|
4412
4557
|
validFromMs: PositiveInt,
|
|
4413
4558
|
validUntilMs: PositiveInt.optional(),
|
|
4414
|
-
metadata:
|
|
4415
|
-
|
|
4416
|
-
|
|
4559
|
+
metadata: z18.record(
|
|
4560
|
+
z18.string().min(1).max(64),
|
|
4561
|
+
z18.union([z18.string().max(280), z18.number(), z18.boolean()])
|
|
4417
4562
|
).optional()
|
|
4418
4563
|
}).refine(
|
|
4419
4564
|
(v) => v.validUntilMs === void 0 || v.validUntilMs > v.validFromMs,
|
|
@@ -4422,10 +4567,10 @@ var PassArtifactSchema = z17.object({
|
|
|
4422
4567
|
path: ["validUntilMs"]
|
|
4423
4568
|
}
|
|
4424
4569
|
);
|
|
4425
|
-
var IdentityArtifactSchema =
|
|
4570
|
+
var IdentityArtifactSchema = z18.object({
|
|
4426
4571
|
attestationId: ShortId,
|
|
4427
4572
|
subjectId: ShortId,
|
|
4428
|
-
claimType:
|
|
4573
|
+
claimType: z18.enum([
|
|
4429
4574
|
"phone_verified",
|
|
4430
4575
|
"email_verified",
|
|
4431
4576
|
"bvn_verified",
|
|
@@ -4435,12 +4580,6 @@ var IdentityArtifactSchema = z17.object({
|
|
|
4435
4580
|
claimValueHash: HexString(32),
|
|
4436
4581
|
attestedAtMs: PositiveInt
|
|
4437
4582
|
});
|
|
4438
|
-
var PayCardArtifactSchema = z17.object({
|
|
4439
|
-
userId: ShortId,
|
|
4440
|
-
phoneE164: z17.string().regex(/^\+[1-9]\d{7,14}$/, "phoneE164 must be normalised E.164"),
|
|
4441
|
-
displayName: z17.string().min(1).max(64),
|
|
4442
|
-
devicePubKeySpkiB64: z17.string().min(64).max(256).regex(/^[A-Za-z0-9+/]+=*$/, "devicePubKeySpkiB64 must be standard base64")
|
|
4443
|
-
});
|
|
4444
4583
|
var ARTIFACT_BODY_SCHEMAS = {
|
|
4445
4584
|
[ARTIFACT_TYPES.OFFLINE_PAYMENT_AUTHORIZATION]: OfflinePaymentAuthorizationArtifactSchema,
|
|
4446
4585
|
[ARTIFACT_TYPES.RECEIPT]: ReceiptArtifactSchema,
|
|
@@ -4452,8 +4591,7 @@ var ARTIFACT_BODY_SCHEMAS = {
|
|
|
4452
4591
|
[ARTIFACT_TYPES.LEDGER_JOURNAL_ENTRY]: LedgerJournalEntryArtifactSchema,
|
|
4453
4592
|
[ARTIFACT_TYPES.STATEMENT]: StatementArtifactSchema,
|
|
4454
4593
|
[ARTIFACT_TYPES.PASS]: PassArtifactSchema,
|
|
4455
|
-
[ARTIFACT_TYPES.IDENTITY]: IdentityArtifactSchema
|
|
4456
|
-
[ARTIFACT_TYPES.PAY_CARD]: PayCardArtifactSchema
|
|
4594
|
+
[ARTIFACT_TYPES.IDENTITY]: IdentityArtifactSchema
|
|
4457
4595
|
};
|
|
4458
4596
|
var HARDENED_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
|
|
4459
4597
|
ARTIFACT_TYPES.OFFLINE_PAYMENT_AUTHORIZATION,
|
|
@@ -4466,8 +4604,7 @@ var HARDENED_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
|
|
|
4466
4604
|
ARTIFACT_TYPES.LEDGER_JOURNAL_ENTRY,
|
|
4467
4605
|
ARTIFACT_TYPES.STATEMENT,
|
|
4468
4606
|
ARTIFACT_TYPES.PASS,
|
|
4469
|
-
ARTIFACT_TYPES.IDENTITY
|
|
4470
|
-
ARTIFACT_TYPES.PAY_CARD
|
|
4607
|
+
ARTIFACT_TYPES.IDENTITY
|
|
4471
4608
|
]);
|
|
4472
4609
|
function isKnownArtifactType(t) {
|
|
4473
4610
|
return Object.values(ARTIFACT_TYPES).includes(t);
|
|
@@ -4552,130 +4689,6 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4552
4689
|
type: ARTIFACT_TYPES.OFFLINE_PAYMENT_AUTHORIZATION
|
|
4553
4690
|
});
|
|
4554
4691
|
}
|
|
4555
|
-
|
|
4556
|
-
// src/artifacts/paycard.ts
|
|
4557
|
-
var PAY_CARD_DEFAULT_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
4558
|
-
var PAY_CARD_REFRESH_THRESHOLD_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4559
|
-
var PAY_CARD_URI_PREFIX = `${FLUR_ARTIFACT_URI_PREFIX}${ARTIFACT_TYPES.PAY_CARD}/`;
|
|
4560
|
-
function createPayCardArtifactUri(input) {
|
|
4561
|
-
if (input.data.userId !== input.issuer) {
|
|
4562
|
-
throw new FlurArtifactError(
|
|
4563
|
-
"pay_card.data.userId must equal envelope issuer",
|
|
4564
|
-
"INVALID_BODY"
|
|
4565
|
-
);
|
|
4566
|
-
}
|
|
4567
|
-
const iat = input.issuedAtSeconds ?? Math.floor(Date.now() / 1e3);
|
|
4568
|
-
const exp = input.expiresAtSeconds ?? iat + Math.floor(PAY_CARD_DEFAULT_TTL_MS / 1e3);
|
|
4569
|
-
return createArtifactUri({
|
|
4570
|
-
type: ARTIFACT_TYPES.PAY_CARD,
|
|
4571
|
-
issuer: input.issuer,
|
|
4572
|
-
keyId: input.keyId,
|
|
4573
|
-
privateKey: input.privateKey,
|
|
4574
|
-
nonce: input.nonce,
|
|
4575
|
-
issuedAtSeconds: iat,
|
|
4576
|
-
expiresAtSeconds: exp,
|
|
4577
|
-
data: input.data
|
|
4578
|
-
});
|
|
4579
|
-
}
|
|
4580
|
-
function isPayCardArtifactUri(uri) {
|
|
4581
|
-
return typeof uri === "string" && uri.startsWith(PAY_CARD_URI_PREFIX);
|
|
4582
|
-
}
|
|
4583
|
-
function decodePayCardArtifact(uri) {
|
|
4584
|
-
if (!isPayCardArtifactUri(uri)) {
|
|
4585
|
-
throw new FlurArtifactError(
|
|
4586
|
-
`URI does not start with ${PAY_CARD_URI_PREFIX}`,
|
|
4587
|
-
"INVALID_URI"
|
|
4588
|
-
);
|
|
4589
|
-
}
|
|
4590
|
-
const decoded = decodeArtifactUri(uri);
|
|
4591
|
-
if (decoded.type !== ARTIFACT_TYPES.PAY_CARD) {
|
|
4592
|
-
throw new FlurArtifactError(
|
|
4593
|
-
`Expected pay_card, got ${decoded.type}`,
|
|
4594
|
-
"TYPE_MISMATCH"
|
|
4595
|
-
);
|
|
4596
|
-
}
|
|
4597
|
-
const parsed = PayCardArtifactSchema.safeParse(decoded.body.data);
|
|
4598
|
-
if (!parsed.success) {
|
|
4599
|
-
throw new FlurArtifactError(
|
|
4600
|
-
`pay_card body invalid: ${parsed.error.message}`,
|
|
4601
|
-
"INVALID_BODY"
|
|
4602
|
-
);
|
|
4603
|
-
}
|
|
4604
|
-
if (parsed.data.userId !== decoded.body.iss) {
|
|
4605
|
-
throw new FlurArtifactError(
|
|
4606
|
-
"pay_card.data.userId must equal envelope issuer",
|
|
4607
|
-
"INVALID_BODY"
|
|
4608
|
-
);
|
|
4609
|
-
}
|
|
4610
|
-
return {
|
|
4611
|
-
body: {
|
|
4612
|
-
...decoded.body,
|
|
4613
|
-
data: parsed.data
|
|
4614
|
-
},
|
|
4615
|
-
sig: decoded.sig,
|
|
4616
|
-
decoded
|
|
4617
|
-
};
|
|
4618
|
-
}
|
|
4619
|
-
function verifyPayCardArtifact(uri, publicKeySpkiB64, options = {}) {
|
|
4620
|
-
if (!isPayCardArtifactUri(uri)) {
|
|
4621
|
-
throw new FlurArtifactError(
|
|
4622
|
-
`URI does not start with ${PAY_CARD_URI_PREFIX}`,
|
|
4623
|
-
"INVALID_URI"
|
|
4624
|
-
);
|
|
4625
|
-
}
|
|
4626
|
-
const verified = verifyArtifactUri(
|
|
4627
|
-
uri,
|
|
4628
|
-
publicKeySpkiB64,
|
|
4629
|
-
options
|
|
4630
|
-
);
|
|
4631
|
-
if (verified.decoded.type !== ARTIFACT_TYPES.PAY_CARD) {
|
|
4632
|
-
throw new FlurArtifactError(
|
|
4633
|
-
`Expected pay_card, got ${verified.decoded.type}`,
|
|
4634
|
-
"TYPE_MISMATCH"
|
|
4635
|
-
);
|
|
4636
|
-
}
|
|
4637
|
-
if (verified.body.data.userId !== verified.body.iss) {
|
|
4638
|
-
throw new FlurArtifactError(
|
|
4639
|
-
"pay_card.data.userId must equal envelope issuer",
|
|
4640
|
-
"INVALID_BODY"
|
|
4641
|
-
);
|
|
4642
|
-
}
|
|
4643
|
-
return {
|
|
4644
|
-
body: verified.body,
|
|
4645
|
-
sig: verified.sig,
|
|
4646
|
-
decoded: verified.decoded
|
|
4647
|
-
};
|
|
4648
|
-
}
|
|
4649
|
-
function inspectPayCardFreshness(decoded, nowMs = Date.now()) {
|
|
4650
|
-
const exp = decoded.body.exp;
|
|
4651
|
-
if (exp === void 0) return "no_expiry";
|
|
4652
|
-
const remainingMs = exp * 1e3 - nowMs;
|
|
4653
|
-
if (remainingMs <= 0) return "expired";
|
|
4654
|
-
if (remainingMs <= PAY_CARD_REFRESH_THRESHOLD_MS)
|
|
4655
|
-
return "refresh_recommended";
|
|
4656
|
-
return "fresh";
|
|
4657
|
-
}
|
|
4658
|
-
function buildPayCardSigningInput(input) {
|
|
4659
|
-
if (input.data.userId !== input.issuer) {
|
|
4660
|
-
throw new FlurArtifactError(
|
|
4661
|
-
"pay_card.data.userId must equal envelope issuer",
|
|
4662
|
-
"INVALID_BODY"
|
|
4663
|
-
);
|
|
4664
|
-
}
|
|
4665
|
-
const parsedData = PayCardArtifactSchema.parse(input.data);
|
|
4666
|
-
const iat = input.issuedAtSeconds ?? Math.floor(Date.now() / 1e3);
|
|
4667
|
-
const exp = input.expiresAtSeconds ?? iat + Math.floor(PAY_CARD_DEFAULT_TTL_MS / 1e3);
|
|
4668
|
-
const body = buildArtifactBody({
|
|
4669
|
-
type: ARTIFACT_TYPES.PAY_CARD,
|
|
4670
|
-
issuer: input.issuer,
|
|
4671
|
-
keyId: input.keyId,
|
|
4672
|
-
data: parsedData,
|
|
4673
|
-
nonce: input.nonce,
|
|
4674
|
-
issuedAtSeconds: iat,
|
|
4675
|
-
expiresAtSeconds: exp
|
|
4676
|
-
});
|
|
4677
|
-
return { body, bodyBytes: canonicalJSONBytes(body) };
|
|
4678
|
-
}
|
|
4679
4692
|
export {
|
|
4680
4693
|
ACCOUNT_FUNDED_OAC_MAX_TTL_MS,
|
|
4681
4694
|
ACCOUNT_STATUSES,
|
|
@@ -4691,6 +4704,8 @@ export {
|
|
|
4691
4704
|
CLAIM_DOMAIN_V2,
|
|
4692
4705
|
COLLECTION_INTENT_STATUSES,
|
|
4693
4706
|
COLLECTION_PAYMENT_STATUSES,
|
|
4707
|
+
CONSUMER_OAC_DOMAIN,
|
|
4708
|
+
CONSUMER_OAC_QR_PREFIX,
|
|
4694
4709
|
CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS,
|
|
4695
4710
|
CONSUMER_PAYMENT_REQUEST_DOMAIN,
|
|
4696
4711
|
CONSUMER_SETTLEMENT_DOMAIN,
|
|
@@ -4729,6 +4744,8 @@ export {
|
|
|
4729
4744
|
IdentityArtifactSchema,
|
|
4730
4745
|
IngestFundingResultSchema,
|
|
4731
4746
|
IssueAccountOacInputSchema,
|
|
4747
|
+
IssuerTrustBundleSchema,
|
|
4748
|
+
IssuerTrustKeySchema,
|
|
4732
4749
|
LedgerJournalEntryArtifactSchema,
|
|
4733
4750
|
ListPayoutDestinationsResultSchema,
|
|
4734
4751
|
MEMBERSHIP_ROLES,
|
|
@@ -4752,6 +4769,7 @@ export {
|
|
|
4752
4769
|
OFFLINE_SMS_SETTLE_SIGNATURE_BYTES,
|
|
4753
4770
|
OFFLINE_SMS_SETTLE_TOKEN_BYTES,
|
|
4754
4771
|
OFFLINE_SMS_SETTLE_VERSION,
|
|
4772
|
+
OacPresentmentRequestSchema,
|
|
4755
4773
|
OfflineClaimArtifactSchema,
|
|
4756
4774
|
OfflinePaymentAuthorizationArtifactSchema,
|
|
4757
4775
|
OfflinePaymentAuthorizationSchema,
|
|
@@ -4769,9 +4787,6 @@ export {
|
|
|
4769
4787
|
PASS_STATES,
|
|
4770
4788
|
PAYLOAD_FORMAT_INDICATOR_VALUE,
|
|
4771
4789
|
PAYOUT_DESTINATION_STATUSES,
|
|
4772
|
-
PAY_CARD_DEFAULT_TTL_MS,
|
|
4773
|
-
PAY_CARD_REFRESH_THRESHOLD_MS,
|
|
4774
|
-
PAY_CARD_URI_PREFIX,
|
|
4775
4790
|
POINT_OF_INITIATION,
|
|
4776
4791
|
PartnerFundingEventInputSchema,
|
|
4777
4792
|
PartnerFundingSchema,
|
|
@@ -4779,7 +4794,6 @@ export {
|
|
|
4779
4794
|
PassArtifactSchema,
|
|
4780
4795
|
PassMetadataSchema,
|
|
4781
4796
|
PassSchema,
|
|
4782
|
-
PayCardArtifactSchema,
|
|
4783
4797
|
PayCollectionInputSchema,
|
|
4784
4798
|
PaymentClaimSchema,
|
|
4785
4799
|
PaymentIntentArtifactSchema,
|
|
@@ -4818,7 +4832,6 @@ export {
|
|
|
4818
4832
|
buildConsumerPaymentRequest,
|
|
4819
4833
|
buildOAC,
|
|
4820
4834
|
buildPass,
|
|
4821
|
-
buildPayCardSigningInput,
|
|
4822
4835
|
buildPaymentRequest,
|
|
4823
4836
|
buildReceipt,
|
|
4824
4837
|
buildRedemption,
|
|
@@ -4832,6 +4845,7 @@ export {
|
|
|
4832
4845
|
computeConsumerClaimEncounterId,
|
|
4833
4846
|
computeEncounterId,
|
|
4834
4847
|
constantTimeEqual,
|
|
4848
|
+
consumerOacSigningPayload,
|
|
4835
4849
|
consumerPaymentRequestSigningBytes,
|
|
4836
4850
|
consumerPaymentRequestSigningPayload,
|
|
4837
4851
|
consumerSettlementSigningPayload,
|
|
@@ -4852,23 +4866,24 @@ export {
|
|
|
4852
4866
|
createPartnerFundingClient,
|
|
4853
4867
|
createPartnerProfileAdminClient,
|
|
4854
4868
|
createPassesClient,
|
|
4855
|
-
createPayCardArtifactUri,
|
|
4856
4869
|
createReceiptArtifactUri,
|
|
4857
4870
|
createReceiptsClient,
|
|
4858
4871
|
createSoftwareP256Signer,
|
|
4859
4872
|
decodeArtifactUri,
|
|
4860
4873
|
decodeAuthorizationQR,
|
|
4861
4874
|
decodeBase45,
|
|
4875
|
+
decodeConsumerOacRequest,
|
|
4862
4876
|
decodeConsumerSettlementReceiptQR,
|
|
4863
4877
|
decodeOfflineClaimSmsMessage,
|
|
4864
4878
|
decodeOfflineSmsSettleToken,
|
|
4865
|
-
decodePayCardArtifact,
|
|
4866
4879
|
decodePaymentRequestQR,
|
|
4880
|
+
decodeUnverifiedConsumerOacQR,
|
|
4867
4881
|
decodeUnverifiedConsumerSettlementReceiptQR,
|
|
4868
4882
|
derToRawP256Signature,
|
|
4869
4883
|
encodeArtifactUri,
|
|
4870
4884
|
encodeAuthorizationQR,
|
|
4871
4885
|
encodeBase45,
|
|
4886
|
+
encodeConsumerOacQR,
|
|
4872
4887
|
encodeConsumerSettlementReceiptQR,
|
|
4873
4888
|
encodeNQR,
|
|
4874
4889
|
encodeOfflineClaimSmsMessage,
|
|
@@ -4880,12 +4895,11 @@ export {
|
|
|
4880
4895
|
generateDynamicQR,
|
|
4881
4896
|
generateStaticQR,
|
|
4882
4897
|
init,
|
|
4883
|
-
|
|
4898
|
+
isConsumerOacQR,
|
|
4884
4899
|
isConsumerPaymentRequestExpired,
|
|
4885
4900
|
isHardenedArtifactType,
|
|
4886
4901
|
isKnownArtifactType,
|
|
4887
4902
|
isPassWithinValidity,
|
|
4888
|
-
isPayCardArtifactUri,
|
|
4889
4903
|
moneyMinorToNumber,
|
|
4890
4904
|
normalizeE164,
|
|
4891
4905
|
parseAmountInput,
|
|
@@ -4911,9 +4925,9 @@ export {
|
|
|
4911
4925
|
verifyConsumerSettlement,
|
|
4912
4926
|
verifyConsumerSettlementReceiptQR,
|
|
4913
4927
|
verifyOAC,
|
|
4928
|
+
verifyOacOffline,
|
|
4914
4929
|
verifyOfflineSmsSettleToken,
|
|
4915
4930
|
verifyPass,
|
|
4916
|
-
verifyPayCardArtifact,
|
|
4917
4931
|
verifyPaymentRequest,
|
|
4918
4932
|
verifyReceipt,
|
|
4919
4933
|
verifyRedemption,
|