@zkp2p/pay-shared 0.0.2 → 2.0.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/LICENSE +6 -0
- package/README.md +30 -26
- package/dist/bitcoin.d.ts +1 -0
- package/dist/bitcoin.js +13 -0
- package/dist/buyerTee.d.ts +4 -0
- package/dist/buyerTee.js +3 -0
- package/dist/chains.d.ts +18 -4
- package/dist/chains.js +65 -3
- package/dist/crypto.d.ts +65 -0
- package/dist/crypto.js +291 -0
- package/dist/fees.d.ts +27 -0
- package/dist/fees.js +195 -0
- package/dist/fiatAmount.d.ts +19 -0
- package/dist/fiatAmount.js +58 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +8 -0
- package/dist/paypalSarPhase.d.ts +5 -0
- package/dist/paypalSarPhase.js +8 -0
- package/dist/paypalSarVerify.d.ts +41 -0
- package/dist/paypalSarVerify.js +31 -0
- package/dist/rails.d.ts +137 -0
- package/dist/rails.js +320 -0
- package/dist/types.d.ts +427 -39
- package/dist/types.js +78 -13
- package/package.json +31 -5
- package/dist/chains.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/src/chains.ts +0 -417
- package/src/index.d.ts +0 -2
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -1
- package/src/index.ts +0 -2
- package/src/types.d.ts +0 -132
- package/src/types.d.ts.map +0 -1
- package/src/types.js +0 -29
- package/src/types.ts +0 -464
package/dist/rails.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
const RELAY_RAIL_PREFIX = 'relay_';
|
|
2
|
+
export var SupportedRail;
|
|
3
|
+
(function (SupportedRail) {
|
|
4
|
+
SupportedRail["VENMO"] = "venmo";
|
|
5
|
+
SupportedRail["CASHAPP"] = "cashapp";
|
|
6
|
+
SupportedRail["REVOLUT"] = "revolut";
|
|
7
|
+
SupportedRail["ZELLE"] = "zelle";
|
|
8
|
+
SupportedRail["PAYPAL"] = "paypal";
|
|
9
|
+
SupportedRail["WISE"] = "wise";
|
|
10
|
+
SupportedRail["MONZO"] = "monzo";
|
|
11
|
+
SupportedRail["N26"] = "n26";
|
|
12
|
+
SupportedRail["CHIME"] = "chime";
|
|
13
|
+
SupportedRail["RELAY_1"] = "relay_1";
|
|
14
|
+
SupportedRail["RELAY_10"] = "relay_10";
|
|
15
|
+
SupportedRail["RELAY_56"] = "relay_56";
|
|
16
|
+
SupportedRail["RELAY_137"] = "relay_137";
|
|
17
|
+
SupportedRail["RELAY_480"] = "relay_480";
|
|
18
|
+
SupportedRail["RELAY_999"] = "relay_999";
|
|
19
|
+
SupportedRail["RELAY_8453"] = "relay_8453";
|
|
20
|
+
SupportedRail["RELAY_42161"] = "relay_42161";
|
|
21
|
+
SupportedRail["RELAY_8253038"] = "relay_8253038";
|
|
22
|
+
SupportedRail["RELAY_728126428"] = "relay_728126428";
|
|
23
|
+
SupportedRail["RELAY_792703809"] = "relay_792703809";
|
|
24
|
+
})(SupportedRail || (SupportedRail = {}));
|
|
25
|
+
/**
|
|
26
|
+
* Canonical relay rails currently supported by zkp2p/pay.
|
|
27
|
+
*
|
|
28
|
+
* Chain id source reference:
|
|
29
|
+
* https://docs.relay.link/references/api/api_resources/supported-chains
|
|
30
|
+
*/
|
|
31
|
+
export var SupportedRelayRail;
|
|
32
|
+
(function (SupportedRelayRail) {
|
|
33
|
+
SupportedRelayRail["RELAY_1"] = "relay_1";
|
|
34
|
+
SupportedRelayRail["RELAY_10"] = "relay_10";
|
|
35
|
+
SupportedRelayRail["RELAY_56"] = "relay_56";
|
|
36
|
+
SupportedRelayRail["RELAY_137"] = "relay_137";
|
|
37
|
+
SupportedRelayRail["RELAY_480"] = "relay_480";
|
|
38
|
+
SupportedRelayRail["RELAY_999"] = "relay_999";
|
|
39
|
+
SupportedRelayRail["RELAY_8453"] = "relay_8453";
|
|
40
|
+
SupportedRelayRail["RELAY_42161"] = "relay_42161";
|
|
41
|
+
SupportedRelayRail["RELAY_8253038"] = "relay_8253038";
|
|
42
|
+
SupportedRelayRail["RELAY_728126428"] = "relay_728126428";
|
|
43
|
+
SupportedRelayRail["RELAY_792703809"] = "relay_792703809";
|
|
44
|
+
})(SupportedRelayRail || (SupportedRelayRail = {}));
|
|
45
|
+
export const DEFAULT_FIAT_RAILS = [
|
|
46
|
+
SupportedRail.VENMO,
|
|
47
|
+
SupportedRail.CASHAPP,
|
|
48
|
+
SupportedRail.PAYPAL,
|
|
49
|
+
SupportedRail.WISE,
|
|
50
|
+
];
|
|
51
|
+
export const SUPPORTED_RAILS = Object.values(SupportedRail);
|
|
52
|
+
export const SUPPORTED_RELAY_RAILS = Object.values(SupportedRelayRail);
|
|
53
|
+
const SUPPORTED_RAIL_SET = new Set(SUPPORTED_RAILS);
|
|
54
|
+
// Globally runtime-disabled fiat rails: removed from new offers/acceptance
|
|
55
|
+
// everywhere isRailRuntimeEnabled / filterRuntimeEnabledRails are consumed,
|
|
56
|
+
// while staying recognized for historical orders. Empty by default; the API
|
|
57
|
+
// seeds this from the DISABLED_RAILS env at boot via setRuntimeDisabledFiatRails.
|
|
58
|
+
// The frontend never sets it, so the browser bundle keeps an empty (no-op) set.
|
|
59
|
+
let runtimeDisabledFiatRails = new Set();
|
|
60
|
+
export const FIAT_SUPPORTED_RAILS = [
|
|
61
|
+
'venmo',
|
|
62
|
+
'cashapp',
|
|
63
|
+
'revolut',
|
|
64
|
+
'wise',
|
|
65
|
+
'zelle',
|
|
66
|
+
'paypal',
|
|
67
|
+
'monzo',
|
|
68
|
+
'n26',
|
|
69
|
+
'chime',
|
|
70
|
+
];
|
|
71
|
+
const PAYMENT_PLATFORM_LABEL_ENTRIES = {
|
|
72
|
+
[SupportedRail.VENMO]: 'Venmo',
|
|
73
|
+
[SupportedRail.CASHAPP]: 'Cash App',
|
|
74
|
+
[SupportedRail.REVOLUT]: 'Revolut',
|
|
75
|
+
[SupportedRail.WISE]: 'Wise',
|
|
76
|
+
[SupportedRail.ZELLE]: 'Zelle',
|
|
77
|
+
[SupportedRail.PAYPAL]: 'PayPal',
|
|
78
|
+
[SupportedRail.MONZO]: 'Monzo',
|
|
79
|
+
[SupportedRail.N26]: 'N26',
|
|
80
|
+
[SupportedRail.CHIME]: 'Chime',
|
|
81
|
+
};
|
|
82
|
+
export const PAYMENT_PLATFORM_LABELS = PAYMENT_PLATFORM_LABEL_ENTRIES;
|
|
83
|
+
export const FIAT_RAIL_DISPLAY_NAMES = PAYMENT_PLATFORM_LABEL_ENTRIES;
|
|
84
|
+
// Shared FE/BE minimum so queries too short for the pg_trgm trigram index are no-ops.
|
|
85
|
+
export const ORDER_SEARCH_MIN_QUERY_LENGTH = 3;
|
|
86
|
+
export function railKeysForQuery(query) {
|
|
87
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
88
|
+
if (normalizedQuery.length === 0) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
return Object.entries(PAYMENT_PLATFORM_LABELS)
|
|
92
|
+
.filter(([, label]) => label.toLowerCase().includes(normalizedQuery))
|
|
93
|
+
.map(([key]) => key);
|
|
94
|
+
}
|
|
95
|
+
const SUPPORTED_FIAT_RAILS = new Set(Object.keys(FIAT_RAIL_DISPLAY_NAMES));
|
|
96
|
+
export const RELAY_CHAIN_DISPLAY_NAMES = {
|
|
97
|
+
1: 'Ethereum',
|
|
98
|
+
10: 'Optimism',
|
|
99
|
+
56: 'BNB Smart Chain',
|
|
100
|
+
137: 'Polygon',
|
|
101
|
+
480: 'World Chain',
|
|
102
|
+
999: 'Hyperliquid',
|
|
103
|
+
8453: 'Base',
|
|
104
|
+
42161: 'Arbitrum',
|
|
105
|
+
8253038: 'Bitcoin',
|
|
106
|
+
728126428: 'Tron',
|
|
107
|
+
792703809: 'Solana',
|
|
108
|
+
};
|
|
109
|
+
const CRYPTO_METHOD_ALIASES = [
|
|
110
|
+
'crypto',
|
|
111
|
+
'usdc',
|
|
112
|
+
'usdt',
|
|
113
|
+
...Object.values(RELAY_CHAIN_DISPLAY_NAMES).map((name) => name.toLowerCase()),
|
|
114
|
+
];
|
|
115
|
+
/**
|
|
116
|
+
* Surfaces crypto-settled orders for token, chain, and "crypto" prefix queries.
|
|
117
|
+
*/
|
|
118
|
+
export function isCryptoMethodQuery(query) {
|
|
119
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
120
|
+
if (normalizedQuery.length === 0) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return CRYPTO_METHOD_ALIASES.some((alias) => alias.startsWith(normalizedQuery));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Normalizes a rail string into a crypto chain id.
|
|
127
|
+
*
|
|
128
|
+
* Supports both:
|
|
129
|
+
* - legacy numeric format: "8453"
|
|
130
|
+
* - canonical relay format: "relay_8453"
|
|
131
|
+
*/
|
|
132
|
+
export function parseCryptoRailChainId(rail) {
|
|
133
|
+
if (typeof rail !== 'string') {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const trimmed = rail.trim();
|
|
137
|
+
if (trimmed === '') {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const numericMatch = /^\d+$/.exec(trimmed);
|
|
141
|
+
if (numericMatch !== null) {
|
|
142
|
+
const parsed = Number(trimmed);
|
|
143
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
144
|
+
}
|
|
145
|
+
const prefixedMatch = /^relay_(\d+)$/i.exec(trimmed);
|
|
146
|
+
if (prefixedMatch === null) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const parsed = Number(prefixedMatch[1]);
|
|
150
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
151
|
+
}
|
|
152
|
+
export function isSupportedRail(rail) {
|
|
153
|
+
return SUPPORTED_RAIL_SET.has(rail);
|
|
154
|
+
}
|
|
155
|
+
export function isFiatSupportedRail(rail) {
|
|
156
|
+
return FIAT_SUPPORTED_RAILS.includes(rail);
|
|
157
|
+
}
|
|
158
|
+
/** Returns false for rails that remain recognized but are disabled at runtime. */
|
|
159
|
+
export function isRailRuntimeEnabled(rail) {
|
|
160
|
+
const normalizedFiatRail = normalizeFiatRail(rail);
|
|
161
|
+
return normalizedFiatRail === null || !runtimeDisabledFiatRails.has(normalizedFiatRail);
|
|
162
|
+
}
|
|
163
|
+
/** Filters out globally disabled runtime rails while preserving caller ordering. */
|
|
164
|
+
export function filterRuntimeEnabledRails(rails) {
|
|
165
|
+
return rails.filter((rail) => isRailRuntimeEnabled(rail));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Replaces the set of globally runtime-disabled fiat rails. Only EXACT base
|
|
169
|
+
* fiat rails are honored (e.g. `venmo`, `paypal`, `zelle`); rail variants
|
|
170
|
+
* (e.g. `zelle-chase`, or an env typo like `paypal-test`), crypto/relay rails,
|
|
171
|
+
* and unknown strings are ignored so a malformed env value cannot silently
|
|
172
|
+
* disable a live rail. Disable is base-granular: disabling `zelle` also
|
|
173
|
+
* disables its variants at check time (isRailRuntimeEnabled normalizes the
|
|
174
|
+
* checked rail to its base). Backend-only: the API applies the `DISABLED_RAILS`
|
|
175
|
+
* env at boot; the frontend never calls this, so its disabled set stays empty.
|
|
176
|
+
*/
|
|
177
|
+
export function setRuntimeDisabledFiatRails(rails) {
|
|
178
|
+
const next = new Set();
|
|
179
|
+
for (const rail of rails) {
|
|
180
|
+
if (typeof rail !== 'string') {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const normalized = rail.trim().toLowerCase();
|
|
184
|
+
if (isFiatSupportedRail(normalized)) {
|
|
185
|
+
next.add(normalized);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
runtimeDisabledFiatRails = next;
|
|
189
|
+
}
|
|
190
|
+
/** Returns the currently runtime-disabled fiat rails (canonical base rails). */
|
|
191
|
+
export function getRuntimeDisabledFiatRails() {
|
|
192
|
+
return [...runtimeDisabledFiatRails];
|
|
193
|
+
}
|
|
194
|
+
/** Returns true when the rail targets a crypto chain (legacy numeric or relay-prefixed format). */
|
|
195
|
+
export function isCryptoRail(rail) {
|
|
196
|
+
return parseCryptoRailChainId(rail) !== null;
|
|
197
|
+
}
|
|
198
|
+
/** Formats a chain id into the canonical relay rail string: `relay_<chainId>`. */
|
|
199
|
+
export function formatRelayRail(chainId) {
|
|
200
|
+
if (!Number.isSafeInteger(chainId) || chainId <= 0) {
|
|
201
|
+
throw new Error(`Invalid chain id for relay rail: ${chainId}`);
|
|
202
|
+
}
|
|
203
|
+
return `${RELAY_RAIL_PREFIX}${chainId}`;
|
|
204
|
+
}
|
|
205
|
+
/** Normalizes any crypto rail into canonical relay format (or null for non-crypto rails). */
|
|
206
|
+
export function normalizeCryptoRail(rail) {
|
|
207
|
+
const chainId = parseCryptoRailChainId(rail);
|
|
208
|
+
return chainId === null ? null : formatRelayRail(chainId);
|
|
209
|
+
}
|
|
210
|
+
/** Returns the canonical base fiat rail for base rails and supported variants such as `zelle-chase`. */
|
|
211
|
+
export function normalizeFiatRail(rail) {
|
|
212
|
+
if (typeof rail !== 'string') {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
const trimmed = rail.trim().toLowerCase();
|
|
216
|
+
if (trimmed === '') {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
if (SUPPORTED_FIAT_RAILS.has(trimmed)) {
|
|
220
|
+
return trimmed;
|
|
221
|
+
}
|
|
222
|
+
for (const baseRail of SUPPORTED_FIAT_RAILS) {
|
|
223
|
+
if (trimmed.startsWith(`${baseRail}-`)) {
|
|
224
|
+
return baseRail;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Fiat rails that support Seller Automated Release (SAR). Merchants in
|
|
231
|
+
* EXCLUSIVE_SAR mode may only offer these rails; every other fiat rail is
|
|
232
|
+
* force-disabled in the merchant and admin dashboards.
|
|
233
|
+
*/
|
|
234
|
+
export const SAR_SUPPORTED_FIAT_RAILS = ['venmo', 'cashapp', 'wise', 'paypal'];
|
|
235
|
+
const SAR_SUPPORTED_FIAT_RAIL_SET = new Set(SAR_SUPPORTED_FIAT_RAILS);
|
|
236
|
+
/** Returns true when the fiat rail (incl. variants like `zelle-chase`) supports SAR. */
|
|
237
|
+
export function isSarSupportedFiatRail(rail) {
|
|
238
|
+
const normalized = normalizeFiatRail(rail);
|
|
239
|
+
return normalized !== null && SAR_SUPPORTED_FIAT_RAIL_SET.has(normalized);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Returns `rails` with non-SAR fiat rails removed, preserving crypto/relay rails,
|
|
243
|
+
* SAR-supported fiat rails, and original ordering. Used to keep an EXCLUSIVE_SAR
|
|
244
|
+
* merchant's `enabledRails` in sync with their quote preference: non-SAR fiat
|
|
245
|
+
* rails (e.g. `zelle`, `revolut`, incl. variants like `zelle-chase`) are dropped
|
|
246
|
+
* while crypto rails and SAR rails (venmo/cashapp/wise/paypal) are kept.
|
|
247
|
+
*/
|
|
248
|
+
export function filterRailsForExclusiveSar(rails) {
|
|
249
|
+
return rails.filter((rail) => {
|
|
250
|
+
const normalizedFiat = normalizeFiatRail(rail);
|
|
251
|
+
// Non-fiat (crypto/relay) or unrecognized rails are preserved verbatim.
|
|
252
|
+
if (normalizedFiat === null) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
// Recognized fiat rail: keep only if it supports SAR.
|
|
256
|
+
return SAR_SUPPORTED_FIAT_RAIL_SET.has(normalizedFiat);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/** Canonical Zelle paymentMethodId -> attestation-service actionType suffix. */
|
|
260
|
+
export const ZELLE_METHOD_ACTION_SUFFIX = {
|
|
261
|
+
'zelle-bofa': 'bofa',
|
|
262
|
+
'zelle-chase': 'chase',
|
|
263
|
+
'zelle-citi': 'citi',
|
|
264
|
+
};
|
|
265
|
+
/** Strict create-boundary set for Zelle method ids and transitional variant rails. */
|
|
266
|
+
export const ZELLE_PAYMENT_METHOD_IDS = new Set(Object.keys(ZELLE_METHOD_ACTION_SUFFIX));
|
|
267
|
+
/** Bank-qualified buyer TEE action types accepted for generic Zelle proofs. */
|
|
268
|
+
export const ZELLE_BUYER_TEE_ACTION_TYPES = new Set(Object.values(ZELLE_METHOD_ACTION_SUFFIX).map((suffix) => `transfer_zelle_${suffix}`));
|
|
269
|
+
/**
|
|
270
|
+
* Resolves the attestation-service verifier route (`platform` + `actionType`)
|
|
271
|
+
* for a persisted `payment.rail` value.
|
|
272
|
+
*
|
|
273
|
+
* Zelle variants map to the new generic `zelle` platform with a bank-qualified
|
|
274
|
+
* actionType. Legacy bank-specific verifier routes are drain-only and should
|
|
275
|
+
* stay outside shared core. Non-Zelle fiat rails use the base rail as platform
|
|
276
|
+
* and `transfer_{rail}` as actionType. Unknown / crypto rails fall back to the
|
|
277
|
+
* raw trimmed rail so callers still surface a verifier mismatch via the
|
|
278
|
+
* attestation response rather than silently mis-routing.
|
|
279
|
+
*/
|
|
280
|
+
export function resolveAttestationRoute(rail) {
|
|
281
|
+
const trimmed = typeof rail === 'string' ? rail.trim().toLowerCase() : '';
|
|
282
|
+
// TRANSITIONAL: rail-variant fold, delete after drain window.
|
|
283
|
+
const zelleVariantSuffix = ZELLE_METHOD_ACTION_SUFFIX[trimmed];
|
|
284
|
+
if (zelleVariantSuffix !== undefined) {
|
|
285
|
+
return { platform: 'zelle', actionType: `transfer_zelle_${zelleVariantSuffix}` };
|
|
286
|
+
}
|
|
287
|
+
const baseRail = normalizeFiatRail(trimmed) ?? trimmed;
|
|
288
|
+
return { platform: baseRail, actionType: `transfer_${baseRail}` };
|
|
289
|
+
}
|
|
290
|
+
/** Canonical generic Zelle on-chain method hash: keccak256("zelle"), lowercase. */
|
|
291
|
+
export const GENERIC_ZELLE_PAYMENT_METHOD_HASH = '0xf752c7d19698ecb0bb8988abf9b9a53a4c3657f3bc8850a6fb59fdf3e3ce8cd3';
|
|
292
|
+
/** Case-insensitive check that a hash is the canonical generic Zelle on-chain method hash. */
|
|
293
|
+
export function isGenericZellePaymentMethodHash(hash) {
|
|
294
|
+
return typeof hash === 'string'
|
|
295
|
+
&& hash.trim().toLowerCase() === GENERIC_ZELLE_PAYMENT_METHOD_HASH;
|
|
296
|
+
}
|
|
297
|
+
/** True when the rail is Zelle (generic or a bank variant). */
|
|
298
|
+
export function isZelleRail(rail) {
|
|
299
|
+
return normalizeFiatRail(rail) === 'zelle';
|
|
300
|
+
}
|
|
301
|
+
/** Formats a rail value for human-readable UI labels. */
|
|
302
|
+
export function getRailDisplayName(rail) {
|
|
303
|
+
if (typeof rail !== 'string') {
|
|
304
|
+
return 'Unknown';
|
|
305
|
+
}
|
|
306
|
+
const trimmed = rail.trim();
|
|
307
|
+
if (trimmed === '') {
|
|
308
|
+
return 'Unknown';
|
|
309
|
+
}
|
|
310
|
+
const normalized = trimmed.toLowerCase();
|
|
311
|
+
if (isFiatSupportedRail(normalized)) {
|
|
312
|
+
return FIAT_RAIL_DISPLAY_NAMES[normalized];
|
|
313
|
+
}
|
|
314
|
+
const chainId = parseCryptoRailChainId(trimmed);
|
|
315
|
+
if (chainId !== null) {
|
|
316
|
+
const chainName = RELAY_CHAIN_DISPLAY_NAMES[chainId] ?? `Chain ${chainId}`;
|
|
317
|
+
return `Relay (${chainName})`;
|
|
318
|
+
}
|
|
319
|
+
return trimmed.toUpperCase();
|
|
320
|
+
}
|