@nokinc-flur/sdk 1.0.0 → 1.0.2
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 +608 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +540 -76
- package/dist/index.d.ts +540 -76
- package/dist/index.js +580 -121
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,12 +17,25 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var index_exports = {};
|
|
22
32
|
__export(index_exports, {
|
|
33
|
+
ACCOUNT_STATUSES: () => ACCOUNT_STATUSES,
|
|
34
|
+
ACCOUNT_TYPES: () => ACCOUNT_TYPES,
|
|
23
35
|
ADDITIONAL_DATA_SUBFIELD: () => ADDITIONAL_DATA_SUBFIELD,
|
|
36
|
+
AccountMembershipSchema: () => AccountMembershipSchema,
|
|
37
|
+
AccountSchema: () => AccountSchema,
|
|
38
|
+
ApiCredentialPublicSchema: () => ApiCredentialPublicSchema,
|
|
24
39
|
FIELD: () => FIELD,
|
|
25
40
|
FlurApiError: () => FlurApiError,
|
|
26
41
|
FlurCapExceededError: () => FlurCapExceededError,
|
|
@@ -28,6 +43,8 @@ __export(index_exports, {
|
|
|
28
43
|
FlurError: () => FlurError,
|
|
29
44
|
FlurExpiredError: () => FlurExpiredError,
|
|
30
45
|
FlurReplayError: () => FlurReplayError,
|
|
46
|
+
MEMBERSHIP_ROLES: () => MEMBERSHIP_ROLES,
|
|
47
|
+
MintedApiCredentialSchema: () => MintedApiCredentialSchema,
|
|
31
48
|
NGN_CURRENCY_CODE: () => NGN_CURRENCY_CODE,
|
|
32
49
|
NG_COUNTRY_CODE: () => NG_COUNTRY_CODE,
|
|
33
50
|
NQRParseError: () => NQRParseError,
|
|
@@ -37,18 +54,23 @@ __export(index_exports, {
|
|
|
37
54
|
OAC_DEFAULT_VALIDITY_MS: () => OAC_DEFAULT_VALIDITY_MS,
|
|
38
55
|
OfflinePaymentAuthorizationSchema: () => OfflinePaymentAuthorizationSchema,
|
|
39
56
|
OfflinePaymentRequestSchema: () => OfflinePaymentRequestSchema,
|
|
57
|
+
OfflineTokenSchema: () => OfflineTokenSchema,
|
|
58
|
+
PARTNER_SCOPES: () => PARTNER_SCOPES,
|
|
40
59
|
PASS_KINDS: () => PASS_KINDS,
|
|
41
60
|
PASS_STATES: () => PASS_STATES,
|
|
42
61
|
PAYLOAD_FORMAT_INDICATOR_VALUE: () => PAYLOAD_FORMAT_INDICATOR_VALUE,
|
|
43
62
|
POINT_OF_INITIATION: () => POINT_OF_INITIATION,
|
|
44
63
|
PassMetadataSchema: () => PassMetadataSchema,
|
|
45
64
|
PassSchema: () => PassSchema,
|
|
65
|
+
PaymentClaimSchema: () => PaymentClaimSchema,
|
|
46
66
|
RECEIPT_CHANNELS: () => RECEIPT_CHANNELS,
|
|
47
67
|
RECEIPT_KINDS: () => RECEIPT_KINDS,
|
|
48
68
|
REPLAY_WINDOW_MS: () => REPLAY_WINDOW_MS,
|
|
49
69
|
ReceiptPayloadSchema: () => ReceiptPayloadSchema,
|
|
50
70
|
ReceiptSchema: () => ReceiptSchema,
|
|
51
71
|
RedemptionSchema: () => RedemptionSchema,
|
|
72
|
+
SettleResponseSchema: () => SettleResponseSchema,
|
|
73
|
+
SettlementSchema: () => SettlementSchema,
|
|
52
74
|
bodySha256Hex: () => bodySha256Hex,
|
|
53
75
|
buildAuthorization: () => buildAuthorization,
|
|
54
76
|
buildOAC: () => buildOAC,
|
|
@@ -59,10 +81,15 @@ __export(index_exports, {
|
|
|
59
81
|
canonicalJSONBytes: () => canonicalJSONBytes,
|
|
60
82
|
canonicalJSONStringify: () => canonicalJSONStringify,
|
|
61
83
|
canonicalRequestString: () => canonicalRequestString,
|
|
84
|
+
computeEncounterId: () => computeEncounterId,
|
|
62
85
|
constantTimeEqual: () => constantTimeEqual,
|
|
63
86
|
crc16ccitt: () => crc16ccitt,
|
|
64
87
|
crc16ccittHex: () => crc16ccittHex,
|
|
88
|
+
createAccountsClient: () => createAccountsClient,
|
|
89
|
+
createApiCredentialsAdminClient: () => createApiCredentialsAdminClient,
|
|
90
|
+
createFlurPartnerClient: () => createFlurPartnerClient,
|
|
65
91
|
createHmacFetch: () => createHmacFetch,
|
|
92
|
+
createOfflineSettlementsClient: () => createOfflineSettlementsClient,
|
|
66
93
|
createPassesClient: () => createPassesClient,
|
|
67
94
|
createReceiptsClient: () => createReceiptsClient,
|
|
68
95
|
decodeAuthorizationQR: () => decodeAuthorizationQR,
|
|
@@ -90,6 +117,7 @@ __export(index_exports, {
|
|
|
90
117
|
signAuthorization: () => signAuthorization,
|
|
91
118
|
signCanonical: () => signCanonical,
|
|
92
119
|
signOAC: () => signOAC,
|
|
120
|
+
signPartnerRequest: () => signPartnerRequest,
|
|
93
121
|
signPass: () => signPass,
|
|
94
122
|
signPaymentRequest: () => signPaymentRequest,
|
|
95
123
|
signReceipt: () => signReceipt,
|
|
@@ -1536,6 +1564,111 @@ function decodeAuthorizationQR(s) {
|
|
|
1536
1564
|
return OfflinePaymentAuthorizationSchema.parse(JSON.parse(json));
|
|
1537
1565
|
}
|
|
1538
1566
|
|
|
1567
|
+
// src/offline/settlements.ts
|
|
1568
|
+
var import_zod6 = require("zod");
|
|
1569
|
+
var OfflineTokenSchema = import_zod6.z.object({
|
|
1570
|
+
tokenId: import_zod6.z.string().uuid(),
|
|
1571
|
+
tokenSerial: import_zod6.z.string(),
|
|
1572
|
+
issuerAccountId: import_zod6.z.string().uuid(),
|
|
1573
|
+
payerUserId: import_zod6.z.string().uuid(),
|
|
1574
|
+
maxAmountKobo: import_zod6.z.number().int().positive(),
|
|
1575
|
+
currency: import_zod6.z.string().length(3),
|
|
1576
|
+
issuedAtMs: import_zod6.z.number().int().nonnegative(),
|
|
1577
|
+
expiresAtMs: import_zod6.z.number().int().nonnegative(),
|
|
1578
|
+
issuerSig: import_zod6.z.string()
|
|
1579
|
+
});
|
|
1580
|
+
var PaymentClaimSchema = import_zod6.z.object({
|
|
1581
|
+
encounterId: import_zod6.z.string().regex(/^[0-9a-f]{64}$/i).optional(),
|
|
1582
|
+
tokenSerial: import_zod6.z.string(),
|
|
1583
|
+
payerUserId: import_zod6.z.string().uuid(),
|
|
1584
|
+
payeeUserId: import_zod6.z.string().uuid(),
|
|
1585
|
+
payerNonce: import_zod6.z.string(),
|
|
1586
|
+
payeeNonce: import_zod6.z.string(),
|
|
1587
|
+
amountKobo: import_zod6.z.number().int().positive(),
|
|
1588
|
+
currency: import_zod6.z.string().length(3).default("NGN"),
|
|
1589
|
+
occurredAtMs: import_zod6.z.number().int().nonnegative(),
|
|
1590
|
+
completedAtMs: import_zod6.z.number().int().nonnegative().optional(),
|
|
1591
|
+
contextId: import_zod6.z.string().optional(),
|
|
1592
|
+
payerPubkey: import_zod6.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
1593
|
+
payerSignature: import_zod6.z.string().regex(/^[0-9a-f]+$/i),
|
|
1594
|
+
payeePubkey: import_zod6.z.string().regex(/^[0-9a-f]{64}$/i).optional(),
|
|
1595
|
+
payeeSignature: import_zod6.z.string().regex(/^[0-9a-f]+$/i).optional()
|
|
1596
|
+
});
|
|
1597
|
+
var SettlementSchema = import_zod6.z.object({
|
|
1598
|
+
settlementId: import_zod6.z.string().uuid(),
|
|
1599
|
+
settlementKey: import_zod6.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
1600
|
+
encounterId: import_zod6.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
1601
|
+
issuerAccountId: import_zod6.z.string().uuid(),
|
|
1602
|
+
tokenSerial: import_zod6.z.string(),
|
|
1603
|
+
payerUserId: import_zod6.z.string().uuid(),
|
|
1604
|
+
payeeUserId: import_zod6.z.string().uuid(),
|
|
1605
|
+
amountKobo: import_zod6.z.number().int().nonnegative(),
|
|
1606
|
+
currency: import_zod6.z.string().length(3),
|
|
1607
|
+
receiptId: import_zod6.z.string().nullable(),
|
|
1608
|
+
status: import_zod6.z.enum(["SETTLED", "REVIEW", "REJECTED"]),
|
|
1609
|
+
issuerSig: import_zod6.z.string(),
|
|
1610
|
+
createdAtMs: import_zod6.z.number().int().nonnegative()
|
|
1611
|
+
});
|
|
1612
|
+
var SettleResponseSchema = import_zod6.z.object({
|
|
1613
|
+
settlement: SettlementSchema,
|
|
1614
|
+
encounterId: import_zod6.z.string().regex(/^[0-9a-f]{64}$/i),
|
|
1615
|
+
replayed: import_zod6.z.boolean()
|
|
1616
|
+
});
|
|
1617
|
+
var ENCOUNTER_DOMAIN = "offline:v1:encounter";
|
|
1618
|
+
async function sha256Hex(input) {
|
|
1619
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1620
|
+
const enc2 = new TextEncoder().encode(input);
|
|
1621
|
+
if (subtle) {
|
|
1622
|
+
const buf = await subtle.digest("SHA-256", enc2);
|
|
1623
|
+
return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1624
|
+
}
|
|
1625
|
+
const mod = await import("crypto");
|
|
1626
|
+
return mod.createHash("sha256").update(input).digest("hex");
|
|
1627
|
+
}
|
|
1628
|
+
async function computeEncounterId(input) {
|
|
1629
|
+
return sha256Hex(
|
|
1630
|
+
ENCOUNTER_DOMAIN + "|" + [
|
|
1631
|
+
input.payerUserId,
|
|
1632
|
+
input.payeeUserId,
|
|
1633
|
+
input.payerNonce,
|
|
1634
|
+
input.payeeNonce,
|
|
1635
|
+
input.tokenSerial
|
|
1636
|
+
].join("|")
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1639
|
+
function createOfflineSettlementsClient(partner) {
|
|
1640
|
+
return {
|
|
1641
|
+
issueOfflineToken: (input) => partner.request({
|
|
1642
|
+
method: "POST",
|
|
1643
|
+
path: "/v1/offline/tokens",
|
|
1644
|
+
body: {
|
|
1645
|
+
payerUserId: input.payerUserId,
|
|
1646
|
+
maxAmountKobo: input.maxAmountKobo,
|
|
1647
|
+
currency: input.currency ?? "NGN",
|
|
1648
|
+
ttlMs: input.ttlMs,
|
|
1649
|
+
...input.tokenSerial ? { tokenSerial: input.tokenSerial } : {}
|
|
1650
|
+
}
|
|
1651
|
+
}),
|
|
1652
|
+
getOfflineToken: (serial) => partner.request({
|
|
1653
|
+
method: "GET",
|
|
1654
|
+
path: `/v1/offline/tokens/${encodeURIComponent(serial)}`
|
|
1655
|
+
}),
|
|
1656
|
+
settle: (claim) => partner.request({
|
|
1657
|
+
method: "POST",
|
|
1658
|
+
path: "/v1/offline/settlements",
|
|
1659
|
+
body: claim
|
|
1660
|
+
}),
|
|
1661
|
+
getSettlement: (id) => partner.request({
|
|
1662
|
+
method: "GET",
|
|
1663
|
+
path: `/v1/offline/settlements/${encodeURIComponent(id)}`
|
|
1664
|
+
}),
|
|
1665
|
+
listConflicts: () => partner.request({
|
|
1666
|
+
method: "GET",
|
|
1667
|
+
path: "/v1/offline/conflicts"
|
|
1668
|
+
})
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1539
1672
|
// src/auth/hmac.ts
|
|
1540
1673
|
var import_hmac = require("@noble/hashes/hmac");
|
|
1541
1674
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
@@ -1660,7 +1793,7 @@ function createHmacFetch(opts) {
|
|
|
1660
1793
|
}
|
|
1661
1794
|
|
|
1662
1795
|
// src/passes/pass.ts
|
|
1663
|
-
var
|
|
1796
|
+
var import_zod7 = require("zod");
|
|
1664
1797
|
var PASS_KINDS = [
|
|
1665
1798
|
"ride-ticket",
|
|
1666
1799
|
"transit-pass",
|
|
@@ -1676,41 +1809,41 @@ var PASS_STATES = [
|
|
|
1676
1809
|
"expired",
|
|
1677
1810
|
"revoked"
|
|
1678
1811
|
];
|
|
1679
|
-
var HexString2 = (length) =>
|
|
1812
|
+
var HexString2 = (length) => import_zod7.z.string().regex(
|
|
1680
1813
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
1681
1814
|
`expected ${length}-byte hex string`
|
|
1682
1815
|
);
|
|
1683
|
-
var PassMetadataSchema =
|
|
1684
|
-
|
|
1816
|
+
var PassMetadataSchema = import_zod7.z.record(
|
|
1817
|
+
import_zod7.z.union([import_zod7.z.string(), import_zod7.z.number(), import_zod7.z.boolean(), import_zod7.z.null()])
|
|
1685
1818
|
);
|
|
1686
|
-
var PassSchema =
|
|
1687
|
-
passId:
|
|
1819
|
+
var PassSchema = import_zod7.z.object({
|
|
1820
|
+
passId: import_zod7.z.string().min(1),
|
|
1688
1821
|
/** Optional client/template grouping id (server may omit). */
|
|
1689
|
-
templateId:
|
|
1822
|
+
templateId: import_zod7.z.string().min(1).optional(),
|
|
1690
1823
|
/** Optional human-facing holder identity (server may omit). The cryptographic binding
|
|
1691
1824
|
* is `holderDevicePubkey` below. */
|
|
1692
|
-
holderUserId:
|
|
1693
|
-
kind:
|
|
1694
|
-
issuerId:
|
|
1695
|
-
issuedAtMs:
|
|
1696
|
-
validFromMs:
|
|
1697
|
-
validUntilMs:
|
|
1698
|
-
state:
|
|
1825
|
+
holderUserId: import_zod7.z.string().min(1).optional(),
|
|
1826
|
+
kind: import_zod7.z.enum(PASS_KINDS),
|
|
1827
|
+
issuerId: import_zod7.z.string().min(1),
|
|
1828
|
+
issuedAtMs: import_zod7.z.number().int().nonnegative(),
|
|
1829
|
+
validFromMs: import_zod7.z.number().int().nonnegative(),
|
|
1830
|
+
validUntilMs: import_zod7.z.number().int().positive(),
|
|
1831
|
+
state: import_zod7.z.enum(PASS_STATES),
|
|
1699
1832
|
metadata: PassMetadataSchema,
|
|
1700
|
-
nonce:
|
|
1833
|
+
nonce: import_zod7.z.string().min(1),
|
|
1701
1834
|
/** Device id this pass is bound to (FK to backend `device_keys`). */
|
|
1702
|
-
holderDeviceId:
|
|
1835
|
+
holderDeviceId: import_zod7.z.string().min(1),
|
|
1703
1836
|
/** 32-byte hex Ed25519 public key of the bound device. The redemption signature
|
|
1704
1837
|
* is verified against this key — it is the security-critical binding. */
|
|
1705
1838
|
holderDevicePubkey: HexString2(32),
|
|
1706
1839
|
/** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
|
|
1707
|
-
amountKobo:
|
|
1840
|
+
amountKobo: import_zod7.z.number().int().nonnegative().optional(),
|
|
1708
1841
|
/** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
|
|
1709
|
-
currency:
|
|
1842
|
+
currency: import_zod7.z.string().min(3).max(8),
|
|
1710
1843
|
/** Monotonic redemption counter floor. Redemption.counter MUST be > counterSeed. */
|
|
1711
|
-
counterSeed:
|
|
1844
|
+
counterSeed: import_zod7.z.number().int().nonnegative(),
|
|
1712
1845
|
/** Optional cumulative spend cap in kobo across all redemptions of this pass. */
|
|
1713
|
-
cumulativeCapKobo:
|
|
1846
|
+
cumulativeCapKobo: import_zod7.z.number().int().nonnegative().optional(),
|
|
1714
1847
|
issuerSig: HexString2(64)
|
|
1715
1848
|
}).refine((v) => v.validUntilMs > v.validFromMs, {
|
|
1716
1849
|
message: "validUntilMs must be greater than validFromMs"
|
|
@@ -1767,18 +1900,18 @@ function isPassWithinValidity(pass, nowMs) {
|
|
|
1767
1900
|
}
|
|
1768
1901
|
|
|
1769
1902
|
// src/passes/redemption.ts
|
|
1770
|
-
var
|
|
1771
|
-
var HexSig2 =
|
|
1772
|
-
var RedemptionSchema =
|
|
1903
|
+
var import_zod8 = require("zod");
|
|
1904
|
+
var HexSig2 = import_zod8.z.string().regex(/^[0-9a-fA-F]{128}$/, "expected 64-byte hex signature");
|
|
1905
|
+
var RedemptionSchema = import_zod8.z.object({
|
|
1773
1906
|
pass: PassSchema,
|
|
1774
|
-
redeemerId:
|
|
1775
|
-
redeemedAtMs:
|
|
1907
|
+
redeemerId: import_zod8.z.string().min(1),
|
|
1908
|
+
redeemedAtMs: import_zod8.z.number().int().nonnegative(),
|
|
1776
1909
|
/** Strictly monotonic counter scoped to a single pass. Must be > pass.counterSeed
|
|
1777
1910
|
* and > the redeemer's lastSeenCounter for this pass. */
|
|
1778
|
-
counter:
|
|
1911
|
+
counter: import_zod8.z.number().int().positive(),
|
|
1779
1912
|
/** Amount being redeemed in kobo (0 for non-monetary passes like ride tickets). */
|
|
1780
|
-
amountKobo:
|
|
1781
|
-
nonce:
|
|
1913
|
+
amountKobo: import_zod8.z.number().int().nonnegative(),
|
|
1914
|
+
nonce: import_zod8.z.string().min(1),
|
|
1782
1915
|
holderSig: HexSig2
|
|
1783
1916
|
});
|
|
1784
1917
|
var REDEEMABLE_STATES = /* @__PURE__ */ new Set(["issued", "active"]);
|
|
@@ -1863,118 +1996,44 @@ function verifyRedemption(r, issuerPublicKey) {
|
|
|
1863
1996
|
}
|
|
1864
1997
|
}
|
|
1865
1998
|
|
|
1866
|
-
// src/passes/client.ts
|
|
1867
|
-
function createPassesClient(opts) {
|
|
1868
|
-
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
1869
|
-
if (!fetchImpl)
|
|
1870
|
-
throw new Error("createPassesClient: no fetch implementation available");
|
|
1871
|
-
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
1872
|
-
async function call(method, path, body, parser) {
|
|
1873
|
-
const init2 = {
|
|
1874
|
-
method,
|
|
1875
|
-
headers: {
|
|
1876
|
-
"content-type": "application/json",
|
|
1877
|
-
accept: "application/json"
|
|
1878
|
-
}
|
|
1879
|
-
};
|
|
1880
|
-
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
1881
|
-
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
1882
|
-
const text = await resp.text();
|
|
1883
|
-
let raw = void 0;
|
|
1884
|
-
if (text) {
|
|
1885
|
-
try {
|
|
1886
|
-
raw = JSON.parse(text);
|
|
1887
|
-
} catch {
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
if (!resp.ok) {
|
|
1891
|
-
const code = (raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : void 0) ?? `http_${resp.status}`;
|
|
1892
|
-
const message = (raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : void 0) ?? `request failed with status ${resp.status}`;
|
|
1893
|
-
throw new FlurApiError(resp.status, code, message, raw);
|
|
1894
|
-
}
|
|
1895
|
-
return parser(raw);
|
|
1896
|
-
}
|
|
1897
|
-
return {
|
|
1898
|
-
issuePass: (input) => call("POST", "/v1/passes", input, (raw) => PassSchema.parse(raw)),
|
|
1899
|
-
listPasses: (input) => {
|
|
1900
|
-
const qs = new URLSearchParams();
|
|
1901
|
-
if (input.holderDeviceId) qs.set("holderDeviceId", input.holderDeviceId);
|
|
1902
|
-
if (input.holderUserId) qs.set("holderUserId", input.holderUserId);
|
|
1903
|
-
if (input.state) qs.set("state", input.state);
|
|
1904
|
-
if (input.kind) qs.set("kind", input.kind);
|
|
1905
|
-
if (input.templateId) qs.set("templateId", input.templateId);
|
|
1906
|
-
if (typeof input.limit === "number") qs.set("limit", String(input.limit));
|
|
1907
|
-
if (input.cursor) qs.set("cursor", input.cursor);
|
|
1908
|
-
const path = `/v1/passes${qs.size > 0 ? `?${qs.toString()}` : ""}`;
|
|
1909
|
-
return call("GET", path, void 0, (raw) => {
|
|
1910
|
-
const obj = raw;
|
|
1911
|
-
const items = obj.items.map(
|
|
1912
|
-
(it) => PassSchema.parse(it)
|
|
1913
|
-
);
|
|
1914
|
-
const nextCursor = typeof obj.nextCursor === "string" ? obj.nextCursor : null;
|
|
1915
|
-
return { items, nextCursor };
|
|
1916
|
-
});
|
|
1917
|
-
},
|
|
1918
|
-
getPass: (passId) => call(
|
|
1919
|
-
"GET",
|
|
1920
|
-
`/v1/passes/${encodeURIComponent(passId)}`,
|
|
1921
|
-
void 0,
|
|
1922
|
-
(raw) => PassSchema.parse(raw)
|
|
1923
|
-
),
|
|
1924
|
-
redeemPass: (passId, redemption) => call(
|
|
1925
|
-
"POST",
|
|
1926
|
-
`/v1/passes/${encodeURIComponent(passId)}/redeem`,
|
|
1927
|
-
RedemptionSchema.parse(redemption),
|
|
1928
|
-
(raw) => PassSchema.parse(raw)
|
|
1929
|
-
),
|
|
1930
|
-
revokePass: (passId, input) => call(
|
|
1931
|
-
"POST",
|
|
1932
|
-
`/v1/passes/${encodeURIComponent(passId)}/revoke`,
|
|
1933
|
-
input,
|
|
1934
|
-
(raw) => PassSchema.parse(raw)
|
|
1935
|
-
),
|
|
1936
|
-
verifyPass: (pass, issuerPublicKey) => verifyPass(pass, issuerPublicKey)
|
|
1937
|
-
};
|
|
1938
|
-
}
|
|
1939
|
-
|
|
1940
1999
|
// src/receipts/receipt.ts
|
|
1941
|
-
var
|
|
2000
|
+
var import_zod9 = require("zod");
|
|
1942
2001
|
var RECEIPT_CHANNELS = ["cash", "pass"];
|
|
1943
2002
|
var RECEIPT_KINDS = RECEIPT_CHANNELS;
|
|
1944
|
-
var HexString3 = (length) =>
|
|
2003
|
+
var HexString3 = (length) => import_zod9.z.string().regex(
|
|
1945
2004
|
new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
|
|
1946
2005
|
`expected ${length}-byte hex string`
|
|
1947
2006
|
);
|
|
1948
|
-
var ReceiptPayloadSchema =
|
|
1949
|
-
|
|
2007
|
+
var ReceiptPayloadSchema = import_zod9.z.record(
|
|
2008
|
+
import_zod9.z.union([import_zod9.z.string(), import_zod9.z.number(), import_zod9.z.boolean(), import_zod9.z.null()])
|
|
1950
2009
|
);
|
|
1951
|
-
var ReceiptSchema =
|
|
1952
|
-
receiptId:
|
|
1953
|
-
channel:
|
|
2010
|
+
var ReceiptSchema = import_zod9.z.object({
|
|
2011
|
+
receiptId: import_zod9.z.string().min(1),
|
|
2012
|
+
channel: import_zod9.z.enum(RECEIPT_CHANNELS),
|
|
1954
2013
|
/** Cash-channel: send_intents.id. Required when channel === 'cash'. */
|
|
1955
|
-
intentId:
|
|
2014
|
+
intentId: import_zod9.z.string().min(1).optional(),
|
|
1956
2015
|
/** Pass-channel: pass_redemptions.id. Required when channel === 'pass'. */
|
|
1957
|
-
passRedemptionId:
|
|
1958
|
-
payerUserId:
|
|
1959
|
-
payeeUserId:
|
|
1960
|
-
amountKobo:
|
|
1961
|
-
currency:
|
|
1962
|
-
issuedAtMs:
|
|
1963
|
-
issuerId:
|
|
2016
|
+
passRedemptionId: import_zod9.z.string().min(1).optional(),
|
|
2017
|
+
payerUserId: import_zod9.z.string().min(1),
|
|
2018
|
+
payeeUserId: import_zod9.z.string().min(1),
|
|
2019
|
+
amountKobo: import_zod9.z.number().int().nonnegative(),
|
|
2020
|
+
currency: import_zod9.z.string().min(3).max(8),
|
|
2021
|
+
issuedAtMs: import_zod9.z.number().int().nonnegative(),
|
|
2022
|
+
issuerId: import_zod9.z.string().min(1),
|
|
1964
2023
|
payload: ReceiptPayloadSchema,
|
|
1965
2024
|
issuerSig: HexString3(64)
|
|
1966
2025
|
}).superRefine((v, ctx) => {
|
|
1967
2026
|
if (v.channel === "cash") {
|
|
1968
2027
|
if (!v.intentId) {
|
|
1969
2028
|
ctx.addIssue({
|
|
1970
|
-
code:
|
|
2029
|
+
code: import_zod9.z.ZodIssueCode.custom,
|
|
1971
2030
|
message: "cash receipts require intentId",
|
|
1972
2031
|
path: ["intentId"]
|
|
1973
2032
|
});
|
|
1974
2033
|
}
|
|
1975
2034
|
if (v.passRedemptionId) {
|
|
1976
2035
|
ctx.addIssue({
|
|
1977
|
-
code:
|
|
2036
|
+
code: import_zod9.z.ZodIssueCode.custom,
|
|
1978
2037
|
message: "cash receipts must not carry passRedemptionId",
|
|
1979
2038
|
path: ["passRedemptionId"]
|
|
1980
2039
|
});
|
|
@@ -1982,14 +2041,14 @@ var ReceiptSchema = import_zod8.z.object({
|
|
|
1982
2041
|
} else if (v.channel === "pass") {
|
|
1983
2042
|
if (!v.passRedemptionId) {
|
|
1984
2043
|
ctx.addIssue({
|
|
1985
|
-
code:
|
|
2044
|
+
code: import_zod9.z.ZodIssueCode.custom,
|
|
1986
2045
|
message: "pass receipts require passRedemptionId",
|
|
1987
2046
|
path: ["passRedemptionId"]
|
|
1988
2047
|
});
|
|
1989
2048
|
}
|
|
1990
2049
|
if (v.intentId) {
|
|
1991
2050
|
ctx.addIssue({
|
|
1992
|
-
code:
|
|
2051
|
+
code: import_zod9.z.ZodIssueCode.custom,
|
|
1993
2052
|
message: "pass receipts must not carry intentId",
|
|
1994
2053
|
path: ["intentId"]
|
|
1995
2054
|
});
|
|
@@ -2047,6 +2106,114 @@ function verifyReceipt(r, issuerPublicKey) {
|
|
|
2047
2106
|
}
|
|
2048
2107
|
}
|
|
2049
2108
|
|
|
2109
|
+
// src/passes/client.ts
|
|
2110
|
+
function createPassesClient(opts) {
|
|
2111
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2112
|
+
if (!fetchImpl)
|
|
2113
|
+
throw new Error("createPassesClient: no fetch implementation available");
|
|
2114
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2115
|
+
async function call(method, path, body, parser) {
|
|
2116
|
+
const init2 = {
|
|
2117
|
+
method,
|
|
2118
|
+
headers: {
|
|
2119
|
+
"content-type": "application/json",
|
|
2120
|
+
accept: "application/json"
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
2124
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
2125
|
+
const text = await resp.text();
|
|
2126
|
+
let raw = void 0;
|
|
2127
|
+
if (text) {
|
|
2128
|
+
try {
|
|
2129
|
+
raw = JSON.parse(text);
|
|
2130
|
+
} catch {
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
if (!resp.ok) {
|
|
2134
|
+
const code = (raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : void 0) ?? `http_${resp.status}`;
|
|
2135
|
+
const message = (raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : void 0) ?? `request failed with status ${resp.status}`;
|
|
2136
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
2137
|
+
}
|
|
2138
|
+
return parser(raw);
|
|
2139
|
+
}
|
|
2140
|
+
return {
|
|
2141
|
+
issuePass: (input) => call("POST", "/v1/passes", input, (raw) => PassSchema.parse(raw)),
|
|
2142
|
+
listPasses: (input) => {
|
|
2143
|
+
const qs = new URLSearchParams();
|
|
2144
|
+
if (input.holderDeviceId) qs.set("holderDeviceId", input.holderDeviceId);
|
|
2145
|
+
if (input.holderUserId) qs.set("holderUserId", input.holderUserId);
|
|
2146
|
+
if (input.state) qs.set("state", input.state);
|
|
2147
|
+
if (input.kind) qs.set("kind", input.kind);
|
|
2148
|
+
if (input.templateId) qs.set("templateId", input.templateId);
|
|
2149
|
+
if (typeof input.limit === "number") qs.set("limit", String(input.limit));
|
|
2150
|
+
if (input.cursor) qs.set("cursor", input.cursor);
|
|
2151
|
+
const path = `/v1/passes${qs.size > 0 ? `?${qs.toString()}` : ""}`;
|
|
2152
|
+
return call("GET", path, void 0, (raw) => {
|
|
2153
|
+
const obj = raw;
|
|
2154
|
+
const items = obj.items.map(
|
|
2155
|
+
(it) => PassSchema.parse(it)
|
|
2156
|
+
);
|
|
2157
|
+
const nextCursor = typeof obj.nextCursor === "string" ? obj.nextCursor : null;
|
|
2158
|
+
return { items, nextCursor };
|
|
2159
|
+
});
|
|
2160
|
+
},
|
|
2161
|
+
getPass: (passId) => call(
|
|
2162
|
+
"GET",
|
|
2163
|
+
`/v1/passes/${encodeURIComponent(passId)}`,
|
|
2164
|
+
void 0,
|
|
2165
|
+
(raw) => PassSchema.parse(raw)
|
|
2166
|
+
),
|
|
2167
|
+
redeemPass: (passId, redemption) => call(
|
|
2168
|
+
"POST",
|
|
2169
|
+
`/v1/passes/${encodeURIComponent(passId)}/redeem`,
|
|
2170
|
+
RedemptionSchema.parse(redemption),
|
|
2171
|
+
(raw) => {
|
|
2172
|
+
const obj = raw;
|
|
2173
|
+
return {
|
|
2174
|
+
pass: PassSchema.parse(raw),
|
|
2175
|
+
redemptionId: typeof obj.redemptionId === "string" ? obj.redemptionId : ""
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
).then((result) => result.pass),
|
|
2179
|
+
redeemPassDetailed: (passId, redemption) => call(
|
|
2180
|
+
"POST",
|
|
2181
|
+
`/v1/passes/${encodeURIComponent(passId)}/redeem`,
|
|
2182
|
+
RedemptionSchema.parse(redemption),
|
|
2183
|
+
(raw) => {
|
|
2184
|
+
const obj = raw;
|
|
2185
|
+
return {
|
|
2186
|
+
pass: PassSchema.parse(raw),
|
|
2187
|
+
redemptionId: typeof obj.redemptionId === "string" ? obj.redemptionId : ""
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
),
|
|
2191
|
+
redeemPassWithReceipt: (passId, redemption, receipt) => call(
|
|
2192
|
+
"POST",
|
|
2193
|
+
`/v1/passes/${encodeURIComponent(passId)}/redeem-with-receipt`,
|
|
2194
|
+
{
|
|
2195
|
+
redemption: RedemptionSchema.parse(redemption),
|
|
2196
|
+
receipt
|
|
2197
|
+
},
|
|
2198
|
+
(raw) => {
|
|
2199
|
+
const obj = raw;
|
|
2200
|
+
return {
|
|
2201
|
+
pass: PassSchema.parse(obj.pass),
|
|
2202
|
+
redemptionId: typeof obj.redemptionId === "string" ? obj.redemptionId : "",
|
|
2203
|
+
receipt: ReceiptSchema.parse(obj.receipt)
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
),
|
|
2207
|
+
revokePass: (passId, input) => call(
|
|
2208
|
+
"POST",
|
|
2209
|
+
`/v1/passes/${encodeURIComponent(passId)}/revoke`,
|
|
2210
|
+
input,
|
|
2211
|
+
(raw) => PassSchema.parse(raw)
|
|
2212
|
+
),
|
|
2213
|
+
verifyPass: (pass, issuerPublicKey) => verifyPass(pass, issuerPublicKey)
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2050
2217
|
// src/receipts/client.ts
|
|
2051
2218
|
function createReceiptsClient(opts) {
|
|
2052
2219
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -2203,9 +2370,316 @@ function init(opts) {
|
|
|
2203
2370
|
receipts
|
|
2204
2371
|
};
|
|
2205
2372
|
}
|
|
2373
|
+
|
|
2374
|
+
// src/accounts/client.ts
|
|
2375
|
+
var import_zod10 = require("zod");
|
|
2376
|
+
var ACCOUNT_TYPES = ["personal", "business", "partner"];
|
|
2377
|
+
var ACCOUNT_STATUSES = ["active", "suspended", "closed"];
|
|
2378
|
+
var MEMBERSHIP_ROLES = ["owner", "admin", "driver", "staff"];
|
|
2379
|
+
var AccountSchema = import_zod10.z.object({
|
|
2380
|
+
accountId: import_zod10.z.string().uuid(),
|
|
2381
|
+
type: import_zod10.z.enum(ACCOUNT_TYPES),
|
|
2382
|
+
displayName: import_zod10.z.string().min(1),
|
|
2383
|
+
status: import_zod10.z.enum(ACCOUNT_STATUSES),
|
|
2384
|
+
ownerUserId: import_zod10.z.string().uuid().nullable(),
|
|
2385
|
+
createdAtMs: import_zod10.z.number().int().nonnegative()
|
|
2386
|
+
});
|
|
2387
|
+
var AccountMembershipSchema = import_zod10.z.object({
|
|
2388
|
+
accountId: import_zod10.z.string().uuid(),
|
|
2389
|
+
userId: import_zod10.z.string().uuid(),
|
|
2390
|
+
role: import_zod10.z.enum(MEMBERSHIP_ROLES),
|
|
2391
|
+
createdAtMs: import_zod10.z.number().int().nonnegative()
|
|
2392
|
+
});
|
|
2393
|
+
function createAccountsClient(opts) {
|
|
2394
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2395
|
+
if (!fetchImpl) {
|
|
2396
|
+
throw new Error("createAccountsClient: no fetch implementation available");
|
|
2397
|
+
}
|
|
2398
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2399
|
+
async function call(method, path, body, parser) {
|
|
2400
|
+
const init2 = {
|
|
2401
|
+
method,
|
|
2402
|
+
headers: {
|
|
2403
|
+
"content-type": "application/json",
|
|
2404
|
+
accept: "application/json"
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
2408
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
2409
|
+
const text = await resp.text();
|
|
2410
|
+
let raw = void 0;
|
|
2411
|
+
if (text) {
|
|
2412
|
+
try {
|
|
2413
|
+
raw = JSON.parse(text);
|
|
2414
|
+
} catch {
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
if (!resp.ok) {
|
|
2418
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2419
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2420
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
2421
|
+
}
|
|
2422
|
+
return parser(raw);
|
|
2423
|
+
}
|
|
2424
|
+
const itemsSchema = import_zod10.z.object({ items: import_zod10.z.array(AccountSchema) });
|
|
2425
|
+
const memberItemsSchema = import_zod10.z.object({
|
|
2426
|
+
items: import_zod10.z.array(AccountMembershipSchema)
|
|
2427
|
+
});
|
|
2428
|
+
return {
|
|
2429
|
+
listMyAccounts: () => call(
|
|
2430
|
+
"GET",
|
|
2431
|
+
"/v1/accounts/me",
|
|
2432
|
+
void 0,
|
|
2433
|
+
(raw) => itemsSchema.parse(raw)
|
|
2434
|
+
),
|
|
2435
|
+
getAccount: (accountId) => call(
|
|
2436
|
+
"GET",
|
|
2437
|
+
`/v1/accounts/${encodeURIComponent(accountId)}`,
|
|
2438
|
+
void 0,
|
|
2439
|
+
(raw) => AccountSchema.parse(raw)
|
|
2440
|
+
),
|
|
2441
|
+
listMembers: (accountId) => call(
|
|
2442
|
+
"GET",
|
|
2443
|
+
`/v1/accounts/${encodeURIComponent(accountId)}/members`,
|
|
2444
|
+
void 0,
|
|
2445
|
+
(raw) => memberItemsSchema.parse(raw)
|
|
2446
|
+
),
|
|
2447
|
+
createBusinessAccount: (input) => call("POST", "/v1/accounts", input, (raw) => AccountSchema.parse(raw)),
|
|
2448
|
+
addMember: (accountId, input) => call(
|
|
2449
|
+
"POST",
|
|
2450
|
+
`/v1/accounts/${encodeURIComponent(accountId)}/members`,
|
|
2451
|
+
input,
|
|
2452
|
+
(raw) => AccountMembershipSchema.parse(raw)
|
|
2453
|
+
)
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
// src/partner/client.ts
|
|
2458
|
+
var import_zod11 = require("zod");
|
|
2459
|
+
var PARTNER_SCOPES = [
|
|
2460
|
+
"passes:write",
|
|
2461
|
+
"passes:read",
|
|
2462
|
+
"passes:redeem",
|
|
2463
|
+
"receipts:write",
|
|
2464
|
+
"receipts:read",
|
|
2465
|
+
"offline:issue",
|
|
2466
|
+
"offline:settle",
|
|
2467
|
+
"offline:read"
|
|
2468
|
+
];
|
|
2469
|
+
var ApiCredentialPublicSchema = import_zod11.z.object({
|
|
2470
|
+
id: import_zod11.z.string().uuid(),
|
|
2471
|
+
accountId: import_zod11.z.string().uuid(),
|
|
2472
|
+
keyId: import_zod11.z.string(),
|
|
2473
|
+
scopes: import_zod11.z.array(import_zod11.z.enum(PARTNER_SCOPES)),
|
|
2474
|
+
label: import_zod11.z.string().nullable(),
|
|
2475
|
+
createdAtMs: import_zod11.z.number().int().nonnegative(),
|
|
2476
|
+
lastUsedAtMs: import_zod11.z.number().int().nonnegative().nullable(),
|
|
2477
|
+
revokedAtMs: import_zod11.z.number().int().nonnegative().nullable()
|
|
2478
|
+
});
|
|
2479
|
+
var MintedApiCredentialSchema = ApiCredentialPublicSchema.extend({
|
|
2480
|
+
secret: import_zod11.z.string().min(1)
|
|
2481
|
+
});
|
|
2482
|
+
async function getSubtle() {
|
|
2483
|
+
const c = globalThis.crypto;
|
|
2484
|
+
if (c?.subtle) return c.subtle;
|
|
2485
|
+
const mod = await import("crypto");
|
|
2486
|
+
return mod.webcrypto.subtle;
|
|
2487
|
+
}
|
|
2488
|
+
var enc = new TextEncoder();
|
|
2489
|
+
function bytesToHex3(buf) {
|
|
2490
|
+
const u = new Uint8Array(buf);
|
|
2491
|
+
let s = "";
|
|
2492
|
+
for (let i = 0; i < u.length; i++) {
|
|
2493
|
+
s += u[i].toString(16).padStart(2, "0");
|
|
2494
|
+
}
|
|
2495
|
+
return s;
|
|
2496
|
+
}
|
|
2497
|
+
async function sha256Hex2(input) {
|
|
2498
|
+
const subtle = await getSubtle();
|
|
2499
|
+
const data = typeof input === "string" ? enc.encode(input) : input;
|
|
2500
|
+
const buffer = data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ? data.buffer : data.slice().buffer;
|
|
2501
|
+
const buf = await subtle.digest("SHA-256", buffer);
|
|
2502
|
+
return bytesToHex3(buf);
|
|
2503
|
+
}
|
|
2504
|
+
async function hmacSha256Hex(keyHex, message) {
|
|
2505
|
+
const subtle = await getSubtle();
|
|
2506
|
+
const key = await subtle.importKey(
|
|
2507
|
+
"raw",
|
|
2508
|
+
enc.encode(keyHex),
|
|
2509
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
2510
|
+
false,
|
|
2511
|
+
["sign"]
|
|
2512
|
+
);
|
|
2513
|
+
const sig = await subtle.sign("HMAC", key, enc.encode(message));
|
|
2514
|
+
return bytesToHex3(sig);
|
|
2515
|
+
}
|
|
2516
|
+
function defaultNonce2() {
|
|
2517
|
+
const c = globalThis.crypto;
|
|
2518
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
2519
|
+
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
|
|
2520
|
+
}
|
|
2521
|
+
function canonical(params) {
|
|
2522
|
+
return [
|
|
2523
|
+
params.method.toUpperCase(),
|
|
2524
|
+
params.path,
|
|
2525
|
+
params.ts,
|
|
2526
|
+
params.nonce,
|
|
2527
|
+
params.bodyHash
|
|
2528
|
+
].join("\n");
|
|
2529
|
+
}
|
|
2530
|
+
async function signPartnerRequest(params) {
|
|
2531
|
+
const ts = String(Math.floor((params.nowMs ?? Date.now()) / 1e3));
|
|
2532
|
+
const nonce = params.nonce ?? defaultNonce2();
|
|
2533
|
+
const bodyData = typeof params.body === "string" ? enc.encode(params.body) : params.body ?? new Uint8Array();
|
|
2534
|
+
const bodyHash = await sha256Hex2(bodyData);
|
|
2535
|
+
const message = canonical({
|
|
2536
|
+
method: params.method,
|
|
2537
|
+
path: params.path,
|
|
2538
|
+
ts,
|
|
2539
|
+
nonce,
|
|
2540
|
+
bodyHash
|
|
2541
|
+
});
|
|
2542
|
+
const signingKey = await sha256Hex2(params.secret);
|
|
2543
|
+
const sig = await hmacSha256Hex(signingKey, message);
|
|
2544
|
+
return {
|
|
2545
|
+
authorization: `Flur-Hmac key=${params.keyId}, ts=${ts}, nonce=${nonce}, sig=${sig}`,
|
|
2546
|
+
ts,
|
|
2547
|
+
nonce,
|
|
2548
|
+
bodyHash
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
function createFlurPartnerClient(opts) {
|
|
2552
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2553
|
+
if (!fetchImpl) {
|
|
2554
|
+
throw new Error(
|
|
2555
|
+
"createFlurPartnerClient: no fetch implementation available"
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2559
|
+
async function makeSignedInit(method, path, body) {
|
|
2560
|
+
const sig = await signPartnerRequest({
|
|
2561
|
+
keyId: opts.keyId,
|
|
2562
|
+
secret: opts.secret,
|
|
2563
|
+
method,
|
|
2564
|
+
path,
|
|
2565
|
+
body: body ?? "",
|
|
2566
|
+
nowMs: opts.nowMs?.(),
|
|
2567
|
+
nonce: opts.nonce?.()
|
|
2568
|
+
});
|
|
2569
|
+
const headers = {
|
|
2570
|
+
authorization: sig.authorization,
|
|
2571
|
+
accept: "application/json"
|
|
2572
|
+
};
|
|
2573
|
+
if (body !== void 0) headers["content-type"] = "application/json";
|
|
2574
|
+
const init2 = { method, headers };
|
|
2575
|
+
if (body !== void 0) init2.body = body;
|
|
2576
|
+
return init2;
|
|
2577
|
+
}
|
|
2578
|
+
async function request(opts2) {
|
|
2579
|
+
const bodyStr = opts2.body === void 0 ? void 0 : JSON.stringify(opts2.body);
|
|
2580
|
+
const init2 = await makeSignedInit(opts2.method, opts2.path, bodyStr);
|
|
2581
|
+
const resp = await fetchImpl(`${baseUrl}${opts2.path}`, init2);
|
|
2582
|
+
const text = await resp.text();
|
|
2583
|
+
let raw;
|
|
2584
|
+
if (text) {
|
|
2585
|
+
try {
|
|
2586
|
+
raw = JSON.parse(text);
|
|
2587
|
+
} catch {
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
if (!resp.ok) {
|
|
2591
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2592
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2593
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
2594
|
+
}
|
|
2595
|
+
return raw;
|
|
2596
|
+
}
|
|
2597
|
+
const fetchLike = async (input, init2) => {
|
|
2598
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
2599
|
+
const u = new URL(url, baseUrl);
|
|
2600
|
+
const path = `${u.pathname}${u.search}`;
|
|
2601
|
+
const method = (init2?.method ?? "GET").toUpperCase();
|
|
2602
|
+
let bodyStr;
|
|
2603
|
+
if (init2?.body) {
|
|
2604
|
+
bodyStr = typeof init2.body === "string" ? init2.body : init2.body.toString();
|
|
2605
|
+
}
|
|
2606
|
+
const signed = await makeSignedInit(method, path, bodyStr);
|
|
2607
|
+
return fetchImpl(`${baseUrl}${path}`, {
|
|
2608
|
+
...init2,
|
|
2609
|
+
...signed,
|
|
2610
|
+
headers: {
|
|
2611
|
+
...init2?.headers,
|
|
2612
|
+
...signed.headers
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
};
|
|
2616
|
+
return { request, fetch: fetchLike };
|
|
2617
|
+
}
|
|
2618
|
+
function createApiCredentialsAdminClient(opts) {
|
|
2619
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2620
|
+
if (!fetchImpl) {
|
|
2621
|
+
throw new Error(
|
|
2622
|
+
"createApiCredentialsAdminClient: no fetch implementation available"
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2625
|
+
const baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2626
|
+
async function call(method, path, body, parser) {
|
|
2627
|
+
const init2 = {
|
|
2628
|
+
method,
|
|
2629
|
+
headers: {
|
|
2630
|
+
"content-type": "application/json",
|
|
2631
|
+
accept: "application/json"
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
if (body !== void 0) init2.body = JSON.stringify(body);
|
|
2635
|
+
const resp = await fetchImpl(`${baseUrl}${path}`, init2);
|
|
2636
|
+
const text = await resp.text();
|
|
2637
|
+
let raw;
|
|
2638
|
+
if (text) {
|
|
2639
|
+
try {
|
|
2640
|
+
raw = JSON.parse(text);
|
|
2641
|
+
} catch {
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
if (!resp.ok) {
|
|
2645
|
+
const code = raw && typeof raw === "object" && "code" in raw && typeof raw.code === "string" ? raw.code : `http_${resp.status}`;
|
|
2646
|
+
const message = raw && typeof raw === "object" && "message" in raw && typeof raw.message === "string" ? raw.message : `request failed with status ${resp.status}`;
|
|
2647
|
+
throw new FlurApiError(resp.status, code, message, raw);
|
|
2648
|
+
}
|
|
2649
|
+
return parser(raw);
|
|
2650
|
+
}
|
|
2651
|
+
const listSchema = import_zod11.z.object({
|
|
2652
|
+
items: import_zod11.z.array(ApiCredentialPublicSchema)
|
|
2653
|
+
});
|
|
2654
|
+
return {
|
|
2655
|
+
list: (accountId) => call(
|
|
2656
|
+
"GET",
|
|
2657
|
+
`/v1/accounts/${accountId}/api-credentials`,
|
|
2658
|
+
void 0,
|
|
2659
|
+
(raw) => listSchema.parse(raw)
|
|
2660
|
+
),
|
|
2661
|
+
mint: (accountId, input) => call(
|
|
2662
|
+
"POST",
|
|
2663
|
+
`/v1/accounts/${accountId}/api-credentials`,
|
|
2664
|
+
input,
|
|
2665
|
+
(raw) => MintedApiCredentialSchema.parse(raw)
|
|
2666
|
+
),
|
|
2667
|
+
revoke: (accountId, credentialId) => call(
|
|
2668
|
+
"DELETE",
|
|
2669
|
+
`/v1/accounts/${accountId}/api-credentials/${credentialId}`,
|
|
2670
|
+
void 0,
|
|
2671
|
+
(raw) => ApiCredentialPublicSchema.parse(raw)
|
|
2672
|
+
)
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2206
2675
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2207
2676
|
0 && (module.exports = {
|
|
2677
|
+
ACCOUNT_STATUSES,
|
|
2678
|
+
ACCOUNT_TYPES,
|
|
2208
2679
|
ADDITIONAL_DATA_SUBFIELD,
|
|
2680
|
+
AccountMembershipSchema,
|
|
2681
|
+
AccountSchema,
|
|
2682
|
+
ApiCredentialPublicSchema,
|
|
2209
2683
|
FIELD,
|
|
2210
2684
|
FlurApiError,
|
|
2211
2685
|
FlurCapExceededError,
|
|
@@ -2213,6 +2687,8 @@ function init(opts) {
|
|
|
2213
2687
|
FlurError,
|
|
2214
2688
|
FlurExpiredError,
|
|
2215
2689
|
FlurReplayError,
|
|
2690
|
+
MEMBERSHIP_ROLES,
|
|
2691
|
+
MintedApiCredentialSchema,
|
|
2216
2692
|
NGN_CURRENCY_CODE,
|
|
2217
2693
|
NG_COUNTRY_CODE,
|
|
2218
2694
|
NQRParseError,
|
|
@@ -2222,18 +2698,23 @@ function init(opts) {
|
|
|
2222
2698
|
OAC_DEFAULT_VALIDITY_MS,
|
|
2223
2699
|
OfflinePaymentAuthorizationSchema,
|
|
2224
2700
|
OfflinePaymentRequestSchema,
|
|
2701
|
+
OfflineTokenSchema,
|
|
2702
|
+
PARTNER_SCOPES,
|
|
2225
2703
|
PASS_KINDS,
|
|
2226
2704
|
PASS_STATES,
|
|
2227
2705
|
PAYLOAD_FORMAT_INDICATOR_VALUE,
|
|
2228
2706
|
POINT_OF_INITIATION,
|
|
2229
2707
|
PassMetadataSchema,
|
|
2230
2708
|
PassSchema,
|
|
2709
|
+
PaymentClaimSchema,
|
|
2231
2710
|
RECEIPT_CHANNELS,
|
|
2232
2711
|
RECEIPT_KINDS,
|
|
2233
2712
|
REPLAY_WINDOW_MS,
|
|
2234
2713
|
ReceiptPayloadSchema,
|
|
2235
2714
|
ReceiptSchema,
|
|
2236
2715
|
RedemptionSchema,
|
|
2716
|
+
SettleResponseSchema,
|
|
2717
|
+
SettlementSchema,
|
|
2237
2718
|
bodySha256Hex,
|
|
2238
2719
|
buildAuthorization,
|
|
2239
2720
|
buildOAC,
|
|
@@ -2244,10 +2725,15 @@ function init(opts) {
|
|
|
2244
2725
|
canonicalJSONBytes,
|
|
2245
2726
|
canonicalJSONStringify,
|
|
2246
2727
|
canonicalRequestString,
|
|
2728
|
+
computeEncounterId,
|
|
2247
2729
|
constantTimeEqual,
|
|
2248
2730
|
crc16ccitt,
|
|
2249
2731
|
crc16ccittHex,
|
|
2732
|
+
createAccountsClient,
|
|
2733
|
+
createApiCredentialsAdminClient,
|
|
2734
|
+
createFlurPartnerClient,
|
|
2250
2735
|
createHmacFetch,
|
|
2736
|
+
createOfflineSettlementsClient,
|
|
2251
2737
|
createPassesClient,
|
|
2252
2738
|
createReceiptsClient,
|
|
2253
2739
|
decodeAuthorizationQR,
|
|
@@ -2275,6 +2761,7 @@ function init(opts) {
|
|
|
2275
2761
|
signAuthorization,
|
|
2276
2762
|
signCanonical,
|
|
2277
2763
|
signOAC,
|
|
2764
|
+
signPartnerRequest,
|
|
2278
2765
|
signPass,
|
|
2279
2766
|
signPaymentRequest,
|
|
2280
2767
|
signReceipt,
|