x402-rails 0.2.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bfc5a5a404b98b156b9c8ad7d833787e5f082eff1b5d5b95e18663a1cb64fc8
4
- data.tar.gz: 0bda82b7d122d5b6fbbe6278db1dcd19efda67a6418ee2d8d6a23fb74ecd55fd
3
+ metadata.gz: 71561f4eae4f693705a405fd47e2b51a493441abd96dfe5a7d415915cdaa1313
4
+ data.tar.gz: 251fa92d63aaaa4e944040192b55b1db1ed154cb26d62aaac7f86a200bbc5e07
5
5
  SHA512:
6
- metadata.gz: 88f0a27e97ee07b7ae746d7e0ecef9c8676bd8b40544fadead68f6eae5d57b5e318c73edf03645d97670ce197334b596e086d948e92c711ad7fe488f2a1666df
7
- data.tar.gz: c5647b5c8ce65f3bbdd2dde221afbd2a27240d77e12ba2f6e0c1387a1b046e170efbdbf5288898cc5b670ea9ee615d92f579e28d4f78b86558787ad5d90ebe77
6
+ metadata.gz: 753e8a4957ebbc21189826904e370ef90ae618aca1078c0889351aa6841106bffc237ee1a893eeba745c8d75773c30f25d083d4e033466ef2a0f3d327f4c239e
7
+ data.tar.gz: 359cdbfd614777f9004a96aa4b2d5b9eff290266247707d9b886cafd1731bd3fa667625e641da16ea9383e5c3d2a3e74d79c0cf068a9be455ad4115a10715b19
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.0.0] - 2026-01-07
6
+
7
+ ### Added
8
+ - **Protocol v2 support** - Full x402 protocol v2 compliance with CAIP-2 network identifiers and updated headers
9
+ - **v2 `PAYMENT-REQUIRED` header** - 402 responses include base64-encoded PaymentRequired in header per v2 HTTP transport spec
10
+ - **v2 PaymentPayload format** - `scheme` and `network` nested inside `accepted` object per v2 spec
11
+ - **v2 PaymentRequired format** - `resource` info at top level, `amount` field (not `maxAmountRequired`), `extensions` object
12
+ - **Multi-chain accept** - `config.accept(chain:, currency:)` allows accepting payments on multiple chains simultaneously
13
+ - **Per-endpoint version override** - `x402_paywall(amount:, version:)` to use v1 or v2 per endpoint
14
+ - **Custom chain registration** - `config.register_chain(name:, chain_id:, standard:)` for custom EVM chains
15
+ - **Custom token registration** - `config.register_token(chain:, symbol:, address:, decimals:, name:)` for custom tokens
16
+ - **CAIP-2 support** - `to_caip2()` and `from_caip2()` for network identifier conversion
17
+ - **Per-chain fee payer** - Configure via `X402_SOLANA_DEVNET_FEE_PAYER`, `X402_SOLANA_FEE_PAYER` env vars
18
+ - **Dynamic HTML paywall** - Detects decimals and asset symbol from chain configuration
19
+
20
+ ### Changed
21
+ - Default protocol version is now v2
22
+ - v2 responses use CAIP-2 network format (e.g., `eip155:84532` instead of `base-sepolia`)
23
+ - v2 402 responses include full PaymentRequired in both header and body for debugging
24
+ - v1 requirements include `resource`, `description`, `mimeType` in each accept; v2 places these at top level
25
+
26
+ ## [0.2.1] - Previous Release
27
+
28
+ - Initial stable release with v1 protocol support
29
+ - Custom chain and token registration
30
+ - Optimistic and non-optimistic settlement modes
data/README.md CHANGED
@@ -1,10 +1,14 @@
1
1
  # x402-rails
2
2
 
3
+ ## Now supporting x402 v2!
4
+
5
+ > **⚠️ Note:** This gem now defaults to x402 protocol **v2**. If you need v1 compatibility, set `config.version = 1` in your initializer. See [Protocol Versions](#protocol-versions) for details on the differences.
6
+
3
7
  ![Coverage](./coverage/coverage.svg)
4
8
 
5
9
  Accept instant blockchain micropayments in your Rails applications using the [x402 payment protocol](https://www.x402.org/).
6
10
 
7
- Supports Base, avalanche, and other blockchain networks.
11
+ Supports 18 networks including Base, Polygon, Avalanche, Sei, Solana, and more.
8
12
 
9
13
  ## Features
10
14
 
@@ -14,7 +18,7 @@ Supports Base, avalanche, and other blockchain networks.
14
18
  - **$0.001 minimum** payment amounts
15
19
  - **Optimistic & non-optimistic** settlement modes
16
20
  - **Automatic settlement** after successful responses
17
- - **Browser paywall** and API support
21
+ - **API paywall** with 402 payment-required responses
18
22
  - **Rails 7.0+** compatible
19
23
 
20
24
  ## Example Video
@@ -59,6 +63,7 @@ Use `x402_paywall` in any controller action:
59
63
  class ApiController < ApplicationController
60
64
  def weather
61
65
  x402_paywall(amount: 0.001) # $0.001 in USD
66
+ return if performed?
62
67
 
63
68
  render json: {
64
69
  temperature: 72,
@@ -79,6 +84,7 @@ Call `x402_paywall` in any action:
79
84
  ```ruby
80
85
  def show
81
86
  x402_paywall(amount: 0.01)
87
+ return if performed?
82
88
  # Action continues after payment verified
83
89
  render json: @data
84
90
  end
@@ -101,6 +107,7 @@ class PremiumController < ApplicationController
101
107
 
102
108
  def require_payment
103
109
  x402_paywall(amount: 0.001, chain: "base")
110
+ return if performed?
104
111
  end
105
112
  end
106
113
  ```
@@ -112,11 +119,13 @@ Different prices for different actions:
112
119
  ```ruby
113
120
  def basic_data
114
121
  x402_paywall(amount: 0.001)
122
+ return if performed?
115
123
  render json: basic_info
116
124
  end
117
125
 
118
126
  def premium_data
119
127
  x402_paywall(amount: 0.01)
128
+ return if performed?
120
129
  render json: premium_info
121
130
  end
122
131
  ```
@@ -136,7 +145,10 @@ X402.configure do |config|
136
145
  config.facilitator = ENV.fetch("X402_FACILITATOR_URL", "https://x402.org/facilitator")
137
146
 
138
147
  # Blockchain network (default: "base-sepolia")
139
- # Options: "base-sepolia", "base", "avalanche-fuji", "avalanche"
148
+ # Built-in: base, base-sepolia, polygon, polygon-amoy, avalanche, avalanche-fuji,
149
+ # sei, sei-testnet, iotex, peaq, xlayer, xlayer-testnet,
150
+ # skale-base, skale-base-sepolia, kiteai, kiteai-testnet,
151
+ # solana, solana-devnet
140
152
  config.chain = ENV.fetch("X402_CHAIN", "base-sepolia")
141
153
 
142
154
  # Payment token (default: "USDC")
@@ -156,51 +168,184 @@ end
156
168
  | ---------------- | -------- | -------------------------------- | --------------------------------------------------------------------------------- |
157
169
  | `wallet_address` | **Yes** | - | Your Ethereum wallet address where payments will be received |
158
170
  | `facilitator` | No | `"https://x402.org/facilitator"` | Facilitator service URL for payment verification and settlement |
159
- | `chain` | No | `"base-sepolia"` | Blockchain network to use (`base-sepolia`, `base`, `avalanche-fuji`, `avalanche`) |
171
+ | `chain` | No | `"base-sepolia"` | Blockchain network (see built-in list above) |
160
172
  | `currency` | No | `"USDC"` | Payment token symbol (currently only USDC supported) |
161
173
  | `optimistic` | No | `true` | Settlement mode (see Optimistic vs Non-Optimistic Mode below) |
162
- | `rpc_urls` | No | `{}` | Custom RPC endpoint URLs per chain (see Custom RPC URLs below) |
174
+ | `version` | No | `2` | Protocol version (1 or 2). See Protocol Versions section |
163
175
 
164
- ### Custom RPC URLs
176
+ ### Custom Chains and Tokens
165
177
 
166
- By default, x402-rails uses public QuickNode RPC endpoints for each supported chain. For production use or higher reliability, you can configure custom RPC URLs from providers like [QuickNode](https://www.quicknode.com/).
178
+ You can register custom EVM chains and tokens beyond the built-in options.
167
179
 
168
- **Configuration Priority** (highest to lowest):
180
+ #### Register a Custom Chain
169
181
 
170
- 1. Programmatic configuration via `config.rpc_urls`
171
- 2. Per-chain environment variables
172
- 3. Built-in default RPC URLs
182
+ Add support for any EVM-compatible chain beyond the 18 built-in networks:
173
183
 
174
- #### Method 1: Programmatic Configuration
184
+ ```ruby
185
+ X402.configure do |config|
186
+ config.wallet_address = ENV['X402_WALLET_ADDRESS']
175
187
 
176
- Configure RPC URLs in your initializer:
188
+ # Register Arbitrum (not built-in)
189
+ config.register_chain(
190
+ name: "arbitrum",
191
+ chain_id: 42161,
192
+ standard: "eip155"
193
+ )
194
+
195
+ # Register the token for that chain
196
+ config.register_token(
197
+ chain: "arbitrum",
198
+ symbol: "USDC",
199
+ address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
200
+ decimals: 6,
201
+ name: "USD Coin",
202
+ version: "2"
203
+ )
204
+
205
+ config.chain = "arbitrum"
206
+ config.currency = "USDC"
207
+ end
208
+ ```
209
+
210
+ #### Register a Custom Token on a Built-in Chain
211
+
212
+ > **⚠️ Note:** The Facilitator used **must support** the specified chain and token to ensure proper functionality.
213
+
214
+ Accept different tokens on existing chains:
177
215
 
178
216
  ```ruby
179
217
  X402.configure do |config|
180
218
  config.wallet_address = ENV['X402_WALLET_ADDRESS']
181
219
 
182
- # Custom RPC URLs per chain
183
- config.rpc_urls["base"] = "https://your-base-rpc.quiknode.pro/your-key"
184
- config.rpc_urls["base-sepolia"] = "https://your-sepolia-rpc.quiknode.pro/your-key"
185
- config.rpc_urls["avalanche"] = "https://your-avalanche-rpc.quiknode.pro/your-key"
220
+ # Accept WETH on Base instead of USDC
221
+ config.register_token(
222
+ chain: "base",
223
+ symbol: "WETH",
224
+ address: "0x4200000000000000000000000000000000000006",
225
+ decimals: 18,
226
+ name: "Wrapped Ether",
227
+ version: "1"
228
+ )
229
+
230
+ config.chain = "base"
231
+ config.currency = "WETH"
186
232
  end
187
233
  ```
188
234
 
189
- #### Method 2: Environment Variables
235
+ #### Token Registration Parameters
190
236
 
191
- Set per-chain environment variables:
237
+ | Parameter | Required | Description |
238
+ | ---------- | -------- | ---------------------------------------------- |
239
+ | `chain` | Yes | Chain name (built-in or custom registered) |
240
+ | `symbol` | Yes | Token symbol (e.g., "USDC", "WETH") |
241
+ | `address` | Yes | Token contract address |
242
+ | `decimals` | Yes | Token decimals (e.g., 6 for USDC, 18 for WETH) |
243
+ | `name` | Yes | Token name for EIP-712 domain |
244
+ | `version` | No | EIP-712 version (default: "1") |
192
245
 
193
- ```bash
194
- # Per-chain RPC URL overrides
195
- X402_BASE_RPC_URL=https://your-base-rpc.quiknode.pro/your-key
196
- X402_BASE_SEPOLIA_RPC_URL=https://your-sepolia-rpc.quiknode.pro/your-key
197
- X402_AVALANCHE_RPC_URL=https://your-avalanche-rpc.quiknode.pro/your-key
198
- X402_AVALANCHE_FUJI_RPC_URL=https://your-fuji-rpc.quiknode.pro/your-key
246
+ **Note:** Custom chains and tokens are only supported for EVM (eip155) networks. Solana chains use a different implementation.
247
+
248
+ ### Accept Multiple Payment Options
249
+
250
+ Allow clients to pay on any of several supported chains by using `config.accept()`:
251
+
252
+ ```ruby
253
+ X402.configure do |config|
254
+ config.wallet_address = ENV['X402_WALLET_ADDRESS']
255
+
256
+ # Register a custom chain not included in the built-in list
257
+ config.register_chain(name: "arbitrum", chain_id: 42161, standard: "eip155")
258
+ config.register_token(
259
+ chain: "arbitrum",
260
+ symbol: "USDC",
261
+ address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
262
+ decimals: 6,
263
+ name: "USD Coin",
264
+ version: "2"
265
+ )
266
+
267
+ # Accept payments on multiple chains (built-in + custom)
268
+ config.accept(chain: "base-sepolia", currency: "USDC")
269
+ config.accept(chain: "polygon-amoy", currency: "USDC")
270
+ config.accept(chain: "arbitrum", currency: "USDC")
271
+ end
272
+ ```
273
+
274
+ When `config.accept()` is used, the 402 response will include all accepted payment options:
275
+
276
+ ```json
277
+ {
278
+ "accepts": [
279
+ { "network": "eip155:84532", "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", ... },
280
+ { "network": "eip155:80002", "asset": "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582", ... }
281
+ ]
282
+ }
199
283
  ```
200
284
 
201
- #### Method 3: Default RPC URLs
285
+ Clients can then choose which chain to pay on based on their preferences or available funds.
286
+
287
+ **Per-accept wallet addresses:** You can specify different recipient addresses per chain:
288
+
289
+ ```ruby
290
+ config.accept(chain: "base-sepolia", currency: "USDC", wallet_address: "0xWallet1")
291
+ config.accept(chain: "polygon-amoy", currency: "USDC", wallet_address: "0xWallet2")
292
+ ```
293
+
294
+ **Fallback behavior:** If no `config.accept()` calls are made, the default `config.chain` and `config.currency` are used.
295
+
296
+ ## Protocol Versions
202
297
 
203
- If no custom RPC URL is configured, it will default to the public QuickNode RPC urls.
298
+ x402-rails supports both v1 and v2 of the x402 protocol. **v2 is the default**.
299
+
300
+ ### Key Differences
301
+
302
+ | Feature | v1 (Legacy) | v2 (Default) |
303
+ | --------------- | ----------------------------- | -------------------------------- |
304
+ | Network format | Simple names (`base-sepolia`) | CAIP-2 (`eip155:84532`) |
305
+ | Payment header | `X-PAYMENT` | `PAYMENT-SIGNATURE` |
306
+ | Response header | `X-PAYMENT-RESPONSE` | `PAYMENT-RESPONSE` |
307
+ | Requirements | Body only | `PAYMENT-REQUIRED` header + body |
308
+ | Amount field | `maxAmountRequired` | `amount` |
309
+
310
+ ### v2 (Default)
311
+
312
+ ```ruby
313
+ X402.configure do |config|
314
+ config.wallet_address = ENV['X402_WALLET_ADDRESS']
315
+ config.version = 2 # Default, can be omitted
316
+ end
317
+ ```
318
+
319
+ v2 uses CAIP-2 network identifiers (`eip155:84532`) and the `PAYMENT-SIGNATURE` header. Payment requirements are sent in both the `PAYMENT-REQUIRED` header (base64-encoded) and the response body (JSON).
320
+
321
+ ### v1 (Legacy)
322
+
323
+ ```ruby
324
+ X402.configure do |config|
325
+ config.wallet_address = ENV['X402_WALLET_ADDRESS']
326
+ config.version = 1
327
+ end
328
+ ```
329
+
330
+ v1 uses simple network names (`base-sepolia`) and the `X-PAYMENT` header. Payment requirements are sent only in the response body.
331
+
332
+ ### Per-Endpoint Version
333
+
334
+ Override the version for specific endpoints:
335
+
336
+ ```ruby
337
+ def premium_v2
338
+ x402_paywall(amount: 0.001, version: 2)
339
+ return if performed?
340
+ render json: { data: "v2 endpoint" }
341
+ end
342
+
343
+ def legacy_v1
344
+ x402_paywall(amount: 0.001, version: 1)
345
+ return if performed?
346
+ render json: { data: "v1 endpoint" }
347
+ end
348
+ ```
204
349
 
205
350
  ## Environment Variables
206
351
 
@@ -216,13 +361,25 @@ X402_CHAIN=base-sepolia
216
361
  X402_CURRENCY=USDC
217
362
  X402_OPTIMISTIC=true # "true" or "false"
218
363
 
219
- # Custom RPC URLs (optional, per-chain overrides)
220
- X402_BASE_RPC_URL=https://your-base-rpc.quiknode.pro/your-key
221
- X402_BASE_SEPOLIA_RPC_URL=https://your-base-speoliarpc.quiknode.pro/your-key
222
- X402_AVALANCHE_RPC_URL=https://your-avalanche.quiknode.pro/your-key
223
- X402_AVALANCHE_FUJI_RPC_URL=https://your-fuji-rpc.quiknode.pro/your-key
364
+ # Solana fee payer overrides (required when using a non-default facilitator)
365
+ # The default fee payer is for the Coinbase facilitator (x402.org).
366
+ # Each facilitator manages its own fee payer — check your facilitator's /supported endpoint.
367
+ X402_FEE_PAYER= # Global override for all Solana chains
368
+ X402_SOLANA_FEE_PAYER= # Solana mainnet override
369
+ X402_SOLANA_DEVNET_FEE_PAYER= # Solana devnet override
370
+
371
+ # Example: PayAI facilitator (https://facilitator.payai.network)
372
+ # X402_FACILITATOR_URL=https://facilitator.payai.network
373
+ # X402_SOLANA_FEE_PAYER=2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4
374
+ # X402_SOLANA_DEVNET_FEE_PAYER=2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4
224
375
  ```
225
376
 
377
+ Fee payer lookup priority:
378
+ 1. `config.fee_payer` (programmatic, global)
379
+ 2. Per-chain ENV variable (e.g., `X402_SOLANA_DEVNET_FEE_PAYER`)
380
+ 3. `X402_FEE_PAYER` (ENV, global)
381
+ 4. Built-in default from chain config
382
+
226
383
  ## Examples
227
384
 
228
385
  ### Weather API
@@ -231,11 +388,13 @@ X402_AVALANCHE_FUJI_RPC_URL=https://your-fuji-rpc.quiknode.pro/your-key
231
388
  class WeatherController < ApplicationController
232
389
  def current
233
390
  x402_paywall(amount: 0.001)
391
+ return if performed?
234
392
  render json: { temp: 72, condition: "sunny" }
235
393
  end
236
394
 
237
395
  def forecast
238
396
  x402_paywall(amount: 0.01)
397
+ return if performed?
239
398
  render json: { forecast: [...] }
240
399
  end
241
400
  end
@@ -282,6 +441,8 @@ The gem raises these errors:
282
441
  - [x402 Protocol Docs](https://docs.cdp.coinbase.com/x402)
283
442
  - [GitHub Repository](https://github.com/coinbase/x402)
284
443
  - [Facilitator API](https://x402.org/facilitator)
444
+ - [Step-by-Step Rails Integration Guide](https://www.quicknode.com/guides/infrastructure/x402-payment-integration-with-rails)
445
+
285
446
 
286
447
  ## License
287
448