rail0-sdk 1.0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +753 -0
- data/lib/rail0/api_error.rb +50 -0
- data/lib/rail0/backoff.rb +66 -0
- data/lib/rail0/client.rb +89 -0
- data/lib/rail0/default_logger.rb +82 -0
- data/lib/rail0/error_hints.rb +80 -0
- data/lib/rail0/http_client.rb +99 -0
- data/lib/rail0/request.rb +211 -0
- data/lib/rail0/resources/accounts.rb +35 -0
- data/lib/rail0/resources/analytics.rb +112 -0
- data/lib/rail0/resources/auth.rb +192 -0
- data/lib/rail0/resources/chains.rb +29 -0
- data/lib/rail0/resources/disputes.rb +35 -0
- data/lib/rail0/resources/health.rb +23 -0
- data/lib/rail0/resources/payment_methods.rb +37 -0
- data/lib/rail0/resources/payments.rb +344 -0
- data/lib/rail0/resources/query.rb +19 -0
- data/lib/rail0/resources/tokens.rb +28 -0
- data/lib/rail0/resources/wallets.rb +160 -0
- data/lib/rail0/resources/webhooks.rb +146 -0
- data/lib/rail0/signing.rb +370 -0
- data/lib/rail0/stablecoins.rb +123 -0
- data/lib/rail0/types.rb +295 -0
- data/lib/rail0/version.rb +9 -0
- data/lib/rail0/webhook_signature.rb +80 -0
- data/lib/rail0-sdk.rb +11 -0
- data/lib/rail0.rb +20 -0
- metadata +91 -0
data/lib/rail0/types.rb
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Code generated by gen/generate.rb — DO NOT EDIT.
|
|
4
|
+
# Re-run `ruby gen/generate.rb` after updating the gateway OpenAPI schema.
|
|
5
|
+
#
|
|
6
|
+
# Source: rail0-gateway/docs/openapi.json
|
|
7
|
+
#
|
|
8
|
+
# These Structs document the gateway's request/response shapes. They are
|
|
9
|
+
# reference only — SDK methods return plain Hashes with symbol keys.
|
|
10
|
+
|
|
11
|
+
module Rail0
|
|
12
|
+
module Types
|
|
13
|
+
# A wallet's on-chain balances, one entry per chain.
|
|
14
|
+
WalletBalances = Struct.new(
|
|
15
|
+
:wallet_id, # String
|
|
16
|
+
:address, # String
|
|
17
|
+
:balances, # Array
|
|
18
|
+
keyword_init: true
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Balances on one chain. `native`/`tokens` are null when `error` is present; `error` is null on
|
|
22
|
+
# success.
|
|
23
|
+
ChainBalance = Struct.new(
|
|
24
|
+
:chain_id, # Integer
|
|
25
|
+
:network_type, # String
|
|
26
|
+
:native, # AssetBalance
|
|
27
|
+
:tokens, # Array
|
|
28
|
+
:error, # BalanceError
|
|
29
|
+
keyword_init: true
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# A single balance line — the native gas token or one ERC-20.
|
|
33
|
+
AssetBalance = Struct.new(
|
|
34
|
+
:symbol, # String
|
|
35
|
+
:address, # Token contract address; null for the native balance.
|
|
36
|
+
:decimals, # Integer
|
|
37
|
+
:raw, # String — Balance in base units (exact).
|
|
38
|
+
:amount, # String — Human-decimal balance.
|
|
39
|
+
keyword_init: true
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Why a chain's balances couldn't be read.
|
|
43
|
+
BalanceError = Struct.new(
|
|
44
|
+
:code, # String
|
|
45
|
+
:message, # String
|
|
46
|
+
keyword_init: true
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Error envelope. Every error this API returns carries `code` (stable and machine-readable — the
|
|
50
|
+
# only field to branch on), `title` (a short label) and `detail` (one or two sentences fit to show a
|
|
51
|
+
# user verbatim). The wording comes from the gateway's error catalogue, so the same condition always
|
|
52
|
+
# reads the same way wherever it surfaces. `status` and `message` are the pre-code/title/detail
|
|
53
|
+
# names, derived from the same entry and kept for existing clients: `status` carries the family a
|
|
54
|
+
# client used to switch on (e.g. `forbidden`, `invalid_state`) where `code` is narrower, and
|
|
55
|
+
# `message` equals `detail`. Context fields may also be present (`resource`, `param`, `chain_id`,
|
|
56
|
+
# `token`, `payee`, `errors`).
|
|
57
|
+
ApiErrorBody = Struct.new(
|
|
58
|
+
:code, # String
|
|
59
|
+
:title, # String
|
|
60
|
+
:detail, # String
|
|
61
|
+
:status, # String — Legacy: the error family, or the code itself when there is no wider family.
|
|
62
|
+
:message, # String — Legacy alias of `detail`.
|
|
63
|
+
keyword_init: true
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Gateway liveness/readiness. Only the database gates the HTTP code (200 healthy, 503 when the DB is
|
|
67
|
+
# unreachable); Sidekiq is reported but never flips the code, since the API still serves synchronous
|
|
68
|
+
# requests when workers are down. `status` is the global signal: `ok` (all good), `degraded` (DB ok
|
|
69
|
+
# but Sidekiq not ok), `error` (DB down — the only 503).
|
|
70
|
+
Health = Struct.new(
|
|
71
|
+
:status, # String
|
|
72
|
+
:api_version, # String
|
|
73
|
+
:contract_version, # String
|
|
74
|
+
:db, # String
|
|
75
|
+
:sidekiq, # Hash — Worker fleet health (does not gate liveness).
|
|
76
|
+
:active_chains, # Integer
|
|
77
|
+
:active_contracts, # Integer
|
|
78
|
+
:timestamp, # String
|
|
79
|
+
keyword_init: true
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# A single-use SIWE nonce to embed in the sign-in message.
|
|
83
|
+
Nonce = Struct.new(
|
|
84
|
+
:nonce, # String — The nonce value to place in the SIWE message's `Nonce:` field.
|
|
85
|
+
:expires_at, # String — When the nonce expires and can no longer be used.
|
|
86
|
+
keyword_init: true
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Issued after a successful SIWE verification. SIWE alone proves control of the address, so a token
|
|
90
|
+
# is issued even when the address is not registered to any account; in that case `account_id` and
|
|
91
|
+
# `name` are null (an account-less session, e.g. a buyer). Clients that require an account must
|
|
92
|
+
# treat a null `account_id` as not-allowed.
|
|
93
|
+
Session = Struct.new(
|
|
94
|
+
:token, # String — JWT bearer token.
|
|
95
|
+
:address, # String — Resolved wallet address.
|
|
96
|
+
:account_id, # String — The account owning the signed-in wallet, or null for an account-less (e.g. buyer) session.
|
|
97
|
+
:name, # String — The account's human-readable name, or null for an account-less session.
|
|
98
|
+
:expires_at, # String
|
|
99
|
+
keyword_init: true
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Public blockchain view.
|
|
103
|
+
#
|
|
104
|
+
# required_confirmations and finality_tag together say how long a payer waits. Read
|
|
105
|
+
# the PAIR, not the number: where a chain serves a finality tag the gateway gates on
|
|
106
|
+
# that tag, and the count is the fallback for chains serving none — so quoting the
|
|
107
|
+
# count on a tagged chain names a wait nobody applies.
|
|
108
|
+
Blockchain = Struct.new(
|
|
109
|
+
:chain_id, # Integer
|
|
110
|
+
:name, # String
|
|
111
|
+
:native_symbol, # String
|
|
112
|
+
:network_type, # String
|
|
113
|
+
:explorer_url, # String
|
|
114
|
+
:required_confirmations, # Integer
|
|
115
|
+
:finality_tag, # String, nil where the chain serves no tag
|
|
116
|
+
keyword_init: true
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Public accepted-token view. The listing is not implicitly active-only (a payment references its
|
|
120
|
+
# token address forever, so a retired token must stay resolvable), so `active` tells a usable token
|
|
121
|
+
# from a retired one.
|
|
122
|
+
Token = Struct.new(
|
|
123
|
+
:chain_id, # Integer
|
|
124
|
+
:symbol, # String
|
|
125
|
+
:address, # String
|
|
126
|
+
:decimals, # Integer
|
|
127
|
+
:active, # Boolean — False for a retired token: still resolvable for historical payments, but not usable for a new one.
|
|
128
|
+
keyword_init: true
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Public-safe wallet view (the reduced set a buyer needs to discover a merchant's payment methods).
|
|
132
|
+
Wallet = Struct.new(
|
|
133
|
+
:id, # String
|
|
134
|
+
:address, # String
|
|
135
|
+
:label,
|
|
136
|
+
:active, # Boolean
|
|
137
|
+
keyword_init: true
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# A wallet's token holding, with the wallet, token, and blockchain nested via their own schemas.
|
|
141
|
+
WalletToken = Struct.new(
|
|
142
|
+
:default, # Boolean
|
|
143
|
+
:active, # Boolean
|
|
144
|
+
:wallet, # Wallet
|
|
145
|
+
:token, # Token
|
|
146
|
+
:blockchain, # Blockchain
|
|
147
|
+
keyword_init: true
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# A wallet's token holding as nested under its wallet (GET /accounts/:id/wallets): the token plus
|
|
151
|
+
# this wallet's per-token flags, without re-nesting the wallet.
|
|
152
|
+
WalletTokenHolding = Struct.new(
|
|
153
|
+
:token, # Token
|
|
154
|
+
:active, # Boolean
|
|
155
|
+
:default, # Boolean
|
|
156
|
+
keyword_init: true
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# A wallet with its token holdings nested inline. `tokens` is empty when the wallet has none — the
|
|
160
|
+
# wallet is still returned, never omitted.
|
|
161
|
+
WalletWithTokens = Struct.new(
|
|
162
|
+
:id, # String
|
|
163
|
+
:address, # String
|
|
164
|
+
:label,
|
|
165
|
+
:active, # Boolean
|
|
166
|
+
:tokens, # Array
|
|
167
|
+
keyword_init: true
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Base persisted payment fields.
|
|
171
|
+
Payment = Struct.new(
|
|
172
|
+
:id, # String
|
|
173
|
+
:contract_id, # String
|
|
174
|
+
:chain_id, # Integer — EVM chain id of the deployment. On list rows too: `amount` is
|
|
175
|
+
# base units, and the token's decimals resolve from `token` PLUS its chain.
|
|
176
|
+
:rail0_id, # String — Protocol-level identifier (66-char hex).
|
|
177
|
+
:status, # String
|
|
178
|
+
:mode, # String
|
|
179
|
+
:amount, # String
|
|
180
|
+
:capturable_amount, # String — Mirrors on-chain capturableAmount (escrow still held); base units.
|
|
181
|
+
:refundable_amount, # String — Mirrors on-chain refundableAmount (held by payee, still refundable); base units.
|
|
182
|
+
# The window after a PARTIAL capture where neither void nor release can return the
|
|
183
|
+
# buyer's remaining escrow — the answer to "why did both just refuse?".
|
|
184
|
+
:escrow_stranded, # Boolean
|
|
185
|
+
:escrow_returnable_at, # String, nil — ISO-8601 end of that window; nil outside it.
|
|
186
|
+
:config_hash, # String
|
|
187
|
+
:payer, # String
|
|
188
|
+
:payee, # String
|
|
189
|
+
:token, # String
|
|
190
|
+
:authorization_expiry, # Integer
|
|
191
|
+
:refund_expiry, # Integer
|
|
192
|
+
:disputed, # Boolean — True while an open dispute exists.
|
|
193
|
+
:last_error_code, # Decoded reason of the last failed on-chain attempt; null once the payment makes forward progress. Non-null means the latest attempt failed.
|
|
194
|
+
:last_error_message, # Human-readable form of last_error_code.
|
|
195
|
+
:description,
|
|
196
|
+
:metadata,
|
|
197
|
+
:signed_at, # When the payer signature was stored (null while unsigned).
|
|
198
|
+
:created_at, # String
|
|
199
|
+
:updated_at, # String
|
|
200
|
+
keyword_init: true
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Buyer-driven, signal-only dispute lifecycle (no fund effect).
|
|
204
|
+
Dispute = Struct.new(
|
|
205
|
+
:id, # String
|
|
206
|
+
:payment_id, # String
|
|
207
|
+
:status, # String
|
|
208
|
+
:reason, # String — On-chain bytes32 reason code (hex).
|
|
209
|
+
:opened_block,
|
|
210
|
+
:opened_at, # String
|
|
211
|
+
:closed_by,
|
|
212
|
+
:close_reason,
|
|
213
|
+
:closed_block,
|
|
214
|
+
:closed_at,
|
|
215
|
+
keyword_init: true
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
Transaction = Struct.new(
|
|
219
|
+
:id, # String
|
|
220
|
+
:payment_id, # String
|
|
221
|
+
:operation, # String
|
|
222
|
+
:status, # String
|
|
223
|
+
:error_code, # Decoded failure code, null unless `status` is "failed". Same catalogue as an error body's `code`: a RAIL0 custom error (`not_payee`), a token-level revert (`insufficient_token_balance`, `invalid_token_signature`, `authorization_already_used`), a Solidity panic, or a rejection that stopped the broadcast before the chain saw it (`insufficient_gas_funds`, `nonce_too_low`).
|
|
224
|
+
:error_title, # String — Short label for `error_code`; null unless failed.
|
|
225
|
+
:error_detail, # String — Sentence explaining the failure; null unless failed. Carries the chain's own words when the revert was not one the gateway recognises.
|
|
226
|
+
:error_message, # Legacy alias of `error_detail`.
|
|
227
|
+
:unsigned_transaction,
|
|
228
|
+
:transaction_hash,
|
|
229
|
+
:amount,
|
|
230
|
+
:block_number,
|
|
231
|
+
:gas_used, # Gas units used, mirrored from the indexer on confirm.
|
|
232
|
+
:gas_limit, # Gas limit, mirrored from the indexer on confirm.
|
|
233
|
+
:effective_gas_price, # Effective gas price in wei, mirrored from the indexer on confirm.
|
|
234
|
+
:base_fee_per_gas, # Block base fee per gas in wei, mirrored from the indexer on confirm.
|
|
235
|
+
:gas_cost, # Total gas cost in wei (gas_used * effective_gas_price); derived, null until confirmed.
|
|
236
|
+
:pending_at,
|
|
237
|
+
:submitted_at,
|
|
238
|
+
:confirmed_at,
|
|
239
|
+
:created_at, # String
|
|
240
|
+
:updated_at, # String
|
|
241
|
+
keyword_init: true
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
Webhook = Struct.new(
|
|
245
|
+
:id, # String
|
|
246
|
+
:name, # String
|
|
247
|
+
:callback_url, # String
|
|
248
|
+
:topics, # Array<WebhookTopic> — every event this subscription delivers
|
|
249
|
+
:active, # Boolean
|
|
250
|
+
:circuit_state, # String
|
|
251
|
+
:circuit_failure_count, # Integer
|
|
252
|
+
:created_at, # String
|
|
253
|
+
:updated_at, # String
|
|
254
|
+
keyword_init: true
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
EventCallback = Struct.new(
|
|
258
|
+
:id, # String
|
|
259
|
+
:webhook_id, # String
|
|
260
|
+
:payment_id, # String
|
|
261
|
+
:topic, # String
|
|
262
|
+
:callback_url, # String
|
|
263
|
+
:response_code,
|
|
264
|
+
:response_message,
|
|
265
|
+
:error_reason,
|
|
266
|
+
:status, # String
|
|
267
|
+
:created_at, # String
|
|
268
|
+
:payload, # The JSON request body POSTed to the callback URL (decompressed).
|
|
269
|
+
keyword_init: true
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Sweeper view of a stale submitted transaction.
|
|
273
|
+
SyncTransaction = Struct.new(
|
|
274
|
+
:transaction_hash, # String
|
|
275
|
+
:operation, # String
|
|
276
|
+
:payment_id, # String — Protocol-level rail0_id.
|
|
277
|
+
:chain_id, # Integer
|
|
278
|
+
keyword_init: true
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Per-chain indexer config.
|
|
282
|
+
SyncBlockchain = Struct.new(
|
|
283
|
+
:chain_id, # Integer
|
|
284
|
+
:start_block, # Integer
|
|
285
|
+
:required_confirmations, # Integer — Fallback burial depth, used where the chain serves no finality tag.
|
|
286
|
+
:finality_tag, # String — Which block the chain calls settled ("safe", "finalized" or
|
|
287
|
+
# "depth"); the indexer gates every notify on it.
|
|
288
|
+
:explorer_url, # Block explorer base URL; null when the chain has none.
|
|
289
|
+
:network_type, # String — "testnet" or "mainnet"; selects which chains a deployment indexes.
|
|
290
|
+
:rpc_urls, # Array — Ordered list of public RPC endpoints tried in turn (serial fallback).
|
|
291
|
+
:contracts, # Array — Active RAIL0 contract addresses on the chain (all watched versions).
|
|
292
|
+
keyword_init: true
|
|
293
|
+
)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rail0
|
|
4
|
+
# 1.0.0 is where the PUBLISHED history starts. The gem reached 1.2.0 while it was
|
|
5
|
+
# consumed by path from a sibling checkout, where the number bought nothing: no registry
|
|
6
|
+
# ever served a 1.x, no tag was ever cut, and the one consumer resolves the working tree.
|
|
7
|
+
# Publishing from 1.2.0 would have implied two releases nobody can install.
|
|
8
|
+
VERSION = "1.0.0"
|
|
9
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module Rail0
|
|
6
|
+
# Verification for the signature the gateway puts on every webhook delivery.
|
|
7
|
+
#
|
|
8
|
+
# The SDK used to hand out `shared_secret` on create/rotate, tell the reader to
|
|
9
|
+
# verify deliveries with it, and provide nothing to verify with — so every
|
|
10
|
+
# consumer had to reverse-engineer the scheme, and getting it wrong is a security
|
|
11
|
+
# bug in THEIR app, not a failed request. (#9)
|
|
12
|
+
#
|
|
13
|
+
# A delivery carries:
|
|
14
|
+
#
|
|
15
|
+
# X-Rail0-Topic the topic, e.g. "payments.captured"
|
|
16
|
+
# X-Rail0-Timestamp unix seconds, as a string
|
|
17
|
+
# X-Rail0-Signature hex HMAC-SHA256 over "{timestamp}.{body}", keyed by the secret
|
|
18
|
+
#
|
|
19
|
+
# The timestamp is inside the signed string on purpose: without it a captured
|
|
20
|
+
# delivery is replayable forever, because the body alone stays valid indefinitely.
|
|
21
|
+
# That is why {verify} rejects a stale timestamp even when the digest matches —
|
|
22
|
+
# checking the digest and ignoring the clock leaves the replay window wide open.
|
|
23
|
+
module WebhookSignature
|
|
24
|
+
# Same window the gateway's own /sync channel uses, symmetric so a consumer
|
|
25
|
+
# whose clock runs fast doesn't reject live deliveries.
|
|
26
|
+
DEFAULT_TOLERANCE_SECONDS = 300
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# Verify a delivery. Returns true / false — nothing here raises on a bad
|
|
30
|
+
# signature, since a spoofed request is an expected condition on a public
|
|
31
|
+
# endpoint, not an exception.
|
|
32
|
+
#
|
|
33
|
+
# @param body [String] the RAW request body, exactly as received. Re-serialising
|
|
34
|
+
# a parsed hash changes key order and whitespace, and the digest with it.
|
|
35
|
+
# @param signature [String] the X-Rail0-Signature header.
|
|
36
|
+
# @param timestamp [String, Integer] the X-Rail0-Timestamp header.
|
|
37
|
+
# @param secret [String] the webhook's shared_secret.
|
|
38
|
+
# @param tolerance [Integer] accepted clock skew in seconds, either direction.
|
|
39
|
+
# @param now [Time] injectable clock, for tests.
|
|
40
|
+
# @return [Boolean]
|
|
41
|
+
# `verify`, not `verify?`: it is the SDK's public API and reads as the action it
|
|
42
|
+
# performs (see Naming/PredicateMethod in .rubocop-shared.yml).
|
|
43
|
+
def verify(body:, signature:, timestamp:, secret:, tolerance: DEFAULT_TOLERANCE_SECONDS, now: Time.now)
|
|
44
|
+
return false if body.nil? || secret.to_s.empty?
|
|
45
|
+
return false unless fresh?(timestamp, tolerance, now)
|
|
46
|
+
|
|
47
|
+
secure_equal?(signature.to_s, expected_signature(body, timestamp, secret))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The signature the gateway would send for this (timestamp, body). Exposed so a
|
|
51
|
+
# consumer can log or diff the two sides when a delivery is being rejected —
|
|
52
|
+
# comparing digests by eye is otherwise the only way to debug it.
|
|
53
|
+
#
|
|
54
|
+
# @return [String] hex HMAC-SHA256 over "{timestamp}.{body}"
|
|
55
|
+
def expected_signature(body, timestamp, secret)
|
|
56
|
+
OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{body}")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def fresh?(timestamp, tolerance, now)
|
|
62
|
+
seconds = Integer(timestamp.to_s, 10)
|
|
63
|
+
(now.to_i - seconds).abs <= tolerance
|
|
64
|
+
rescue ArgumentError, TypeError
|
|
65
|
+
# A missing or non-numeric timestamp can't be inside any window.
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Constant-time compare: a byte-by-byte early return leaks how much of the
|
|
70
|
+
# digest matched, which is enough to forge one byte at a time.
|
|
71
|
+
# OpenSSL.secure_compare needs equal lengths to be meaningful, and returns
|
|
72
|
+
# false rather than raising on a mismatch, so the length check is explicit.
|
|
73
|
+
def secure_equal?(given, expected)
|
|
74
|
+
return false unless given.bytesize == expected.bytesize
|
|
75
|
+
|
|
76
|
+
OpenSSL.secure_compare(given, expected)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/rail0-sdk.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The gem is "rail0-sdk"; its entry point is `rail0`.
|
|
4
|
+
#
|
|
5
|
+
# This file is not cosmetic. Bundler requires a gem by its OWN name by default, so
|
|
6
|
+
# `gem "rail0-sdk"` in a Gemfile with Bundler.require raises LoadError without it. It also
|
|
7
|
+
# gives anyone who reaches for the name they installed the file they expect.
|
|
8
|
+
#
|
|
9
|
+
# The canonical path stays `rail0`, unchanged: it is what every existing caller, example and
|
|
10
|
+
# integration test requires, and what the Rail0 module is named after.
|
|
11
|
+
require_relative "rail0"
|
data/lib/rail0.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Entry point. Everything below is loaded with require_relative on purpose: the
|
|
4
|
+
# gem used to unshift lib/rail0 and lib/rail0/resources onto the GLOBAL $LOAD_PATH
|
|
5
|
+
# so its files could require each other by bare name ("request", "query", …).
|
|
6
|
+
# That put ~20 generic basenames at the FRONT of the search path for the whole
|
|
7
|
+
# process, so any gem loaded afterwards doing a bare `require "request"` silently
|
|
8
|
+
# got ours — order-dependent and effectively undiagnosable. require_relative needs
|
|
9
|
+
# no path at all. (#5)
|
|
10
|
+
|
|
11
|
+
require_relative "rail0/version"
|
|
12
|
+
require_relative "rail0/error_hints"
|
|
13
|
+
require_relative "rail0/api_error"
|
|
14
|
+
require_relative "rail0/backoff"
|
|
15
|
+
require_relative "rail0/default_logger"
|
|
16
|
+
require_relative "rail0/request"
|
|
17
|
+
require_relative "rail0/http_client"
|
|
18
|
+
require_relative "rail0/client"
|
|
19
|
+
require_relative "rail0/webhook_signature"
|
|
20
|
+
require_relative "rail0/stablecoins"
|
metadata
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rail0-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Commerce Layer
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: logger
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.6'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.6'
|
|
27
|
+
description: |
|
|
28
|
+
REST client for the RAIL0 stablecoin payment API. Wraps the authorize →
|
|
29
|
+
capture → refund lifecycle with full type documentation, retry support,
|
|
30
|
+
pluggable logging, and optional off-chain EIP-3009 signing.
|
|
31
|
+
email:
|
|
32
|
+
executables: []
|
|
33
|
+
extensions: []
|
|
34
|
+
extra_rdoc_files: []
|
|
35
|
+
files:
|
|
36
|
+
- LICENSE
|
|
37
|
+
- README.md
|
|
38
|
+
- lib/rail0-sdk.rb
|
|
39
|
+
- lib/rail0.rb
|
|
40
|
+
- lib/rail0/api_error.rb
|
|
41
|
+
- lib/rail0/backoff.rb
|
|
42
|
+
- lib/rail0/client.rb
|
|
43
|
+
- lib/rail0/default_logger.rb
|
|
44
|
+
- lib/rail0/error_hints.rb
|
|
45
|
+
- lib/rail0/http_client.rb
|
|
46
|
+
- lib/rail0/request.rb
|
|
47
|
+
- lib/rail0/resources/accounts.rb
|
|
48
|
+
- lib/rail0/resources/analytics.rb
|
|
49
|
+
- lib/rail0/resources/auth.rb
|
|
50
|
+
- lib/rail0/resources/chains.rb
|
|
51
|
+
- lib/rail0/resources/disputes.rb
|
|
52
|
+
- lib/rail0/resources/health.rb
|
|
53
|
+
- lib/rail0/resources/payment_methods.rb
|
|
54
|
+
- lib/rail0/resources/payments.rb
|
|
55
|
+
- lib/rail0/resources/query.rb
|
|
56
|
+
- lib/rail0/resources/tokens.rb
|
|
57
|
+
- lib/rail0/resources/wallets.rb
|
|
58
|
+
- lib/rail0/resources/webhooks.rb
|
|
59
|
+
- lib/rail0/signing.rb
|
|
60
|
+
- lib/rail0/stablecoins.rb
|
|
61
|
+
- lib/rail0/types.rb
|
|
62
|
+
- lib/rail0/version.rb
|
|
63
|
+
- lib/rail0/webhook_signature.rb
|
|
64
|
+
homepage: https://github.com/commercelayer/rail0-ruby
|
|
65
|
+
licenses:
|
|
66
|
+
- MIT
|
|
67
|
+
metadata:
|
|
68
|
+
source_code_uri: https://github.com/commercelayer/rail0-ruby
|
|
69
|
+
bug_tracker_uri: https://github.com/commercelayer/rail0-ruby/issues
|
|
70
|
+
changelog_uri: https://github.com/commercelayer/rail0-ruby/releases
|
|
71
|
+
rubygems_mfa_required: 'true'
|
|
72
|
+
post_install_message:
|
|
73
|
+
rdoc_options: []
|
|
74
|
+
require_paths:
|
|
75
|
+
- lib
|
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '3.0'
|
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
requirements: []
|
|
87
|
+
rubygems_version: 3.5.11
|
|
88
|
+
signing_key:
|
|
89
|
+
specification_version: 4
|
|
90
|
+
summary: Ruby SDK for the RAIL0 stablecoin payment protocol
|
|
91
|
+
test_files: []
|