@relai-fi/x402 0.6.6 → 0.6.7

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
@@ -1599,6 +1599,141 @@ See [`examples/bridge/`](./examples/bridge/) for runnable examples of each appro
1599
1599
 
1600
1600
  ---
1601
1601
 
1602
+ ## Payment Codes
1603
+
1604
+ BLIK-style one-time payment codes — pre-signed EIP-3009 tokens that can be generated in advance and redeemed later, without a wallet at redemption time. Ideal for AI agents and walletless buyers.
1605
+
1606
+ ```typescript
1607
+ import {
1608
+ createPrivateKeySigner,
1609
+ generatePaymentCode,
1610
+ generatePaymentCodesBatch,
1611
+ redeemPaymentCode,
1612
+ getPaymentCode,
1613
+ cancelPaymentCode,
1614
+ } from '@relai-fi/x402';
1615
+ import { ethers } from 'ethers';
1616
+
1617
+ const config = { facilitatorUrl: 'https://relai.fi/facilitator' };
1618
+
1619
+ // ── Create a signer from a private key (agent / server-side) ──────────────
1620
+ const signer = createPrivateKeySigner(process.env.AGENT_PRIVATE_KEY!);
1621
+
1622
+ // ── Generate a single payment code ($10 USDC, expires in 24 h) ───────────
1623
+ const code = await generatePaymentCode(config, signer, {
1624
+ amount: 10_000_000, // $10.00 in µUSDC
1625
+ network: 'base-sepolia',
1626
+ });
1627
+ console.log('Code:', code.code); // "ABCD1234"
1628
+
1629
+ // ── Batch generate 5 codes (requires API key) ─────────────────────────────
1630
+ const batch = await generatePaymentCodesBatch(config, signer, {
1631
+ amount: 5_000_000,
1632
+ network: 'base-sepolia',
1633
+ count: 5,
1634
+ apiKey: process.env.RELAI_API_KEY!,
1635
+ });
1636
+
1637
+ // ── Check status ──────────────────────────────────────────────────────────
1638
+ const status = await getPaymentCode(config, 'ABCD1234');
1639
+ console.log(status.redeemed, status.expired, status.value);
1640
+
1641
+ // ── Redeem (settle USDC to a payee) ──────────────────────────────────────
1642
+ const result = await redeemPaymentCode(config, 'ABCD1234', {
1643
+ payee: '0xMerchantWallet',
1644
+ });
1645
+ console.log('Explorer:', result.explorerUrl);
1646
+
1647
+ // ── Cancel before use ─────────────────────────────────────────────────────
1648
+ await cancelPaymentCode(config, 'ABCD1234');
1649
+ ```
1650
+
1651
+ ### `generatePaymentCode` options
1652
+
1653
+ | Option | Type | Default | Description |
1654
+ |--------|------|---------|-------------|
1655
+ | `amount` | `number` | — | Amount in µUSDC (1 USDC = 1 000 000) |
1656
+ | `network` | `string` | `'base-sepolia'` | Settlement network |
1657
+ | `description` | `string` | — | Optional note stored with the code |
1658
+ | `payee` | `string` | — | Lock the code to a specific payee address |
1659
+ | `ttl` | `number` | `86400` | Expiry in seconds (default: 24 h) |
1660
+
1661
+ ### `RedeemResult` fields
1662
+
1663
+ | Field | Type | Description |
1664
+ |-------|------|-------------|
1665
+ | `success` | `boolean` | Settlement succeeded |
1666
+ | `code` | `string` | The redeemed code |
1667
+ | `l2TxHash` | `string` | On-chain tx hash |
1668
+ | `explorerUrl` | `string` | Block explorer link |
1669
+ | `amount` | `string` | Amount settled (µUSDC) |
1670
+ | `change` | `string?` | Remainder returned to buyer (µUSDC), if partial |
1671
+ | `changeMode` | `'code' \| 'wallet'?` | How change was returned |
1672
+ | `changeCode` | `string?` | New payment code for change (when `changeMode === 'code'`) |
1673
+
1674
+ ---
1675
+
1676
+ ## Payment Requests
1677
+
1678
+ Merchant-initiated invoices — the merchant creates a payment request, shares the code or link, and any buyer (or agent) can pay it.
1679
+
1680
+ ```typescript
1681
+ import {
1682
+ createPayRequest,
1683
+ getPayRequest,
1684
+ payPayRequest,
1685
+ payPayRequestWithCode,
1686
+ } from '@relai-fi/x402';
1687
+
1688
+ const config = { facilitatorUrl: 'https://relai.fi/facilitator' };
1689
+
1690
+ // ── Merchant: create an invoice ───────────────────────────────────────────
1691
+ const req = await createPayRequest(config, {
1692
+ to: '0xMerchantWallet',
1693
+ amount: 5_000_000, // $5.00 USDC
1694
+ network: 'base-sepolia',
1695
+ description: 'Order #42',
1696
+ ttl: 3600, // expires in 1 h
1697
+ });
1698
+ console.log('Invoice code:', req.code); // "MW78SGTW"
1699
+ console.log('Pay URL:', req.payUrl); // "https://relai.fi/pay#MW78SGTW"
1700
+
1701
+ // ── Buyer: read the request ────────────────────────────────────────────────
1702
+ const info = await getPayRequest(config, 'MW78SGTW');
1703
+ console.log(`$${Number(info.amount) / 1e6} USDC → ${info.to}`);
1704
+
1705
+ // ── Buyer: pay with EIP-3009 signer (has wallet) ─────────────────────────
1706
+ const signer = createPrivateKeySigner(process.env.BUYER_PRIVATE_KEY!);
1707
+ const paid = await payPayRequest(config, 'MW78SGTW', signer);
1708
+ console.log('Explorer:', paid.explorerUrl);
1709
+
1710
+ // ── Buyer: pay using a pre-generated payment code (no wallet at payment time)
1711
+ const result = await payPayRequestWithCode(config, 'MW78SGTW', 'MYBLIK78');
1712
+ console.log('Success:', result.success);
1713
+ console.log('Explorer:', result.explorerUrl);
1714
+
1715
+ // If the code covers more than the invoice → change is returned as a new code
1716
+ if (result.changeCode) {
1717
+ console.log(`Change $${Number(result.change) / 1e6} USDC → code: ${result.changeCode}`);
1718
+ }
1719
+ ```
1720
+
1721
+ ### `payPayRequestWithCode` options
1722
+
1723
+ | Option | Type | Default | Description |
1724
+ |--------|------|---------|-------------|
1725
+ | `returnChange` | `'code' \| 'wallet'` | `'code'` | How to return surplus when code value > invoice |
1726
+ | `allowOverpayment` | `boolean` | `true` | If `false`, throws when code value ≠ invoice amount |
1727
+
1728
+ **`returnChange` behaviour:**
1729
+
1730
+ | Value | Mechanism | Requires buyer wallet? |
1731
+ |-------|-----------|----------------------|
1732
+ | `'code'` (default) | Relayer pays merchant, generates a new code for the remainder | No |
1733
+ | `'wallet'` | `PaymentSettler.settleExact()` — merchant + change in one atomic tx | Yes (`from` address) |
1734
+
1735
+ ---
1736
+
1602
1737
  ## Development
1603
1738
 
1604
1739
  ```bash
package/dist/client.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { W as WalletSet, k as SolanaWallet, a as RelaiNetwork } from './types-Y9ni5XwY.cjs';
1
+ import { W as WalletSet, S as SolanaWallet, R as RelaiNetwork } from './types-DjEveKgt.cjs';
2
2
 
3
3
  type X402NetworkSelectionMode = 'prefer_then_any' | 'strict_preferred';
4
4
  /**
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { W as WalletSet, k as SolanaWallet, a as RelaiNetwork } from './types-Y9ni5XwY.js';
1
+ import { W as WalletSet, S as SolanaWallet, R as RelaiNetwork } from './types-DjEveKgt.js';
2
2
 
3
3
  type X402NetworkSelectionMode = 'prefer_then_any' | 'strict_preferred';
4
4
  /**
package/dist/index.cjs CHANGED
@@ -7021,6 +7021,7 @@ __export(index_exports, {
7021
7021
  CHAIN_IDS: () => CHAIN_IDS,
7022
7022
  EXPLORER_TX_URL: () => EXPLORER_TX_URL,
7023
7023
  NETWORK_CAIP2: () => NETWORK_CAIP2,
7024
+ NETWORK_CONFIGS: () => NETWORK_CONFIGS,
7024
7025
  NETWORK_LABELS: () => NETWORK_LABELS,
7025
7026
  NETWORK_TOKENS: () => NETWORK_TOKENS,
7026
7027
  NETWORK_V1_TO_V2: () => NETWORK_V1_TO_V2,
@@ -7032,9 +7033,12 @@ __export(index_exports, {
7032
7033
  USDC_ADDRESSES: () => USDC_ADDRESSES,
7033
7034
  USDC_BASE: () => USDC_BASE,
7034
7035
  USDC_SOLANA: () => USDC_SOLANA,
7036
+ cancelPaymentCode: () => cancelPaymentCode,
7035
7037
  convertPayloadToVersion: () => convertPayloadToVersion,
7036
7038
  convertV1ToV2: () => convertV1ToV2,
7037
7039
  convertV2ToV1: () => convertV2ToV1,
7040
+ createPayRequest: () => createPayRequest,
7041
+ createPrivateKeySigner: () => createPrivateKeySigner,
7038
7042
  createX402Client: () => createX402Client,
7039
7043
  default: () => Relai,
7040
7044
  detectPayloadVersion: () => detectPayloadVersion,
@@ -7044,6 +7048,8 @@ __export(index_exports, {
7044
7048
  formatUsd: () => formatUsd,
7045
7049
  fromAtomicUnits: () => fromAtomicUnits,
7046
7050
  generatePaymentCode: () => generatePaymentCode,
7051
+ generatePaymentCodesBatch: () => generatePaymentCodesBatch,
7052
+ getPayRequest: () => getPayRequest,
7047
7053
  getPaymentCode: () => getPaymentCode,
7048
7054
  isEvm: () => isEvm,
7049
7055
  isEvmNetwork: () => isEvmNetwork,
@@ -7053,6 +7059,8 @@ __export(index_exports, {
7053
7059
  networkV2ToV1: () => networkV2ToV1,
7054
7060
  normalizeNetwork: () => normalizeNetwork,
7055
7061
  normalizePaymentHeader: () => normalizePaymentHeader,
7062
+ payPayRequest: () => payPayRequest,
7063
+ payPayRequestWithCode: () => payPayRequestWithCode,
7056
7064
  redeemPaymentCode: () => redeemPaymentCode,
7057
7065
  resolveToken: () => resolveToken,
7058
7066
  stripePayTo: () => stripePayTo,
@@ -9435,8 +9443,37 @@ function submitRelayFeedback(config2) {
9435
9443
  }
9436
9444
 
9437
9445
  // src/payment-codes.ts
9438
- var DEFAULT_FACILITATOR = "https://relai.fi/facilitator";
9439
- var DEFAULT_USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
9446
+ var import_ethers2 = require("ethers");
9447
+ var NETWORK_CONFIGS = {
9448
+ "base": {
9449
+ chainId: 8453,
9450
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
9451
+ domainName: "USD Coin",
9452
+ rpc: "https://mainnet.base.org",
9453
+ settlementNetwork: "base"
9454
+ },
9455
+ "base-sepolia": {
9456
+ chainId: 84532,
9457
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
9458
+ domainName: "USDC",
9459
+ rpc: "https://sepolia.base.org",
9460
+ settlementNetwork: "base-sepolia"
9461
+ },
9462
+ "skale-base-sepolia": {
9463
+ chainId: 324705682,
9464
+ usdc: "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",
9465
+ domainName: "Bridged USDC (SKALE Bridge)",
9466
+ rpc: "https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",
9467
+ settlementNetwork: "skale-base-sepolia"
9468
+ },
9469
+ "skale-base": {
9470
+ chainId: 1482601649,
9471
+ usdc: "0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",
9472
+ domainName: "Bridged USDC",
9473
+ rpc: "https://skale-base.skalenodes.com/v1/base",
9474
+ settlementNetwork: "skale-base"
9475
+ }
9476
+ };
9440
9477
  var EIP3009_TYPES = {
9441
9478
  TransferWithAuthorization: [
9442
9479
  { name: "from", type: "address" },
@@ -9447,6 +9484,14 @@ var EIP3009_TYPES = {
9447
9484
  { name: "nonce", type: "bytes32" }
9448
9485
  ]
9449
9486
  };
9487
+ function createPrivateKeySigner(privateKey) {
9488
+ const wallet = new import_ethers2.ethers.Wallet(privateKey);
9489
+ return {
9490
+ getAddress: () => Promise.resolve(wallet.address),
9491
+ signTypedData: (domain2, types, value) => wallet.signTypedData(domain2, types, value)
9492
+ };
9493
+ }
9494
+ var DEFAULT_FACILITATOR = "https://relai.fi/facilitator";
9450
9495
  function randomBytes32() {
9451
9496
  const bytes = new Uint8Array(32);
9452
9497
  if (typeof globalThis.crypto !== "undefined") {
@@ -9457,69 +9502,323 @@ function randomBytes32() {
9457
9502
  }
9458
9503
  return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
9459
9504
  }
9460
- async function generatePaymentCode(config2, params) {
9461
- const { signer, to, value, usdcContract, ttl = 120 } = params;
9462
- const facilitatorUrl = config2.facilitatorUrl || DEFAULT_FACILITATOR;
9463
- const usdc = usdcContract || DEFAULT_USDC_BASE;
9464
- const from4 = await signer.getAddress();
9465
- const now = Math.floor(Date.now() / 1e3);
9466
- const validAfter = 0;
9467
- const validBefore = now + ttl;
9468
- const nonce = randomBytes32();
9505
+ async function fetchToAddress(facilitatorUrl, network) {
9506
+ const res = await fetch(`${facilitatorUrl}/payment-codes/relayer?network=${network}`);
9507
+ if (!res.ok) throw new Error("Failed to fetch relayer address from facilitator");
9508
+ const data = await res.json();
9509
+ if (!data.toAddress) throw new Error("Facilitator returned no toAddress");
9510
+ return data.toAddress;
9511
+ }
9512
+ async function signEip3009(signer, net, from4, toAddress, value, validAfter, validBefore, nonce, usdcOverride) {
9469
9513
  const domain2 = {
9470
- name: "USD Coin",
9514
+ name: net.domainName,
9471
9515
  version: "2",
9472
- chainId: 8453,
9473
- // Base mainnet
9474
- verifyingContract: usdc
9516
+ chainId: net.chainId,
9517
+ verifyingContract: usdcOverride ?? net.usdc
9475
9518
  };
9476
- const message = {
9519
+ return signer.signTypedData(domain2, EIP3009_TYPES, {
9477
9520
  from: from4,
9478
- to,
9479
- value: BigInt(value).toString(),
9521
+ to: toAddress,
9522
+ value,
9480
9523
  validAfter,
9481
9524
  validBefore,
9482
9525
  nonce
9483
- };
9484
- const signature2 = await signer.signTypedData(domain2, EIP3009_TYPES, message);
9526
+ });
9527
+ }
9528
+ async function generatePaymentCode(config2, params) {
9529
+ const {
9530
+ signer,
9531
+ value,
9532
+ ttl = 86400,
9533
+ description,
9534
+ payee,
9535
+ usdcContract,
9536
+ network = "base-sepolia"
9537
+ } = params;
9538
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
9539
+ const net = NETWORK_CONFIGS[network];
9540
+ if (!net) throw new Error(`Unsupported network: ${network}`);
9541
+ const from4 = await signer.getAddress();
9542
+ const now = Math.floor(Date.now() / 1e3);
9543
+ const validBefore = now + ttl;
9544
+ const nonce = randomBytes32();
9545
+ const usdc = usdcContract ?? net.usdc;
9546
+ const toAddress = await fetchToAddress(facilitatorUrl, network);
9547
+ const signature2 = await signEip3009(
9548
+ signer,
9549
+ net,
9550
+ from4,
9551
+ toAddress,
9552
+ BigInt(value).toString(),
9553
+ 0,
9554
+ validBefore,
9555
+ nonce,
9556
+ usdc
9557
+ );
9485
9558
  const res = await fetch(`${facilitatorUrl}/payment-codes`, {
9486
9559
  method: "POST",
9487
9560
  headers: { "Content-Type": "application/json" },
9488
9561
  body: JSON.stringify({
9489
9562
  from: from4,
9490
- to,
9491
9563
  value: BigInt(value).toString(),
9492
- validAfter,
9564
+ validAfter: 0,
9493
9565
  validBefore,
9494
9566
  nonce,
9495
9567
  signature: signature2,
9496
- usdcContract: usdc
9568
+ usdcContract: usdc,
9569
+ settlementNetwork: net.settlementNetwork,
9570
+ ...description ? { description } : {},
9571
+ ...payee ? { payee } : {}
9497
9572
  })
9498
9573
  });
9499
9574
  if (!res.ok) {
9500
9575
  const err = await res.json().catch(() => ({}));
9501
- throw new Error(`Failed to register payment code: ${err.error || res.status}`);
9576
+ throw new Error(`Failed to register payment code: ${err.error ?? res.status}`);
9502
9577
  }
9503
9578
  return res.json();
9504
9579
  }
9505
- async function redeemPaymentCode(config2, code) {
9506
- const facilitatorUrl = config2.facilitatorUrl || DEFAULT_FACILITATOR;
9507
- const res = await fetch(`${facilitatorUrl}/payment-codes/${code.toUpperCase()}/redeem`, {
9580
+ async function generatePaymentCodesBatch(config2, params) {
9581
+ const {
9582
+ signer,
9583
+ codes,
9584
+ payee,
9585
+ usdcContract,
9586
+ network = "base-sepolia",
9587
+ authToken
9588
+ } = params;
9589
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
9590
+ const net = NETWORK_CONFIGS[network];
9591
+ if (!net) throw new Error(`Unsupported network: ${network}`);
9592
+ if (codes.length > 20) throw new Error("Maximum 20 codes per batch");
9593
+ const from4 = await signer.getAddress();
9594
+ const usdc = usdcContract ?? net.usdc;
9595
+ const now = Math.floor(Date.now() / 1e3);
9596
+ const toAddress = await fetchToAddress(facilitatorUrl, network);
9597
+ const signedCodes = await Promise.all(
9598
+ codes.map(async (item) => {
9599
+ const validBefore = now + (item.ttl ?? 86400);
9600
+ const nonce = randomBytes32();
9601
+ const value = BigInt(item.value).toString();
9602
+ const signature2 = await signEip3009(
9603
+ signer,
9604
+ net,
9605
+ from4,
9606
+ toAddress,
9607
+ value,
9608
+ 0,
9609
+ validBefore,
9610
+ nonce,
9611
+ usdc
9612
+ );
9613
+ return { value, validAfter: 0, validBefore, nonce, signature: signature2 };
9614
+ })
9615
+ );
9616
+ const res = await fetch(`${facilitatorUrl}/payment-codes/batch`, {
9508
9617
  method: "POST",
9509
- headers: { "Content-Type": "application/json" }
9618
+ headers: {
9619
+ "Content-Type": "application/json",
9620
+ "Authorization": `Bearer ${authToken}`
9621
+ },
9622
+ body: JSON.stringify({
9623
+ from: from4,
9624
+ settlementNetwork: net.settlementNetwork,
9625
+ usdcContract: usdc,
9626
+ ...payee ? { payee } : {},
9627
+ codes: signedCodes
9628
+ })
9510
9629
  });
9511
9630
  if (!res.ok) {
9512
9631
  const err = await res.json().catch(() => ({}));
9513
- throw new Error(`Failed to redeem payment code: ${err.error || res.status}`);
9632
+ throw new Error(`Batch registration failed: ${err.error ?? res.status}`);
9633
+ }
9634
+ return res.json();
9635
+ }
9636
+ async function redeemPaymentCode(config2, code, payee) {
9637
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
9638
+ const res = await fetch(
9639
+ `${facilitatorUrl}/payment-codes/${code.trim().toUpperCase()}/redeem`,
9640
+ {
9641
+ method: "POST",
9642
+ headers: { "Content-Type": "application/json" },
9643
+ body: JSON.stringify(payee ? { payee } : {})
9644
+ }
9645
+ );
9646
+ if (!res.ok) {
9647
+ const err = await res.json().catch(() => ({}));
9648
+ throw new Error(err.error ?? `Redeem failed: ${res.status}`);
9514
9649
  }
9515
9650
  return res.json();
9516
9651
  }
9517
9652
  async function getPaymentCode(config2, code) {
9518
- const facilitatorUrl = config2.facilitatorUrl || DEFAULT_FACILITATOR;
9519
- const res = await fetch(`${facilitatorUrl}/payment-codes/${code.toUpperCase()}`);
9653
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
9654
+ const res = await fetch(
9655
+ `${facilitatorUrl}/payment-codes/${code.trim().toUpperCase()}`
9656
+ );
9657
+ if (!res.ok) {
9658
+ const err = await res.json().catch(() => ({}));
9659
+ throw new Error(`Payment code not found: ${err.error ?? res.status}`);
9660
+ }
9661
+ return res.json();
9662
+ }
9663
+ async function cancelPaymentCode(config2, code) {
9664
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
9665
+ const res = await fetch(
9666
+ `${facilitatorUrl}/payment-codes/${code.trim().toUpperCase()}`,
9667
+ { method: "DELETE" }
9668
+ );
9669
+ if (!res.ok) {
9670
+ const err = await res.json().catch(() => ({}));
9671
+ throw new Error(`Cancel failed: ${err.error ?? res.status}`);
9672
+ }
9673
+ return res.json();
9674
+ }
9675
+
9676
+ // src/payment-requests.ts
9677
+ var DEFAULT_FACILITATOR2 = "https://relai.fi/facilitator";
9678
+ var EIP3009_TYPES2 = {
9679
+ TransferWithAuthorization: [
9680
+ { name: "from", type: "address" },
9681
+ { name: "to", type: "address" },
9682
+ { name: "value", type: "uint256" },
9683
+ { name: "validAfter", type: "uint256" },
9684
+ { name: "validBefore", type: "uint256" },
9685
+ { name: "nonce", type: "bytes32" }
9686
+ ]
9687
+ };
9688
+ function randomBytes322() {
9689
+ const bytes = new Uint8Array(32);
9690
+ if (typeof globalThis.crypto !== "undefined") {
9691
+ globalThis.crypto.getRandomValues(bytes);
9692
+ } else {
9693
+ const { randomBytes: randomBytes2 } = require("crypto");
9694
+ randomBytes2(32).copy(Buffer.from(bytes.buffer));
9695
+ }
9696
+ return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
9697
+ }
9698
+ async function createPayRequest(config2, params) {
9699
+ const {
9700
+ to,
9701
+ amount: amount2,
9702
+ network = "base-sepolia",
9703
+ description,
9704
+ ttlSeconds
9705
+ } = params;
9706
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR2;
9707
+ const res = await fetch(`${facilitatorUrl}/payment-requests`, {
9708
+ method: "POST",
9709
+ headers: { "Content-Type": "application/json" },
9710
+ body: JSON.stringify({
9711
+ to,
9712
+ amount: Number(amount2),
9713
+ network,
9714
+ ...description ? { description } : {},
9715
+ ...ttlSeconds ? { ttlSeconds } : {}
9716
+ })
9717
+ });
9718
+ if (!res.ok) {
9719
+ const err = await res.json().catch(() => ({}));
9720
+ throw new Error(`Failed to create payment request: ${err.error ?? res.status}`);
9721
+ }
9722
+ return res.json();
9723
+ }
9724
+ async function getPayRequest(config2, code) {
9725
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR2;
9726
+ const res = await fetch(
9727
+ `${facilitatorUrl}/payment-requests/${code.trim().toUpperCase()}`
9728
+ );
9729
+ if (!res.ok) {
9730
+ const err = await res.json().catch(() => ({}));
9731
+ throw new Error(`Payment request not found: ${err.error ?? res.status}`);
9732
+ }
9733
+ return res.json();
9734
+ }
9735
+ async function payPayRequest(config2, code, signer) {
9736
+ const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR2;
9737
+ const info = await getPayRequest(config2, code);
9738
+ if (!info.payable) {
9739
+ throw new Error(
9740
+ info.status === "paid" ? "Payment request already paid" : "Payment request expired or not payable"
9741
+ );
9742
+ }
9743
+ const netKey = info.network;
9744
+ const net = NETWORK_CONFIGS[netKey];
9745
+ if (!net) throw new Error(`Unknown network from payment request: ${info.network}`);
9746
+ const from4 = await signer.getAddress();
9747
+ const now = Math.floor(Date.now() / 1e3);
9748
+ const validBefore = Math.min(now + 300, info.validUntil);
9749
+ const nonce = randomBytes322();
9750
+ const domain2 = {
9751
+ name: net.domainName,
9752
+ version: "2",
9753
+ chainId: net.chainId,
9754
+ verifyingContract: info.usdcContract
9755
+ };
9756
+ const signature2 = await signer.signTypedData(domain2, EIP3009_TYPES2, {
9757
+ from: from4,
9758
+ to: info.toAddress,
9759
+ // settler/relayer — NOT the merchant directly
9760
+ value: String(info.amount),
9761
+ validAfter: 0,
9762
+ validBefore,
9763
+ nonce
9764
+ });
9765
+ const res = await fetch(
9766
+ `${facilitatorUrl}/payment-requests/${code.trim().toUpperCase()}/pay`,
9767
+ {
9768
+ method: "POST",
9769
+ headers: { "Content-Type": "application/json" },
9770
+ body: JSON.stringify({ from: from4, validAfter: 0, validBefore, nonce, signature: signature2 })
9771
+ }
9772
+ );
9773
+ if (!res.ok) {
9774
+ const err = await res.json().catch(() => ({}));
9775
+ throw new Error(err.error ?? `Payment failed: ${res.status}`);
9776
+ }
9777
+ return res.json();
9778
+ }
9779
+ async function payPayRequestWithCode(config2, requestCode, paymentCode, options = {}) {
9780
+ const { allowOverpayment = true, returnChange = "code" } = options;
9781
+ const info = await getPayRequest(config2, requestCode);
9782
+ if (!info.payable) {
9783
+ throw new Error(
9784
+ info.status === "paid" ? "Payment request already paid" : "Payment request expired or not payable"
9785
+ );
9786
+ }
9787
+ const codeStatus = await getPaymentCode(config2, paymentCode);
9788
+ if (!codeStatus.redeemable) {
9789
+ throw new Error(
9790
+ codeStatus.redeemed ? "Payment code already redeemed" : "Payment code expired or not redeemable"
9791
+ );
9792
+ }
9793
+ const codeValue = BigInt(codeStatus.value);
9794
+ const reqAmount = BigInt(info.amount);
9795
+ if (codeValue < reqAmount) {
9796
+ throw new Error(
9797
+ `Payment code value (${Number(codeValue) / 1e6} USDC) is less than the request amount (${Number(reqAmount) / 1e6} USDC)`
9798
+ );
9799
+ }
9800
+ if (!allowOverpayment && codeValue > reqAmount) {
9801
+ throw new Error(
9802
+ `Payment code value (${Number(codeValue) / 1e6} USDC) exceeds the request amount (${Number(reqAmount) / 1e6} USDC). Generate a code for the exact amount, or pass { allowOverpayment: true }.`
9803
+ );
9804
+ }
9805
+ const facilitatorUrl = config2.facilitatorUrl ?? "https://relai.fi/facilitator";
9806
+ const usePartial = codeValue > reqAmount;
9807
+ const res = await fetch(
9808
+ `${facilitatorUrl}/payment-codes/${paymentCode.trim().toUpperCase()}/redeem`,
9809
+ {
9810
+ method: "POST",
9811
+ headers: { "Content-Type": "application/json" },
9812
+ body: JSON.stringify({
9813
+ payee: info.to,
9814
+ ...usePartial ? { invoiceAmount: info.amount.toString() } : {},
9815
+ ...usePartial ? { returnChangeAsCode: returnChange === "code" } : {}
9816
+ })
9817
+ }
9818
+ );
9520
9819
  if (!res.ok) {
9521
9820
  const err = await res.json().catch(() => ({}));
9522
- throw new Error(`Payment code not found: ${err.error || res.status}`);
9821
+ throw new Error(err.error ?? `Redeem failed: ${res.status}`);
9523
9822
  }
9524
9823
  return res.json();
9525
9824
  }
@@ -26517,6 +26816,7 @@ var BRIDGE_INFO_TTL_MS = 5 * 60 * 1e3;
26517
26816
  CHAIN_IDS,
26518
26817
  EXPLORER_TX_URL,
26519
26818
  NETWORK_CAIP2,
26819
+ NETWORK_CONFIGS,
26520
26820
  NETWORK_LABELS,
26521
26821
  NETWORK_TOKENS,
26522
26822
  NETWORK_V1_TO_V2,
@@ -26528,9 +26828,12 @@ var BRIDGE_INFO_TTL_MS = 5 * 60 * 1e3;
26528
26828
  USDC_ADDRESSES,
26529
26829
  USDC_BASE,
26530
26830
  USDC_SOLANA,
26831
+ cancelPaymentCode,
26531
26832
  convertPayloadToVersion,
26532
26833
  convertV1ToV2,
26533
26834
  convertV2ToV1,
26835
+ createPayRequest,
26836
+ createPrivateKeySigner,
26534
26837
  createX402Client,
26535
26838
  detectPayloadVersion,
26536
26839
  evmChargeClient,
@@ -26539,6 +26842,8 @@ var BRIDGE_INFO_TTL_MS = 5 * 60 * 1e3;
26539
26842
  formatUsd,
26540
26843
  fromAtomicUnits,
26541
26844
  generatePaymentCode,
26845
+ generatePaymentCodesBatch,
26846
+ getPayRequest,
26542
26847
  getPaymentCode,
26543
26848
  isEvm,
26544
26849
  isEvmNetwork,
@@ -26548,6 +26853,8 @@ var BRIDGE_INFO_TTL_MS = 5 * 60 * 1e3;
26548
26853
  networkV2ToV1,
26549
26854
  normalizeNetwork,
26550
26855
  normalizePaymentHeader,
26856
+ payPayRequest,
26857
+ payPayRequestWithCode,
26551
26858
  redeemPaymentCode,
26552
26859
  resolveToken,
26553
26860
  stripePayTo,