@nokinc-flur/sdk 2.1.0 → 2.3.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 +303 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +449 -7
- package/dist/index.d.ts +449 -7
- package/dist/index.js +284 -11
- 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,101 @@ function base64UrlDecodeUtf8(input) {
|
|
|
3549
3580
|
throw new Error("base64 decoder unavailable");
|
|
3550
3581
|
}
|
|
3551
3582
|
|
|
3583
|
+
// src/me-offline/oac.ts
|
|
3584
|
+
var CONSUMER_OAC_DOMAIN = "flur:consumer-offline:v1:oac";
|
|
3585
|
+
function consumerOacSigningPayload(oac) {
|
|
3586
|
+
return { domain: CONSUMER_OAC_DOMAIN, ...oac };
|
|
3587
|
+
}
|
|
3588
|
+
function verifyOacOffline(signed, trustedKeys, options = {}) {
|
|
3589
|
+
const parsed = SignedConsumerOACSchema.safeParse(signed);
|
|
3590
|
+
if (!parsed.success) return { ok: false, reason: "malformed" };
|
|
3591
|
+
const oacParsed = ConsumerOACSchema.safeParse(parsed.data.oac);
|
|
3592
|
+
if (!oacParsed.success) return { ok: false, reason: "malformed" };
|
|
3593
|
+
const oac = oacParsed.data;
|
|
3594
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
3595
|
+
const pinned = trustedKeys.filter(
|
|
3596
|
+
(k) => k.issuerId === oac.issuerId && (k.notBeforeMs === void 0 || nowMs >= k.notBeforeMs) && (k.notAfterMs === void 0 || nowMs <= k.notAfterMs)
|
|
3597
|
+
);
|
|
3598
|
+
if (pinned.length === 0) return { ok: false, reason: "untrusted_issuer" };
|
|
3599
|
+
const signingBytes = canonicalJSONBytes(consumerOacSigningPayload(oac));
|
|
3600
|
+
const signatureOk = pinned.some(
|
|
3601
|
+
(k) => verifyIssuerP256(signingBytes, parsed.data.issuerSig, k.publicKeySpkiB64)
|
|
3602
|
+
);
|
|
3603
|
+
if (!signatureOk) return { ok: false, reason: "signature_invalid" };
|
|
3604
|
+
if (oac.validUntilMs - oac.validFromMs > ACCOUNT_FUNDED_OAC_MAX_TTL_MS) {
|
|
3605
|
+
return { ok: false, reason: "window_too_long" };
|
|
3606
|
+
}
|
|
3607
|
+
if (nowMs < oac.validFromMs) return { ok: false, reason: "not_yet_valid" };
|
|
3608
|
+
if (nowMs >= oac.validUntilMs) return { ok: false, reason: "expired" };
|
|
3609
|
+
return {
|
|
3610
|
+
ok: true,
|
|
3611
|
+
oac,
|
|
3612
|
+
identity: {
|
|
3613
|
+
oacId: oac.oacId,
|
|
3614
|
+
issuerId: oac.issuerId,
|
|
3615
|
+
userId: oac.userId,
|
|
3616
|
+
phoneE164: oac.phoneE164,
|
|
3617
|
+
displayName: oac.displayName,
|
|
3618
|
+
devicePubkeySpkiB64: oac.devicePubkeySpkiB64
|
|
3619
|
+
}
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
var CONSUMER_OAC_QR_PREFIX = "FLUROAC1.";
|
|
3623
|
+
function isConsumerOacQR(value) {
|
|
3624
|
+
return value.startsWith(CONSUMER_OAC_QR_PREFIX);
|
|
3625
|
+
}
|
|
3626
|
+
function encodeConsumerOacQR(signed) {
|
|
3627
|
+
const parsed = SignedConsumerOACSchema.parse(signed);
|
|
3628
|
+
return `${CONSUMER_OAC_QR_PREFIX}${base64UrlEncodeUtf82(JSON.stringify(parsed))}`;
|
|
3629
|
+
}
|
|
3630
|
+
function decodeUnverifiedConsumerOacQR(value) {
|
|
3631
|
+
if (!value.startsWith(CONSUMER_OAC_QR_PREFIX)) {
|
|
3632
|
+
throw new Error("not a Flur consumer OAC QR");
|
|
3633
|
+
}
|
|
3634
|
+
const encoded = value.slice(CONSUMER_OAC_QR_PREFIX.length);
|
|
3635
|
+
let raw;
|
|
3636
|
+
try {
|
|
3637
|
+
raw = JSON.parse(base64UrlDecodeUtf82(encoded));
|
|
3638
|
+
} catch {
|
|
3639
|
+
throw new Error("consumer OAC QR is malformed");
|
|
3640
|
+
}
|
|
3641
|
+
return SignedConsumerOACSchema.parse(raw);
|
|
3642
|
+
}
|
|
3643
|
+
function base64UrlEncodeUtf82(input) {
|
|
3644
|
+
const bytes = new TextEncoder().encode(input);
|
|
3645
|
+
let binary = "";
|
|
3646
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
3647
|
+
const base64 = typeof btoa === "function" ? btoa(binary) : typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : void 0;
|
|
3648
|
+
if (!base64) throw new Error("base64 encoder unavailable");
|
|
3649
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
3650
|
+
}
|
|
3651
|
+
function base64UrlDecodeUtf82(input) {
|
|
3652
|
+
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
3653
|
+
const padded = base64.padEnd(
|
|
3654
|
+
base64.length + (4 - base64.length % 4) % 4,
|
|
3655
|
+
"="
|
|
3656
|
+
);
|
|
3657
|
+
if (typeof atob === "function") {
|
|
3658
|
+
const binary = atob(padded);
|
|
3659
|
+
const bytes = new Uint8Array(binary.length);
|
|
3660
|
+
for (let index = 0; index < binary.length; index++) {
|
|
3661
|
+
bytes[index] = binary.charCodeAt(index);
|
|
3662
|
+
}
|
|
3663
|
+
return new TextDecoder().decode(bytes);
|
|
3664
|
+
}
|
|
3665
|
+
if (typeof Buffer !== "undefined") {
|
|
3666
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
3667
|
+
}
|
|
3668
|
+
throw new Error("base64 decoder unavailable");
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3552
3671
|
// src/me-offline/sms.ts
|
|
3553
3672
|
import { p256 as p2563 } from "@noble/curves/nist";
|
|
3554
3673
|
var OFFLINE_CLAIM_SMS_PREFIX = "FLURC1.";
|
|
3555
3674
|
var CLAIM_TOKEN_RE = /(?:^|\s)(FLURC1\.[A-Za-z0-9_-]+={0,2})(?:\s|$)/;
|
|
3556
3675
|
function encodeOfflineClaimSmsMessage(claim) {
|
|
3557
3676
|
const parsed = ConsumerPaymentClaimSchema.parse(claim);
|
|
3558
|
-
return `${OFFLINE_CLAIM_SMS_PREFIX}${
|
|
3677
|
+
return `${OFFLINE_CLAIM_SMS_PREFIX}${base64UrlEncodeUtf83(
|
|
3559
3678
|
JSON.stringify(parsed)
|
|
3560
3679
|
)}`;
|
|
3561
3680
|
}
|
|
@@ -3565,7 +3684,7 @@ function decodeOfflineClaimSmsMessage(message) {
|
|
|
3565
3684
|
const encoded = token.slice(OFFLINE_CLAIM_SMS_PREFIX.length);
|
|
3566
3685
|
let raw;
|
|
3567
3686
|
try {
|
|
3568
|
-
raw = JSON.parse(
|
|
3687
|
+
raw = JSON.parse(base64UrlDecodeUtf83(encoded));
|
|
3569
3688
|
} catch {
|
|
3570
3689
|
throw new Error("offline claim QR token is malformed");
|
|
3571
3690
|
}
|
|
@@ -3791,10 +3910,10 @@ function bytesToBase64Url(bytes) {
|
|
|
3791
3910
|
}
|
|
3792
3911
|
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
3793
3912
|
}
|
|
3794
|
-
function
|
|
3913
|
+
function base64UrlEncodeUtf83(input) {
|
|
3795
3914
|
return bytesToBase64Url(utf8(input));
|
|
3796
3915
|
}
|
|
3797
|
-
function
|
|
3916
|
+
function base64UrlDecodeUtf83(input) {
|
|
3798
3917
|
return new TextDecoder().decode(base64UrlToBytes(input));
|
|
3799
3918
|
}
|
|
3800
3919
|
function base64UrlToBytes(input) {
|
|
@@ -4294,7 +4413,10 @@ var ARTIFACT_TYPES = {
|
|
|
4294
4413
|
LEDGER_JOURNAL_ENTRY: "ledger_journal_entry",
|
|
4295
4414
|
STATEMENT: "statement",
|
|
4296
4415
|
PASS: "pass",
|
|
4297
|
-
IDENTITY: "identity"
|
|
4416
|
+
IDENTITY: "identity",
|
|
4417
|
+
// Tier B: holder-signed identity attestation for offline trust. The
|
|
4418
|
+
// envelope.iat / envelope.exp express the card's canonical lifetime.
|
|
4419
|
+
PAY_CARD: "pay_card"
|
|
4298
4420
|
};
|
|
4299
4421
|
var HexString = (length) => z17.string().regex(
|
|
4300
4422
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
@@ -4432,6 +4554,12 @@ var IdentityArtifactSchema = z17.object({
|
|
|
4432
4554
|
claimValueHash: HexString(32),
|
|
4433
4555
|
attestedAtMs: PositiveInt
|
|
4434
4556
|
});
|
|
4557
|
+
var PayCardArtifactSchema = z17.object({
|
|
4558
|
+
userId: ShortId,
|
|
4559
|
+
phoneE164: z17.string().regex(/^\+[1-9]\d{7,14}$/, "phoneE164 must be normalised E.164"),
|
|
4560
|
+
displayName: z17.string().min(1).max(64),
|
|
4561
|
+
devicePubKeySpkiB64: z17.string().min(64).max(256).regex(/^[A-Za-z0-9+/]+=*$/, "devicePubKeySpkiB64 must be standard base64")
|
|
4562
|
+
});
|
|
4435
4563
|
var ARTIFACT_BODY_SCHEMAS = {
|
|
4436
4564
|
[ARTIFACT_TYPES.OFFLINE_PAYMENT_AUTHORIZATION]: OfflinePaymentAuthorizationArtifactSchema,
|
|
4437
4565
|
[ARTIFACT_TYPES.RECEIPT]: ReceiptArtifactSchema,
|
|
@@ -4443,7 +4571,8 @@ var ARTIFACT_BODY_SCHEMAS = {
|
|
|
4443
4571
|
[ARTIFACT_TYPES.LEDGER_JOURNAL_ENTRY]: LedgerJournalEntryArtifactSchema,
|
|
4444
4572
|
[ARTIFACT_TYPES.STATEMENT]: StatementArtifactSchema,
|
|
4445
4573
|
[ARTIFACT_TYPES.PASS]: PassArtifactSchema,
|
|
4446
|
-
[ARTIFACT_TYPES.IDENTITY]: IdentityArtifactSchema
|
|
4574
|
+
[ARTIFACT_TYPES.IDENTITY]: IdentityArtifactSchema,
|
|
4575
|
+
[ARTIFACT_TYPES.PAY_CARD]: PayCardArtifactSchema
|
|
4447
4576
|
};
|
|
4448
4577
|
var HARDENED_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
|
|
4449
4578
|
ARTIFACT_TYPES.OFFLINE_PAYMENT_AUTHORIZATION,
|
|
@@ -4456,7 +4585,8 @@ var HARDENED_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
|
|
|
4456
4585
|
ARTIFACT_TYPES.LEDGER_JOURNAL_ENTRY,
|
|
4457
4586
|
ARTIFACT_TYPES.STATEMENT,
|
|
4458
4587
|
ARTIFACT_TYPES.PASS,
|
|
4459
|
-
ARTIFACT_TYPES.IDENTITY
|
|
4588
|
+
ARTIFACT_TYPES.IDENTITY,
|
|
4589
|
+
ARTIFACT_TYPES.PAY_CARD
|
|
4460
4590
|
]);
|
|
4461
4591
|
function isKnownArtifactType(t) {
|
|
4462
4592
|
return Object.values(ARTIFACT_TYPES).includes(t);
|
|
@@ -4541,6 +4671,130 @@ function createOfflinePaymentAuthorizationArtifactUri(input) {
|
|
|
4541
4671
|
type: ARTIFACT_TYPES.OFFLINE_PAYMENT_AUTHORIZATION
|
|
4542
4672
|
});
|
|
4543
4673
|
}
|
|
4674
|
+
|
|
4675
|
+
// src/artifacts/paycard.ts
|
|
4676
|
+
var PAY_CARD_DEFAULT_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
4677
|
+
var PAY_CARD_REFRESH_THRESHOLD_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4678
|
+
var PAY_CARD_URI_PREFIX = `${FLUR_ARTIFACT_URI_PREFIX}${ARTIFACT_TYPES.PAY_CARD}/`;
|
|
4679
|
+
function createPayCardArtifactUri(input) {
|
|
4680
|
+
if (input.data.userId !== input.issuer) {
|
|
4681
|
+
throw new FlurArtifactError(
|
|
4682
|
+
"pay_card.data.userId must equal envelope issuer",
|
|
4683
|
+
"INVALID_BODY"
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
const iat = input.issuedAtSeconds ?? Math.floor(Date.now() / 1e3);
|
|
4687
|
+
const exp = input.expiresAtSeconds ?? iat + Math.floor(PAY_CARD_DEFAULT_TTL_MS / 1e3);
|
|
4688
|
+
return createArtifactUri({
|
|
4689
|
+
type: ARTIFACT_TYPES.PAY_CARD,
|
|
4690
|
+
issuer: input.issuer,
|
|
4691
|
+
keyId: input.keyId,
|
|
4692
|
+
privateKey: input.privateKey,
|
|
4693
|
+
nonce: input.nonce,
|
|
4694
|
+
issuedAtSeconds: iat,
|
|
4695
|
+
expiresAtSeconds: exp,
|
|
4696
|
+
data: input.data
|
|
4697
|
+
});
|
|
4698
|
+
}
|
|
4699
|
+
function isPayCardArtifactUri(uri) {
|
|
4700
|
+
return typeof uri === "string" && uri.startsWith(PAY_CARD_URI_PREFIX);
|
|
4701
|
+
}
|
|
4702
|
+
function decodePayCardArtifact(uri) {
|
|
4703
|
+
if (!isPayCardArtifactUri(uri)) {
|
|
4704
|
+
throw new FlurArtifactError(
|
|
4705
|
+
`URI does not start with ${PAY_CARD_URI_PREFIX}`,
|
|
4706
|
+
"INVALID_URI"
|
|
4707
|
+
);
|
|
4708
|
+
}
|
|
4709
|
+
const decoded = decodeArtifactUri(uri);
|
|
4710
|
+
if (decoded.type !== ARTIFACT_TYPES.PAY_CARD) {
|
|
4711
|
+
throw new FlurArtifactError(
|
|
4712
|
+
`Expected pay_card, got ${decoded.type}`,
|
|
4713
|
+
"TYPE_MISMATCH"
|
|
4714
|
+
);
|
|
4715
|
+
}
|
|
4716
|
+
const parsed = PayCardArtifactSchema.safeParse(decoded.body.data);
|
|
4717
|
+
if (!parsed.success) {
|
|
4718
|
+
throw new FlurArtifactError(
|
|
4719
|
+
`pay_card body invalid: ${parsed.error.message}`,
|
|
4720
|
+
"INVALID_BODY"
|
|
4721
|
+
);
|
|
4722
|
+
}
|
|
4723
|
+
if (parsed.data.userId !== decoded.body.iss) {
|
|
4724
|
+
throw new FlurArtifactError(
|
|
4725
|
+
"pay_card.data.userId must equal envelope issuer",
|
|
4726
|
+
"INVALID_BODY"
|
|
4727
|
+
);
|
|
4728
|
+
}
|
|
4729
|
+
return {
|
|
4730
|
+
body: {
|
|
4731
|
+
...decoded.body,
|
|
4732
|
+
data: parsed.data
|
|
4733
|
+
},
|
|
4734
|
+
sig: decoded.sig,
|
|
4735
|
+
decoded
|
|
4736
|
+
};
|
|
4737
|
+
}
|
|
4738
|
+
function verifyPayCardArtifact(uri, publicKeySpkiB64, options = {}) {
|
|
4739
|
+
if (!isPayCardArtifactUri(uri)) {
|
|
4740
|
+
throw new FlurArtifactError(
|
|
4741
|
+
`URI does not start with ${PAY_CARD_URI_PREFIX}`,
|
|
4742
|
+
"INVALID_URI"
|
|
4743
|
+
);
|
|
4744
|
+
}
|
|
4745
|
+
const verified = verifyArtifactUri(
|
|
4746
|
+
uri,
|
|
4747
|
+
publicKeySpkiB64,
|
|
4748
|
+
options
|
|
4749
|
+
);
|
|
4750
|
+
if (verified.decoded.type !== ARTIFACT_TYPES.PAY_CARD) {
|
|
4751
|
+
throw new FlurArtifactError(
|
|
4752
|
+
`Expected pay_card, got ${verified.decoded.type}`,
|
|
4753
|
+
"TYPE_MISMATCH"
|
|
4754
|
+
);
|
|
4755
|
+
}
|
|
4756
|
+
if (verified.body.data.userId !== verified.body.iss) {
|
|
4757
|
+
throw new FlurArtifactError(
|
|
4758
|
+
"pay_card.data.userId must equal envelope issuer",
|
|
4759
|
+
"INVALID_BODY"
|
|
4760
|
+
);
|
|
4761
|
+
}
|
|
4762
|
+
return {
|
|
4763
|
+
body: verified.body,
|
|
4764
|
+
sig: verified.sig,
|
|
4765
|
+
decoded: verified.decoded
|
|
4766
|
+
};
|
|
4767
|
+
}
|
|
4768
|
+
function inspectPayCardFreshness(decoded, nowMs = Date.now()) {
|
|
4769
|
+
const exp = decoded.body.exp;
|
|
4770
|
+
if (exp === void 0) return "no_expiry";
|
|
4771
|
+
const remainingMs = exp * 1e3 - nowMs;
|
|
4772
|
+
if (remainingMs <= 0) return "expired";
|
|
4773
|
+
if (remainingMs <= PAY_CARD_REFRESH_THRESHOLD_MS)
|
|
4774
|
+
return "refresh_recommended";
|
|
4775
|
+
return "fresh";
|
|
4776
|
+
}
|
|
4777
|
+
function buildPayCardSigningInput(input) {
|
|
4778
|
+
if (input.data.userId !== input.issuer) {
|
|
4779
|
+
throw new FlurArtifactError(
|
|
4780
|
+
"pay_card.data.userId must equal envelope issuer",
|
|
4781
|
+
"INVALID_BODY"
|
|
4782
|
+
);
|
|
4783
|
+
}
|
|
4784
|
+
const parsedData = PayCardArtifactSchema.parse(input.data);
|
|
4785
|
+
const iat = input.issuedAtSeconds ?? Math.floor(Date.now() / 1e3);
|
|
4786
|
+
const exp = input.expiresAtSeconds ?? iat + Math.floor(PAY_CARD_DEFAULT_TTL_MS / 1e3);
|
|
4787
|
+
const body = buildArtifactBody({
|
|
4788
|
+
type: ARTIFACT_TYPES.PAY_CARD,
|
|
4789
|
+
issuer: input.issuer,
|
|
4790
|
+
keyId: input.keyId,
|
|
4791
|
+
data: parsedData,
|
|
4792
|
+
nonce: input.nonce,
|
|
4793
|
+
issuedAtSeconds: iat,
|
|
4794
|
+
expiresAtSeconds: exp
|
|
4795
|
+
});
|
|
4796
|
+
return { body, bodyBytes: canonicalJSONBytes(body) };
|
|
4797
|
+
}
|
|
4544
4798
|
export {
|
|
4545
4799
|
ACCOUNT_FUNDED_OAC_MAX_TTL_MS,
|
|
4546
4800
|
ACCOUNT_STATUSES,
|
|
@@ -4556,6 +4810,8 @@ export {
|
|
|
4556
4810
|
CLAIM_DOMAIN_V2,
|
|
4557
4811
|
COLLECTION_INTENT_STATUSES,
|
|
4558
4812
|
COLLECTION_PAYMENT_STATUSES,
|
|
4813
|
+
CONSUMER_OAC_DOMAIN,
|
|
4814
|
+
CONSUMER_OAC_QR_PREFIX,
|
|
4559
4815
|
CONSUMER_OFFLINE_CLAIM_SUBMIT_GRACE_MS,
|
|
4560
4816
|
CONSUMER_PAYMENT_REQUEST_DOMAIN,
|
|
4561
4817
|
CONSUMER_SETTLEMENT_DOMAIN,
|
|
@@ -4594,6 +4850,8 @@ export {
|
|
|
4594
4850
|
IdentityArtifactSchema,
|
|
4595
4851
|
IngestFundingResultSchema,
|
|
4596
4852
|
IssueAccountOacInputSchema,
|
|
4853
|
+
IssuerTrustBundleSchema,
|
|
4854
|
+
IssuerTrustKeySchema,
|
|
4597
4855
|
LedgerJournalEntryArtifactSchema,
|
|
4598
4856
|
ListPayoutDestinationsResultSchema,
|
|
4599
4857
|
MEMBERSHIP_ROLES,
|
|
@@ -4634,6 +4892,9 @@ export {
|
|
|
4634
4892
|
PASS_STATES,
|
|
4635
4893
|
PAYLOAD_FORMAT_INDICATOR_VALUE,
|
|
4636
4894
|
PAYOUT_DESTINATION_STATUSES,
|
|
4895
|
+
PAY_CARD_DEFAULT_TTL_MS,
|
|
4896
|
+
PAY_CARD_REFRESH_THRESHOLD_MS,
|
|
4897
|
+
PAY_CARD_URI_PREFIX,
|
|
4637
4898
|
POINT_OF_INITIATION,
|
|
4638
4899
|
PartnerFundingEventInputSchema,
|
|
4639
4900
|
PartnerFundingSchema,
|
|
@@ -4641,6 +4902,7 @@ export {
|
|
|
4641
4902
|
PassArtifactSchema,
|
|
4642
4903
|
PassMetadataSchema,
|
|
4643
4904
|
PassSchema,
|
|
4905
|
+
PayCardArtifactSchema,
|
|
4644
4906
|
PayCollectionInputSchema,
|
|
4645
4907
|
PaymentClaimSchema,
|
|
4646
4908
|
PaymentIntentArtifactSchema,
|
|
@@ -4679,6 +4941,7 @@ export {
|
|
|
4679
4941
|
buildConsumerPaymentRequest,
|
|
4680
4942
|
buildOAC,
|
|
4681
4943
|
buildPass,
|
|
4944
|
+
buildPayCardSigningInput,
|
|
4682
4945
|
buildPaymentRequest,
|
|
4683
4946
|
buildReceipt,
|
|
4684
4947
|
buildRedemption,
|
|
@@ -4692,6 +4955,7 @@ export {
|
|
|
4692
4955
|
computeConsumerClaimEncounterId,
|
|
4693
4956
|
computeEncounterId,
|
|
4694
4957
|
constantTimeEqual,
|
|
4958
|
+
consumerOacSigningPayload,
|
|
4695
4959
|
consumerPaymentRequestSigningBytes,
|
|
4696
4960
|
consumerPaymentRequestSigningPayload,
|
|
4697
4961
|
consumerSettlementSigningPayload,
|
|
@@ -4712,6 +4976,7 @@ export {
|
|
|
4712
4976
|
createPartnerFundingClient,
|
|
4713
4977
|
createPartnerProfileAdminClient,
|
|
4714
4978
|
createPassesClient,
|
|
4979
|
+
createPayCardArtifactUri,
|
|
4715
4980
|
createReceiptArtifactUri,
|
|
4716
4981
|
createReceiptsClient,
|
|
4717
4982
|
createSoftwareP256Signer,
|
|
@@ -4721,12 +4986,15 @@ export {
|
|
|
4721
4986
|
decodeConsumerSettlementReceiptQR,
|
|
4722
4987
|
decodeOfflineClaimSmsMessage,
|
|
4723
4988
|
decodeOfflineSmsSettleToken,
|
|
4989
|
+
decodePayCardArtifact,
|
|
4724
4990
|
decodePaymentRequestQR,
|
|
4991
|
+
decodeUnverifiedConsumerOacQR,
|
|
4725
4992
|
decodeUnverifiedConsumerSettlementReceiptQR,
|
|
4726
4993
|
derToRawP256Signature,
|
|
4727
4994
|
encodeArtifactUri,
|
|
4728
4995
|
encodeAuthorizationQR,
|
|
4729
4996
|
encodeBase45,
|
|
4997
|
+
encodeConsumerOacQR,
|
|
4730
4998
|
encodeConsumerSettlementReceiptQR,
|
|
4731
4999
|
encodeNQR,
|
|
4732
5000
|
encodeOfflineClaimSmsMessage,
|
|
@@ -4738,10 +5006,13 @@ export {
|
|
|
4738
5006
|
generateDynamicQR,
|
|
4739
5007
|
generateStaticQR,
|
|
4740
5008
|
init,
|
|
5009
|
+
inspectPayCardFreshness,
|
|
5010
|
+
isConsumerOacQR,
|
|
4741
5011
|
isConsumerPaymentRequestExpired,
|
|
4742
5012
|
isHardenedArtifactType,
|
|
4743
5013
|
isKnownArtifactType,
|
|
4744
5014
|
isPassWithinValidity,
|
|
5015
|
+
isPayCardArtifactUri,
|
|
4745
5016
|
moneyMinorToNumber,
|
|
4746
5017
|
normalizeE164,
|
|
4747
5018
|
parseAmountInput,
|
|
@@ -4767,8 +5038,10 @@ export {
|
|
|
4767
5038
|
verifyConsumerSettlement,
|
|
4768
5039
|
verifyConsumerSettlementReceiptQR,
|
|
4769
5040
|
verifyOAC,
|
|
5041
|
+
verifyOacOffline,
|
|
4770
5042
|
verifyOfflineSmsSettleToken,
|
|
4771
5043
|
verifyPass,
|
|
5044
|
+
verifyPayCardArtifact,
|
|
4772
5045
|
verifyPaymentRequest,
|
|
4773
5046
|
verifyReceipt,
|
|
4774
5047
|
verifyRedemption,
|