x402-rails 1.0.0 → 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: 9b70874283a564f69295e16df7dcb11d4764480c62151f130335ee3d116d62f5
4
- data.tar.gz: e4462c35ac33efc1993f166fa407b1e06f1b1fa35e3ba6547afce9031c07d3c4
3
+ metadata.gz: 71561f4eae4f693705a405fd47e2b51a493441abd96dfe5a7d415915cdaa1313
4
+ data.tar.gz: 251fa92d63aaaa4e944040192b55b1db1ed154cb26d62aaac7f86a200bbc5e07
5
5
  SHA512:
6
- metadata.gz: fb7d7df8bda864f433e062f41cc1e956c6a979446e02bd3bb88ef5bccc2ac42e06a2933a5cafe7b7475b53665d360b5bc034b80e4c9c9ff10b668df1f61a106d
7
- data.tar.gz: ea4523288ae8c5242fb03c90f1bc83f41779d46c170354cf2a3ccfdbaaf34da3f6656a03ab4b47d01f0d11e01e4adf1da4a52b405ddf25aa12f91270f11a6eaf
6
+ metadata.gz: 753e8a4957ebbc21189826904e370ef90ae618aca1078c0889351aa6841106bffc237ee1a893eeba745c8d75773c30f25d083d4e033466ef2a0f3d327f4c239e
7
+ data.tar.gz: 359cdbfd614777f9004a96aa4b2d5b9eff290266247707d9b886cafd1731bd3fa667625e641da16ea9383e5c3d2a3e74d79c0cf068a9be455ad4115a10715b19
data/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  Accept instant blockchain micropayments in your Rails applications using the [x402 payment protocol](https://www.x402.org/).
10
10
 
11
- Supports Base, avalanche, and other blockchain networks.
11
+ Supports 18 networks including Base, Polygon, Avalanche, Sei, Solana, and more.
12
12
 
13
13
  ## Features
14
14
 
@@ -18,7 +18,7 @@ Supports Base, avalanche, and other blockchain networks.
18
18
  - **$0.001 minimum** payment amounts
19
19
  - **Optimistic & non-optimistic** settlement modes
20
20
  - **Automatic settlement** after successful responses
21
- - **Browser paywall** and API support
21
+ - **API paywall** with 402 payment-required responses
22
22
  - **Rails 7.0+** compatible
23
23
 
24
24
  ## Example Video
@@ -145,7 +145,10 @@ X402.configure do |config|
145
145
  config.facilitator = ENV.fetch("X402_FACILITATOR_URL", "https://x402.org/facilitator")
146
146
 
147
147
  # Blockchain network (default: "base-sepolia")
148
- # 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
149
152
  config.chain = ENV.fetch("X402_CHAIN", "base-sepolia")
150
153
 
151
154
  # Payment token (default: "USDC")
@@ -165,7 +168,7 @@ end
165
168
  | ---------------- | -------- | -------------------------------- | --------------------------------------------------------------------------------- |
166
169
  | `wallet_address` | **Yes** | - | Your Ethereum wallet address where payments will be received |
167
170
  | `facilitator` | No | `"https://x402.org/facilitator"` | Facilitator service URL for payment verification and settlement |
168
- | `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) |
169
172
  | `currency` | No | `"USDC"` | Payment token symbol (currently only USDC supported) |
170
173
  | `optimistic` | No | `true` | Settlement mode (see Optimistic vs Non-Optimistic Mode below) |
171
174
  | `version` | No | `2` | Protocol version (1 or 2). See Protocol Versions section |
@@ -176,30 +179,30 @@ You can register custom EVM chains and tokens beyond the built-in options.
176
179
 
177
180
  #### Register a Custom Chain
178
181
 
179
- Add support for any EVM-compatible chain:
182
+ Add support for any EVM-compatible chain beyond the 18 built-in networks:
180
183
 
181
184
  ```ruby
182
185
  X402.configure do |config|
183
186
  config.wallet_address = ENV['X402_WALLET_ADDRESS']
184
187
 
185
- # Register Polygon mainnet
188
+ # Register Arbitrum (not built-in)
186
189
  config.register_chain(
187
- name: "polygon",
188
- chain_id: 137,
190
+ name: "arbitrum",
191
+ chain_id: 42161,
189
192
  standard: "eip155"
190
193
  )
191
194
 
192
195
  # Register the token for that chain
193
196
  config.register_token(
194
- chain: "polygon",
197
+ chain: "arbitrum",
195
198
  symbol: "USDC",
196
- address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
199
+ address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
197
200
  decimals: 6,
198
201
  name: "USD Coin",
199
202
  version: "2"
200
203
  )
201
204
 
202
- config.chain = "polygon"
205
+ config.chain = "arbitrum"
203
206
  config.currency = "USDC"
204
207
  end
205
208
  ```
@@ -250,20 +253,21 @@ Allow clients to pay on any of several supported chains by using `config.accept(
250
253
  X402.configure do |config|
251
254
  config.wallet_address = ENV['X402_WALLET_ADDRESS']
252
255
 
253
- # Register a custom chain
254
- config.register_chain(name: "polygon-amoy", chain_id: 80002, standard: "eip155")
256
+ # Register a custom chain not included in the built-in list
257
+ config.register_chain(name: "arbitrum", chain_id: 42161, standard: "eip155")
255
258
  config.register_token(
256
- chain: "polygon-amoy",
259
+ chain: "arbitrum",
257
260
  symbol: "USDC",
258
- address: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
261
+ address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
259
262
  decimals: 6,
260
263
  name: "USD Coin",
261
264
  version: "2"
262
265
  )
263
266
 
264
- # Accept payments on multiple chains
267
+ # Accept payments on multiple chains (built-in + custom)
265
268
  config.accept(chain: "base-sepolia", currency: "USDC")
266
269
  config.accept(chain: "polygon-amoy", currency: "USDC")
270
+ config.accept(chain: "arbitrum", currency: "USDC")
267
271
  end
268
272
  ```
269
273
 
@@ -356,8 +360,26 @@ X402_FACILITATOR_URL=https://x402.org/facilitator
356
360
  X402_CHAIN=base-sepolia
357
361
  X402_CURRENCY=USDC
358
362
  X402_OPTIMISTIC=true # "true" or "false"
363
+
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
359
375
  ```
360
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
+
361
383
  ## Examples
362
384
 
363
385
  ### Weather API
@@ -419,6 +441,8 @@ The gem raises these errors:
419
441
  - [x402 Protocol Docs](https://docs.cdp.coinbase.com/x402)
420
442
  - [GitHub Repository](https://github.com/coinbase/x402)
421
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
+
422
446
 
423
447
  ## License
424
448
 
data/lib/x402/chains.rb CHANGED
@@ -1,91 +1,160 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module X402
4
- # Chain configurations for supported networks
4
+ # Currency config constants for EIP-712 signature domain.
5
+ # The `name` field must match the on-chain ERC-20 name() return value.
6
+ # The `version` field must match the EIP-712 domain version.
7
+ USDC_NAMED_USD_COIN_V2 = { symbol: "USDC", decimals: 6, name: "USD Coin", version: "2" }.freeze
8
+ USDC_NAMED_USDC_V2 = { symbol: "USDC", decimals: 6, name: "USDC", version: "2" }.freeze
9
+ USDC_NAMED_USDC = { symbol: "USDC", decimals: 6, name: "USDC", version: nil }.freeze
10
+ USDC_NAMED_USD_COIN = { symbol: "USDC", decimals: 6, name: "USD Coin", version: nil }.freeze
11
+
12
+ # Unified chain registry — single source of truth for all chain metadata.
13
+ # Each entry contains: chain_id, caip2, currency, and optionally usdc_address, explorer_url, fee_payer.
5
14
  CHAINS = {
15
+ # --- Base ---
16
+ "base" => {
17
+ chain_id: 8453,
18
+ caip2: "eip155:8453",
19
+ usdc_address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
20
+ explorer_url: "https://basescan.org",
21
+ currency: USDC_NAMED_USD_COIN_V2,
22
+ },
6
23
  "base-sepolia" => {
7
24
  chain_id: 84532,
25
+ caip2: "eip155:84532",
8
26
  usdc_address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
9
- explorer_url: "https://sepolia.basescan.org"
27
+ explorer_url: "https://sepolia.basescan.org",
28
+ currency: USDC_NAMED_USDC_V2,
10
29
  },
11
- "base" => {
12
- chain_id: 8453,
13
- usdc_address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
14
- explorer_url: "https://basescan.org"
30
+
31
+ # --- Polygon ---
32
+ "polygon" => {
33
+ chain_id: 137,
34
+ caip2: "eip155:137",
35
+ usdc_address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
36
+ explorer_url: "https://polygonscan.com",
37
+ currency: USDC_NAMED_USD_COIN_V2,
15
38
  },
16
- "avalanche-fuji" => {
17
- chain_id: 43113,
18
- usdc_address: "0x5425890298aed601595a70AB815c96711a31Bc65",
19
- explorer_url: "https://testnet.snowtrace.io"
39
+ "polygon-amoy" => {
40
+ chain_id: 80002,
41
+ caip2: "eip155:80002",
42
+ usdc_address: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
43
+ explorer_url: "https://amoy.polygonscan.com",
44
+ currency: USDC_NAMED_USDC_V2,
20
45
  },
46
+
47
+ # --- Avalanche ---
21
48
  "avalanche" => {
22
49
  chain_id: 43114,
50
+ caip2: "eip155:43114",
23
51
  usdc_address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
24
- explorer_url: "https://snowtrace.io"
52
+ explorer_url: "https://snowtrace.io",
53
+ currency: USDC_NAMED_USDC_V2,
25
54
  },
26
- "solana-devnet" => {
27
- chain_id: 103,
28
- usdc_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
29
- explorer_url: "https://explorer.solana.com/?cluster=devnet",
30
- fee_payer: "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5"
55
+ "avalanche-fuji" => {
56
+ chain_id: 43113,
57
+ caip2: "eip155:43113",
58
+ usdc_address: "0x5425890298aed601595a70AB815c96711a31Bc65",
59
+ explorer_url: "https://testnet.snowtrace.io",
60
+ currency: USDC_NAMED_USD_COIN_V2,
31
61
  },
32
- "solana" => {
33
- chain_id: 101,
34
- usdc_address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
35
- explorer_url: "https://explorer.solana.com",
36
- fee_payer: "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5"
37
- }
38
- }.freeze
39
62
 
40
- # Currency configurations by chain
41
- CURRENCY_BY_CHAIN = {
42
- "base-sepolia" => {
43
- symbol: "USDC",
44
- decimals: 6,
45
- name: "USDC", # Testnet uses "USDC"
46
- version: "2"
63
+ # --- Sei ---
64
+ "sei" => {
65
+ chain_id: 1329,
66
+ caip2: "eip155:1329",
67
+ usdc_address: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392",
68
+ explorer_url: "https://seitrace.com",
69
+ currency: USDC_NAMED_USDC,
47
70
  },
48
- "base" => {
49
- symbol: "USDC",
50
- decimals: 6,
51
- name: "USD Coin", # Mainnet uses "USD Coin"
52
- version: "2"
71
+ "sei-testnet" => {
72
+ chain_id: 713715,
73
+ caip2: "eip155:713715",
74
+ explorer_url: "https://seitrace.com/?chain=arctic-1",
75
+ currency: USDC_NAMED_USDC,
53
76
  },
54
- "avalanche-fuji" => {
55
- symbol: "USDC",
56
- decimals: 6,
57
- name: "USD Coin", # Testnet uses "USD Coin"
58
- version: "2"
77
+
78
+ # --- X Layer ---
79
+ "xlayer" => {
80
+ chain_id: 196,
81
+ caip2: "eip155:196",
82
+ explorer_url: "https://www.oklink.com/xlayer",
83
+ currency: USDC_NAMED_USDC,
59
84
  },
60
- "avalanche" => {
61
- symbol: "USDC",
62
- decimals: 6,
63
- name: "USDC", # Mainnet uses "USDC"
64
- version: "2"
85
+ "xlayer-testnet" => {
86
+ chain_id: 1952,
87
+ caip2: "eip155:1952",
88
+ explorer_url: "https://www.oklink.com/xlayer-test",
89
+ currency: USDC_NAMED_USDC,
65
90
  },
66
- "solana-devnet" => {
67
- symbol: "USDC",
68
- decimals: 6,
69
- name: "USDC",
70
- version: nil
91
+
92
+ # --- SKALE ---
93
+ "skale-base" => {
94
+ chain_id: 1_187_947_933,
95
+ caip2: "eip155:1187947933",
96
+ explorer_url: "https://skale-base-explorer.skalenodes.com",
97
+ currency: USDC_NAMED_USDC,
98
+ },
99
+ "skale-base-sepolia" => {
100
+ chain_id: 324_705_682,
101
+ caip2: "eip155:324705682",
102
+ explorer_url: "https://base-sepolia-testnet-explorer.skalenodes.com",
103
+ currency: USDC_NAMED_USDC,
104
+ },
105
+
106
+ # --- KiteAI ---
107
+ "kiteai" => {
108
+ chain_id: 2366,
109
+ caip2: "eip155:2366",
110
+ explorer_url: "https://kitescan.ai",
111
+ currency: USDC_NAMED_USDC,
112
+ },
113
+ "kiteai-testnet" => {
114
+ chain_id: 2368,
115
+ caip2: "eip155:2368",
116
+ explorer_url: "https://testnet.kitescan.ai",
117
+ currency: USDC_NAMED_USDC,
118
+ },
119
+
120
+ # --- IoTeX (mainnet only) ---
121
+ "iotex" => {
122
+ chain_id: 4689,
123
+ caip2: "eip155:4689",
124
+ explorer_url: "https://iotexscan.io",
125
+ currency: USDC_NAMED_USDC,
71
126
  },
72
- "solana" => {
73
- symbol: "USDC",
74
- decimals: 6,
75
- name: "USD Coin",
76
- version: nil
77
- }
78
- }.freeze
79
127
 
80
- CAIP2_MAPPING = {
81
- "base-sepolia" => "eip155:84532",
82
- "base" => "eip155:8453",
83
- "avalanche-fuji" => "eip155:43113",
84
- "avalanche" => "eip155:43114",
85
- "solana-devnet" => "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
86
- "solana" => "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
128
+ # --- Peaq (mainnet only) ---
129
+ "peaq" => {
130
+ chain_id: 3338,
131
+ caip2: "eip155:3338",
132
+ explorer_url: "https://peaq.subscan.io",
133
+ currency: USDC_NAMED_USDC,
134
+ },
135
+
136
+ # --- Solana ---
137
+ "solana" => {
138
+ chain_id: 101,
139
+ caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
140
+ usdc_address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
141
+ explorer_url: "https://explorer.solana.com",
142
+ fee_payer: "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5",
143
+ currency: USDC_NAMED_USD_COIN,
144
+ },
145
+ "solana-devnet" => {
146
+ chain_id: 103,
147
+ caip2: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
148
+ usdc_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
149
+ explorer_url: "https://explorer.solana.com/?cluster=devnet",
150
+ fee_payer: "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5",
151
+ currency: USDC_NAMED_USDC,
152
+ },
87
153
  }.freeze
88
154
 
155
+ # Derived lookups for backwards compatibility
156
+ CURRENCY_BY_CHAIN = CHAINS.transform_values { |v| v[:currency] }.freeze
157
+ CAIP2_MAPPING = CHAINS.transform_values { |v| v[:caip2] }.freeze
89
158
  REVERSE_CAIP2_MAPPING = CAIP2_MAPPING.invert.freeze
90
159
 
91
160
  class << self
@@ -97,7 +166,10 @@ module X402
97
166
  end
98
167
 
99
168
  def currency_config_for_chain(chain_name)
100
- CURRENCY_BY_CHAIN[chain_name] || raise(ConfigurationError, "Unsupported chain for currency: #{chain_name}")
169
+ config = CHAINS[chain_name]
170
+ raise(ConfigurationError, "Unsupported chain for currency: #{chain_name}") unless config
171
+
172
+ config[:currency]
101
173
  end
102
174
 
103
175
  def supported_chains
@@ -129,8 +201,9 @@ module X402
129
201
  custom = X402.configuration.token_config(chain_name, symbol)
130
202
  return custom if custom
131
203
 
132
- if symbol.upcase == "USDC" && CURRENCY_BY_CHAIN[chain_name]
133
- currency_config_for_chain(chain_name)
204
+ config = CHAINS[chain_name]
205
+ if symbol.upcase == "USDC" && config&.dig(:currency)
206
+ config[:currency]
134
207
  else
135
208
  raise ConfigurationError, "Unknown token #{symbol} for chain #{chain_name}. Register with config.register_token()"
136
209
  end
@@ -13,7 +13,7 @@ module X402
13
13
  @currency = ENV.fetch("X402_CURRENCY", "USDC")
14
14
  @optimistic = ENV.fetch("X402_OPTIMISTIC", "false") == "true"
15
15
  @version = ENV.fetch("X402_VERSION", "2").to_i
16
- @fee_payer = ENV.fetch("X402_FEE_PAYER", nil)
16
+ @fee_payer = nil
17
17
  @custom_chains = {}
18
18
  @custom_tokens = {}
19
19
  @accepted_payments = []
@@ -2,6 +2,6 @@
2
2
 
3
3
  module X402
4
4
  module Rails
5
- VERSION = "1.0.0"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: x402-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - QuickNode
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-01-08 00:00:00.000000000 Z
11
+ date: 2026-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '6.0'
125
125
  description: Accept instant blockchain micropayments in Rails applications using the
126
- x402 protocol.
126
+ x402 protocol (supporting x402 v2)
127
127
  email:
128
128
  - zach+x402@quiknode.io
129
129
  executables: []
@@ -162,7 +162,7 @@ metadata:
162
162
  homepage_uri: https://github.com/quiknode-labs/x402-rails
163
163
  source_code_uri: https://github.com/quiknode-labs/x402-rails
164
164
  documentation_uri: https://github.com/quiknode-labs/x402-rails#readme
165
- post_install_message:
165
+ post_install_message:
166
166
  rdoc_options: []
167
167
  require_paths:
168
168
  - lib
@@ -177,8 +177,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
177
  - !ruby/object:Gem::Version
178
178
  version: '0'
179
179
  requirements: []
180
- rubygems_version: 3.5.16
181
- signing_key:
180
+ rubygems_version: 3.0.3.1
181
+ signing_key:
182
182
  specification_version: 4
183
183
  summary: Rails integration for x402 payment protocol (supporting x402 v2).
184
184
  test_files: []