@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/CHAINS.md +152 -0
- package/CHANGELOG.md +98 -0
- package/ERRORS.md +57 -5
- package/README.md +96 -13
- package/dist/{chunk-3TQJJ4SQ.js → chunk-DTIJYDG6.js} +16 -0
- package/dist/chunk-NK64H3RM.cjs +173 -0
- package/dist/index.cjs +77 -62
- package/dist/index.d.cts +75 -17
- package/dist/index.d.ts +75 -17
- package/dist/index.js +39 -24
- package/dist/{near-RVXGF7TW.js → near-RJUETWY3.js} +99 -12
- package/dist/{near-4P5XNMMB.cjs → near-VZ6XGVNJ.cjs} +116 -29
- package/dist/{solana-F7H4YDW5.cjs → solana-CRLWAM7C.cjs} +14 -14
- package/dist/{solana-7PZG3CDO.js → solana-USZHRZFN.js} +1 -1
- package/dist/{stellar-PAZ352JL.js → stellar-JZBVCLNV.js} +19 -4
- package/dist/{stellar-BPPQTLNI.cjs → stellar-LIGJKRRK.cjs} +36 -21
- package/dist/{sui-XV4YYSGV.cjs → sui-JLVWFDOS.cjs} +17 -17
- package/dist/{sui-6N4ZPAGD.js → sui-UBDATSQV.js} +1 -1
- package/dist/{ton-EFZKQAAK.js → ton-2N74GKNB.js} +2 -2
- package/dist/{ton-E5RLUPD2.cjs → ton-OVSQZ4OM.cjs} +15 -15
- package/dist/{tron-243DT6PF.js → tron-N3EAAKU7.js} +123 -5
- package/dist/{tron-3UDH7KGF.cjs → tron-V3A6L3X3.cjs} +148 -30
- package/dist/{xrpl-6NRFT5CA.cjs → xrpl-QECPQCFS.cjs} +42 -26
- package/dist/{xrpl-7GWXDAVZ.js → xrpl-RTT3UOLX.js} +25 -9
- package/package.json +5 -1
- package/dist/chunk-WQWNPAYQ.cjs +0 -157
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
1258
|
+
reason: `server returned 402 after broadcasting payment ${ref}${unconfirmedNote} (${why})`
|
|
1246
1259
|
});
|
|
1247
1260
|
throw new MaxRetriesExceededError(
|
|
1248
|
-
`Server still returned 402 after ${
|
|
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,
|