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 +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +193 -32
- data/lib/x402/chains.rb +218 -53
- data/lib/x402/configuration.rb +62 -4
- data/lib/x402/payment_payload.rb +82 -10
- data/lib/x402/payment_requirement.rb +34 -13
- data/lib/x402/payment_validator.rb +18 -9
- data/lib/x402/rails/controller_extensions.rb +151 -133
- data/lib/x402/rails/generators/templates/x402_initializer.rb +2 -13
- data/lib/x402/rails/version.rb +1 -1
- data/lib/x402/rails.rb +1 -0
- data/lib/x402/requirement_generator.rb +74 -31
- data/lib/x402/versions/base.rb +39 -0
- data/lib/x402/versions/v1.rb +54 -0
- data/lib/x402/versions/v2.rb +61 -0
- data/lib/x402/versions.rb +17 -0
- metadata +13 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71561f4eae4f693705a405fd47e2b51a493441abd96dfe5a7d415915cdaa1313
|
|
4
|
+
data.tar.gz: 251fa92d63aaaa4e944040192b55b1db1ed154cb26d62aaac7f86a200bbc5e07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|

|
|
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,
|
|
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
|
-
- **
|
|
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
|
-
#
|
|
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
|
|
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
|
-
| `
|
|
174
|
+
| `version` | No | `2` | Protocol version (1 or 2). See Protocol Versions section |
|
|
163
175
|
|
|
164
|
-
### Custom
|
|
176
|
+
### Custom Chains and Tokens
|
|
165
177
|
|
|
166
|
-
|
|
178
|
+
You can register custom EVM chains and tokens beyond the built-in options.
|
|
167
179
|
|
|
168
|
-
|
|
180
|
+
#### Register a Custom Chain
|
|
169
181
|
|
|
170
|
-
|
|
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
|
-
|
|
184
|
+
```ruby
|
|
185
|
+
X402.configure do |config|
|
|
186
|
+
config.wallet_address = ENV['X402_WALLET_ADDRESS']
|
|
175
187
|
|
|
176
|
-
|
|
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
|
-
#
|
|
183
|
-
config.
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
####
|
|
235
|
+
#### Token Registration Parameters
|
|
190
236
|
|
|
191
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|