@zkp2p/pay-shared 1.0.0 → 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/README.md CHANGED
@@ -9,7 +9,7 @@ Shared TypeScript types, enums, and chain utilities used by ZKP2P Pay API, SDK,
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- npm install @zkp2p/pay-shared@1.0.0
12
+ npm install @zkp2p/pay-shared@2.0.0
13
13
  ```
14
14
 
15
15
  ## Published artifacts
@@ -66,7 +66,7 @@ import {
66
66
 
67
67
  const chainId = parseSupportedChainSelection('polygon'); // 137
68
68
  const usdc = resolveDestinationTokenAddress('USDC', 137); // Chain-specific USDC address
69
- const supportedAliases = getSupportedDestinationTokenAliasesForChain(BASE_CHAIN_ID); // ["USDC"]
69
+ const supportedAliases = getSupportedDestinationTokenAliasesForChain(BASE_CHAIN_ID); // ["USDC", "USDT"]
70
70
 
71
71
  console.log(getSupportedChainIds(), chainId, usdc, supportedAliases);
72
72
  ```
@@ -0,0 +1,4 @@
1
+ export declare const ProofMode: {
2
+ readonly BUYER_TEE: "buyerTee";
3
+ };
4
+ export type ProofMode = typeof ProofMode[keyof typeof ProofMode];
@@ -0,0 +1,3 @@
1
+ export const ProofMode = {
2
+ BUYER_TEE: 'buyerTee',
3
+ };
package/dist/chains.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * Import this module instead of duplicating chain configs in each app.
6
6
  */
7
7
  export declare const BASE_CHAIN_ID = 8453;
8
+ export declare const ETHEREUM_CHAIN_ID = 1;
8
9
  export declare const HYPEREVM_CHAIN_ID = 999;
9
10
  export declare const SOLANA_CHAIN_ID = 792703809;
10
11
  export declare const SUPPORTED_DESTINATION_TOKEN_ALIASES: readonly ["USDC", "USDT"];
package/dist/chains.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  // Chain IDs
8
8
  export const BASE_CHAIN_ID = 8453;
9
+ export const ETHEREUM_CHAIN_ID = 1;
9
10
  export const HYPEREVM_CHAIN_ID = 999;
10
11
  export const SOLANA_CHAIN_ID = 792703809;
11
12
  export const SUPPORTED_DESTINATION_TOKEN_ALIASES = ['USDC', 'USDT'];
@@ -135,6 +136,10 @@ for (const chainId of SUPPORTED_CHAIN_IDS) {
135
136
  }
136
137
  }
137
138
  const DESTINATION_TOKEN_ADDRESSES = {
139
+ // Base
140
+ 8453: {
141
+ usdt: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',
142
+ },
138
143
  // Ethereum
139
144
  1: {
140
145
  usdt: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
package/dist/crypto.d.ts CHANGED
@@ -4,6 +4,12 @@ export declare const PAY_CRYPTO_TOKEN_SYMBOLS: readonly ["BTC", "USDC", "USDT",
4
4
  export type PayCryptoTokenSymbol = (typeof PAY_CRYPTO_TOKEN_SYMBOLS)[number];
5
5
  export declare const DEFAULT_ENABLED_PAY_CRYPTO_TOKENS: PayCryptoTokenSymbol[];
6
6
  export declare const DEFAULT_ENABLED_DESTINATION_TOKENS: PayCryptoTokenSymbol[];
7
+ /**
8
+ * Display-order priority for the "Pay with crypto" chain dropdown.
9
+ * Chains not in this list fall back to the existing numeric chain-ID
10
+ * iteration order of PAY_CRYPTO_CHAIN_CONFIGS as a stable tiebreaker.
11
+ */
12
+ export declare const PAY_CRYPTO_CHAIN_DISPLAY_PRIORITY: readonly number[];
7
13
  export declare const STABLECOIN_SYMBOLS: Set<"USDC" | "USDT" | "ETH" | "BNB" | "SOL" | "BTC" | "PYUSD" | "HYPE" | "WBTC" | "USDH">;
8
14
  export interface SupportedCryptoTokenConfig {
9
15
  chainId: number;
@@ -18,6 +24,31 @@ export declare function normalizePayCryptoTokenSymbol(value: string | null | und
18
24
  export declare function isStablecoinSymbol(symbol: string | null | undefined): boolean;
19
25
  export declare function getSupportedPayCryptoTokenConfigsForChain(chainId: number, enabledSymbols?: readonly string[] | null): SupportedCryptoTokenConfig[];
20
26
  export declare function getSupportedPayCryptoChainIds(enabledSymbols?: readonly string[] | null): number[];
27
+ /**
28
+ * Returns the union of tokens supported by any of `enabledChainIds`,
29
+ * sorted by the canonical pay-crypto display order
30
+ * (DEFAULT_ENABLED_PAY_CRYPTO_TOKENS order).
31
+ *
32
+ * When `enabledChainIds` is undefined, uses every supported chain.
33
+ * When `enabledChainIds` is an empty array, returns [].
34
+ */
35
+ export declare function getAvailablePayCryptoTokens(enabledChainIds?: readonly number[]): PayCryptoTokenSymbol[];
36
+ /**
37
+ * Returns the chain IDs that support `symbol`, optionally filtered
38
+ * to `enabledChainIds`, sorted by PAY_CRYPTO_CHAIN_DISPLAY_PRIORITY
39
+ * with ascending numeric chain-ID order as the stable tiebreaker.
40
+ *
41
+ * When `enabledChainIds` is undefined, uses every supported chain.
42
+ * When `enabledChainIds` is an empty array, returns [].
43
+ */
44
+ export declare function getSupportedPayCryptoChainIdsForToken(symbol: PayCryptoTokenSymbol, enabledChainIds?: readonly number[]): number[];
45
+ /**
46
+ * Returns the display name for a chain used by the "Pay with crypto"
47
+ * flow. Resolves through PAY_CRYPTO_CHAIN_CONFIGS first so Bitcoin
48
+ * and Tron (which are not in MAINNET_CHAINS) get the correct label.
49
+ * Falls back to getChainName for chains not registered here.
50
+ */
51
+ export declare function getPayCryptoChainName(chainId: number): string;
21
52
  export declare function resolvePayCryptoTokenConfig(chainId: number, symbol: string | null | undefined): SupportedCryptoTokenConfig | null;
22
53
  /**
23
54
  * Reverse lookup of a token config by contract address on a specific chain.
package/dist/crypto.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MAINNET_CHAINS, } from './chains.js';
1
+ import { MAINNET_CHAINS, BASE_CHAIN_ID, ETHEREUM_CHAIN_ID, SOLANA_CHAIN_ID, getChainName, } from './chains.js';
2
2
  export const BITCOIN_CHAIN_ID = 8253038;
3
3
  export const TRON_CHAIN_ID = 728126428;
4
4
  export const PAY_CRYPTO_TOKEN_SYMBOLS = [
@@ -15,9 +15,9 @@ export const PAY_CRYPTO_TOKEN_SYMBOLS = [
15
15
  ];
16
16
  export const DEFAULT_ENABLED_PAY_CRYPTO_TOKENS = [
17
17
  'BTC',
18
- 'USDC',
19
- 'USDT',
20
18
  'ETH',
19
+ 'USDT',
20
+ 'USDC',
21
21
  'PYUSD',
22
22
  'SOL',
23
23
  'BNB',
@@ -29,6 +29,17 @@ export const DEFAULT_ENABLED_DESTINATION_TOKENS = [
29
29
  'USDC',
30
30
  'USDT',
31
31
  ];
32
+ /**
33
+ * Display-order priority for the "Pay with crypto" chain dropdown.
34
+ * Chains not in this list fall back to the existing numeric chain-ID
35
+ * iteration order of PAY_CRYPTO_CHAIN_CONFIGS as a stable tiebreaker.
36
+ */
37
+ export const PAY_CRYPTO_CHAIN_DISPLAY_PRIORITY = [
38
+ BITCOIN_CHAIN_ID,
39
+ ETHEREUM_CHAIN_ID,
40
+ SOLANA_CHAIN_ID,
41
+ BASE_CHAIN_ID,
42
+ ];
32
43
  export const STABLECOIN_SYMBOLS = new Set([
33
44
  'USDC',
34
45
  'USDT',
@@ -160,6 +171,60 @@ export function getSupportedPayCryptoChainIds(enabledSymbols) {
160
171
  .map(Number)
161
172
  .filter((chainId) => getSupportedPayCryptoTokenConfigsForChain(chainId, enabledSymbols).length > 0);
162
173
  }
174
+ /**
175
+ * Returns the union of tokens supported by any of `enabledChainIds`,
176
+ * sorted by the canonical pay-crypto display order
177
+ * (DEFAULT_ENABLED_PAY_CRYPTO_TOKENS order).
178
+ *
179
+ * When `enabledChainIds` is undefined, uses every supported chain.
180
+ * When `enabledChainIds` is an empty array, returns [].
181
+ */
182
+ export function getAvailablePayCryptoTokens(enabledChainIds) {
183
+ const chainIds = enabledChainIds === undefined
184
+ ? Object.keys(CHAIN_TOKEN_CONFIGS).map(Number)
185
+ : enabledChainIds;
186
+ const available = new Set();
187
+ for (const chainId of chainIds) {
188
+ const chainTokens = CHAIN_TOKEN_CONFIGS[chainId];
189
+ if (chainTokens === undefined)
190
+ continue;
191
+ for (const symbol of Object.keys(chainTokens)) {
192
+ available.add(symbol);
193
+ }
194
+ }
195
+ return DEFAULT_ENABLED_PAY_CRYPTO_TOKENS.filter((symbol) => available.has(symbol));
196
+ }
197
+ /**
198
+ * Returns the chain IDs that support `symbol`, optionally filtered
199
+ * to `enabledChainIds`, sorted by PAY_CRYPTO_CHAIN_DISPLAY_PRIORITY
200
+ * with ascending numeric chain-ID order as the stable tiebreaker.
201
+ *
202
+ * When `enabledChainIds` is undefined, uses every supported chain.
203
+ * When `enabledChainIds` is an empty array, returns [].
204
+ */
205
+ export function getSupportedPayCryptoChainIdsForToken(symbol, enabledChainIds) {
206
+ const candidateChainIds = enabledChainIds === undefined
207
+ ? Object.keys(CHAIN_TOKEN_CONFIGS).map(Number)
208
+ : Array.from(new Set(enabledChainIds));
209
+ const supporting = candidateChainIds.filter((chainId) => (CHAIN_TOKEN_CONFIGS[chainId]?.[symbol] !== undefined));
210
+ const supportingSet = new Set(supporting);
211
+ const priorityPortion = PAY_CRYPTO_CHAIN_DISPLAY_PRIORITY
212
+ .filter((chainId) => supportingSet.has(chainId));
213
+ const prioritySet = new Set(PAY_CRYPTO_CHAIN_DISPLAY_PRIORITY);
214
+ const restPortion = supporting
215
+ .filter((chainId) => !prioritySet.has(chainId))
216
+ .sort((a, b) => a - b);
217
+ return [...priorityPortion, ...restPortion];
218
+ }
219
+ /**
220
+ * Returns the display name for a chain used by the "Pay with crypto"
221
+ * flow. Resolves through PAY_CRYPTO_CHAIN_CONFIGS first so Bitcoin
222
+ * and Tron (which are not in MAINNET_CHAINS) get the correct label.
223
+ * Falls back to getChainName for chains not registered here.
224
+ */
225
+ export function getPayCryptoChainName(chainId) {
226
+ return PAY_CRYPTO_CHAIN_CONFIGS[chainId]?.name ?? getChainName(chainId);
227
+ }
163
228
  export function resolvePayCryptoTokenConfig(chainId, symbol) {
164
229
  const normalizedSymbol = normalizePayCryptoTokenSymbol(symbol);
165
230
  if (!normalizedSymbol) {
@@ -0,0 +1,19 @@
1
+ export interface DisplayedFiatAmountInput {
2
+ readonly feePayer: 'MERCHANT' | 'PAYEE';
3
+ readonly signalIntentAmountBaseUnits: string | null;
4
+ readonly conversionRateDecimal: string | null;
5
+ readonly remainingUsdcAmount: string;
6
+ readonly currencyPerUsdRate: string;
7
+ readonly paymentAmountFallback: string;
8
+ }
9
+ /**
10
+ * The exact fiat amount the buyer is instructed to pay, as a decimal string.
11
+ * BYTE-IDENTICAL to checkoutData.ts mapCheckoutPaymentToQuote (lines 336-351):
12
+ * quotedFiatAmount (PAYEE ceil-cents, else null)
13
+ * ?? (validRemaining&Rate ? (remaining*rate).toFixed(2) : paymentAmount)
14
+ * Always returns a string; the API caller passes it to fiatMinorUnitsFromDisplayedAmount,
15
+ * which returns null for an unusable "0"/malformed value so the API fails SAR closed.
16
+ */
17
+ export declare function computeDisplayedFiatAmount(input: DisplayedFiatAmountInput): string;
18
+ /** Parse a 2-decimal (or fewer) positive decimal string to integer minor units. Null if non-positive/malformed/>2dp. */
19
+ export declare function fiatMinorUnitsFromDisplayedAmount(displayed: string): bigint | null;
@@ -0,0 +1,58 @@
1
+ const USDC_SCALE = 1000000n;
2
+ const RATE_SCALE = 10n ** 18n;
3
+ const CENT_SCALE = 100n;
4
+ const QUOTE_DENOMINATOR = USDC_SCALE * RATE_SCALE; // 1e6 * 1e18
5
+ function parseDecimalToScaledUnits(value, decimals) {
6
+ const normalized = value.trim();
7
+ if (!/^\d+(\.\d+)?$/.test(normalized))
8
+ return null;
9
+ const [whole, fraction = ''] = normalized.split('.');
10
+ const base = 10n ** BigInt(decimals);
11
+ const scaledFraction = fraction.slice(0, decimals).padEnd(decimals, '0');
12
+ return BigInt(whole) * base + BigInt(scaledFraction);
13
+ }
14
+ function ceilCentsFromSignalIntent(amountBaseUnits, conversionRateDecimal) {
15
+ if (!/^\d+$/.test(amountBaseUnits))
16
+ return null;
17
+ const units = BigInt(amountBaseUnits);
18
+ const rate = parseDecimalToScaledUnits(conversionRateDecimal, 18);
19
+ if (rate === null || rate <= 0n || units <= 0n)
20
+ return null;
21
+ const numerator = units * rate * CENT_SCALE;
22
+ return (numerator + QUOTE_DENOMINATOR - 1n) / QUOTE_DENOMINATOR; // ceil
23
+ }
24
+ function centsToDecimalString(cents) {
25
+ const whole = cents / CENT_SCALE;
26
+ const fraction = (cents % CENT_SCALE).toString().padStart(2, '0');
27
+ return `${whole.toString()}.${fraction}`;
28
+ }
29
+ /**
30
+ * The exact fiat amount the buyer is instructed to pay, as a decimal string.
31
+ * BYTE-IDENTICAL to checkoutData.ts mapCheckoutPaymentToQuote (lines 336-351):
32
+ * quotedFiatAmount (PAYEE ceil-cents, else null)
33
+ * ?? (validRemaining&Rate ? (remaining*rate).toFixed(2) : paymentAmount)
34
+ * Always returns a string; the API caller passes it to fiatMinorUnitsFromDisplayedAmount,
35
+ * which returns null for an unusable "0"/malformed value so the API fails SAR closed.
36
+ */
37
+ export function computeDisplayedFiatAmount(input) {
38
+ const quotedCents = input.feePayer === 'PAYEE' && input.signalIntentAmountBaseUnits !== null && input.conversionRateDecimal !== null
39
+ ? ceilCentsFromSignalIntent(input.signalIntentAmountBaseUnits, input.conversionRateDecimal)
40
+ : null;
41
+ if (quotedCents !== null)
42
+ return centsToDecimalString(quotedCents);
43
+ const remainingUsdc = Number(input.remainingUsdcAmount);
44
+ const rate = Number(input.currencyPerUsdRate);
45
+ if (Number.isFinite(remainingUsdc) && Number.isFinite(rate) && remainingUsdc > 0 && rate > 0) {
46
+ return (remainingUsdc * rate).toFixed(2);
47
+ }
48
+ return input.paymentAmountFallback;
49
+ }
50
+ /** Parse a 2-decimal (or fewer) positive decimal string to integer minor units. Null if non-positive/malformed/>2dp. */
51
+ export function fiatMinorUnitsFromDisplayedAmount(displayed) {
52
+ const normalized = displayed.trim();
53
+ if (!/^\d+(\.\d{1,2})?$/.test(normalized))
54
+ return null;
55
+ const [whole, fraction = ''] = normalized.split('.');
56
+ const cents = BigInt(whole) * CENT_SCALE + BigInt(fraction.padEnd(2, '0'));
57
+ return cents > 0n ? cents : null;
58
+ }
package/dist/index.d.ts CHANGED
@@ -3,4 +3,8 @@ export * from './chains.js';
3
3
  export * from './fees.js';
4
4
  export * from './crypto.js';
5
5
  export * from './rails.js';
6
+ export * from './buyerTee.js';
6
7
  export * from './bitcoin.js';
8
+ export * from './fiatAmount.js';
9
+ export * from './paypalSarPhase.js';
10
+ export * from './paypalSarVerify.js';
package/dist/index.js CHANGED
@@ -3,4 +3,8 @@ export * from './chains.js';
3
3
  export * from './fees.js';
4
4
  export * from './crypto.js';
5
5
  export * from './rails.js';
6
+ export * from './buyerTee.js';
6
7
  export * from './bitcoin.js';
8
+ export * from './fiatAmount.js';
9
+ export * from './paypalSarPhase.js';
10
+ export * from './paypalSarVerify.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Whether a name-mode SAR verify poll outcome is worth retrying. Only a "no receipt found yet" (404)
3
+ * may resolve on a later poll; every other terminal status is deterministic and must fail fast.
4
+ */
5
+ export declare function isRetryableSarPollStatus(statusCode: number): boolean;
@@ -0,0 +1,8 @@
1
+ const NO_RECEIPT_STATUS = 404; // SELLER_TXID_NOT_FOUND — the only status worth re-polling
2
+ /**
3
+ * Whether a name-mode SAR verify poll outcome is worth retrying. Only a "no receipt found yet" (404)
4
+ * may resolve on a later poll; every other terminal status is deterministic and must fail fast.
5
+ */
6
+ export function isRetryableSarPollStatus(statusCode) {
7
+ return statusCode === NO_RECEIPT_STATUS;
8
+ }
@@ -0,0 +1,41 @@
1
+ export type SarAttemptOutcome = {
2
+ kind: 'success';
3
+ proofAttemptId: string;
4
+ } | {
5
+ kind: 'unavailable';
6
+ } | {
7
+ kind: 'error';
8
+ errorCode: string;
9
+ errorMessage: string;
10
+ statusCode: number;
11
+ };
12
+ export type SarVerifyResult = {
13
+ kind: 'success';
14
+ proofAttemptId: string;
15
+ } | {
16
+ kind: 'unavailable';
17
+ } | {
18
+ kind: 'error';
19
+ errorCode: string;
20
+ errorMessage: string;
21
+ } | {
22
+ kind: 'aborted';
23
+ };
24
+ export interface RunPaypalSarNameVerifyDeps {
25
+ /** POST a name-mode attempt. attemptNumber is 1-based and increments per name poll. */
26
+ submitNameAttempt: (attemptNumber: number) => Promise<SarAttemptOutcome>;
27
+ sleep: (ms: number) => Promise<void>;
28
+ /** Re-checked after every await; returning false short-circuits to { kind: 'aborted' }. */
29
+ isMounted: () => boolean;
30
+ now: () => number;
31
+ retryIntervalMs: number;
32
+ retryDurationMs: number;
33
+ }
34
+ /**
35
+ * Name-only PayPal SAR verification.
36
+ * Polls name-mode verify (retrying only on retryable poll statuses until the deadline). Terminal
37
+ * name failures surface directly. Success / unavailable short-circuit. The caller maps the returned
38
+ * result to its own UI side effects; 'aborted' means the component unmounted and the caller should
39
+ * do nothing.
40
+ */
41
+ export declare function runPaypalSarNameVerify(deps: RunPaypalSarNameVerifyDeps): Promise<SarVerifyResult>;
@@ -0,0 +1,31 @@
1
+ import { isRetryableSarPollStatus } from './paypalSarPhase.js';
2
+ /**
3
+ * Name-only PayPal SAR verification.
4
+ * Polls name-mode verify (retrying only on retryable poll statuses until the deadline). Terminal
5
+ * name failures surface directly. Success / unavailable short-circuit. The caller maps the returned
6
+ * result to its own UI side effects; 'aborted' means the component unmounted and the caller should
7
+ * do nothing.
8
+ */
9
+ export async function runPaypalSarNameVerify(deps) {
10
+ const { submitNameAttempt, sleep, isMounted, now, retryIntervalMs, retryDurationMs } = deps;
11
+ const deadline = now() + retryDurationMs;
12
+ let attemptNumber = 1;
13
+ while (true) {
14
+ const outcome = await submitNameAttempt(attemptNumber);
15
+ if (!isMounted())
16
+ return { kind: 'aborted' };
17
+ if (outcome.kind === 'success')
18
+ return { kind: 'success', proofAttemptId: outcome.proofAttemptId };
19
+ if (outcome.kind === 'unavailable')
20
+ return { kind: 'unavailable' };
21
+ const deadlineReached = now() + retryIntervalMs > deadline;
22
+ if (isRetryableSarPollStatus(outcome.statusCode) && !deadlineReached) {
23
+ await sleep(retryIntervalMs);
24
+ if (!isMounted())
25
+ return { kind: 'aborted' };
26
+ attemptNumber += 1;
27
+ continue;
28
+ }
29
+ return { kind: 'error', errorCode: outcome.errorCode, errorMessage: outcome.errorMessage };
30
+ }
31
+ }
package/dist/rails.d.ts CHANGED
@@ -42,6 +42,17 @@ export declare enum SupportedRelayRail {
42
42
  export declare const DEFAULT_FIAT_RAILS: readonly SupportedRail[];
43
43
  export declare const SUPPORTED_RAILS: readonly SupportedRail[];
44
44
  export declare const SUPPORTED_RELAY_RAILS: readonly SupportedRelayRail[];
45
+ export declare const FIAT_SUPPORTED_RAILS: readonly ["venmo", "cashapp", "revolut", "wise", "zelle", "paypal", "monzo", "n26", "chime"];
46
+ export type FiatSupportedRail = typeof FIAT_SUPPORTED_RAILS[number];
47
+ export declare const PAYMENT_PLATFORM_LABELS: Record<string, string>;
48
+ export declare const FIAT_RAIL_DISPLAY_NAMES: Record<FiatSupportedRail, string>;
49
+ export declare const ORDER_SEARCH_MIN_QUERY_LENGTH = 3;
50
+ export declare function railKeysForQuery(query: string): string[];
51
+ export declare const RELAY_CHAIN_DISPLAY_NAMES: Record<number, string>;
52
+ /**
53
+ * Surfaces crypto-settled orders for token, chain, and "crypto" prefix queries.
54
+ */
55
+ export declare function isCryptoMethodQuery(query: string): boolean;
45
56
  /**
46
57
  * Normalizes a rail string into a crypto chain id.
47
58
  *
@@ -51,11 +62,76 @@ export declare const SUPPORTED_RELAY_RAILS: readonly SupportedRelayRail[];
51
62
  */
52
63
  export declare function parseCryptoRailChainId(rail: string | null | undefined): number | null;
53
64
  export declare function isSupportedRail(rail: string): rail is SupportedRail;
65
+ export declare function isFiatSupportedRail(rail: string): rail is FiatSupportedRail;
66
+ /** Returns false for rails that remain recognized but are disabled at runtime. */
67
+ export declare function isRailRuntimeEnabled(rail: string | null | undefined): boolean;
68
+ /** Filters out globally disabled runtime rails while preserving caller ordering. */
69
+ export declare function filterRuntimeEnabledRails<T extends string>(rails: readonly T[]): T[];
70
+ /**
71
+ * Replaces the set of globally runtime-disabled fiat rails. Only EXACT base
72
+ * fiat rails are honored (e.g. `venmo`, `paypal`, `zelle`); rail variants
73
+ * (e.g. `zelle-chase`, or an env typo like `paypal-test`), crypto/relay rails,
74
+ * and unknown strings are ignored so a malformed env value cannot silently
75
+ * disable a live rail. Disable is base-granular: disabling `zelle` also
76
+ * disables its variants at check time (isRailRuntimeEnabled normalizes the
77
+ * checked rail to its base). Backend-only: the API applies the `DISABLED_RAILS`
78
+ * env at boot; the frontend never calls this, so its disabled set stays empty.
79
+ */
80
+ export declare function setRuntimeDisabledFiatRails(rails: readonly string[]): void;
81
+ /** Returns the currently runtime-disabled fiat rails (canonical base rails). */
82
+ export declare function getRuntimeDisabledFiatRails(): string[];
54
83
  /** Returns true when the rail targets a crypto chain (legacy numeric or relay-prefixed format). */
55
84
  export declare function isCryptoRail(rail: string | null | undefined): boolean;
56
85
  /** Formats a chain id into the canonical relay rail string: `relay_<chainId>`. */
57
86
  export declare function formatRelayRail(chainId: number): string;
58
87
  /** Normalizes any crypto rail into canonical relay format (or null for non-crypto rails). */
59
88
  export declare function normalizeCryptoRail(rail: string | null | undefined): string | null;
89
+ /** Returns the canonical base fiat rail for base rails and supported variants such as `zelle-chase`. */
90
+ export declare function normalizeFiatRail(rail: string | null | undefined): string | null;
91
+ /**
92
+ * Fiat rails that support Seller Automated Release (SAR). Merchants in
93
+ * EXCLUSIVE_SAR mode may only offer these rails; every other fiat rail is
94
+ * force-disabled in the merchant and admin dashboards.
95
+ */
96
+ export declare const SAR_SUPPORTED_FIAT_RAILS: readonly ["venmo", "cashapp", "wise", "paypal"];
97
+ export type SarSupportedFiatRail = typeof SAR_SUPPORTED_FIAT_RAILS[number];
98
+ /** Returns true when the fiat rail (incl. variants like `zelle-chase`) supports SAR. */
99
+ export declare function isSarSupportedFiatRail(rail: string | null | undefined): boolean;
100
+ /**
101
+ * Returns `rails` with non-SAR fiat rails removed, preserving crypto/relay rails,
102
+ * SAR-supported fiat rails, and original ordering. Used to keep an EXCLUSIVE_SAR
103
+ * merchant's `enabledRails` in sync with their quote preference: non-SAR fiat
104
+ * rails (e.g. `zelle`, `revolut`, incl. variants like `zelle-chase`) are dropped
105
+ * while crypto rails and SAR rails (venmo/cashapp/wise/paypal) are kept.
106
+ */
107
+ export declare function filterRailsForExclusiveSar(rails: readonly string[]): string[];
108
+ /** Canonical Zelle paymentMethodId -> attestation-service actionType suffix. */
109
+ export declare const ZELLE_METHOD_ACTION_SUFFIX: Record<string, string>;
110
+ /** Strict create-boundary set for Zelle method ids and transitional variant rails. */
111
+ export declare const ZELLE_PAYMENT_METHOD_IDS: ReadonlySet<string>;
112
+ /** Bank-qualified buyer TEE action types accepted for generic Zelle proofs. */
113
+ export declare const ZELLE_BUYER_TEE_ACTION_TYPES: ReadonlySet<string>;
114
+ export type AttestationRoute = {
115
+ readonly platform: string;
116
+ readonly actionType: string;
117
+ };
118
+ /**
119
+ * Resolves the attestation-service verifier route (`platform` + `actionType`)
120
+ * for a persisted `payment.rail` value.
121
+ *
122
+ * Zelle variants map to the new generic `zelle` platform with a bank-qualified
123
+ * actionType. Legacy bank-specific verifier routes are drain-only and should
124
+ * stay outside shared core. Non-Zelle fiat rails use the base rail as platform
125
+ * and `transfer_{rail}` as actionType. Unknown / crypto rails fall back to the
126
+ * raw trimmed rail so callers still surface a verifier mismatch via the
127
+ * attestation response rather than silently mis-routing.
128
+ */
129
+ export declare function resolveAttestationRoute(rail: string | null | undefined): AttestationRoute;
130
+ /** Canonical generic Zelle on-chain method hash: keccak256("zelle"), lowercase. */
131
+ export declare const GENERIC_ZELLE_PAYMENT_METHOD_HASH = "0xf752c7d19698ecb0bb8988abf9b9a53a4c3657f3bc8850a6fb59fdf3e3ce8cd3";
132
+ /** Case-insensitive check that a hash is the canonical generic Zelle on-chain method hash. */
133
+ export declare function isGenericZellePaymentMethodHash(hash: string | null | undefined): boolean;
134
+ /** True when the rail is Zelle (generic or a bank variant). */
135
+ export declare function isZelleRail(rail: string | null | undefined): boolean;
60
136
  /** Formats a rail value for human-readable UI labels. */
61
137
  export declare function getRailDisplayName(rail: string | null | undefined): string;
package/dist/rails.js CHANGED
@@ -51,18 +51,49 @@ export const DEFAULT_FIAT_RAILS = [
51
51
  export const SUPPORTED_RAILS = Object.values(SupportedRail);
52
52
  export const SUPPORTED_RELAY_RAILS = Object.values(SupportedRelayRail);
53
53
  const SUPPORTED_RAIL_SET = new Set(SUPPORTED_RAILS);
54
- const FIAT_RAIL_DISPLAY_NAMES = {
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 = {
55
72
  [SupportedRail.VENMO]: 'Venmo',
56
73
  [SupportedRail.CASHAPP]: 'Cash App',
57
74
  [SupportedRail.REVOLUT]: 'Revolut',
75
+ [SupportedRail.WISE]: 'Wise',
58
76
  [SupportedRail.ZELLE]: 'Zelle',
59
77
  [SupportedRail.PAYPAL]: 'PayPal',
60
- [SupportedRail.WISE]: 'Wise',
61
78
  [SupportedRail.MONZO]: 'Monzo',
62
79
  [SupportedRail.N26]: 'N26',
63
80
  [SupportedRail.CHIME]: 'Chime',
64
81
  };
65
- const RELAY_CHAIN_DISPLAY_NAMES = {
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 = {
66
97
  1: 'Ethereum',
67
98
  10: 'Optimism',
68
99
  56: 'BNB Smart Chain',
@@ -75,6 +106,22 @@ const RELAY_CHAIN_DISPLAY_NAMES = {
75
106
  728126428: 'Tron',
76
107
  792703809: 'Solana',
77
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
+ }
78
125
  /**
79
126
  * Normalizes a rail string into a crypto chain id.
80
127
  *
@@ -105,6 +152,45 @@ export function parseCryptoRailChainId(rail) {
105
152
  export function isSupportedRail(rail) {
106
153
  return SUPPORTED_RAIL_SET.has(rail);
107
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
+ }
108
194
  /** Returns true when the rail targets a crypto chain (legacy numeric or relay-prefixed format). */
109
195
  export function isCryptoRail(rail) {
110
196
  return parseCryptoRailChainId(rail) !== null;
@@ -121,6 +207,97 @@ export function normalizeCryptoRail(rail) {
121
207
  const chainId = parseCryptoRailChainId(rail);
122
208
  return chainId === null ? null : formatRelayRail(chainId);
123
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
+ }
124
301
  /** Formats a rail value for human-readable UI labels. */
125
302
  export function getRailDisplayName(rail) {
126
303
  if (typeof rail !== 'string') {
@@ -131,9 +308,8 @@ export function getRailDisplayName(rail) {
131
308
  return 'Unknown';
132
309
  }
133
310
  const normalized = trimmed.toLowerCase();
134
- const fiatDisplayName = FIAT_RAIL_DISPLAY_NAMES[normalized];
135
- if (fiatDisplayName) {
136
- return fiatDisplayName;
311
+ if (isFiatSupportedRail(normalized)) {
312
+ return FIAT_RAIL_DISPLAY_NAMES[normalized];
137
313
  }
138
314
  const chainId = parseCryptoRailChainId(trimmed);
139
315
  if (chainId !== null) {
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { PayCryptoTokenSymbol } from './crypto.js';
2
+ import type { ProofMode } from './buyerTee.js';
2
3
  export declare const PaymentPlatform: {
3
4
  readonly VENMO: "venmo";
4
5
  readonly CASHAPP: "cashapp";
@@ -11,6 +12,11 @@ export declare const PaymentPlatform: {
11
12
  readonly CHIME: "chime";
12
13
  };
13
14
  export type PaymentPlatformType = typeof PaymentPlatform[keyof typeof PaymentPlatform];
15
+ /**
16
+ * Type guard for PaymentPlatform values.
17
+ * Returns true if `value` is one of the known PaymentPlatform enum values.
18
+ */
19
+ export declare function isPaymentPlatform(value: unknown): value is PaymentPlatformType;
14
20
  export declare const SupportedChains: Record<number, string>;
15
21
  export type SupportedChainId = keyof typeof SupportedChains;
16
22
  export declare const CheckoutMode: {
@@ -57,6 +63,13 @@ export declare const FeePayer: {
57
63
  readonly PAYEE: "PAYEE";
58
64
  };
59
65
  export type FeePayerType = typeof FeePayer[keyof typeof FeePayer];
66
+ export declare const MerchantPaymentFlowMode: {
67
+ readonly EXCLUSIVE_SAR: "EXCLUSIVE_SAR";
68
+ readonly PREFER_SAR: "PREFER_SAR";
69
+ readonly PREFER_BUYER_TEE: "PREFER_BUYER_TEE";
70
+ readonly EXCLUSIVE_BUYER_TEE: "EXCLUSIVE_BUYER_TEE";
71
+ };
72
+ export type MerchantPaymentFlowModeType = typeof MerchantPaymentFlowMode[keyof typeof MerchantPaymentFlowMode];
60
73
  export declare const CheckoutOrderStatus: {
61
74
  readonly CREATED: "CREATED";
62
75
  readonly PARTIALLY_FULFILLED: "PARTIALLY_FULFILLED";
@@ -64,6 +77,12 @@ export declare const CheckoutOrderStatus: {
64
77
  readonly CANCELLED: "CANCELLED";
65
78
  };
66
79
  export type CheckoutOrderStatusType = typeof CheckoutOrderStatus[keyof typeof CheckoutOrderStatus];
80
+ export declare const CheckoutOrderRefundStatus: {
81
+ readonly NONE: "NONE";
82
+ readonly PENDING: "PENDING";
83
+ readonly COMPLETED: "COMPLETED";
84
+ };
85
+ export type CheckoutOrderRefundStatusType = typeof CheckoutOrderRefundStatus[keyof typeof CheckoutOrderRefundStatus];
67
86
  export declare const CheckoutPaymentStatus: {
68
87
  readonly CREATED: "CREATED";
69
88
  readonly SETTLED: "SETTLED";
@@ -72,6 +91,12 @@ export declare const CheckoutPaymentStatus: {
72
91
  readonly FAILED: "FAILED";
73
92
  };
74
93
  export type CheckoutPaymentStatusType = typeof CheckoutPaymentStatus[keyof typeof CheckoutPaymentStatus];
94
+ export declare const CheckoutPaymentMemoPolicy: {
95
+ readonly NONE: "NONE";
96
+ readonly REQUIRED: "REQUIRED";
97
+ readonly EMPTY: "EMPTY";
98
+ };
99
+ export type CheckoutPaymentMemoPolicyType = typeof CheckoutPaymentMemoPolicy[keyof typeof CheckoutPaymentMemoPolicy];
75
100
  export type ReferralFeeEntry = {
76
101
  recipient: string;
77
102
  feeBps: number;
@@ -125,11 +150,19 @@ export type CheckoutMerchant = {
125
150
  disableBranding: boolean;
126
151
  checkoutTheme: MerchantCheckoutTheme | null;
127
152
  defaultPaymentCurrency?: string | null;
153
+ /** Whether the merchant has been verified by ZKP2P. */
154
+ verified: boolean;
128
155
  };
129
156
  export type CheckoutOrder = {
130
157
  id: string;
131
158
  merchantId: string;
132
159
  status: CheckoutOrderStatusType;
160
+ refundStatus: CheckoutOrderRefundStatusType;
161
+ refundAmountUsdc: string | null;
162
+ refundDepositId: string | null;
163
+ refundTransactionHash: string | null;
164
+ refundMetadata: unknown | null;
165
+ inPersonCheckout: boolean;
133
166
  requestedUsdcAmount: string;
134
167
  remainingUsdcAmount: string;
135
168
  destinationAddress: string;
@@ -163,6 +196,7 @@ export type CheckoutPaymentQuote = {
163
196
  depositData: Record<string, string>;
164
197
  };
165
198
  intentHash?: string;
199
+ sellerAutomatedReleaseAvailable?: boolean;
166
200
  };
167
201
  export type CheckoutQuote = {
168
202
  displayAmount: string;
@@ -172,6 +206,7 @@ export type CheckoutQuoteEntry = {
172
206
  platform: string;
173
207
  displayAmount: string;
174
208
  spreadPercentage: number;
209
+ sellerAutomatedReleaseAvailable?: boolean;
175
210
  };
176
211
  export type CheckoutQuotes = {
177
212
  orderId: string;
@@ -183,10 +218,19 @@ export type CheckoutPayment = {
183
218
  orderId: string;
184
219
  status: CheckoutPaymentStatusType;
185
220
  rail: string;
221
+ paymentMethodId: string | null;
222
+ proofMode: ProofMode;
223
+ attestation: {
224
+ serviceUrl: string;
225
+ };
186
226
  currency: string;
187
227
  payTo: string;
228
+ /** Recipient rating in the 1.0–5.0 range, rounded to one decimal. */
229
+ recipientRating: number;
188
230
  quote: CheckoutPaymentQuote;
189
231
  quoteExpiresAt: string;
232
+ memoPolicy: CheckoutPaymentMemoPolicyType;
233
+ suggestedMemo: string | null;
190
234
  paymentAmount: string;
191
235
  currencyPerUsdRate: string;
192
236
  netSettledUsdcAmount: string | null;
@@ -194,14 +238,19 @@ export type CheckoutPayment = {
194
238
  referralFees: SettlementReferralFeeEntry[] | null;
195
239
  railIdentifier: string | null;
196
240
  fulfillTransaction: string | null;
241
+ errorCode?: string | null;
242
+ errorMessage?: string | null;
197
243
  completedAt: string | null;
198
244
  createdAt: string;
199
245
  updatedAt: string | null;
200
246
  };
247
+ export type CheckoutAggregatePayment = Omit<CheckoutPayment, 'paymentMethodId'> & {
248
+ paymentMethodId?: string | null;
249
+ };
201
250
  export type CheckoutAggregate = {
202
251
  order: CheckoutOrder;
203
252
  merchant: CheckoutMerchant;
204
- currentPayment: CheckoutPayment | null;
253
+ currentPayment: CheckoutAggregatePayment | null;
205
254
  };
206
255
  export type CreateOrderResponse = {
207
256
  order: CheckoutOrder;
@@ -211,6 +260,7 @@ export type CreateCheckoutResponse = CreateOrderResponse;
211
260
  export type CheckoutCreateResponse = CreateOrderResponse;
212
261
  export type CreatePaymentRequest = {
213
262
  rail: string;
263
+ paymentMethodId?: string;
214
264
  fiatCurrency?: string;
215
265
  currency?: string;
216
266
  replacementPaymentId?: string;
@@ -227,6 +277,7 @@ export type PaymentDeeplinkResponse = {
227
277
  url: string;
228
278
  proofSubmissionUrl: string;
229
279
  checkoutReturnUrl: string;
280
+ linkKind?: 'app_clip' | 'deeplink';
230
281
  };
231
282
  export type CheckoutDeeplinkResponse = PaymentDeeplinkResponse;
232
283
  export type MerchantConfig = {
@@ -246,11 +297,24 @@ export type MerchantConfig = {
246
297
  createdAt: string;
247
298
  updatedAt: string | null;
248
299
  };
300
+ export declare const DashboardPrivyApp: {
301
+ readonly LEGACY: "LEGACY";
302
+ readonly V1: "V1";
303
+ };
304
+ export type DashboardPrivyAppType = typeof DashboardPrivyApp[keyof typeof DashboardPrivyApp];
305
+ export declare const MerchantMigrationStatus: {
306
+ readonly PENDING: "PENDING";
307
+ readonly MIGRATING: "MIGRATING";
308
+ readonly COMPLETE: "COMPLETE";
309
+ readonly FAILED: "FAILED";
310
+ };
311
+ export type MerchantMigrationStatusType = typeof MerchantMigrationStatus[keyof typeof MerchantMigrationStatus];
249
312
  export type MerchantUser = {
250
313
  id: string;
251
314
  email: string | null;
252
- privyUserId: string;
253
- role: 'OWNER' | 'MANAGER';
315
+ privyUserId: string | null;
316
+ v1PrivyUserId: string | null;
317
+ role: 'OWNER' | 'MANAGER' | 'CASHIER';
254
318
  merchantId: string | null;
255
319
  createdAt: string;
256
320
  updatedAt: string | null;
@@ -259,11 +323,17 @@ export type MerchantProfile = {
259
323
  id: string;
260
324
  name: string;
261
325
  logoUrl: string | null;
326
+ industryType: string | null;
327
+ industryOtherText: string | null;
262
328
  apiKey: string;
263
329
  environment: MerchantEnvironmentType;
264
330
  sandboxMerchantId: string | null;
331
+ inPersonCheckoutEnabled: boolean;
332
+ migrationStatus: MerchantMigrationStatusType;
265
333
  evmWalletAddress: string | null;
266
334
  solanaWalletAddress: string | null;
335
+ v1EvmWalletAddress: string | null;
336
+ v1SolanaWalletAddress: string | null;
267
337
  tier: 'FREE' | 'PRO';
268
338
  createdAt: string;
269
339
  updatedAt: string | null;
@@ -328,6 +398,8 @@ export type Merchant = {
328
398
  id: string;
329
399
  name: string;
330
400
  logoUrl?: string | null;
401
+ industryType?: string | null;
402
+ industryOtherText?: string | null;
331
403
  disableBranding?: boolean;
332
404
  apiKey: string;
333
405
  environment?: MerchantEnvironmentType;
@@ -605,6 +677,8 @@ export declare const WebhookEventType: {
605
677
  readonly PAYMENT_CANCELLED: "PAYMENT_CANCELLED";
606
678
  readonly PAYMENT_EXPIRED: "PAYMENT_EXPIRED";
607
679
  readonly PAYMENT_FAILED: "PAYMENT_FAILED";
680
+ readonly REFUND_PENDING: "REFUND_PENDING";
681
+ readonly REFUND_COMPLETED: "REFUND_COMPLETED";
608
682
  readonly PAYMENT_BRIDGE_PENDING: "PAYMENT_BRIDGE_PENDING";
609
683
  readonly PAYMENT_BRIDGE_SUBMITTED: "PAYMENT_BRIDGE_SUBMITTED";
610
684
  readonly PAYMENT_BRIDGE_COMPLETED: "PAYMENT_BRIDGE_COMPLETED";
@@ -653,6 +727,7 @@ export type WebhookPayload = {
653
727
  data: {
654
728
  order: CheckoutOrder | null;
655
729
  payment: CheckoutPayment | null;
730
+ refund: Record<string, unknown> | null;
656
731
  paymentBridge: Record<string, unknown> | null;
657
732
  };
658
733
  };
package/dist/types.js CHANGED
@@ -10,6 +10,14 @@ export const PaymentPlatform = {
10
10
  N26: 'n26',
11
11
  CHIME: 'chime',
12
12
  };
13
+ const PAYMENT_PLATFORM_VALUES = new Set(Object.values(PaymentPlatform));
14
+ /**
15
+ * Type guard for PaymentPlatform values.
16
+ * Returns true if `value` is one of the known PaymentPlatform enum values.
17
+ */
18
+ export function isPaymentPlatform(value) {
19
+ return typeof value === 'string' && PAYMENT_PLATFORM_VALUES.has(value);
20
+ }
13
21
  // Supported chains for USDC destination
14
22
  // Re-exported from chains.ts for backward compatibility
15
23
  export const SupportedChains = SupportedChainsMap;
@@ -53,12 +61,23 @@ export const FeePayer = {
53
61
  MERCHANT: 'MERCHANT',
54
62
  PAYEE: 'PAYEE',
55
63
  };
64
+ export const MerchantPaymentFlowMode = {
65
+ EXCLUSIVE_SAR: 'EXCLUSIVE_SAR',
66
+ PREFER_SAR: 'PREFER_SAR',
67
+ PREFER_BUYER_TEE: 'PREFER_BUYER_TEE',
68
+ EXCLUSIVE_BUYER_TEE: 'EXCLUSIVE_BUYER_TEE',
69
+ };
56
70
  export const CheckoutOrderStatus = {
57
71
  CREATED: 'CREATED',
58
72
  PARTIALLY_FULFILLED: 'PARTIALLY_FULFILLED',
59
73
  FULFILLED: 'FULFILLED',
60
74
  CANCELLED: 'CANCELLED',
61
75
  };
76
+ export const CheckoutOrderRefundStatus = {
77
+ NONE: 'NONE',
78
+ PENDING: 'PENDING',
79
+ COMPLETED: 'COMPLETED',
80
+ };
62
81
  export const CheckoutPaymentStatus = {
63
82
  CREATED: 'CREATED',
64
83
  SETTLED: 'SETTLED',
@@ -66,6 +85,21 @@ export const CheckoutPaymentStatus = {
66
85
  EXPIRED: 'EXPIRED',
67
86
  FAILED: 'FAILED',
68
87
  };
88
+ export const CheckoutPaymentMemoPolicy = {
89
+ NONE: 'NONE',
90
+ REQUIRED: 'REQUIRED',
91
+ EMPTY: 'EMPTY',
92
+ };
93
+ export const DashboardPrivyApp = {
94
+ LEGACY: 'LEGACY',
95
+ V1: 'V1',
96
+ };
97
+ export const MerchantMigrationStatus = {
98
+ PENDING: 'PENDING',
99
+ MIGRATING: 'MIGRATING',
100
+ COMPLETE: 'COMPLETE',
101
+ FAILED: 'FAILED',
102
+ };
69
103
  // Structured error codes for Order failures
70
104
  export const OrderErrorCode = {
71
105
  // Proof verification errors
@@ -107,6 +141,8 @@ export const WebhookEventType = {
107
141
  PAYMENT_CANCELLED: 'PAYMENT_CANCELLED',
108
142
  PAYMENT_EXPIRED: 'PAYMENT_EXPIRED',
109
143
  PAYMENT_FAILED: 'PAYMENT_FAILED',
144
+ REFUND_PENDING: 'REFUND_PENDING',
145
+ REFUND_COMPLETED: 'REFUND_COMPLETED',
110
146
  PAYMENT_BRIDGE_PENDING: 'PAYMENT_BRIDGE_PENDING',
111
147
  PAYMENT_BRIDGE_SUBMITTED: 'PAYMENT_BRIDGE_SUBMITTED',
112
148
  PAYMENT_BRIDGE_COMPLETED: 'PAYMENT_BRIDGE_COMPLETED',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkp2p/pay-shared",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Shared TypeScript types, enums, and chain utilities used by ZKP2P Pay API, SDK, and frontend apps.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -37,7 +37,11 @@
37
37
  "scripts": {
38
38
  "build": "rm -rf dist && tsc -p tsconfig.json",
39
39
  "lint": "eslint . --ext .ts,.tsx",
40
+ "test": "vitest run",
40
41
  "prepack": "npm run build",
41
42
  "prepublishOnly": "npm pack --dry-run"
43
+ },
44
+ "devDependencies": {
45
+ "vitest": "^1.6.1"
42
46
  }
43
47
  }