@piprail/sdk 1.0.0 → 1.1.1

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/index.d.cts CHANGED
@@ -155,7 +155,7 @@ declare function pickAccept(challenge: X402Challenge, matches: (network: string)
155
155
  * Chains are a parameter, and the popular ones are built in.
156
156
  *
157
157
  * The easy path — name a built-in chain:
158
- * requirePayment({ chain: 'base', amount: '0.05', payTo }) // USDC on Base
158
+ * requirePayment({ chain: 'base', token: 'USDC', amount: '0.05', payTo }) // USDC on Base
159
159
  *
160
160
  * The exotic path — ANY EVM chain we don't ship, by viem `Chain` or a bare
161
161
  * `{ id, rpcUrl }`, plus the token you want paid in:
@@ -812,7 +812,7 @@ declare const CHAINS: {
812
812
  maxPriorityFeePerGas?: undefined | undefined;
813
813
  isSystemTx?: undefined | undefined;
814
814
  mint?: undefined | undefined;
815
- sourceHash? /** Display name. Defaults to `EVM <id>`. */: undefined | undefined;
815
+ sourceHash?: undefined | undefined;
816
816
  } | {
817
817
  blockHash: `0x${string}` | null;
818
818
  blockNumber: bigint | null;
@@ -1580,13 +1580,11 @@ declare const CHAINS: {
1580
1580
  yParity: number;
1581
1581
  accessList: viem.AccessList;
1582
1582
  authorizationList?: undefined | undefined;
1583
- blobVersionedHashes?: undefined | undefined;
1583
+ blobVersionedHashes? /** EVM chain id, e.g. 5000 for Mantle. */: undefined | undefined;
1584
1584
  chainId: number;
1585
1585
  type: "eip1559";
1586
1586
  gasPrice?: undefined | undefined;
1587
- maxFeePerBlobGas
1588
- /** Native coin metadata. Defaults to 18-decimal ETH. */
1589
- ? /** Native coin metadata. Defaults to 18-decimal ETH. */: undefined | undefined;
1587
+ maxFeePerBlobGas?: undefined | undefined;
1590
1588
  maxFeePerGas: bigint;
1591
1589
  maxPriorityFeePerGas: bigint;
1592
1590
  feeCurrency: abitype.Address | null;
@@ -2984,7 +2982,7 @@ declare const CHAINS: {
2984
2982
  } | {
2985
2983
  blockHash: `0x${string}` | null;
2986
2984
  blockNumber: bigint | null;
2987
- blockTimestamp?: bigint | undefined;
2985
+ blockTimestamp? /** JSON-RPC endpoint. */: bigint | undefined;
2988
2986
  from: abitype.Address;
2989
2987
  gas: bigint;
2990
2988
  hash: viem.Hash;
@@ -3318,10 +3316,8 @@ declare const CHAINS: {
3318
3316
  yParity?: undefined | undefined;
3319
3317
  type: "legacy";
3320
3318
  gasPrice: bigint;
3321
- maxFeePerBlobGas
3322
- /** JSON-RPC endpoint. */
3323
- ? /** JSON-RPC endpoint. */: undefined | undefined;
3324
- maxFeePerGas? /** Display name. Defaults to `EVM <id>`. */: undefined | undefined;
3319
+ maxFeePerBlobGas?: undefined | undefined;
3320
+ maxFeePerGas?: undefined | undefined;
3325
3321
  maxPriorityFeePerGas?: undefined | undefined;
3326
3322
  isSystemTx?: undefined | undefined;
3327
3323
  mint?: undefined | undefined;
@@ -3931,6 +3927,18 @@ type PipRailEvent = {
3931
3927
  kind: 'payment-confirmed';
3932
3928
  ref: string;
3933
3929
  blockNumber: bigint;
3930
+ }
3931
+ /**
3932
+ * Broadcast succeeded (we hold `ref`) but LOCAL confirmation timed out — the
3933
+ * RPC was likely lagging/throttled while the tx is in fact on-chain. The proof
3934
+ * is NOT discarded: the client submits it to the server (whose own on-chain
3935
+ * verify is the authority) instead of throwing, so a real payment is never
3936
+ * orphaned into a double-pay. `reason` is the confirm error's message.
3937
+ */
3938
+ | {
3939
+ kind: 'payment-unconfirmed';
3940
+ ref: string;
3941
+ reason: string;
3934
3942
  } | {
3935
3943
  kind: 'payment-settled';
3936
3944
  receipt: X402Receipt | null;
@@ -4049,7 +4057,13 @@ interface PipRailClientOptions {
4049
4057
  * giving up. Default 3, with a short backoff between attempts — this
4050
4058
  * absorbs RPC propagation lag (the server's node briefly trailing the
4051
4059
  * client's, so it hasn't seen the confirmation yet). If the server still
4052
- * returns 402 on the last attempt the SDK throws `MaxRetriesExceededError`.
4060
+ * returns 402 on the last attempt the SDK throws `MaxRetriesExceededError`
4061
+ * (which carries `.ref` — re-verify, never re-pay).
4062
+ *
4063
+ * If the broadcast succeeded but the client's OWN confirmation timed out
4064
+ * (a throttled RPC), the proof is NOT discarded: the client submits it
4065
+ * anyway and automatically uses MORE patient retries (a floor of 6, longer
4066
+ * backoff), since the on-chain tx may still be settling.
4053
4067
  */
4054
4068
  maxPaymentRetries?: number;
4055
4069
  /** Timeout (ms) for the retry leg after broadcast. Default 30_000. */
@@ -4159,7 +4173,7 @@ declare function paymentTools(client: PipRailClient): AgentTool[];
4159
4173
  * import { requirePayment } from '@piprail/sdk'
4160
4174
  *
4161
4175
  * app.get('/report',
4162
- * requirePayment({ chain: 'base', amount: '0.05', payTo: '0xMerchant…' }),
4176
+ * requirePayment({ chain: 'base', token: 'USDC', amount: '0.05', payTo: '0xMerchant…' }),
4163
4177
  * (req, res) => res.json({ secret: 42 })
4164
4178
  * )
4165
4179
  *
@@ -4357,6 +4371,26 @@ declare abstract class PipRailError extends Error {
4357
4371
  declare class InsufficientFundsError extends PipRailError {
4358
4372
  readonly code = "INSUFFICIENT_FUNDS";
4359
4373
  }
4374
+ /**
4375
+ * The payment can't be delivered because the RECIPIENT (`payTo`) isn't set up to
4376
+ * receive on this chain yet — a chain-level *state* requirement, NOT the payer's
4377
+ * balance. It's deliberately distinct from {@link InsufficientFundsError} so a
4378
+ * caller (especially an AI agent) can tell the two fixes apart:
4379
+ *
4380
+ * - `INSUFFICIENT_FUNDS` → fund the **payer** (more token, native gas, or reserve).
4381
+ * - `RECIPIENT_NOT_READY` → set up the **recipient**, e.g.
4382
+ * · XRPL — activate the account (it must hold ≥1 XRP base reserve to exist);
4383
+ * · Stellar — the account must exist (≥1 XLM reserve) and hold a trustline for the asset;
4384
+ * · NEAR — `storage_deposit`-register the recipient on the NEP-141 token (~0.00125 NEAR).
4385
+ *
4386
+ * The message states the requirement and the fix in plain language **and echoes
4387
+ * the raw chain code** (e.g. `(XRPL: tecNO_DST_INSUF_XRP)`), while the untouched
4388
+ * chain error is preserved on `.cause` for deeper debugging. Chains with no
4389
+ * receive prerequisite (EVM, Solana, Sui, Tron, and native TON/NEAR) never throw it.
4390
+ */
4391
+ declare class RecipientNotReadyError extends PipRailError {
4392
+ readonly code = "RECIPIENT_NOT_READY";
4393
+ }
4360
4394
  /**
4361
4395
  * Best-effort: turn a chain library's "wallet can't afford it" error into the
4362
4396
  * SDK's typed {@link InsufficientFundsError}, by matching its message. Drivers
@@ -4378,15 +4412,39 @@ declare class WrongChainError extends PipRailError {
4378
4412
  }
4379
4413
  /**
4380
4414
  * Broadcast confirmed on-chain but server didn't return 200 within timeout.
4381
- * The user got their tokens debited but the gated content is unreachable
4382
- * retry manually with the receipt nonce.
4415
+ * The user got their tokens debited but the gated content is unreachable.
4416
+ *
4417
+ * `.ref` is the on-chain proof (tx hash / signature / locator) that was already
4418
+ * broadcast. **Re-verify or re-submit `ref` — never re-pay** (a fresh payment
4419
+ * would double-spend). The same proof stays valid until the server's
4420
+ * `maxTimeoutSeconds` recency window elapses (default 600s).
4383
4421
  */
4384
4422
  declare class PaymentTimeoutError extends PipRailError {
4385
4423
  readonly code = "PAYMENT_TIMEOUT";
4424
+ /** The already-broadcast proof ref — recover with it, don't re-pay. */
4425
+ readonly ref?: string;
4426
+ constructor(message: string, options?: ErrorOptions & {
4427
+ ref?: string;
4428
+ });
4386
4429
  }
4387
- /** Paid, retried, still got 402. Means the server rejected our proof. */
4430
+ /**
4431
+ * Paid, retried, still got 402 — the server rejected our proof on every attempt.
4432
+ *
4433
+ * `.ref` is the on-chain proof that was broadcast. The rejection may be transient
4434
+ * (the server's RPC node lagging/throttled and not yet seeing the tx) — so
4435
+ * **re-verify or re-submit `ref` before doing anything else; never re-pay**, or
4436
+ * you risk a double payment. The proof stays redeemable until the server's
4437
+ * `maxTimeoutSeconds` recency window elapses (default 600s). A persistent
4438
+ * rejection with a definitive code (`amount_too_low`, `wrong_recipient`, …)
4439
+ * means the proof genuinely doesn't satisfy the challenge.
4440
+ */
4388
4441
  declare class MaxRetriesExceededError extends PipRailError {
4389
4442
  readonly code = "MAX_RETRIES_EXCEEDED";
4443
+ /** The already-broadcast proof ref — recover with it, don't re-pay. */
4444
+ readonly ref?: string;
4445
+ constructor(message: string, options?: ErrorOptions & {
4446
+ ref?: string;
4447
+ });
4390
4448
  }
4391
4449
  /**
4392
4450
  * The client refused to pay BEFORE any on-chain send — the quoted payment
@@ -4575,4 +4633,4 @@ declare function encodeXPaymentHeader(input: {
4575
4633
  x402Version?: number;
4576
4634
  }): string;
4577
4635
 
4578
- export { type AcceptOption, type AddressId, type AgentTool, type AssetId, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, InsufficientFundsError, InvalidEnvelopeError, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPolicy, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletHandle, type WalletInput, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402Challenge, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildChallengeHeader, buildExactAuthorization, buildReceiptHeader, buildSignatureHeader, chainIdForExactNetwork, createPaymentGate, encodeXPaymentHeader, evaluatePolicy, parseChallenge, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, registerDriver, requirePayment, resolveChain, toInsufficientFundsError, toInvalidBody };
4636
+ export { type AcceptOption, type AddressId, type AgentTool, type AssetId, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, InsufficientFundsError, InvalidEnvelopeError, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPolicy, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, RecipientNotReadyError, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletHandle, type WalletInput, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402Challenge, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildChallengeHeader, buildExactAuthorization, buildReceiptHeader, buildSignatureHeader, chainIdForExactNetwork, createPaymentGate, encodeXPaymentHeader, evaluatePolicy, parseChallenge, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, registerDriver, requirePayment, resolveChain, toInsufficientFundsError, toInvalidBody };
package/dist/index.d.ts CHANGED
@@ -155,7 +155,7 @@ declare function pickAccept(challenge: X402Challenge, matches: (network: string)
155
155
  * Chains are a parameter, and the popular ones are built in.
156
156
  *
157
157
  * The easy path — name a built-in chain:
158
- * requirePayment({ chain: 'base', amount: '0.05', payTo }) // USDC on Base
158
+ * requirePayment({ chain: 'base', token: 'USDC', amount: '0.05', payTo }) // USDC on Base
159
159
  *
160
160
  * The exotic path — ANY EVM chain we don't ship, by viem `Chain` or a bare
161
161
  * `{ id, rpcUrl }`, plus the token you want paid in:
@@ -812,7 +812,7 @@ declare const CHAINS: {
812
812
  maxPriorityFeePerGas?: undefined | undefined;
813
813
  isSystemTx?: undefined | undefined;
814
814
  mint?: undefined | undefined;
815
- sourceHash? /** Display name. Defaults to `EVM <id>`. */: undefined | undefined;
815
+ sourceHash?: undefined | undefined;
816
816
  } | {
817
817
  blockHash: `0x${string}` | null;
818
818
  blockNumber: bigint | null;
@@ -1580,13 +1580,11 @@ declare const CHAINS: {
1580
1580
  yParity: number;
1581
1581
  accessList: viem.AccessList;
1582
1582
  authorizationList?: undefined | undefined;
1583
- blobVersionedHashes?: undefined | undefined;
1583
+ blobVersionedHashes? /** EVM chain id, e.g. 5000 for Mantle. */: undefined | undefined;
1584
1584
  chainId: number;
1585
1585
  type: "eip1559";
1586
1586
  gasPrice?: undefined | undefined;
1587
- maxFeePerBlobGas
1588
- /** Native coin metadata. Defaults to 18-decimal ETH. */
1589
- ? /** Native coin metadata. Defaults to 18-decimal ETH. */: undefined | undefined;
1587
+ maxFeePerBlobGas?: undefined | undefined;
1590
1588
  maxFeePerGas: bigint;
1591
1589
  maxPriorityFeePerGas: bigint;
1592
1590
  feeCurrency: abitype.Address | null;
@@ -2984,7 +2982,7 @@ declare const CHAINS: {
2984
2982
  } | {
2985
2983
  blockHash: `0x${string}` | null;
2986
2984
  blockNumber: bigint | null;
2987
- blockTimestamp?: bigint | undefined;
2985
+ blockTimestamp? /** JSON-RPC endpoint. */: bigint | undefined;
2988
2986
  from: abitype.Address;
2989
2987
  gas: bigint;
2990
2988
  hash: viem.Hash;
@@ -3318,10 +3316,8 @@ declare const CHAINS: {
3318
3316
  yParity?: undefined | undefined;
3319
3317
  type: "legacy";
3320
3318
  gasPrice: bigint;
3321
- maxFeePerBlobGas
3322
- /** JSON-RPC endpoint. */
3323
- ? /** JSON-RPC endpoint. */: undefined | undefined;
3324
- maxFeePerGas? /** Display name. Defaults to `EVM <id>`. */: undefined | undefined;
3319
+ maxFeePerBlobGas?: undefined | undefined;
3320
+ maxFeePerGas?: undefined | undefined;
3325
3321
  maxPriorityFeePerGas?: undefined | undefined;
3326
3322
  isSystemTx?: undefined | undefined;
3327
3323
  mint?: undefined | undefined;
@@ -3931,6 +3927,18 @@ type PipRailEvent = {
3931
3927
  kind: 'payment-confirmed';
3932
3928
  ref: string;
3933
3929
  blockNumber: bigint;
3930
+ }
3931
+ /**
3932
+ * Broadcast succeeded (we hold `ref`) but LOCAL confirmation timed out — the
3933
+ * RPC was likely lagging/throttled while the tx is in fact on-chain. The proof
3934
+ * is NOT discarded: the client submits it to the server (whose own on-chain
3935
+ * verify is the authority) instead of throwing, so a real payment is never
3936
+ * orphaned into a double-pay. `reason` is the confirm error's message.
3937
+ */
3938
+ | {
3939
+ kind: 'payment-unconfirmed';
3940
+ ref: string;
3941
+ reason: string;
3934
3942
  } | {
3935
3943
  kind: 'payment-settled';
3936
3944
  receipt: X402Receipt | null;
@@ -4049,7 +4057,13 @@ interface PipRailClientOptions {
4049
4057
  * giving up. Default 3, with a short backoff between attempts — this
4050
4058
  * absorbs RPC propagation lag (the server's node briefly trailing the
4051
4059
  * client's, so it hasn't seen the confirmation yet). If the server still
4052
- * returns 402 on the last attempt the SDK throws `MaxRetriesExceededError`.
4060
+ * returns 402 on the last attempt the SDK throws `MaxRetriesExceededError`
4061
+ * (which carries `.ref` — re-verify, never re-pay).
4062
+ *
4063
+ * If the broadcast succeeded but the client's OWN confirmation timed out
4064
+ * (a throttled RPC), the proof is NOT discarded: the client submits it
4065
+ * anyway and automatically uses MORE patient retries (a floor of 6, longer
4066
+ * backoff), since the on-chain tx may still be settling.
4053
4067
  */
4054
4068
  maxPaymentRetries?: number;
4055
4069
  /** Timeout (ms) for the retry leg after broadcast. Default 30_000. */
@@ -4159,7 +4173,7 @@ declare function paymentTools(client: PipRailClient): AgentTool[];
4159
4173
  * import { requirePayment } from '@piprail/sdk'
4160
4174
  *
4161
4175
  * app.get('/report',
4162
- * requirePayment({ chain: 'base', amount: '0.05', payTo: '0xMerchant…' }),
4176
+ * requirePayment({ chain: 'base', token: 'USDC', amount: '0.05', payTo: '0xMerchant…' }),
4163
4177
  * (req, res) => res.json({ secret: 42 })
4164
4178
  * )
4165
4179
  *
@@ -4357,6 +4371,26 @@ declare abstract class PipRailError extends Error {
4357
4371
  declare class InsufficientFundsError extends PipRailError {
4358
4372
  readonly code = "INSUFFICIENT_FUNDS";
4359
4373
  }
4374
+ /**
4375
+ * The payment can't be delivered because the RECIPIENT (`payTo`) isn't set up to
4376
+ * receive on this chain yet — a chain-level *state* requirement, NOT the payer's
4377
+ * balance. It's deliberately distinct from {@link InsufficientFundsError} so a
4378
+ * caller (especially an AI agent) can tell the two fixes apart:
4379
+ *
4380
+ * - `INSUFFICIENT_FUNDS` → fund the **payer** (more token, native gas, or reserve).
4381
+ * - `RECIPIENT_NOT_READY` → set up the **recipient**, e.g.
4382
+ * · XRPL — activate the account (it must hold ≥1 XRP base reserve to exist);
4383
+ * · Stellar — the account must exist (≥1 XLM reserve) and hold a trustline for the asset;
4384
+ * · NEAR — `storage_deposit`-register the recipient on the NEP-141 token (~0.00125 NEAR).
4385
+ *
4386
+ * The message states the requirement and the fix in plain language **and echoes
4387
+ * the raw chain code** (e.g. `(XRPL: tecNO_DST_INSUF_XRP)`), while the untouched
4388
+ * chain error is preserved on `.cause` for deeper debugging. Chains with no
4389
+ * receive prerequisite (EVM, Solana, Sui, Tron, and native TON/NEAR) never throw it.
4390
+ */
4391
+ declare class RecipientNotReadyError extends PipRailError {
4392
+ readonly code = "RECIPIENT_NOT_READY";
4393
+ }
4360
4394
  /**
4361
4395
  * Best-effort: turn a chain library's "wallet can't afford it" error into the
4362
4396
  * SDK's typed {@link InsufficientFundsError}, by matching its message. Drivers
@@ -4378,15 +4412,39 @@ declare class WrongChainError extends PipRailError {
4378
4412
  }
4379
4413
  /**
4380
4414
  * Broadcast confirmed on-chain but server didn't return 200 within timeout.
4381
- * The user got their tokens debited but the gated content is unreachable
4382
- * retry manually with the receipt nonce.
4415
+ * The user got their tokens debited but the gated content is unreachable.
4416
+ *
4417
+ * `.ref` is the on-chain proof (tx hash / signature / locator) that was already
4418
+ * broadcast. **Re-verify or re-submit `ref` — never re-pay** (a fresh payment
4419
+ * would double-spend). The same proof stays valid until the server's
4420
+ * `maxTimeoutSeconds` recency window elapses (default 600s).
4383
4421
  */
4384
4422
  declare class PaymentTimeoutError extends PipRailError {
4385
4423
  readonly code = "PAYMENT_TIMEOUT";
4424
+ /** The already-broadcast proof ref — recover with it, don't re-pay. */
4425
+ readonly ref?: string;
4426
+ constructor(message: string, options?: ErrorOptions & {
4427
+ ref?: string;
4428
+ });
4386
4429
  }
4387
- /** Paid, retried, still got 402. Means the server rejected our proof. */
4430
+ /**
4431
+ * Paid, retried, still got 402 — the server rejected our proof on every attempt.
4432
+ *
4433
+ * `.ref` is the on-chain proof that was broadcast. The rejection may be transient
4434
+ * (the server's RPC node lagging/throttled and not yet seeing the tx) — so
4435
+ * **re-verify or re-submit `ref` before doing anything else; never re-pay**, or
4436
+ * you risk a double payment. The proof stays redeemable until the server's
4437
+ * `maxTimeoutSeconds` recency window elapses (default 600s). A persistent
4438
+ * rejection with a definitive code (`amount_too_low`, `wrong_recipient`, …)
4439
+ * means the proof genuinely doesn't satisfy the challenge.
4440
+ */
4388
4441
  declare class MaxRetriesExceededError extends PipRailError {
4389
4442
  readonly code = "MAX_RETRIES_EXCEEDED";
4443
+ /** The already-broadcast proof ref — recover with it, don't re-pay. */
4444
+ readonly ref?: string;
4445
+ constructor(message: string, options?: ErrorOptions & {
4446
+ ref?: string;
4447
+ });
4390
4448
  }
4391
4449
  /**
4392
4450
  * The client refused to pay BEFORE any on-chain send — the quoted payment
@@ -4575,4 +4633,4 @@ declare function encodeXPaymentHeader(input: {
4575
4633
  x402Version?: number;
4576
4634
  }): string;
4577
4635
 
4578
- export { type AcceptOption, type AddressId, type AgentTool, type AssetId, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, InsufficientFundsError, InvalidEnvelopeError, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPolicy, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletHandle, type WalletInput, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402Challenge, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildChallengeHeader, buildExactAuthorization, buildReceiptHeader, buildSignatureHeader, chainIdForExactNetwork, createPaymentGate, encodeXPaymentHeader, evaluatePolicy, parseChallenge, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, registerDriver, requirePayment, resolveChain, toInsufficientFundsError, toInvalidBody };
4636
+ export { type AcceptOption, type AddressId, type AgentTool, type AssetId, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, InsufficientFundsError, InvalidEnvelopeError, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPolicy, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, RecipientNotReadyError, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletHandle, type WalletInput, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402Challenge, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildChallengeHeader, buildExactAuthorization, buildReceiptHeader, buildSignatureHeader, chainIdForExactNetwork, createPaymentGate, encodeXPaymentHeader, evaluatePolicy, parseChallenge, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, registerDriver, requirePayment, resolveChain, toInsufficientFundsError, toInvalidBody };
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  PaymentDeclinedError,
10
10
  PaymentTimeoutError,
11
11
  PipRailError,
12
+ RecipientNotReadyError,
12
13
  UnknownTokenError,
13
14
  UnsupportedNetworkError,
14
15
  WrongChainError,
@@ -19,7 +20,7 @@ import {
19
20
  parseUnits,
20
21
  rejectForeignToken,
21
22
  toInsufficientFundsError
22
- } from "./chunk-3TQJJ4SQ.js";
23
+ } from "./chunk-DTIJYDG6.js";
23
24
 
24
25
  // src/drivers/registry.ts
25
26
  var byFamily = /* @__PURE__ */ new Map();
@@ -712,7 +713,7 @@ var loaders = {
712
713
  solana: async () => {
713
714
  let mod;
714
715
  try {
715
- mod = await import("./solana-7PZG3CDO.js");
716
+ mod = await import("./solana-USZHRZFN.js");
716
717
  } catch (cause) {
717
718
  throw new MissingDriverError(
718
719
  `Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
@@ -724,7 +725,7 @@ var loaders = {
724
725
  ton: async () => {
725
726
  let mod;
726
727
  try {
727
- mod = await import("./ton-EFZKQAAK.js");
728
+ mod = await import("./ton-2N74GKNB.js");
728
729
  } catch (cause) {
729
730
  throw new MissingDriverError(
730
731
  `TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
@@ -736,7 +737,7 @@ var loaders = {
736
737
  stellar: async () => {
737
738
  let mod;
738
739
  try {
739
- mod = await import("./stellar-PAZ352JL.js");
740
+ mod = await import("./stellar-JZBVCLNV.js");
740
741
  } catch (cause) {
741
742
  throw new MissingDriverError(
742
743
  `Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
@@ -748,7 +749,7 @@ var loaders = {
748
749
  xrpl: async () => {
749
750
  let mod;
750
751
  try {
751
- mod = await import("./xrpl-7GWXDAVZ.js");
752
+ mod = await import("./xrpl-RTT3UOLX.js");
752
753
  } catch (cause) {
753
754
  throw new MissingDriverError(
754
755
  `XRPL selected, but its package isn't installed. Run: npm install xrpl`,
@@ -760,7 +761,7 @@ var loaders = {
760
761
  tron: async () => {
761
762
  let mod;
762
763
  try {
763
- mod = await import("./tron-243DT6PF.js");
764
+ mod = await import("./tron-N3EAAKU7.js");
764
765
  } catch (cause) {
765
766
  throw new MissingDriverError(
766
767
  `Tron selected, but its package isn't installed. Run: npm install tronweb`,
@@ -772,7 +773,7 @@ var loaders = {
772
773
  sui: async () => {
773
774
  let mod;
774
775
  try {
775
- mod = await import("./sui-6N4ZPAGD.js");
776
+ mod = await import("./sui-UBDATSQV.js");
776
777
  } catch (cause) {
777
778
  throw new MissingDriverError(
778
779
  `Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
@@ -784,7 +785,7 @@ var loaders = {
784
785
  near: async () => {
785
786
  let mod;
786
787
  try {
787
- mod = await import("./near-RVXGF7TW.js");
788
+ mod = await import("./near-RJUETWY3.js");
788
789
  } catch (cause) {
789
790
  throw new MissingDriverError(
790
791
  `NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
@@ -1055,8 +1056,8 @@ var PipRailClient = class {
1055
1056
  );
1056
1057
  this.safeEmit({ kind: "payment-required", challenge, accept });
1057
1058
  await this.authorize(quote);
1058
- const ref = await this.payAndConfirm(net, wallet, accept);
1059
- const response = await this.retryWithProof(url, init, accept, ref);
1059
+ const { ref, confirmed } = await this.payAndConfirm(net, wallet, accept);
1060
+ const response = await this.retryWithProof(url, init, accept, ref, confirmed);
1060
1061
  this.recordSpend(quote, ref);
1061
1062
  return response;
1062
1063
  }
@@ -1187,15 +1188,24 @@ var PipRailClient = class {
1187
1188
  }
1188
1189
  const ref = await net.send(wallet, accept);
1189
1190
  this.safeEmit({ kind: "payment-broadcast", ref });
1190
- const { height } = await net.confirm(ref, accept.extra.minConfirmations ?? 1);
1191
- this.safeEmit({
1192
- kind: "payment-confirmed",
1193
- ref,
1194
- blockNumber: BigInt(height)
1195
- });
1196
- return ref;
1191
+ try {
1192
+ const { height } = await net.confirm(ref, accept.extra.minConfirmations ?? 1);
1193
+ this.safeEmit({
1194
+ kind: "payment-confirmed",
1195
+ ref,
1196
+ blockNumber: BigInt(height)
1197
+ });
1198
+ return { ref, confirmed: true };
1199
+ } catch (err) {
1200
+ this.safeEmit({
1201
+ kind: "payment-unconfirmed",
1202
+ ref,
1203
+ reason: err instanceof Error ? err.message : String(err)
1204
+ });
1205
+ return { ref, confirmed: false };
1206
+ }
1197
1207
  }
1198
- async retryWithProof(url, originalInit, accept, ref) {
1208
+ async retryWithProof(url, originalInit, accept, ref, confirmed) {
1199
1209
  const signature = {
1200
1210
  x402Version: 2,
1201
1211
  accepted: accept,
@@ -1205,9 +1215,11 @@ var PipRailClient = class {
1205
1215
  headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
1206
1216
  let lastResponse = null;
1207
1217
  let lastReason = null;
1208
- for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
1218
+ const attempts = confirmed ? this.maxRetries : Math.max(this.maxRetries, 6);
1219
+ const backoffCap = confirmed ? 2e3 : 5e3;
1220
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
1209
1221
  if (attempt > 0) {
1210
- await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
1222
+ await new Promise((r) => setTimeout(r, Math.min(backoffCap, 400 * 2 ** (attempt - 1))));
1211
1223
  }
1212
1224
  const timeoutController = new AbortController();
1213
1225
  const timeoutId = setTimeout(
@@ -1224,8 +1236,8 @@ var PipRailClient = class {
1224
1236
  } catch (err) {
1225
1237
  if (timeoutController.signal.aborted) {
1226
1238
  throw new PaymentTimeoutError(
1227
- `Server did not respond within ${this.retryTimeoutMs}ms after on-chain payment ${ref} confirmed.`,
1228
- { cause: err }
1239
+ `Server did not respond within ${this.retryTimeoutMs}ms after broadcasting payment ${ref}. Re-verify or re-submit ref=${ref} \u2014 do NOT re-pay.`,
1240
+ { cause: err, ref }
1229
1241
  );
1230
1242
  }
1231
1243
  throw err;
@@ -1240,12 +1252,14 @@ var PipRailClient = class {
1240
1252
  lastReason = await readInvalidReason(lastResponse) ?? lastReason;
1241
1253
  }
1242
1254
  const why = lastReason ? `${lastReason.error}${lastReason.detail ? ` \u2014 ${lastReason.detail}` : ""}` : "server gave no reason";
1255
+ const unconfirmedNote = confirmed ? "" : " (broadcast but NOT locally confirmed \u2014 it may still have settled on-chain)";
1243
1256
  this.safeEmit({
1244
1257
  kind: "payment-failed",
1245
- reason: `server returned 402 after on-chain payment ${ref} confirmed (${why})`
1258
+ reason: `server returned 402 after broadcasting payment ${ref}${unconfirmedNote} (${why})`
1246
1259
  });
1247
1260
  throw new MaxRetriesExceededError(
1248
- `Server still returned 402 after ${this.maxRetries} retry attempt(s) with on-chain proof ref=${ref}. Last server rejection: ${why}.`
1261
+ `Server still returned 402 after ${attempts} attempt(s) with on-chain proof ref=${ref}${unconfirmedNote}. Last server rejection: ${why}. Re-verify or re-submit ref=${ref} before retrying \u2014 never re-pay (it would double-spend).`,
1262
+ { ref }
1249
1263
  );
1250
1264
  }
1251
1265
  };
@@ -1655,6 +1669,7 @@ export {
1655
1669
  PaymentTimeoutError,
1656
1670
  PipRailClient,
1657
1671
  PipRailError,
1672
+ RecipientNotReadyError,
1658
1673
  UnknownTokenError,
1659
1674
  UnsupportedNetworkError,
1660
1675
  WrongChainError,