@relai-fi/x402 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.cjs +27 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +33 -1
- package/dist/client.d.ts +33 -1
- package/dist/client.js +27 -1
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +268 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +272 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins.d.cts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/react/index.cjs +27 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +27 -1
- package/dist/react/index.js.map +1 -1
- package/dist/{server-8gWh9oky.d.cts → server-CBZ2RjEP.d.cts} +49 -1
- package/dist/{server-JtlTglrW.d.ts → server-DaySqG5H.d.ts} +49 -1
- package/dist/server.cjs +145 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +145 -0
- package/dist/server.js.map +1 -1
- package/package.json +6 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,84 @@
|
|
|
1
|
-
export { B as BridgePluginConfig, D as DynamicPrice, j as FeedbackPluginConfig, F as FreeTierPluginConfig, b as PaymentInfo, g as PluginContext, h as PluginResult, P as ProtectOptions, R as Relai, d as RelaiIntegritasFlow, e as RelaiIntegritasOptions, f as RelaiPlugin, a as RelaiServerConfig, i as ScorePluginConfig, S as SettleResult, k as SolanaFeedbackPluginConfig, c as StripePayTo, R as default, s as stripePayTo } from './server-
|
|
2
|
-
export { RelayWebSocketFactory, RelayWebSocketLike, X402Client, X402ClientConfig, X402FetchInit, X402IntegritasConfig, X402IntegritasFlow, X402NetworkSelectionMode, X402RelayWsConfig, X402RelayWsError, X402RelayWsResponse, X402RequestOptions, default as createX402Client } from './client.cjs';
|
|
1
|
+
export { B as BridgePluginConfig, D as DynamicPrice, j as FeedbackPluginConfig, F as FreeTierPluginConfig, M as MppServerHandler, b as PaymentInfo, g as PluginContext, h as PluginResult, P as ProtectOptions, R as Relai, d as RelaiIntegritasFlow, e as RelaiIntegritasOptions, f as RelaiPlugin, a as RelaiServerConfig, i as ScorePluginConfig, S as SettleResult, k as SolanaFeedbackPluginConfig, c as StripePayTo, R as default, s as stripePayTo } from './server-CBZ2RjEP.cjs';
|
|
2
|
+
export { MppHandler, RelayWebSocketFactory, RelayWebSocketLike, X402Client, X402ClientConfig, X402FetchInit, X402IntegritasConfig, X402IntegritasFlow, X402NetworkSelectionMode, X402RelayWsConfig, X402RelayWsError, X402RelayWsResponse, X402RequestOptions, default as createX402Client } from './client.cjs';
|
|
3
3
|
export { RelayFeedbackConfig, submitRelayFeedback } from './relay-feedback.cjs';
|
|
4
4
|
export { A as AcceptsExtra, B as BASE_MAINNET_NETWORK, C as CAIP2_TO_NETWORK, b as CHAIN_IDS, E as EXPLORER_TX_URL, l as EvmWallet, N as NETWORK_CAIP2, e as NETWORK_LABELS, d as NETWORK_TOKENS, c as NetworkToken, P as PaymentAccept, o as PaymentRequired, R as RELAI_FACILITATOR_URL, h as RELAI_NETWORKS, a as RelaiNetwork, m as ResourceInfo, S as SOLANA_MAINNET_NETWORK, k as SolanaWallet, U as USDC_ADDRESSES, g as USDC_BASE, f as USDC_SOLANA, W as WalletSet, j as isEvm, i as isSolana, n as normalizeNetwork, r as resolveToken } from './types-Y9ni5XwY.cjs';
|
|
5
5
|
export { BridgeBalances, BridgeQuoteResult, BridgeResult } from './management.cjs';
|
|
6
6
|
export { NETWORK_V1_TO_V2, NETWORK_V2_TO_V1, convertPayloadToVersion, convertV1ToV2, convertV2ToV1, detectPayloadVersion, formatUsd, fromAtomicUnits, isEvmNetwork, isSolanaNetwork, networkV1ToV2, networkV2ToV1, normalizePaymentHeader, toAtomicUnits } from './utils/index.cjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Payment Code API — BLIK-style x402 payment codes
|
|
10
|
+
*
|
|
11
|
+
* generatePaymentCode() — sign EIP-3009 authorization and register on SKALE L3
|
|
12
|
+
* redeemPaymentCode() — redeem a code (payee calls this, triggers Base L2 settlement)
|
|
13
|
+
* getPaymentCode() — check code status
|
|
14
|
+
*/
|
|
15
|
+
interface PaymentCodeConfig {
|
|
16
|
+
facilitatorUrl?: string;
|
|
17
|
+
}
|
|
18
|
+
interface GeneratePaymentCodeParams {
|
|
19
|
+
/** EIP-3009 signer — must implement signTypedData */
|
|
20
|
+
signer: {
|
|
21
|
+
getAddress(): Promise<string>;
|
|
22
|
+
signTypedData(domain: object, types: object, value: object): Promise<string>;
|
|
23
|
+
};
|
|
24
|
+
/** Payee wallet address */
|
|
25
|
+
to: string;
|
|
26
|
+
/** Amount in USDC micro-units (6 decimals), e.g. 1000 = $0.001 */
|
|
27
|
+
value: string | bigint;
|
|
28
|
+
/** USDC contract address on Base L2 (defaults to Base mainnet USDC) */
|
|
29
|
+
usdcContract?: string;
|
|
30
|
+
/** TTL in seconds (default: 120) */
|
|
31
|
+
ttl?: number;
|
|
32
|
+
}
|
|
33
|
+
interface PaymentCode {
|
|
34
|
+
code: string;
|
|
35
|
+
validBefore: number;
|
|
36
|
+
expiresIn: number;
|
|
37
|
+
}
|
|
38
|
+
interface RedeemResult {
|
|
39
|
+
success: boolean;
|
|
40
|
+
code: string;
|
|
41
|
+
l3TxHash: string;
|
|
42
|
+
l2TxHash: string;
|
|
43
|
+
explorerUrl: string;
|
|
44
|
+
amount: string;
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
}
|
|
48
|
+
interface CodeStatus {
|
|
49
|
+
code: string;
|
|
50
|
+
from: string;
|
|
51
|
+
to: string;
|
|
52
|
+
value: string;
|
|
53
|
+
validBefore: number;
|
|
54
|
+
redeemed: boolean;
|
|
55
|
+
expired: boolean;
|
|
56
|
+
redeemable: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate a BLIK-style x402 payment code backed by EIP-3009.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const { code } = await generatePaymentCode(config, {
|
|
63
|
+
* signer: walletClient,
|
|
64
|
+
* to: "0xMerchant...",
|
|
65
|
+
* value: "1000", // $0.001 USDC
|
|
66
|
+
* ttl: 120,
|
|
67
|
+
* });
|
|
68
|
+
* // code = "X7K9P2AB"
|
|
69
|
+
*/
|
|
70
|
+
declare function generatePaymentCode(config: PaymentCodeConfig, params: GeneratePaymentCodeParams): Promise<PaymentCode>;
|
|
71
|
+
/**
|
|
72
|
+
* Redeem a payment code. Triggers Base L2 settlement via relayer.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* const result = await redeemPaymentCode(config, "X7K9P2AB");
|
|
76
|
+
* // result.l2TxHash = "0x..."
|
|
77
|
+
*/
|
|
78
|
+
declare function redeemPaymentCode(config: PaymentCodeConfig, code: string): Promise<RedeemResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Get the status of a payment code.
|
|
81
|
+
*/
|
|
82
|
+
declare function getPaymentCode(config: PaymentCodeConfig, code: string): Promise<CodeStatus>;
|
|
83
|
+
|
|
84
|
+
export { type CodeStatus, type GeneratePaymentCodeParams, type PaymentCode, type PaymentCodeConfig, type RedeemResult, generatePaymentCode, getPaymentCode, redeemPaymentCode };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,84 @@
|
|
|
1
|
-
export { B as BridgePluginConfig, D as DynamicPrice, j as FeedbackPluginConfig, F as FreeTierPluginConfig, b as PaymentInfo, g as PluginContext, h as PluginResult, P as ProtectOptions, R as Relai, d as RelaiIntegritasFlow, e as RelaiIntegritasOptions, f as RelaiPlugin, a as RelaiServerConfig, i as ScorePluginConfig, S as SettleResult, k as SolanaFeedbackPluginConfig, c as StripePayTo, R as default, s as stripePayTo } from './server-
|
|
2
|
-
export { RelayWebSocketFactory, RelayWebSocketLike, X402Client, X402ClientConfig, X402FetchInit, X402IntegritasConfig, X402IntegritasFlow, X402NetworkSelectionMode, X402RelayWsConfig, X402RelayWsError, X402RelayWsResponse, X402RequestOptions, default as createX402Client } from './client.js';
|
|
1
|
+
export { B as BridgePluginConfig, D as DynamicPrice, j as FeedbackPluginConfig, F as FreeTierPluginConfig, M as MppServerHandler, b as PaymentInfo, g as PluginContext, h as PluginResult, P as ProtectOptions, R as Relai, d as RelaiIntegritasFlow, e as RelaiIntegritasOptions, f as RelaiPlugin, a as RelaiServerConfig, i as ScorePluginConfig, S as SettleResult, k as SolanaFeedbackPluginConfig, c as StripePayTo, R as default, s as stripePayTo } from './server-DaySqG5H.js';
|
|
2
|
+
export { MppHandler, RelayWebSocketFactory, RelayWebSocketLike, X402Client, X402ClientConfig, X402FetchInit, X402IntegritasConfig, X402IntegritasFlow, X402NetworkSelectionMode, X402RelayWsConfig, X402RelayWsError, X402RelayWsResponse, X402RequestOptions, default as createX402Client } from './client.js';
|
|
3
3
|
export { RelayFeedbackConfig, submitRelayFeedback } from './relay-feedback.js';
|
|
4
4
|
export { A as AcceptsExtra, B as BASE_MAINNET_NETWORK, C as CAIP2_TO_NETWORK, b as CHAIN_IDS, E as EXPLORER_TX_URL, l as EvmWallet, N as NETWORK_CAIP2, e as NETWORK_LABELS, d as NETWORK_TOKENS, c as NetworkToken, P as PaymentAccept, o as PaymentRequired, R as RELAI_FACILITATOR_URL, h as RELAI_NETWORKS, a as RelaiNetwork, m as ResourceInfo, S as SOLANA_MAINNET_NETWORK, k as SolanaWallet, U as USDC_ADDRESSES, g as USDC_BASE, f as USDC_SOLANA, W as WalletSet, j as isEvm, i as isSolana, n as normalizeNetwork, r as resolveToken } from './types-Y9ni5XwY.js';
|
|
5
5
|
export { BridgeBalances, BridgeQuoteResult, BridgeResult } from './management.js';
|
|
6
6
|
export { NETWORK_V1_TO_V2, NETWORK_V2_TO_V1, convertPayloadToVersion, convertV1ToV2, convertV2ToV1, detectPayloadVersion, formatUsd, fromAtomicUnits, isEvmNetwork, isSolanaNetwork, networkV1ToV2, networkV2ToV1, normalizePaymentHeader, toAtomicUnits } from './utils/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Payment Code API — BLIK-style x402 payment codes
|
|
10
|
+
*
|
|
11
|
+
* generatePaymentCode() — sign EIP-3009 authorization and register on SKALE L3
|
|
12
|
+
* redeemPaymentCode() — redeem a code (payee calls this, triggers Base L2 settlement)
|
|
13
|
+
* getPaymentCode() — check code status
|
|
14
|
+
*/
|
|
15
|
+
interface PaymentCodeConfig {
|
|
16
|
+
facilitatorUrl?: string;
|
|
17
|
+
}
|
|
18
|
+
interface GeneratePaymentCodeParams {
|
|
19
|
+
/** EIP-3009 signer — must implement signTypedData */
|
|
20
|
+
signer: {
|
|
21
|
+
getAddress(): Promise<string>;
|
|
22
|
+
signTypedData(domain: object, types: object, value: object): Promise<string>;
|
|
23
|
+
};
|
|
24
|
+
/** Payee wallet address */
|
|
25
|
+
to: string;
|
|
26
|
+
/** Amount in USDC micro-units (6 decimals), e.g. 1000 = $0.001 */
|
|
27
|
+
value: string | bigint;
|
|
28
|
+
/** USDC contract address on Base L2 (defaults to Base mainnet USDC) */
|
|
29
|
+
usdcContract?: string;
|
|
30
|
+
/** TTL in seconds (default: 120) */
|
|
31
|
+
ttl?: number;
|
|
32
|
+
}
|
|
33
|
+
interface PaymentCode {
|
|
34
|
+
code: string;
|
|
35
|
+
validBefore: number;
|
|
36
|
+
expiresIn: number;
|
|
37
|
+
}
|
|
38
|
+
interface RedeemResult {
|
|
39
|
+
success: boolean;
|
|
40
|
+
code: string;
|
|
41
|
+
l3TxHash: string;
|
|
42
|
+
l2TxHash: string;
|
|
43
|
+
explorerUrl: string;
|
|
44
|
+
amount: string;
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
}
|
|
48
|
+
interface CodeStatus {
|
|
49
|
+
code: string;
|
|
50
|
+
from: string;
|
|
51
|
+
to: string;
|
|
52
|
+
value: string;
|
|
53
|
+
validBefore: number;
|
|
54
|
+
redeemed: boolean;
|
|
55
|
+
expired: boolean;
|
|
56
|
+
redeemable: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate a BLIK-style x402 payment code backed by EIP-3009.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const { code } = await generatePaymentCode(config, {
|
|
63
|
+
* signer: walletClient,
|
|
64
|
+
* to: "0xMerchant...",
|
|
65
|
+
* value: "1000", // $0.001 USDC
|
|
66
|
+
* ttl: 120,
|
|
67
|
+
* });
|
|
68
|
+
* // code = "X7K9P2AB"
|
|
69
|
+
*/
|
|
70
|
+
declare function generatePaymentCode(config: PaymentCodeConfig, params: GeneratePaymentCodeParams): Promise<PaymentCode>;
|
|
71
|
+
/**
|
|
72
|
+
* Redeem a payment code. Triggers Base L2 settlement via relayer.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* const result = await redeemPaymentCode(config, "X7K9P2AB");
|
|
76
|
+
* // result.l2TxHash = "0x..."
|
|
77
|
+
*/
|
|
78
|
+
declare function redeemPaymentCode(config: PaymentCodeConfig, code: string): Promise<RedeemResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Get the status of a payment code.
|
|
81
|
+
*/
|
|
82
|
+
declare function getPaymentCode(config: PaymentCodeConfig, code: string): Promise<CodeStatus>;
|
|
83
|
+
|
|
84
|
+
export { type CodeStatus, type GeneratePaymentCodeParams, type PaymentCode, type PaymentCodeConfig, type RedeemResult, generatePaymentCode, getPaymentCode, redeemPaymentCode };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/types.ts
|
|
2
9
|
var RELAI_FACILITATOR_URL = "https://facilitator.x402.fi";
|
|
3
10
|
var NETWORK_CAIP2 = {
|
|
@@ -454,6 +461,7 @@ var Relai = class {
|
|
|
454
461
|
this.network = config.network;
|
|
455
462
|
this.facilitatorUrl = config.facilitatorUrl || RELAI_FACILITATOR_URL;
|
|
456
463
|
this.plugins = config.plugins ?? [];
|
|
464
|
+
this.mpp = config.mpp;
|
|
457
465
|
}
|
|
458
466
|
async runPluginInit() {
|
|
459
467
|
for (const plugin of this.plugins) {
|
|
@@ -558,6 +566,119 @@ var Relai = class {
|
|
|
558
566
|
const integritasFlow = headerIntegritasFlow || configuredIntegritas.flow;
|
|
559
567
|
const integritasMode = integritasFlow === "single" ? "single_signature_fee_included" : integritasFlow === "dual" ? "dual_signature_split" : void 0;
|
|
560
568
|
const paymentHeader = req.headers["x-payment"] || req.headers["payment-signature"] || req.headers["x-payment-signature"];
|
|
569
|
+
const authHeader = req.headers["authorization"] || "";
|
|
570
|
+
console.log(`[Relai] MPP check: paymentHeader=${!!paymentHeader}, hasMpp=${!!self.mpp}, authHeader=${authHeader?.slice(0, 30)}`);
|
|
571
|
+
if (!paymentHeader && self.mpp && /^Payment\s+/i.test(authHeader)) {
|
|
572
|
+
try {
|
|
573
|
+
const mppAmount = resolvedPrice.toFixed(6);
|
|
574
|
+
const mppUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
575
|
+
const mppHeaders = new Headers();
|
|
576
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
577
|
+
if (typeof v === "string") mppHeaders.set(k, v);
|
|
578
|
+
}
|
|
579
|
+
const mppRequest = new Request(mppUrl, { method: req.method, headers: mppHeaders });
|
|
580
|
+
const chargeHandler = self.mpp.charge({ amount: mppAmount });
|
|
581
|
+
const mppResult = await chargeHandler(mppRequest);
|
|
582
|
+
console.log(`[Relai] MPP charge result: status=${mppResult.status}, keys=${Object.keys(mppResult)}, hasChallenge=${!!mppResult.challenge}, hasWithReceipt=${!!mppResult.withReceipt}`);
|
|
583
|
+
if (mppResult.status === 402 && mppResult.challenge instanceof Response) {
|
|
584
|
+
const retryAuth = mppResult.challenge.headers.get("www-authenticate");
|
|
585
|
+
console.log(`[Relai] MPP re-challenged (credential not accepted). New WWW-Auth: ${retryAuth?.slice(0, 60)}`);
|
|
586
|
+
}
|
|
587
|
+
if (mppResult.status !== 200 && !mppResult.withReceipt && mppResult.status !== 402) {
|
|
588
|
+
if (self.plugins.length > 0) {
|
|
589
|
+
const mppFailCtx = {
|
|
590
|
+
network,
|
|
591
|
+
price: resolvedPrice,
|
|
592
|
+
path: req.path || req.originalUrl || "/",
|
|
593
|
+
method: (req.method || "GET").toUpperCase()
|
|
594
|
+
};
|
|
595
|
+
const mppFailResult = {
|
|
596
|
+
success: false,
|
|
597
|
+
payer: "mpp",
|
|
598
|
+
error: `MPP charge failed with status ${mppResult.status}`
|
|
599
|
+
};
|
|
600
|
+
for (const plugin of self.plugins) {
|
|
601
|
+
if (!plugin.afterSettled) continue;
|
|
602
|
+
try {
|
|
603
|
+
await plugin.afterSettled(req, mppFailResult, mppFailCtx);
|
|
604
|
+
} catch (pluginErr) {
|
|
605
|
+
console.warn(`[Relai] Plugin '${plugin.name}' afterSettled error (non-blocking):`, pluginErr);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (mppResult.status === 200 || mppResult.withReceipt) {
|
|
611
|
+
const receipt = mppResult.receipt || {};
|
|
612
|
+
const paymentInfo2 = {
|
|
613
|
+
verified: true,
|
|
614
|
+
transactionId: receipt.reference || "",
|
|
615
|
+
payer: receipt.method || "mpp",
|
|
616
|
+
network,
|
|
617
|
+
amount: resolvedPrice
|
|
618
|
+
};
|
|
619
|
+
req.payment = paymentInfo2;
|
|
620
|
+
req.x402Payer = receipt.method ? `${receipt.method}-mpp` : "mpp";
|
|
621
|
+
req.x402Paid = true;
|
|
622
|
+
req.x402Transaction = receipt.reference || "";
|
|
623
|
+
req.x402Network = network;
|
|
624
|
+
if (mppResult.withReceipt) {
|
|
625
|
+
const dummyResponse = new Response(null);
|
|
626
|
+
const receiptResponse = mppResult.withReceipt(dummyResponse);
|
|
627
|
+
const receiptHeader = receiptResponse.headers.get("payment-receipt");
|
|
628
|
+
if (receiptHeader) res.setHeader("Payment-Receipt", receiptHeader);
|
|
629
|
+
}
|
|
630
|
+
options.onPaymentSettled?.(req, {
|
|
631
|
+
success: true,
|
|
632
|
+
transaction: receipt.reference,
|
|
633
|
+
payer: req.x402Payer
|
|
634
|
+
});
|
|
635
|
+
if (self.plugins.length > 0) {
|
|
636
|
+
const settleCtx = {
|
|
637
|
+
network,
|
|
638
|
+
price: resolvedPrice,
|
|
639
|
+
path: req.path || req.originalUrl || "/",
|
|
640
|
+
method: (req.method || "GET").toUpperCase()
|
|
641
|
+
};
|
|
642
|
+
for (const plugin of self.plugins) {
|
|
643
|
+
if (!plugin.afterSettled) continue;
|
|
644
|
+
try {
|
|
645
|
+
await plugin.afterSettled(req, {
|
|
646
|
+
success: true,
|
|
647
|
+
transaction: receipt.reference,
|
|
648
|
+
payer: req.x402Payer
|
|
649
|
+
}, settleCtx);
|
|
650
|
+
} catch (pluginErr) {
|
|
651
|
+
console.warn(`[Relai] Plugin '${plugin.name}' afterSettled error (non-blocking):`, pluginErr);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return next();
|
|
656
|
+
}
|
|
657
|
+
} catch (mppErr) {
|
|
658
|
+
console.warn(`[Relai] MPP verification failed (${mppErr instanceof Error ? mppErr.message : mppErr}), falling through to x402`);
|
|
659
|
+
if (self.plugins.length > 0) {
|
|
660
|
+
const mppErrCtx = {
|
|
661
|
+
network,
|
|
662
|
+
price: resolvedPrice,
|
|
663
|
+
path: req.path || req.originalUrl || "/",
|
|
664
|
+
method: (req.method || "GET").toUpperCase()
|
|
665
|
+
};
|
|
666
|
+
const mppErrResult = {
|
|
667
|
+
success: false,
|
|
668
|
+
payer: "mpp",
|
|
669
|
+
error: mppErr instanceof Error ? mppErr.message : String(mppErr)
|
|
670
|
+
};
|
|
671
|
+
for (const plugin of self.plugins) {
|
|
672
|
+
if (!plugin.afterSettled) continue;
|
|
673
|
+
try {
|
|
674
|
+
await plugin.afterSettled(req, mppErrResult, mppErrCtx);
|
|
675
|
+
} catch (pluginErr) {
|
|
676
|
+
console.warn(`[Relai] Plugin '${plugin.name}' afterSettled error (non-blocking):`, pluginErr);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
561
682
|
if (!paymentHeader && self.plugins.length > 0) {
|
|
562
683
|
if (!self.pluginInitPromise) {
|
|
563
684
|
self.pluginInitPromise = self.runPluginInit();
|
|
@@ -712,6 +833,21 @@ var Relai = class {
|
|
|
712
833
|
}
|
|
713
834
|
}
|
|
714
835
|
}
|
|
836
|
+
if (self.mpp?.charge) {
|
|
837
|
+
try {
|
|
838
|
+
const mppAmount = resolvedPrice.toFixed(6);
|
|
839
|
+
const chargeHandler = self.mpp.charge({ amount: mppAmount });
|
|
840
|
+
const mockReq = new Request(`${req.protocol}://${req.get("host")}${req.originalUrl}`);
|
|
841
|
+
const mppResult = await chargeHandler(mockReq);
|
|
842
|
+
if (mppResult?.challenge instanceof Response) {
|
|
843
|
+
const wwwAuth = mppResult.challenge.headers.get("www-authenticate");
|
|
844
|
+
if (wwwAuth) {
|
|
845
|
+
res.setHeader("WWW-Authenticate", wwwAuth);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
} catch {
|
|
849
|
+
}
|
|
850
|
+
}
|
|
715
851
|
return res.status(402).json(paymentRequiredResponse);
|
|
716
852
|
}
|
|
717
853
|
let paymentProof;
|
|
@@ -802,6 +938,22 @@ var Relai = class {
|
|
|
802
938
|
});
|
|
803
939
|
const result = await settleRes.json();
|
|
804
940
|
if (!result.success) {
|
|
941
|
+
if (self.plugins.length > 0) {
|
|
942
|
+
const failCtx = {
|
|
943
|
+
network,
|
|
944
|
+
price: resolvedPrice,
|
|
945
|
+
path: req.path || req.originalUrl || "/",
|
|
946
|
+
method: (req.method || "GET").toUpperCase()
|
|
947
|
+
};
|
|
948
|
+
for (const plugin of self.plugins) {
|
|
949
|
+
if (!plugin.afterSettled) continue;
|
|
950
|
+
try {
|
|
951
|
+
await plugin.afterSettled(req, result, failCtx);
|
|
952
|
+
} catch (pluginErr) {
|
|
953
|
+
console.warn(`[Relai] Plugin '${plugin.name}' afterSettled error (non-blocking):`, pluginErr);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
805
957
|
return res.status(402).json({
|
|
806
958
|
x402Version: 2,
|
|
807
959
|
error: result.errorReason || result.error || "Payment settlement failed"
|
|
@@ -926,7 +1078,8 @@ function createX402Client(config) {
|
|
|
926
1078
|
maxAmountAtomic,
|
|
927
1079
|
integritas,
|
|
928
1080
|
verbose = false,
|
|
929
|
-
defaultHeaders = {}
|
|
1081
|
+
defaultHeaders = {},
|
|
1082
|
+
mpp
|
|
930
1083
|
} = config;
|
|
931
1084
|
const relayWsEnabled = relayWs?.enabled === true;
|
|
932
1085
|
const relayWsPreflightTimeoutMs = relayWs?.preflightTimeoutMs ?? 5e3;
|
|
@@ -1906,6 +2059,31 @@ function createX402Client(config) {
|
|
|
1906
2059
|
const response = await fetch(input, requestInitWithHeaders);
|
|
1907
2060
|
if (response.status !== 402) return response;
|
|
1908
2061
|
log("Got 402 Payment Required");
|
|
2062
|
+
if (mpp) {
|
|
2063
|
+
const wwwAuth = response.headers.get("www-authenticate");
|
|
2064
|
+
if (wwwAuth && /^Payment\s+/i.test(wwwAuth.trim())) {
|
|
2065
|
+
log("MPP challenge detected in WWW-Authenticate header");
|
|
2066
|
+
try {
|
|
2067
|
+
const credential = await mpp.createCredential(response);
|
|
2068
|
+
if (credential) {
|
|
2069
|
+
log("MPP credential created, retrying with Authorization: Payment");
|
|
2070
|
+
const mppRetry = await fetch(input, {
|
|
2071
|
+
...requestInitWithHeaders,
|
|
2072
|
+
headers: {
|
|
2073
|
+
...requestHeaders,
|
|
2074
|
+
"Authorization": credential.startsWith("Payment ") ? credential : `Payment ${credential}`
|
|
2075
|
+
}
|
|
2076
|
+
});
|
|
2077
|
+
if (mppRetry.status !== 402) {
|
|
2078
|
+
return mppRetry;
|
|
2079
|
+
}
|
|
2080
|
+
log("MPP retry still returned 402, falling through to x402");
|
|
2081
|
+
}
|
|
2082
|
+
} catch (mppErr) {
|
|
2083
|
+
log(`MPP payment failed (${mppErr instanceof Error ? mppErr.message : mppErr}), falling through to x402`);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1909
2087
|
let requirementsFromBody = null;
|
|
1910
2088
|
try {
|
|
1911
2089
|
requirementsFromBody = await response.clone().json();
|
|
@@ -2033,6 +2211,96 @@ function submitRelayFeedback(config) {
|
|
|
2033
2211
|
})();
|
|
2034
2212
|
}
|
|
2035
2213
|
|
|
2214
|
+
// src/payment-codes.ts
|
|
2215
|
+
var DEFAULT_FACILITATOR = "https://relai.fi/facilitator";
|
|
2216
|
+
var DEFAULT_USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
2217
|
+
var EIP3009_TYPES = {
|
|
2218
|
+
TransferWithAuthorization: [
|
|
2219
|
+
{ name: "from", type: "address" },
|
|
2220
|
+
{ name: "to", type: "address" },
|
|
2221
|
+
{ name: "value", type: "uint256" },
|
|
2222
|
+
{ name: "validAfter", type: "uint256" },
|
|
2223
|
+
{ name: "validBefore", type: "uint256" },
|
|
2224
|
+
{ name: "nonce", type: "bytes32" }
|
|
2225
|
+
]
|
|
2226
|
+
};
|
|
2227
|
+
function randomBytes32() {
|
|
2228
|
+
const bytes = new Uint8Array(32);
|
|
2229
|
+
if (typeof globalThis.crypto !== "undefined") {
|
|
2230
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2231
|
+
} else {
|
|
2232
|
+
const { randomBytes } = __require("crypto");
|
|
2233
|
+
randomBytes(32).copy(Buffer.from(bytes.buffer));
|
|
2234
|
+
}
|
|
2235
|
+
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2236
|
+
}
|
|
2237
|
+
async function generatePaymentCode(config, params) {
|
|
2238
|
+
const { signer, to, value, usdcContract, ttl = 120 } = params;
|
|
2239
|
+
const facilitatorUrl = config.facilitatorUrl || DEFAULT_FACILITATOR;
|
|
2240
|
+
const usdc = usdcContract || DEFAULT_USDC_BASE;
|
|
2241
|
+
const from = await signer.getAddress();
|
|
2242
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2243
|
+
const validAfter = 0;
|
|
2244
|
+
const validBefore = now + ttl;
|
|
2245
|
+
const nonce = randomBytes32();
|
|
2246
|
+
const domain = {
|
|
2247
|
+
name: "USD Coin",
|
|
2248
|
+
version: "2",
|
|
2249
|
+
chainId: 8453,
|
|
2250
|
+
// Base mainnet
|
|
2251
|
+
verifyingContract: usdc
|
|
2252
|
+
};
|
|
2253
|
+
const message = {
|
|
2254
|
+
from,
|
|
2255
|
+
to,
|
|
2256
|
+
value: BigInt(value).toString(),
|
|
2257
|
+
validAfter,
|
|
2258
|
+
validBefore,
|
|
2259
|
+
nonce
|
|
2260
|
+
};
|
|
2261
|
+
const signature = await signer.signTypedData(domain, EIP3009_TYPES, message);
|
|
2262
|
+
const res = await fetch(`${facilitatorUrl}/payment-codes`, {
|
|
2263
|
+
method: "POST",
|
|
2264
|
+
headers: { "Content-Type": "application/json" },
|
|
2265
|
+
body: JSON.stringify({
|
|
2266
|
+
from,
|
|
2267
|
+
to,
|
|
2268
|
+
value: BigInt(value).toString(),
|
|
2269
|
+
validAfter,
|
|
2270
|
+
validBefore,
|
|
2271
|
+
nonce,
|
|
2272
|
+
signature,
|
|
2273
|
+
usdcContract: usdc
|
|
2274
|
+
})
|
|
2275
|
+
});
|
|
2276
|
+
if (!res.ok) {
|
|
2277
|
+
const err = await res.json().catch(() => ({}));
|
|
2278
|
+
throw new Error(`Failed to register payment code: ${err.error || res.status}`);
|
|
2279
|
+
}
|
|
2280
|
+
return res.json();
|
|
2281
|
+
}
|
|
2282
|
+
async function redeemPaymentCode(config, code) {
|
|
2283
|
+
const facilitatorUrl = config.facilitatorUrl || DEFAULT_FACILITATOR;
|
|
2284
|
+
const res = await fetch(`${facilitatorUrl}/payment-codes/${code.toUpperCase()}/redeem`, {
|
|
2285
|
+
method: "POST",
|
|
2286
|
+
headers: { "Content-Type": "application/json" }
|
|
2287
|
+
});
|
|
2288
|
+
if (!res.ok) {
|
|
2289
|
+
const err = await res.json().catch(() => ({}));
|
|
2290
|
+
throw new Error(`Failed to redeem payment code: ${err.error || res.status}`);
|
|
2291
|
+
}
|
|
2292
|
+
return res.json();
|
|
2293
|
+
}
|
|
2294
|
+
async function getPaymentCode(config, code) {
|
|
2295
|
+
const facilitatorUrl = config.facilitatorUrl || DEFAULT_FACILITATOR;
|
|
2296
|
+
const res = await fetch(`${facilitatorUrl}/payment-codes/${code.toUpperCase()}`);
|
|
2297
|
+
if (!res.ok) {
|
|
2298
|
+
const err = await res.json().catch(() => ({}));
|
|
2299
|
+
throw new Error(`Payment code not found: ${err.error || res.status}`);
|
|
2300
|
+
}
|
|
2301
|
+
return res.json();
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2036
2304
|
// src/utils/payload-converter.ts
|
|
2037
2305
|
var NETWORK_V1_TO_V2 = {
|
|
2038
2306
|
"solana": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
@@ -2197,6 +2465,8 @@ export {
|
|
|
2197
2465
|
detectPayloadVersion,
|
|
2198
2466
|
formatUsd,
|
|
2199
2467
|
fromAtomicUnits,
|
|
2468
|
+
generatePaymentCode,
|
|
2469
|
+
getPaymentCode,
|
|
2200
2470
|
isEvm,
|
|
2201
2471
|
isEvmNetwork,
|
|
2202
2472
|
isSolana,
|
|
@@ -2205,6 +2475,7 @@ export {
|
|
|
2205
2475
|
networkV2ToV1,
|
|
2206
2476
|
normalizeNetwork,
|
|
2207
2477
|
normalizePaymentHeader,
|
|
2478
|
+
redeemPaymentCode,
|
|
2208
2479
|
resolveToken,
|
|
2209
2480
|
stripePayTo,
|
|
2210
2481
|
submitRelayFeedback,
|