@zkp2p/pay-shared 4.0.1 → 6.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 +15 -2
- package/dist/addressValidators.d.ts +2 -0
- package/dist/addressValidators.js +12 -1
- package/dist/attribution.d.ts +2 -1
- package/dist/attribution.js +1 -0
- package/dist/bitcoin.d.ts +4 -0
- package/dist/bitcoin.js +38 -6
- package/dist/cashoutRails.d.ts +34 -0
- package/dist/cashoutRails.js +71 -0
- package/dist/chains.js +9 -0
- package/dist/clientTelemetry.d.ts +25 -0
- package/dist/clientTelemetry.js +30 -0
- package/dist/conciergeIntake.d.ts +56 -0
- package/dist/conciergeIntake.js +15 -0
- package/dist/crypto.d.ts +43 -2
- package/dist/crypto.js +123 -3
- package/dist/fees.d.ts +2 -0
- package/dist/fees.js +10 -0
- package/dist/fiatAmount.d.ts +9 -4
- package/dist/fiatAmount.js +40 -4
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/llmIntegration.d.ts +0 -4
- package/dist/masterMerchantSubMerchants.d.ts +74 -0
- package/dist/masterMerchantSubMerchants.js +20 -0
- package/dist/onboarding.js +6 -5
- package/dist/payeeFeeCap.d.ts +10 -0
- package/dist/payeeFeeCap.js +20 -0
- package/dist/paymentRecovery.d.ts +26 -0
- package/dist/paymentRecovery.js +27 -0
- package/dist/rails.d.ts +20 -2
- package/dist/rails.js +42 -4
- package/dist/sandboxPricing.d.ts +30 -0
- package/dist/sandboxPricing.js +53 -0
- package/dist/tiers.d.ts +3 -1
- package/dist/tiers.js +4 -2
- package/dist/types.d.ts +979 -12
- package/dist/types.js +276 -0
- package/package.json +2 -2
package/dist/fees.d.ts
CHANGED
|
@@ -24,4 +24,6 @@ export declare function resolveExactTokenGrossAmountUsdc(config: ReferralFeeConf
|
|
|
24
24
|
export declare function resolveMaxFeeConfig(config: MaxFeeConfig, amountUsd: string): {
|
|
25
25
|
maxFeePercentage: number;
|
|
26
26
|
};
|
|
27
|
+
/** Split endpoints use exactly the established merchant/buyer pricing paths. */
|
|
28
|
+
export declare function resolveEffectiveFeePayer(feePayer: 'MERCHANT' | 'PAYEE' | 'SPLIT', buyerFeeShareBps: number): 'MERCHANT' | 'PAYEE' | 'SPLIT';
|
|
27
29
|
export {};
|
package/dist/fees.js
CHANGED
|
@@ -193,3 +193,13 @@ export function resolveMaxFeeConfig(config, amountUsd) {
|
|
|
193
193
|
maxFeePercentage: config.tranches[matchedTrancheIndex].value,
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
|
+
/** Split endpoints use exactly the established merchant/buyer pricing paths. */
|
|
197
|
+
export function resolveEffectiveFeePayer(feePayer, buyerFeeShareBps) {
|
|
198
|
+
if (feePayer !== 'SPLIT')
|
|
199
|
+
return feePayer;
|
|
200
|
+
if (buyerFeeShareBps === 0)
|
|
201
|
+
return 'MERCHANT';
|
|
202
|
+
if (buyerFeeShareBps === 10000)
|
|
203
|
+
return 'PAYEE';
|
|
204
|
+
return 'SPLIT';
|
|
205
|
+
}
|
package/dist/fiatAmount.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface DisplayedFiatAmountInput {
|
|
2
|
-
readonly
|
|
2
|
+
readonly buyerFiatAmount?: string | null;
|
|
3
|
+
readonly feePayer: 'MERCHANT' | 'PAYEE' | 'SPLIT';
|
|
3
4
|
readonly signalIntentAmountBaseUnits: string | null;
|
|
4
5
|
readonly conversionRateDecimal: string | null;
|
|
5
6
|
readonly remainingUsdcAmount: string;
|
|
@@ -8,12 +9,16 @@ export interface DisplayedFiatAmountInput {
|
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* The exact fiat amount the buyer is instructed to pay, as a decimal string.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Shared by checkout rendering and seller verification:
|
|
13
|
+
* PAYEE and SPLIT use the persisted quote, rounded upward to fiat cents.
|
|
14
|
+
* MERCHANT: remaining amount at the FX peg, or the actual settled payment amount.
|
|
14
15
|
* Always returns a string; the API caller passes it to fiatMinorUnitsFromDisplayedAmount,
|
|
15
16
|
* which returns null for an unusable "0"/malformed value so the API fails SAR closed.
|
|
16
17
|
*/
|
|
17
18
|
export declare function computeDisplayedFiatAmount(input: DisplayedFiatAmountInput): string;
|
|
18
19
|
/** Parse a 2-decimal (or fewer) positive decimal string to integer minor units. Null if non-positive/malformed/>2dp. */
|
|
19
20
|
export declare function fiatMinorUnitsFromDisplayedAmount(displayed: string): bigint | null;
|
|
21
|
+
/** Buyer/merchant share of the difference between paid value and merchant net. */
|
|
22
|
+
export declare function computeSplitFeeContribution(buyerValueUsd: string, netMerchantUsd: string, buyerFeeShareBps: number, quotedBridgeSpreadBps?: string): string;
|
|
23
|
+
/** Reads the server-owned buyer amount from a persisted quote. */
|
|
24
|
+
export declare function readQuoteBuyerFiatAmount(quote: unknown): string | undefined;
|
package/dist/fiatAmount.js
CHANGED
|
@@ -28,18 +28,29 @@ function centsToDecimalString(cents) {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* The exact fiat amount the buyer is instructed to pay, as a decimal string.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Shared by checkout rendering and seller verification:
|
|
32
|
+
* PAYEE and SPLIT use the persisted quote, rounded upward to fiat cents.
|
|
33
|
+
* MERCHANT: remaining amount at the FX peg, or the actual settled payment amount.
|
|
34
34
|
* Always returns a string; the API caller passes it to fiatMinorUnitsFromDisplayedAmount,
|
|
35
35
|
* which returns null for an unusable "0"/malformed value so the API fails SAR closed.
|
|
36
36
|
*/
|
|
37
37
|
export function computeDisplayedFiatAmount(input) {
|
|
38
|
-
|
|
38
|
+
if (input.buyerFiatAmount != null && input.feePayer !== 'MERCHANT') {
|
|
39
|
+
const cents = fiatMinorUnitsFromDisplayedAmount(input.buyerFiatAmount);
|
|
40
|
+
if (cents === null)
|
|
41
|
+
throw new Error('Buffered quote buyer amount must be positive fiat cents');
|
|
42
|
+
return centsToDecimalString(cents);
|
|
43
|
+
}
|
|
44
|
+
const quotedCents = input.feePayer !== 'MERCHANT' && input.signalIntentAmountBaseUnits !== null && input.conversionRateDecimal !== null
|
|
39
45
|
? ceilCentsFromSignalIntent(input.signalIntentAmountBaseUnits, input.conversionRateDecimal)
|
|
40
46
|
: null;
|
|
41
47
|
if (quotedCents !== null)
|
|
42
48
|
return centsToDecimalString(quotedCents);
|
|
49
|
+
if (input.feePayer === 'SPLIT') {
|
|
50
|
+
if (Number(input.remainingUsdcAmount) <= 0)
|
|
51
|
+
return input.paymentAmountFallback;
|
|
52
|
+
throw new Error('Split pricing requires the persisted signal amount and conversion rate');
|
|
53
|
+
}
|
|
43
54
|
const remainingUsdc = Number(input.remainingUsdcAmount);
|
|
44
55
|
const rate = Number(input.currencyPerUsdRate);
|
|
45
56
|
if (Number.isFinite(remainingUsdc) && Number.isFinite(rate) && remainingUsdc > 0 && rate > 0) {
|
|
@@ -56,3 +67,28 @@ export function fiatMinorUnitsFromDisplayedAmount(displayed) {
|
|
|
56
67
|
const cents = BigInt(whole) * CENT_SCALE + BigInt(fraction.padEnd(2, '0'));
|
|
57
68
|
return cents > 0n ? cents : null;
|
|
58
69
|
}
|
|
70
|
+
/** Buyer/merchant share of the difference between paid value and merchant net. */
|
|
71
|
+
export function computeSplitFeeContribution(buyerValueUsd, netMerchantUsd, buyerFeeShareBps, quotedBridgeSpreadBps = '0') {
|
|
72
|
+
const paid = parseDecimalToScaledUnits(buyerValueUsd, 6);
|
|
73
|
+
const net = parseDecimalToScaledUnits(netMerchantUsd, 6);
|
|
74
|
+
if (paid === null || net === null)
|
|
75
|
+
throw new Error('Split fee contribution requires nonnegative decimal amounts');
|
|
76
|
+
const bridge = parseDecimalToScaledUnits(quotedBridgeSpreadBps, 18);
|
|
77
|
+
if (bridge === null)
|
|
78
|
+
throw new Error('Quoted bridge spread must be a nonnegative decimal');
|
|
79
|
+
const bridgeDenominator = 10000n * RATE_SCALE + bridge;
|
|
80
|
+
const units = (paid * BigInt(10000 - buyerFeeShareBps) * bridgeDenominator
|
|
81
|
+
+ net * BigInt(buyerFeeShareBps) * 10000n * RATE_SCALE) / (10000n * bridgeDenominator);
|
|
82
|
+
return `${units / USDC_SCALE}.${(units % USDC_SCALE).toString().padStart(6, '0')}`;
|
|
83
|
+
}
|
|
84
|
+
/** Reads the server-owned buyer amount from a persisted quote. */
|
|
85
|
+
export function readQuoteBuyerFiatAmount(quote) {
|
|
86
|
+
if (typeof quote !== 'object' || quote === null || !('amountAdjustment' in quote))
|
|
87
|
+
return undefined;
|
|
88
|
+
const adjustment = quote.amountAdjustment;
|
|
89
|
+
if (typeof adjustment !== 'object' || adjustment === null || !('buyerFiatAmount' in adjustment)
|
|
90
|
+
|| typeof adjustment.buyerFiatAmount !== 'string') {
|
|
91
|
+
throw new Error('Buffered quote is missing its buyer fiat amount');
|
|
92
|
+
}
|
|
93
|
+
return adjustment.buyerFiatAmount;
|
|
94
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
|
+
export * from './paymentRecovery.js';
|
|
2
3
|
export * from './chains.js';
|
|
3
4
|
export * from './fees.js';
|
|
4
5
|
export * from './crypto.js';
|
|
5
6
|
export * from './rails.js';
|
|
7
|
+
export * from './cashoutRails.js';
|
|
6
8
|
export * from './buyerTee.js';
|
|
7
9
|
export * from './bitcoin.js';
|
|
8
10
|
export * from './addressValidators.js';
|
|
9
11
|
export { doubleSha256, encodeBase58 } from './addressEncoding.js';
|
|
10
12
|
export * from './fiatAmount.js';
|
|
13
|
+
export * from './sandboxPricing.js';
|
|
11
14
|
export * from './paypalSarPhase.js';
|
|
12
15
|
export * from './paypalSarVerify.js';
|
|
13
16
|
export * from './payeeFeeCap.js';
|
|
14
17
|
export * from './onboarding.js';
|
|
15
18
|
export * from './llmIntegration.js';
|
|
16
19
|
export * from './tiers.js';
|
|
20
|
+
export * from './masterMerchantSubMerchants.js';
|
|
17
21
|
export * from './attribution.js';
|
|
22
|
+
export * from './conciergeIntake.js';
|
|
23
|
+
export * from './clientTelemetry.js';
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
|
+
export * from './paymentRecovery.js';
|
|
2
3
|
export * from './chains.js';
|
|
3
4
|
export * from './fees.js';
|
|
4
5
|
export * from './crypto.js';
|
|
5
6
|
export * from './rails.js';
|
|
7
|
+
export * from './cashoutRails.js';
|
|
6
8
|
export * from './buyerTee.js';
|
|
7
9
|
export * from './bitcoin.js';
|
|
8
10
|
export * from './addressValidators.js';
|
|
9
11
|
export { doubleSha256, encodeBase58 } from './addressEncoding.js';
|
|
10
12
|
export * from './fiatAmount.js';
|
|
13
|
+
export * from './sandboxPricing.js';
|
|
11
14
|
export * from './paypalSarPhase.js';
|
|
12
15
|
export * from './paypalSarVerify.js';
|
|
13
16
|
export * from './payeeFeeCap.js';
|
|
14
17
|
export * from './onboarding.js';
|
|
15
18
|
export * from './llmIntegration.js';
|
|
16
19
|
export * from './tiers.js';
|
|
20
|
+
export * from './masterMerchantSubMerchants.js';
|
|
17
21
|
export * from './attribution.js';
|
|
22
|
+
export * from './conciergeIntake.js';
|
|
23
|
+
export * from './clientTelemetry.js';
|
package/dist/llmIntegration.d.ts
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** The highest master merchant fee in bps, the referral-split schema's per-entry and total ceiling. */
|
|
2
|
+
export declare const MASTER_MERCHANT_FEE_MAX_BPS = 5000;
|
|
3
|
+
/** A Master Merchant Account sub merchant's ownership as its Master Merchant Account sees it. PR 5 reports only OWNED_BY_MASTER_MERCHANT; PR 6 fills the others. */
|
|
4
|
+
export declare const SubMerchantTransferState: {
|
|
5
|
+
readonly OWNED_BY_MASTER_MERCHANT: "OWNED_BY_MASTER_MERCHANT";
|
|
6
|
+
readonly PENDING: "PENDING";
|
|
7
|
+
readonly TRANSFERRED: "TRANSFERRED";
|
|
8
|
+
};
|
|
9
|
+
export type SubMerchantTransferStateType = typeof SubMerchantTransferState[keyof typeof SubMerchantTransferState];
|
|
10
|
+
export type SubMerchantTransfer = {
|
|
11
|
+
state: typeof SubMerchantTransferState.OWNED_BY_MASTER_MERCHANT;
|
|
12
|
+
} | {
|
|
13
|
+
state: typeof SubMerchantTransferState.PENDING;
|
|
14
|
+
recipientEmail: string;
|
|
15
|
+
expiresAt: string;
|
|
16
|
+
} | {
|
|
17
|
+
state: typeof SubMerchantTransferState.TRANSFERRED;
|
|
18
|
+
recipientEmail: string;
|
|
19
|
+
acceptedAt: string;
|
|
20
|
+
};
|
|
21
|
+
/** One row of GET /merchants/me/sub-merchants, also the POST response. */
|
|
22
|
+
export type SubMerchantSummary = {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
masterMerchantFeeBps: number;
|
|
27
|
+
ownerEmail: string;
|
|
28
|
+
transfer: SubMerchantTransfer;
|
|
29
|
+
};
|
|
30
|
+
export type SubMerchantListResponse = {
|
|
31
|
+
subMerchants: SubMerchantSummary[];
|
|
32
|
+
};
|
|
33
|
+
export type CreateSubMerchantRequest = {
|
|
34
|
+
name: string;
|
|
35
|
+
masterMerchantFeeBps: number;
|
|
36
|
+
};
|
|
37
|
+
export type SetMasterMerchantFeeRequest = {
|
|
38
|
+
masterMerchantFeeBps: number;
|
|
39
|
+
};
|
|
40
|
+
export type MasterMerchantFeeResult = {
|
|
41
|
+
subMerchantId: string;
|
|
42
|
+
masterMerchantFeeBps: number;
|
|
43
|
+
};
|
|
44
|
+
export type BpsRange = {
|
|
45
|
+
min: number;
|
|
46
|
+
max: number;
|
|
47
|
+
};
|
|
48
|
+
/** Configured fees for one enabled rail: Peer's platform fee across its tranches, and that plus the master merchant fee. */
|
|
49
|
+
export type MasterMerchantFeePreviewRail = {
|
|
50
|
+
rail: string;
|
|
51
|
+
platformFeeBps: BpsRange;
|
|
52
|
+
totalFeeBps: BpsRange;
|
|
53
|
+
};
|
|
54
|
+
/** The first amount band (inclusive USD bounds; null max = open-ended) where configured fees exceed the max fee. */
|
|
55
|
+
export type MasterMerchantMaxFeeViolation = {
|
|
56
|
+
rail: string;
|
|
57
|
+
minAmountUsd: string;
|
|
58
|
+
maxAmountUsd: string | null;
|
|
59
|
+
platformFeeBps: number;
|
|
60
|
+
splitFeeBps: number;
|
|
61
|
+
maxFeePercentage: number;
|
|
62
|
+
};
|
|
63
|
+
export type MasterMerchantFeePreviewResponse = {
|
|
64
|
+
masterMerchantFeeBps: number;
|
|
65
|
+
/** Null means no Master Merchant Account-specific cap. */
|
|
66
|
+
masterMerchantMaxFeeBps: number | null;
|
|
67
|
+
rails: MasterMerchantFeePreviewRail[];
|
|
68
|
+
maxFeeViolation: MasterMerchantMaxFeeViolation | null;
|
|
69
|
+
};
|
|
70
|
+
export declare const MASTER_MERCHANT_NOT_ENABLED_MESSAGE = "This merchant is not enabled as a Master Merchant Account";
|
|
71
|
+
export declare const MASTER_MERCHANT_FEE_LOCKED_TRANSFERRED_MESSAGE = "This sub merchant now belongs to someone else; only an admin can change its master merchant fee";
|
|
72
|
+
export declare const MASTER_MERCHANT_FEE_LOCKED_PENDING_MESSAGE = "Cancel the pending ownership transfer before changing the master merchant fee";
|
|
73
|
+
export declare function formatMasterMerchantFeeExceedsCapMessage(masterMerchantFeeBps: number, masterMerchantMaxFeeBps: number): string;
|
|
74
|
+
export declare function formatMasterMerchantMaxFeeViolationMessage(violation: MasterMerchantMaxFeeViolation): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The highest master merchant fee in bps, the referral-split schema's per-entry and total ceiling. */
|
|
2
|
+
export const MASTER_MERCHANT_FEE_MAX_BPS = 5000;
|
|
3
|
+
/** A Master Merchant Account sub merchant's ownership as its Master Merchant Account sees it. PR 5 reports only OWNED_BY_MASTER_MERCHANT; PR 6 fills the others. */
|
|
4
|
+
export const SubMerchantTransferState = {
|
|
5
|
+
OWNED_BY_MASTER_MERCHANT: 'OWNED_BY_MASTER_MERCHANT',
|
|
6
|
+
PENDING: 'PENDING',
|
|
7
|
+
TRANSFERRED: 'TRANSFERRED',
|
|
8
|
+
};
|
|
9
|
+
export const MASTER_MERCHANT_NOT_ENABLED_MESSAGE = 'This merchant is not enabled as a Master Merchant Account';
|
|
10
|
+
export const MASTER_MERCHANT_FEE_LOCKED_TRANSFERRED_MESSAGE = 'This sub merchant now belongs to someone else; only an admin can change its master merchant fee';
|
|
11
|
+
export const MASTER_MERCHANT_FEE_LOCKED_PENDING_MESSAGE = 'Cancel the pending ownership transfer before changing the master merchant fee';
|
|
12
|
+
export function formatMasterMerchantFeeExceedsCapMessage(masterMerchantFeeBps, masterMerchantMaxFeeBps) {
|
|
13
|
+
return `Master merchant fee ${masterMerchantFeeBps} bps exceeds the ${masterMerchantMaxFeeBps} bps cap`;
|
|
14
|
+
}
|
|
15
|
+
export function formatMasterMerchantMaxFeeViolationMessage(violation) {
|
|
16
|
+
const band = violation.maxAmountUsd === null
|
|
17
|
+
? `$${violation.minAmountUsd} and above`
|
|
18
|
+
: `$${violation.minAmountUsd}–$${violation.maxAmountUsd}`;
|
|
19
|
+
return `Configured fees on ${violation.rail} for orders of ${band} would be ${violation.platformFeeBps + violation.splitFeeBps} bps, above the ${violation.maxFeePercentage}% max fee`;
|
|
20
|
+
}
|
package/dist/onboarding.js
CHANGED
|
@@ -70,10 +70,11 @@ export const ONBOARDING_STEP_REGISTRY = [
|
|
|
70
70
|
{
|
|
71
71
|
id: OnboardingStepId.RAILS,
|
|
72
72
|
paths: 'ALL',
|
|
73
|
-
|
|
73
|
+
// Merchants start with default rails, so there is nothing to observe: the step is done once the
|
|
74
|
+
// merchant confirms their payment methods. The dashboard wizard acknowledges it after saving them.
|
|
75
|
+
verifiedBy: OnboardingStepVerifiedBy.MANUAL,
|
|
74
76
|
inChecklist: false,
|
|
75
|
-
//
|
|
76
|
-
// it to imply earlier steps would mark every merchant's onboarding complete.
|
|
77
|
+
// Confirming payment methods says nothing about the profile or industry.
|
|
77
78
|
impliesPrevious: false,
|
|
78
79
|
},
|
|
79
80
|
{ id: OnboardingStepId.PATH, paths: 'ALL', verifiedBy: OnboardingStepVerifiedBy.AUTO, inChecklist: true, impliesPrevious: true },
|
|
@@ -158,11 +159,11 @@ export const ONBOARDING_STEP_REGISTRY = [
|
|
|
158
159
|
},
|
|
159
160
|
];
|
|
160
161
|
const autoStepPredicates = {
|
|
161
|
-
|
|
162
|
+
// The logo is optional: the wizard acknowledges the profile once the merchant confirms a name.
|
|
163
|
+
[OnboardingStepId.PROFILE]: (facts) => facts.hasName && (facts.hasLogo || facts.manualSteps.includes(OnboardingStepId.PROFILE)),
|
|
162
164
|
[OnboardingStepId.INDUSTRY]: (facts) => facts.hasIndustry,
|
|
163
165
|
[OnboardingStepId.PLAN]: (facts) => facts.hasSelectedTier,
|
|
164
166
|
[OnboardingStepId.PATH]: (facts) => facts.hasIntegrationPath,
|
|
165
|
-
[OnboardingStepId.RAILS]: () => true,
|
|
166
167
|
[OnboardingStepId.INTEGRATION]: (facts) => facts.hasSandboxFulfilledOrder,
|
|
167
168
|
[OnboardingStepId.WEBHOOK]: (facts) => facts.hasWebhook,
|
|
168
169
|
[OnboardingStepId.TEST_ORDER]: (facts) => facts.hasSandboxFulfilledOrder,
|
package/dist/payeeFeeCap.d.ts
CHANGED
|
@@ -18,4 +18,14 @@ export declare function resolvePayeeReferralFeeCapacity(input: {
|
|
|
18
18
|
readonly platformRecipient: string;
|
|
19
19
|
readonly referralFees: readonly PayeeFeeEntry[];
|
|
20
20
|
}): PayeeReferralFeeCapacityResult;
|
|
21
|
+
export declare class QuoteAmountCapacityError extends RangeError {
|
|
22
|
+
}
|
|
23
|
+
/** Preserve the full-size quote's net settlement by reducing only Peer's fee. */
|
|
24
|
+
export declare function resolveQuoteAmountFeeCapacity(input: {
|
|
25
|
+
readonly requestedSignalAmountRaw: string;
|
|
26
|
+
readonly grossSignalAmountRaw: string;
|
|
27
|
+
readonly quotedTokenOutputAmountRaw: string;
|
|
28
|
+
readonly platformRecipient: string;
|
|
29
|
+
readonly referralFees: readonly PayeeFeeEntry[];
|
|
30
|
+
}): PayeeFeeEntry[];
|
|
21
31
|
export declare function computeNetAfterPreciseFees(grossAmount: string, feeRates18dec: readonly string[]): string;
|
package/dist/payeeFeeCap.js
CHANGED
|
@@ -50,6 +50,26 @@ export function resolvePayeeReferralFeeCapacity(input) {
|
|
|
50
50
|
requiredNetCovered,
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
export class QuoteAmountCapacityError extends RangeError {
|
|
54
|
+
}
|
|
55
|
+
/** Preserve the full-size quote's net settlement by reducing only Peer's fee. */
|
|
56
|
+
export function resolveQuoteAmountFeeCapacity(input) {
|
|
57
|
+
const requested = BigInt(input.requestedSignalAmountRaw);
|
|
58
|
+
const gross = BigInt(input.grossSignalAmountRaw);
|
|
59
|
+
const output = BigInt(input.quotedTokenOutputAmountRaw);
|
|
60
|
+
const nominalFees = input.referralFees.reduce((total, fee) => total + requested * BigInt(fee.feeBps) / BPS_DENOMINATOR, 0n);
|
|
61
|
+
const capacity = resolvePayeeReferralFeeCapacity({
|
|
62
|
+
grossSignalAmount: gross.toString(),
|
|
63
|
+
quotedTokenOutput: output.toString(),
|
|
64
|
+
requiredNetAmount: (output + requested - gross - nominalFees).toString(),
|
|
65
|
+
platformRecipient: input.platformRecipient,
|
|
66
|
+
referralFees: input.referralFees,
|
|
67
|
+
});
|
|
68
|
+
if (capacity.insufficientCapacity || !capacity.requiredNetCovered) {
|
|
69
|
+
throw new QuoteAmountCapacityError('Insufficient Peer fee capacity for quote amount shortfall');
|
|
70
|
+
}
|
|
71
|
+
return capacity.referralFees;
|
|
72
|
+
}
|
|
53
73
|
export function computeNetAfterPreciseFees(grossAmount, feeRates18dec) {
|
|
54
74
|
const gross = parsePositiveUnits(grossAmount, 'gross amount');
|
|
55
75
|
const totalFees = feeRates18dec.reduce((total, feeRate) => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Customer-safe history. No quote/proof payloads or executable payment instructions. */
|
|
2
|
+
export type PaymentRecoveryAttempt = {
|
|
3
|
+
id: string;
|
|
4
|
+
rail: string;
|
|
5
|
+
currency: string;
|
|
6
|
+
amount: string | null;
|
|
7
|
+
recipient: string;
|
|
8
|
+
status: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
expiresAt: string;
|
|
11
|
+
};
|
|
12
|
+
export type PaymentRecoveryHistory = {
|
|
13
|
+
attempts: PaymentRecoveryAttempt[];
|
|
14
|
+
nextCursor: string | null;
|
|
15
|
+
};
|
|
16
|
+
/** Fiat payTo is a protocol hash; the saved quote owns the readable recipient. */
|
|
17
|
+
export declare function paymentRecoveryRecipient(payment: {
|
|
18
|
+
payTo: string;
|
|
19
|
+
quote: unknown;
|
|
20
|
+
}): string;
|
|
21
|
+
/** Old attempts without a snapshot have no reliable historical requested amount. */
|
|
22
|
+
export declare function paymentRecoveryAmount(payment: {
|
|
23
|
+
status: string;
|
|
24
|
+
paymentAmount: string;
|
|
25
|
+
quote: unknown;
|
|
26
|
+
}): string | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Fiat payTo is a protocol hash; the saved quote owns the readable recipient. */
|
|
2
|
+
export function paymentRecoveryRecipient(payment) {
|
|
3
|
+
const quote = payment.quote;
|
|
4
|
+
if (typeof quote === 'object' && quote !== null && 'maker' in quote) {
|
|
5
|
+
const maker = quote.maker;
|
|
6
|
+
if (typeof maker === 'object' && maker !== null && 'depositData' in maker) {
|
|
7
|
+
const data = maker.depositData;
|
|
8
|
+
if (typeof data === 'object' && data !== null && 'offchainId' in data) {
|
|
9
|
+
const recipient = data.offchainId;
|
|
10
|
+
if (typeof recipient === 'string' && recipient.trim() !== '')
|
|
11
|
+
return recipient;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// Historical rows can lack maker data. Never present a bytes32 hash as a name.
|
|
16
|
+
return /^0x[\da-f]{64}$/i.test(payment.payTo) ? 'Recipient unavailable' : payment.payTo;
|
|
17
|
+
}
|
|
18
|
+
/** Old attempts without a snapshot have no reliable historical requested amount. */
|
|
19
|
+
export function paymentRecoveryAmount(payment) {
|
|
20
|
+
if (payment.status === 'SETTLED')
|
|
21
|
+
return Number(payment.paymentAmount) > 0 ? payment.paymentAmount : null;
|
|
22
|
+
const quote = payment.quote;
|
|
23
|
+
if (typeof quote !== 'object' || quote === null || !('recoveryDisplayAmount' in quote))
|
|
24
|
+
return null;
|
|
25
|
+
const amount = quote.recoveryDisplayAmount;
|
|
26
|
+
return typeof amount === 'string' && /^\d+(\.\d+)?$/.test(amount) && Number(amount) > 0 ? amount : null;
|
|
27
|
+
}
|
package/dist/rails.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
export declare const ZCASH_RAIL = "near_intents_133701";
|
|
1
2
|
export declare enum SupportedRail {
|
|
3
|
+
NEAR_INTENTS_ZCASH = "near_intents_133701",
|
|
4
|
+
APPLE_PAY = "apple_pay",
|
|
2
5
|
VENMO = "venmo",
|
|
3
6
|
CASHAPP = "cashapp",
|
|
4
7
|
REVOLUT = "revolut",
|
|
@@ -14,6 +17,7 @@ export declare enum SupportedRail {
|
|
|
14
17
|
RELAY_137 = "relay_137",
|
|
15
18
|
RELAY_480 = "relay_480",
|
|
16
19
|
RELAY_999 = "relay_999",
|
|
20
|
+
RELAY_5042 = "relay_5042",
|
|
17
21
|
RELAY_8453 = "relay_8453",
|
|
18
22
|
RELAY_42161 = "relay_42161",
|
|
19
23
|
RELAY_8253038 = "relay_8253038",
|
|
@@ -33,6 +37,7 @@ export declare enum SupportedRelayRail {
|
|
|
33
37
|
RELAY_137 = "relay_137",
|
|
34
38
|
RELAY_480 = "relay_480",
|
|
35
39
|
RELAY_999 = "relay_999",
|
|
40
|
+
RELAY_5042 = "relay_5042",
|
|
36
41
|
RELAY_8453 = "relay_8453",
|
|
37
42
|
RELAY_42161 = "relay_42161",
|
|
38
43
|
RELAY_8253038 = "relay_8253038",
|
|
@@ -43,6 +48,16 @@ export declare const DEFAULT_FIAT_RAILS: readonly SupportedRail[];
|
|
|
43
48
|
export declare const SUPPORTED_RAILS: readonly SupportedRail[];
|
|
44
49
|
export declare const SUPPORTED_RELAY_RAILS: readonly SupportedRelayRail[];
|
|
45
50
|
export declare const FIAT_SUPPORTED_RAILS: readonly ["venmo", "cashapp", "revolut", "wise", "zelle", "paypal", "monzo", "n26", "chime"];
|
|
51
|
+
/**
|
|
52
|
+
* Coinbase Apple Pay. A third rail category: it is not a fiat rail (no fiat
|
|
53
|
+
* payment and no attestation) and not a crypto rail (the customer never sends
|
|
54
|
+
* crypto). Merchants enable it like any other method, but the resulting payment
|
|
55
|
+
* is recorded against the Relay rail it settles on, not against this key.
|
|
56
|
+
*/
|
|
57
|
+
export declare const APPLE_PAY_RAIL = SupportedRail.APPLE_PAY;
|
|
58
|
+
export declare const APPLE_PAY_MAX_AMOUNT_USDC_BASE_UNITS = 2500000000n;
|
|
59
|
+
export declare const APPLE_PAY_LIMIT_MESSAGE = "This order exceeds the Apple Pay limit. Choose another payment method.";
|
|
60
|
+
export declare function isOnrampRail(rail: string | null | undefined): boolean;
|
|
46
61
|
export type FiatSupportedRail = typeof FIAT_SUPPORTED_RAILS[number];
|
|
47
62
|
export declare const PAYMENT_PLATFORM_LABELS: Record<string, string>;
|
|
48
63
|
export declare const FIAT_RAIL_DISPLAY_NAMES: Record<FiatSupportedRail, string>;
|
|
@@ -82,9 +97,12 @@ export declare function setRuntimeDisabledFiatRails(rails: readonly string[]): v
|
|
|
82
97
|
export declare function getRuntimeDisabledFiatRails(): string[];
|
|
83
98
|
/** Returns true when the rail targets a crypto chain (legacy numeric or relay-prefixed format). */
|
|
84
99
|
export declare function isCryptoRail(rail: string | null | undefined): boolean;
|
|
100
|
+
/** A rail the customer pays on with fiat: excludes crypto rails and onramp
|
|
101
|
+
* rails such as Apple Pay, whose payments settle on a Relay rail instead. */
|
|
102
|
+
export declare function isFiatPaymentRail(rail: string | null | undefined): boolean;
|
|
85
103
|
/** Formats a chain id into the canonical relay rail string: `relay_<chainId>`. */
|
|
86
104
|
export declare function formatRelayRail(chainId: number): string;
|
|
87
|
-
/** Normalizes
|
|
105
|
+
/** Normalizes a crypto rail into its canonical provider format (or null for non-crypto rails). */
|
|
88
106
|
export declare function normalizeCryptoRail(rail: string | null | undefined): string | null;
|
|
89
107
|
/** Returns the canonical base fiat rail for base rails and supported variants such as `zelle-chase`. */
|
|
90
108
|
export declare function normalizeFiatRail(rail: string | null | undefined): string | null;
|
|
@@ -104,7 +122,7 @@ export declare function isSarSupportedFiatRail(rail: string | null | undefined):
|
|
|
104
122
|
* rails (e.g. `zelle`, `revolut`, incl. variants like `zelle-chase`) are dropped
|
|
105
123
|
* while crypto rails and SAR rails (venmo/cashapp/wise/paypal) are kept.
|
|
106
124
|
*/
|
|
107
|
-
export declare function filterRailsForExclusiveSar(rails: readonly
|
|
125
|
+
export declare function filterRailsForExclusiveSar<T extends string>(rails: readonly T[]): T[];
|
|
108
126
|
/** Canonical Zelle paymentMethodId -> attestation-service actionType suffix. */
|
|
109
127
|
export declare const ZELLE_METHOD_ACTION_SUFFIX: Record<string, string>;
|
|
110
128
|
/** Strict create-boundary set for Zelle method ids and transitional variant rails. */
|
package/dist/rails.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { ZCASH_CHAIN_ID } from './crypto.js';
|
|
1
2
|
const RELAY_RAIL_PREFIX = 'relay_';
|
|
3
|
+
export const ZCASH_RAIL = 'near_intents_133701';
|
|
2
4
|
export var SupportedRail;
|
|
3
5
|
(function (SupportedRail) {
|
|
6
|
+
SupportedRail["NEAR_INTENTS_ZCASH"] = "near_intents_133701";
|
|
7
|
+
SupportedRail["APPLE_PAY"] = "apple_pay";
|
|
4
8
|
SupportedRail["VENMO"] = "venmo";
|
|
5
9
|
SupportedRail["CASHAPP"] = "cashapp";
|
|
6
10
|
SupportedRail["REVOLUT"] = "revolut";
|
|
@@ -16,6 +20,7 @@ export var SupportedRail;
|
|
|
16
20
|
SupportedRail["RELAY_137"] = "relay_137";
|
|
17
21
|
SupportedRail["RELAY_480"] = "relay_480";
|
|
18
22
|
SupportedRail["RELAY_999"] = "relay_999";
|
|
23
|
+
SupportedRail["RELAY_5042"] = "relay_5042";
|
|
19
24
|
SupportedRail["RELAY_8453"] = "relay_8453";
|
|
20
25
|
SupportedRail["RELAY_42161"] = "relay_42161";
|
|
21
26
|
SupportedRail["RELAY_8253038"] = "relay_8253038";
|
|
@@ -36,6 +41,7 @@ export var SupportedRelayRail;
|
|
|
36
41
|
SupportedRelayRail["RELAY_137"] = "relay_137";
|
|
37
42
|
SupportedRelayRail["RELAY_480"] = "relay_480";
|
|
38
43
|
SupportedRelayRail["RELAY_999"] = "relay_999";
|
|
44
|
+
SupportedRelayRail["RELAY_5042"] = "relay_5042";
|
|
39
45
|
SupportedRelayRail["RELAY_8453"] = "relay_8453";
|
|
40
46
|
SupportedRelayRail["RELAY_42161"] = "relay_42161";
|
|
41
47
|
SupportedRelayRail["RELAY_8253038"] = "relay_8253038";
|
|
@@ -68,6 +74,19 @@ export const FIAT_SUPPORTED_RAILS = [
|
|
|
68
74
|
'n26',
|
|
69
75
|
'chime',
|
|
70
76
|
];
|
|
77
|
+
/**
|
|
78
|
+
* Coinbase Apple Pay. A third rail category: it is not a fiat rail (no fiat
|
|
79
|
+
* payment and no attestation) and not a crypto rail (the customer never sends
|
|
80
|
+
* crypto). Merchants enable it like any other method, but the resulting payment
|
|
81
|
+
* is recorded against the Relay rail it settles on, not against this key.
|
|
82
|
+
*/
|
|
83
|
+
export const APPLE_PAY_RAIL = SupportedRail.APPLE_PAY;
|
|
84
|
+
// Peer Pay per-order ceiling, in USDC base units. Coinbase may allow less per buyer.
|
|
85
|
+
export const APPLE_PAY_MAX_AMOUNT_USDC_BASE_UNITS = 2500000000n;
|
|
86
|
+
export const APPLE_PAY_LIMIT_MESSAGE = 'This order exceeds the Apple Pay limit. Choose another payment method.';
|
|
87
|
+
export function isOnrampRail(rail) {
|
|
88
|
+
return rail === APPLE_PAY_RAIL;
|
|
89
|
+
}
|
|
71
90
|
const PAYMENT_PLATFORM_LABEL_ENTRIES = {
|
|
72
91
|
[SupportedRail.VENMO]: 'Venmo',
|
|
73
92
|
[SupportedRail.CASHAPP]: 'Cash App',
|
|
@@ -79,7 +98,10 @@ const PAYMENT_PLATFORM_LABEL_ENTRIES = {
|
|
|
79
98
|
[SupportedRail.N26]: 'N26',
|
|
80
99
|
[SupportedRail.CHIME]: 'Chime',
|
|
81
100
|
};
|
|
82
|
-
export const PAYMENT_PLATFORM_LABELS =
|
|
101
|
+
export const PAYMENT_PLATFORM_LABELS = {
|
|
102
|
+
...PAYMENT_PLATFORM_LABEL_ENTRIES,
|
|
103
|
+
[SupportedRail.APPLE_PAY]: 'Apple Pay',
|
|
104
|
+
};
|
|
83
105
|
export const FIAT_RAIL_DISPLAY_NAMES = PAYMENT_PLATFORM_LABEL_ENTRIES;
|
|
84
106
|
// Shared FE/BE minimum so queries too short for the pg_trgm trigram index are no-ops.
|
|
85
107
|
export const ORDER_SEARCH_MIN_QUERY_LENGTH = 3;
|
|
@@ -100,6 +122,7 @@ export const RELAY_CHAIN_DISPLAY_NAMES = {
|
|
|
100
122
|
137: 'Polygon',
|
|
101
123
|
480: 'World Chain',
|
|
102
124
|
999: 'Hyperliquid',
|
|
125
|
+
5042: 'Arc',
|
|
103
126
|
8453: 'Base',
|
|
104
127
|
42161: 'Arbitrum',
|
|
105
128
|
8253038: 'Bitcoin',
|
|
@@ -107,6 +130,8 @@ export const RELAY_CHAIN_DISPLAY_NAMES = {
|
|
|
107
130
|
792703809: 'Solana',
|
|
108
131
|
};
|
|
109
132
|
const CRYPTO_METHOD_ALIASES = [
|
|
133
|
+
'zcash',
|
|
134
|
+
'zec',
|
|
110
135
|
'crypto',
|
|
111
136
|
'usdc',
|
|
112
137
|
'usdt',
|
|
@@ -134,20 +159,22 @@ export function parseCryptoRailChainId(rail) {
|
|
|
134
159
|
return null;
|
|
135
160
|
}
|
|
136
161
|
const trimmed = rail.trim();
|
|
162
|
+
if (trimmed === ZCASH_RAIL)
|
|
163
|
+
return ZCASH_CHAIN_ID;
|
|
137
164
|
if (trimmed === '') {
|
|
138
165
|
return null;
|
|
139
166
|
}
|
|
140
167
|
const numericMatch = /^\d+$/.exec(trimmed);
|
|
141
168
|
if (numericMatch !== null) {
|
|
142
169
|
const parsed = Number(trimmed);
|
|
143
|
-
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
170
|
+
return Number.isSafeInteger(parsed) && parsed > 0 && parsed !== ZCASH_CHAIN_ID ? parsed : null;
|
|
144
171
|
}
|
|
145
172
|
const prefixedMatch = /^relay_(\d+)$/i.exec(trimmed);
|
|
146
173
|
if (prefixedMatch === null) {
|
|
147
174
|
return null;
|
|
148
175
|
}
|
|
149
176
|
const parsed = Number(prefixedMatch[1]);
|
|
150
|
-
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
177
|
+
return Number.isSafeInteger(parsed) && parsed > 0 && parsed !== ZCASH_CHAIN_ID ? parsed : null;
|
|
151
178
|
}
|
|
152
179
|
export function isSupportedRail(rail) {
|
|
153
180
|
return SUPPORTED_RAIL_SET.has(rail);
|
|
@@ -195,6 +222,11 @@ export function getRuntimeDisabledFiatRails() {
|
|
|
195
222
|
export function isCryptoRail(rail) {
|
|
196
223
|
return parseCryptoRailChainId(rail) !== null;
|
|
197
224
|
}
|
|
225
|
+
/** A rail the customer pays on with fiat: excludes crypto rails and onramp
|
|
226
|
+
* rails such as Apple Pay, whose payments settle on a Relay rail instead. */
|
|
227
|
+
export function isFiatPaymentRail(rail) {
|
|
228
|
+
return !isCryptoRail(rail) && !isOnrampRail(rail);
|
|
229
|
+
}
|
|
198
230
|
/** Formats a chain id into the canonical relay rail string: `relay_<chainId>`. */
|
|
199
231
|
export function formatRelayRail(chainId) {
|
|
200
232
|
if (!Number.isSafeInteger(chainId) || chainId <= 0) {
|
|
@@ -202,8 +234,10 @@ export function formatRelayRail(chainId) {
|
|
|
202
234
|
}
|
|
203
235
|
return `${RELAY_RAIL_PREFIX}${chainId}`;
|
|
204
236
|
}
|
|
205
|
-
/** Normalizes
|
|
237
|
+
/** Normalizes a crypto rail into its canonical provider format (or null for non-crypto rails). */
|
|
206
238
|
export function normalizeCryptoRail(rail) {
|
|
239
|
+
if (rail?.trim() === ZCASH_RAIL)
|
|
240
|
+
return ZCASH_RAIL;
|
|
207
241
|
const chainId = parseCryptoRailChainId(rail);
|
|
208
242
|
return chainId === null ? null : formatRelayRail(chainId);
|
|
209
243
|
}
|
|
@@ -307,7 +341,11 @@ export function getRailDisplayName(rail) {
|
|
|
307
341
|
if (trimmed === '') {
|
|
308
342
|
return 'Unknown';
|
|
309
343
|
}
|
|
344
|
+
if (trimmed === ZCASH_RAIL)
|
|
345
|
+
return 'Zcash (NEAR Intents)';
|
|
310
346
|
const normalized = trimmed.toLowerCase();
|
|
347
|
+
if (isOnrampRail(normalized))
|
|
348
|
+
return PAYMENT_PLATFORM_LABELS[APPLE_PAY_RAIL];
|
|
311
349
|
if (isFiatSupportedRail(normalized)) {
|
|
312
350
|
return FIAT_RAIL_DISPLAY_NAMES[normalized];
|
|
313
351
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies a deterministic per-rail spread (±3%) on top of the live fiat-per-USD
|
|
3
|
+
* rate so sandbox quotes vary by payment method. The spread is hashed from the
|
|
4
|
+
* rail identifier + currency so refreshes are stable and different merchants
|
|
5
|
+
* see consistent rates, without any rail getting an unrealistic 0% spread.
|
|
6
|
+
*/
|
|
7
|
+
export declare function applySandboxRailSpread(baseRate: number, rail: string, currency: string): number;
|
|
8
|
+
/** Deterministic sandbox maker spread and fee-share sizing, shared with the CLI. */
|
|
9
|
+
export declare function buildSandboxSplitFiatAmounts(input: {
|
|
10
|
+
principalUsd: string;
|
|
11
|
+
currencyPerUsdRate: string;
|
|
12
|
+
rail: string;
|
|
13
|
+
fiatCurrency: string;
|
|
14
|
+
buyerFeeShareBps: number;
|
|
15
|
+
totalReferralFeeBps: number;
|
|
16
|
+
}): {
|
|
17
|
+
paymentAmount: string;
|
|
18
|
+
grossUsdcAmount: string;
|
|
19
|
+
conversionRate: string;
|
|
20
|
+
signalIntentAmountBaseUnits: string;
|
|
21
|
+
};
|
|
22
|
+
/** Preserve the saved signal for a full payment; scale a partial fiat payment at its saved maker rate. */
|
|
23
|
+
export declare function settleSandboxSplitFiatAmounts(input: {
|
|
24
|
+
signalIntentAmountBaseUnits: string;
|
|
25
|
+
conversionRateDecimal: string;
|
|
26
|
+
paidFiatAmount?: string;
|
|
27
|
+
}): {
|
|
28
|
+
paymentAmount: string;
|
|
29
|
+
grossUsdcAmount: string;
|
|
30
|
+
};
|