nowpayments 0.2.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/.env.example +4 -0
- data/CHANGELOG.md +122 -0
- data/LICENSE.txt +21 -0
- data/README.md +578 -0
- data/Rakefile +12 -0
- data/docs/API.md +922 -0
- data/examples/jwt_authentication_example.rb +254 -0
- data/examples/simple_demo.rb +72 -0
- data/examples/webhook_server.rb +85 -0
- data/lib/nowpayments/api/authentication.rb +66 -0
- data/lib/nowpayments/api/conversions.rb +42 -0
- data/lib/nowpayments/api/currencies.rb +32 -0
- data/lib/nowpayments/api/custody.rb +147 -0
- data/lib/nowpayments/api/estimation.rb +34 -0
- data/lib/nowpayments/api/fiat_payouts.rb +150 -0
- data/lib/nowpayments/api/invoices.rb +88 -0
- data/lib/nowpayments/api/payments.rb +93 -0
- data/lib/nowpayments/api/payouts.rb +107 -0
- data/lib/nowpayments/api/status.rb +15 -0
- data/lib/nowpayments/api/subscriptions.rb +93 -0
- data/lib/nowpayments/client.rb +91 -0
- data/lib/nowpayments/errors.rb +60 -0
- data/lib/nowpayments/middleware/error_handler.rb +33 -0
- data/lib/nowpayments/rack.rb +25 -0
- data/lib/nowpayments/version.rb +5 -0
- data/lib/nowpayments/webhook.rb +62 -0
- data/lib/nowpayments.rb +12 -0
- data/sig/nowpayments.rbs +4 -0
- metadata +92 -0
data/README.md
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
# NOWPayments Ruby SDK
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/nowpayments)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Production-ready Ruby wrapper for the [NOWPayments API](https://documenter.getpostman.com/view/7907941/2s93JusNJt). Accept cryptocurrency payments with minimal code.
|
|
7
|
+
|
|
8
|
+
## Why NOWPayments?
|
|
9
|
+
|
|
10
|
+
- **150+ cryptocurrencies** - Bitcoin, Ethereum, USDT, and more
|
|
11
|
+
- **No KYC required** - Accept payments immediately
|
|
12
|
+
- **Instant settlement** - Real-time payment processing
|
|
13
|
+
- **Low fees** - Competitive transaction costs
|
|
14
|
+
- **Global reach** - Accept payments from anywhere
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add to your Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem 'nowpayments', git: 'https://github.com/Sentia/nowpayments'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install directly:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gem install nowpayments
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require 'nowpayments'
|
|
34
|
+
|
|
35
|
+
# Initialize client (sandbox for testing, production when ready)
|
|
36
|
+
client = NOWPayments::Client.new(
|
|
37
|
+
api_key: ENV['NOWPAYMENTS_API_KEY'],
|
|
38
|
+
sandbox: true
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Create a payment
|
|
42
|
+
payment = client.create_payment(
|
|
43
|
+
price_amount: 100.0,
|
|
44
|
+
price_currency: 'usd',
|
|
45
|
+
pay_currency: 'btc',
|
|
46
|
+
order_id: 'order-123',
|
|
47
|
+
ipn_callback_url: 'https://yourdomain.com/webhooks/nowpayments'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
puts "Payment address: #{payment['pay_address']}"
|
|
51
|
+
puts "Amount: #{payment['pay_amount']} BTC"
|
|
52
|
+
puts "Status: #{payment['payment_status']}"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
### 🎉 Complete API Coverage - 57 Methods, 100% Coverage!
|
|
58
|
+
|
|
59
|
+
**11 API Modules with Full Implementation:**
|
|
60
|
+
|
|
61
|
+
1. **Authentication (5 methods)** - JWT token management for protected endpoints
|
|
62
|
+
2. **Status (1 method)** - API health checks
|
|
63
|
+
3. **Currencies (3 methods)** - Available cryptocurrencies and details
|
|
64
|
+
4. **Payments (4 methods)** - Create and track cryptocurrency payments
|
|
65
|
+
5. **Invoices (3 methods)** - Hosted payment pages with status tracking
|
|
66
|
+
6. **Estimates (2 methods)** - Price calculations and minimum amounts
|
|
67
|
+
7. **Mass Payouts (8 methods)** - Batch withdrawals with 2FA verification
|
|
68
|
+
8. **Conversions (3 methods)** - Currency conversions at market rates
|
|
69
|
+
9. **Subscriptions (9 methods)** - Recurring payment plans and billing
|
|
70
|
+
10. **Custody/Sub-accounts (11 methods)** - User wallet management for marketplaces
|
|
71
|
+
11. **Fiat Payouts (8 methods)** - Beta: Crypto to fiat withdrawals
|
|
72
|
+
|
|
73
|
+
**Security & Production Ready:**
|
|
74
|
+
- **JWT Authentication** - Bearer token support for sensitive operations
|
|
75
|
+
- **Webhook Verification** - HMAC-SHA512 signature validation
|
|
76
|
+
- **Constant-time comparison** - Prevents timing attacks
|
|
77
|
+
- **Comprehensive error handling** - 8 exception classes with detailed messages
|
|
78
|
+
- **100% tested** - 23 passing tests, RuboCop clean
|
|
79
|
+
|
|
80
|
+
### Complete Method List (57 Methods)
|
|
81
|
+
|
|
82
|
+
<details>
|
|
83
|
+
<summary><b>Authentication (5 methods)</b> - JWT token management</summary>
|
|
84
|
+
|
|
85
|
+
- `authenticate(email:, password:)` - Get JWT token (5-min expiry)
|
|
86
|
+
- `jwt_token(email:, password:)` - Get token with auto-refresh
|
|
87
|
+
- `jwt_expired?` - Check if token is expired
|
|
88
|
+
- `clear_jwt_token` - Clear stored token
|
|
89
|
+
- `jwt_time_remaining` - Seconds until expiry
|
|
90
|
+
|
|
91
|
+
</details>
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary><b>Status & Currencies (4 methods)</b> - API health and currency info</summary>
|
|
95
|
+
|
|
96
|
+
- `status` - Check API status
|
|
97
|
+
- `currencies(fixed_rate: nil)` - Get available currencies
|
|
98
|
+
- `full_currencies` - Detailed currency information
|
|
99
|
+
- `merchant_coins` - Your enabled currencies
|
|
100
|
+
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
<details>
|
|
104
|
+
<summary><b>Payments (4 methods)</b> - Standard cryptocurrency payments</summary>
|
|
105
|
+
|
|
106
|
+
- `create_payment(...)` - Create new payment
|
|
107
|
+
- `payment(payment_id)` - Get payment status
|
|
108
|
+
- `payments(limit:, page:, ...)` - List payments with filters
|
|
109
|
+
- `update_payment_estimate(payment_id)` - Update exchange rate
|
|
110
|
+
|
|
111
|
+
</details>
|
|
112
|
+
|
|
113
|
+
<details>
|
|
114
|
+
<summary><b>Invoices (3 methods)</b> - Hosted payment pages</summary>
|
|
115
|
+
|
|
116
|
+
- `create_invoice(...)` - Create invoice with payment page
|
|
117
|
+
- `create_invoice_payment(...)` - Create payment by invoice ID
|
|
118
|
+
- `invoice(invoice_id)` - Get invoice status
|
|
119
|
+
|
|
120
|
+
</details>
|
|
121
|
+
|
|
122
|
+
<details>
|
|
123
|
+
<summary><b>Estimates (2 methods)</b> - Price calculations</summary>
|
|
124
|
+
|
|
125
|
+
- `estimate(amount:, currency_from:, currency_to:)` - Price estimate
|
|
126
|
+
- `min_amount(currency_from:, currency_to:)` - Minimum payment amount
|
|
127
|
+
|
|
128
|
+
</details>
|
|
129
|
+
|
|
130
|
+
<details>
|
|
131
|
+
<summary><b>Mass Payouts (8 methods)</b> - Batch withdrawals (JWT required)</summary>
|
|
132
|
+
|
|
133
|
+
- `balance` - Get account balance
|
|
134
|
+
- `create_payout(withdrawals:, ...)` - Create batch payout (JWT)
|
|
135
|
+
- `verify_payout(batch_withdrawal_id:, verification_code:)` - 2FA verify (JWT)
|
|
136
|
+
- `payout_status(payout_id)` - Get payout status
|
|
137
|
+
- `list_payouts(limit:, offset:)` - List all payouts (JWT)
|
|
138
|
+
- `validate_payout_address(address:, currency:, ...)` - Validate address
|
|
139
|
+
- `min_payout_amount(currency:)` - Minimum payout amount
|
|
140
|
+
- `payout_fee(currency:, amount:)` - Calculate payout fee
|
|
141
|
+
|
|
142
|
+
</details>
|
|
143
|
+
|
|
144
|
+
<details>
|
|
145
|
+
<summary><b>Conversions (3 methods)</b> - Currency conversions (JWT required)</summary>
|
|
146
|
+
|
|
147
|
+
- `create_conversion(from_currency:, to_currency:, amount:)` - Convert crypto (JWT)
|
|
148
|
+
- `conversion_status(conversion_id)` - Check conversion status (JWT)
|
|
149
|
+
- `list_conversions(limit:, offset:)` - List all conversions (JWT)
|
|
150
|
+
|
|
151
|
+
</details>
|
|
152
|
+
|
|
153
|
+
<details>
|
|
154
|
+
<summary><b>Subscriptions (9 methods)</b> - Recurring payments</summary>
|
|
155
|
+
|
|
156
|
+
- `subscription_plans` - List all subscription plans
|
|
157
|
+
- `create_subscription_plan(plan_data)` - Create new plan
|
|
158
|
+
- `update_subscription_plan(plan_id, plan_data)` - Update plan
|
|
159
|
+
- `subscription_plan(plan_id)` - Get plan details
|
|
160
|
+
- `create_subscription(plan_id:, email:)` - Create subscription
|
|
161
|
+
- `list_recurring_payments(...)` - List recurring payments with filters
|
|
162
|
+
- `recurring_payment(subscription_id)` - Get subscription details
|
|
163
|
+
- `delete_recurring_payment(subscription_id)` - Cancel subscription (JWT)
|
|
164
|
+
- `subscription_payments(subscription_id)` - List subscription payments
|
|
165
|
+
|
|
166
|
+
</details>
|
|
167
|
+
|
|
168
|
+
<details>
|
|
169
|
+
<summary><b>Custody/Sub-accounts (11 methods)</b> - User wallet management</summary>
|
|
170
|
+
|
|
171
|
+
- `create_sub_account(user_id:)` - Create user account
|
|
172
|
+
- `sub_account_balance(user_id)` - Get user balance
|
|
173
|
+
- `sub_account_balances` - Get all balances
|
|
174
|
+
- `list_sub_accounts(...)` - List all sub-accounts
|
|
175
|
+
- `transfer_between_sub_accounts(...)` - Transfer between users (JWT)
|
|
176
|
+
- `create_sub_account_deposit(user_id:, currency:, ...)` - Generate deposit address
|
|
177
|
+
- `create_sub_account_payment_deposit(...)` - Payment to sub-account
|
|
178
|
+
- `transfer_to_sub_account(user_id:, currency:, amount:)` - Deposit to user
|
|
179
|
+
- `withdraw_from_sub_account(user_id:, currency:, amount:)` - Withdraw from user (JWT)
|
|
180
|
+
- `sub_account_transfer(transfer_id)` - Get transfer details
|
|
181
|
+
- `sub_account_transfers(...)` - List all transfers
|
|
182
|
+
|
|
183
|
+
</details>
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary><b>Fiat Payouts (8 methods)</b> - Beta: Crypto to fiat (JWT required)</summary>
|
|
187
|
+
|
|
188
|
+
- `fiat_payout_payment_methods(fiat_currency: nil)` - Available payment methods (JWT)
|
|
189
|
+
- `create_fiat_payout_account(...)` - Create payout account (JWT)
|
|
190
|
+
- `fiat_payout_accounts(...)` - List payout accounts (JWT)
|
|
191
|
+
- `update_fiat_payout_account(account_id:, ...)` - Update account (JWT)
|
|
192
|
+
- `create_fiat_payout(...)` - Create fiat payout (JWT)
|
|
193
|
+
- `fiat_payout_status(payout_id)` - Get payout status (JWT)
|
|
194
|
+
- `fiat_payouts(...)` - List all fiat payouts with filters (JWT)
|
|
195
|
+
- `fiat_payout_rates(...)` - Get conversion rates (JWT)
|
|
196
|
+
|
|
197
|
+
</details>
|
|
198
|
+
|
|
199
|
+
### Built for Production
|
|
200
|
+
|
|
201
|
+
- **Comprehensive error handling** - 8 exception classes with detailed messages
|
|
202
|
+
- **Faraday middleware** - Automatic error mapping and retries
|
|
203
|
+
- **Tested** - 23 passing tests with VCR cassettes for integration
|
|
204
|
+
- **Rails-ready** - Drop-in Rack middleware for webhook verification
|
|
205
|
+
- **Type-safe** - All responses return Ruby Hashes from parsed JSON
|
|
206
|
+
|
|
207
|
+
## Usage Examples
|
|
208
|
+
|
|
209
|
+
### Accept Payment on Your Site
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
# 1. Create payment
|
|
213
|
+
payment = client.create_payment(
|
|
214
|
+
price_amount: 49.99,
|
|
215
|
+
price_currency: 'usd',
|
|
216
|
+
pay_currency: 'btc',
|
|
217
|
+
order_id: "order-#{order.id}",
|
|
218
|
+
order_description: 'Pro Plan - Annual',
|
|
219
|
+
ipn_callback_url: 'https://example.com/webhooks/nowpayments'
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# 2. Show payment address to customer
|
|
223
|
+
@payment_address = payment['pay_address']
|
|
224
|
+
@payment_amount = payment['pay_amount']
|
|
225
|
+
|
|
226
|
+
# 3. Check status
|
|
227
|
+
status = client.payment(payment['payment_id'])
|
|
228
|
+
# => {"payment_status"=>"finished", ...}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Hosted Invoice Page
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
# Create invoice with hosted payment page
|
|
235
|
+
invoice = client.create_invoice(
|
|
236
|
+
price_amount: 99.0,
|
|
237
|
+
price_currency: 'usd',
|
|
238
|
+
order_id: "inv-#{invoice.id}",
|
|
239
|
+
success_url: 'https://example.com/thank-you',
|
|
240
|
+
cancel_url: 'https://example.com/checkout'
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Redirect customer to payment page
|
|
244
|
+
redirect_to invoice['invoice_url']
|
|
245
|
+
# Customer can choose from 150+ cryptocurrencies
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Custody API - Sub-accounts (Marketplaces & Casinos)
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
# Create sub-account for a user
|
|
252
|
+
sub_account = client.create_sub_account(user_id: user.id)
|
|
253
|
+
# => {"id"=>123, "user_id"=>456, "created_at"=>"2025-11-01T..."}
|
|
254
|
+
|
|
255
|
+
# Generate deposit address for user's BTC wallet
|
|
256
|
+
deposit = client.create_sub_account_deposit(
|
|
257
|
+
user_id: user.id,
|
|
258
|
+
currency: 'btc'
|
|
259
|
+
)
|
|
260
|
+
# => {"address"=>"bc1q...", "currency"=>"btc"}
|
|
261
|
+
|
|
262
|
+
# Check user's balance
|
|
263
|
+
balances = client.sub_account_balances(user_id: user.id)
|
|
264
|
+
# => {"balances"=>{"btc"=>0.05, "eth"=>1.2}}
|
|
265
|
+
|
|
266
|
+
# Transfer funds to sub-account
|
|
267
|
+
transfer = client.transfer_to_sub_account(
|
|
268
|
+
user_id: user.id,
|
|
269
|
+
currency: 'btc',
|
|
270
|
+
amount: 0.01
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Process withdrawal
|
|
274
|
+
withdrawal = client.withdraw_from_sub_account(
|
|
275
|
+
user_id: user.id,
|
|
276
|
+
currency: 'btc',
|
|
277
|
+
amount: 0.005
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### JWT Authentication (Required for Advanced Features)
|
|
282
|
+
|
|
283
|
+
**Some endpoints require JWT authentication (expires every 5 minutes):**
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
# Authenticate to get JWT token
|
|
287
|
+
client.authenticate(
|
|
288
|
+
email: 'your_email@example.com',
|
|
289
|
+
password: 'your_password'
|
|
290
|
+
)
|
|
291
|
+
# Token is automatically stored and injected in subsequent requests
|
|
292
|
+
|
|
293
|
+
# Check token status
|
|
294
|
+
client.jwt_expired? # => false
|
|
295
|
+
client.jwt_time_remaining # => 287 (seconds)
|
|
296
|
+
|
|
297
|
+
# JWT is required for these endpoints:
|
|
298
|
+
# - Mass Payouts (create_payout, verify_payout, list_payouts)
|
|
299
|
+
# - Conversions (create_conversion, conversion_status, list_conversions)
|
|
300
|
+
# - Custody Operations (transfer_between_sub_accounts, write_off_sub_account_balance)
|
|
301
|
+
# - Recurring Payments (delete_recurring_payment)
|
|
302
|
+
|
|
303
|
+
# Example: Create payout (requires JWT)
|
|
304
|
+
client.authenticate(email: 'your@email.com', password: 'password')
|
|
305
|
+
payout = client.create_payout(
|
|
306
|
+
withdrawals: [
|
|
307
|
+
{
|
|
308
|
+
address: 'TEmGwPeRTPiLFLVfBxXkSP91yc5GMNQhfS',
|
|
309
|
+
currency: 'trx',
|
|
310
|
+
amount: 10
|
|
311
|
+
}
|
|
312
|
+
],
|
|
313
|
+
payout_description: 'Weekly payouts'
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Verify payout with 2FA code (from Google Authenticator)
|
|
317
|
+
client.verify_payout(
|
|
318
|
+
batch_withdrawal_id: payout['id'],
|
|
319
|
+
verification_code: '123456'
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Token auto-refresh pattern
|
|
323
|
+
def ensure_authenticated(client, email, password)
|
|
324
|
+
return unless client.jwt_expired?
|
|
325
|
+
client.authenticate(email: email, password: password)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Before JWT-required operations
|
|
329
|
+
ensure_authenticated(client, EMAIL, PASSWORD)
|
|
330
|
+
payouts = client.list_payouts(limit: 10, offset: 0)
|
|
331
|
+
|
|
332
|
+
# Clear token when done (optional, for security)
|
|
333
|
+
client.clear_jwt_token
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**See [examples/jwt_authentication_example.rb](examples/jwt_authentication_example.rb) for complete usage patterns.**
|
|
337
|
+
|
|
338
|
+
### Currency Conversions (JWT Required)
|
|
339
|
+
|
|
340
|
+
**Convert between cryptocurrencies at market rates:**
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
# Authenticate first
|
|
344
|
+
client.authenticate(email: 'your@email.com', password: 'password')
|
|
345
|
+
|
|
346
|
+
# Create conversion
|
|
347
|
+
conversion = client.create_conversion(
|
|
348
|
+
from_currency: 'btc',
|
|
349
|
+
to_currency: 'eth',
|
|
350
|
+
amount: 0.1
|
|
351
|
+
)
|
|
352
|
+
# => {"conversion_id" => "conv_123", "status" => "processing", ...}
|
|
353
|
+
|
|
354
|
+
# Check conversion status
|
|
355
|
+
status = client.conversion_status(conversion['conversion_id'])
|
|
356
|
+
# => {"status" => "completed", "from_amount" => 0.1, "to_amount" => 2.5, ...}
|
|
357
|
+
|
|
358
|
+
# List all conversions
|
|
359
|
+
conversions = client.list_conversions(limit: 10, offset: 0)
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Fiat Payouts (Beta - JWT Required)
|
|
363
|
+
|
|
364
|
+
**Withdraw cryptocurrency to fiat bank accounts:**
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
# Authenticate first
|
|
368
|
+
client.authenticate(email: 'your@email.com', password: 'password')
|
|
369
|
+
|
|
370
|
+
# Get available payment methods
|
|
371
|
+
methods = client.fiat_payout_payment_methods(fiat_currency: 'EUR')
|
|
372
|
+
# => {"result" => [{"provider" => "transfi", "methods" => [...]}]}
|
|
373
|
+
|
|
374
|
+
# Create payout account
|
|
375
|
+
account = client.create_fiat_payout_account(
|
|
376
|
+
provider: 'transfi',
|
|
377
|
+
fiat_currency: 'EUR',
|
|
378
|
+
account_data: {
|
|
379
|
+
accountHolderName: 'John Doe',
|
|
380
|
+
iban: 'DE89370400440532013000'
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
# => {"result" => {"id" => "acc_123", ...}}
|
|
384
|
+
|
|
385
|
+
# Get conversion rates
|
|
386
|
+
rates = client.fiat_payout_rates(
|
|
387
|
+
crypto_currency: 'btc',
|
|
388
|
+
fiat_currency: 'EUR',
|
|
389
|
+
crypto_amount: 0.1
|
|
390
|
+
)
|
|
391
|
+
# => {"result" => {"fiatAmount" => "2500.00", "rate" => "25000.00", ...}}
|
|
392
|
+
|
|
393
|
+
# Create fiat payout
|
|
394
|
+
payout = client.create_fiat_payout(
|
|
395
|
+
account_id: account['result']['id'],
|
|
396
|
+
crypto_currency: 'btc',
|
|
397
|
+
crypto_amount: 0.1
|
|
398
|
+
)
|
|
399
|
+
# => {"result" => {"id" => "payout_123", "status" => "PENDING", ...}}
|
|
400
|
+
|
|
401
|
+
# Check payout status
|
|
402
|
+
status = client.fiat_payout_status(payout['result']['id'])
|
|
403
|
+
# => {"result" => {"status" => "FINISHED", ...}}
|
|
404
|
+
|
|
405
|
+
# List all fiat payouts with filters
|
|
406
|
+
payouts = client.fiat_payouts(
|
|
407
|
+
status: 'FINISHED',
|
|
408
|
+
fiat_currency: 'EUR',
|
|
409
|
+
limit: 10,
|
|
410
|
+
page: 0
|
|
411
|
+
)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Webhook Verification (Critical!)
|
|
415
|
+
|
|
416
|
+
**Always verify webhook signatures to prevent fraud:**
|
|
417
|
+
|
|
418
|
+
```ruby
|
|
419
|
+
# app/controllers/webhooks_controller.rb
|
|
420
|
+
class WebhooksController < ApplicationController
|
|
421
|
+
skip_before_action :verify_authenticity_token
|
|
422
|
+
|
|
423
|
+
def nowpayments
|
|
424
|
+
# Verify signature - raises SecurityError if invalid
|
|
425
|
+
payload = NOWPayments::Rack.verify_webhook(
|
|
426
|
+
request,
|
|
427
|
+
ENV['NOWPAYMENTS_IPN_SECRET']
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
# Process payment status
|
|
431
|
+
order = Order.find_by(id: payload['order_id'])
|
|
432
|
+
|
|
433
|
+
case payload['payment_status']
|
|
434
|
+
when 'finished'
|
|
435
|
+
order.mark_paid!
|
|
436
|
+
OrderMailer.payment_received(order).deliver_later
|
|
437
|
+
when 'failed', 'expired'
|
|
438
|
+
order.cancel!
|
|
439
|
+
when 'partially_paid'
|
|
440
|
+
# Customer sent wrong amount
|
|
441
|
+
logger.warn "Underpaid: #{payload['actually_paid']} vs #{payload['pay_amount']}"
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
head :ok
|
|
445
|
+
|
|
446
|
+
rescue NOWPayments::SecurityError => e
|
|
447
|
+
logger.error "Invalid webhook signature: #{e.message}"
|
|
448
|
+
head :forbidden
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# config/routes.rb
|
|
453
|
+
post '/webhooks/nowpayments', to: 'webhooks#nowpayments'
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Error Handling
|
|
457
|
+
|
|
458
|
+
```ruby
|
|
459
|
+
begin
|
|
460
|
+
payment = client.create_payment(...)
|
|
461
|
+
|
|
462
|
+
rescue NOWPayments::AuthenticationError
|
|
463
|
+
# Invalid API key
|
|
464
|
+
|
|
465
|
+
rescue NOWPayments::BadRequestError => e
|
|
466
|
+
# Invalid parameters
|
|
467
|
+
puts "Error: #{e.message}"
|
|
468
|
+
puts "Details: #{e.body}"
|
|
469
|
+
|
|
470
|
+
rescue NOWPayments::RateLimitError => e
|
|
471
|
+
# Too many requests
|
|
472
|
+
retry_after = e.headers['Retry-After']
|
|
473
|
+
|
|
474
|
+
rescue NOWPayments::ServerError
|
|
475
|
+
# NOWPayments server error
|
|
476
|
+
|
|
477
|
+
rescue NOWPayments::ConnectionError
|
|
478
|
+
# Network error
|
|
479
|
+
end
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Documentation
|
|
483
|
+
|
|
484
|
+
- **[Complete API Reference](docs/API.md)** - All methods with examples
|
|
485
|
+
- **[Official API Docs](https://documenter.getpostman.com/view/7907941/2s93JusNJt)** - NOWPayments API documentation
|
|
486
|
+
- **[Dashboard](https://nowpayments.io/)** - Production environment
|
|
487
|
+
- **[Sandbox Dashboard](https://account-sandbox.nowpayments.io/)** - Testing environment
|
|
488
|
+
|
|
489
|
+
## Testing with Sandbox
|
|
490
|
+
|
|
491
|
+
```ruby
|
|
492
|
+
# Use sandbox for development
|
|
493
|
+
client = NOWPayments::Client.new(
|
|
494
|
+
api_key: ENV['NOWPAYMENTS_SANDBOX_API_KEY'],
|
|
495
|
+
ipn_secret: ENV['NOWPAYMENTS_SANDBOX_IPN_SECRET'],
|
|
496
|
+
sandbox: true
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
# All API calls go to sandbox environment
|
|
500
|
+
payment = client.create_payment(...)
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Get sandbox credentials:**
|
|
504
|
+
1. Create account at https://account-sandbox.nowpayments.io/
|
|
505
|
+
2. Generate API key from dashboard
|
|
506
|
+
3. Generate IPN secret for webhooks
|
|
507
|
+
4. Add to `.env` file
|
|
508
|
+
|
|
509
|
+
## Configuration
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
# .env
|
|
513
|
+
NOWPAYMENTS_API_KEY=your_production_api_key
|
|
514
|
+
NOWPAYMENTS_IPN_SECRET=your_ipn_secret
|
|
515
|
+
|
|
516
|
+
# Testing
|
|
517
|
+
NOWPAYMENTS_SANDBOX_API_KEY=your_sandbox_api_key
|
|
518
|
+
NOWPAYMENTS_SANDBOX_IPN_SECRET=your_sandbox_ipn_secret
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Examples
|
|
522
|
+
|
|
523
|
+
See the `examples/` directory:
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
# API usage demo
|
|
527
|
+
cp .env.example .env
|
|
528
|
+
# Add your sandbox credentials to .env
|
|
529
|
+
ruby examples/simple_demo.rb
|
|
530
|
+
|
|
531
|
+
# Webhook receiver (Sinatra)
|
|
532
|
+
ruby examples/webhook_server.rb
|
|
533
|
+
# Use ngrok to expose: ngrok http 4567
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Development
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
# Install dependencies
|
|
540
|
+
bundle install
|
|
541
|
+
|
|
542
|
+
# Run tests
|
|
543
|
+
bundle exec rspec
|
|
544
|
+
|
|
545
|
+
# Run tests with coverage
|
|
546
|
+
COVERAGE=true bundle exec rspec
|
|
547
|
+
|
|
548
|
+
# Lint code
|
|
549
|
+
bundle exec rubocop
|
|
550
|
+
|
|
551
|
+
# Interactive console
|
|
552
|
+
bundle exec rake console
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
## Contributing
|
|
556
|
+
|
|
557
|
+
1. Fork it
|
|
558
|
+
2. Create your feature branch (`git checkout -b feature/my-feature`)
|
|
559
|
+
3. Run tests (`bundle exec rspec`)
|
|
560
|
+
4. Commit your changes (`git commit -am 'Add feature'`)
|
|
561
|
+
5. Push to the branch (`git push origin feature/my-feature`)
|
|
562
|
+
6. Create a Pull Request
|
|
563
|
+
|
|
564
|
+
## Security
|
|
565
|
+
|
|
566
|
+
**Report security vulnerabilities to:** security@yourdomain.com
|
|
567
|
+
|
|
568
|
+
Never commit API keys or secrets. Always use environment variables.
|
|
569
|
+
|
|
570
|
+
## License
|
|
571
|
+
|
|
572
|
+
MIT License - see [LICENSE.txt](LICENSE.txt)
|
|
573
|
+
|
|
574
|
+
## Support
|
|
575
|
+
|
|
576
|
+
- [GitHub Issues](https://github.com/Sentia/nowpayments/issues)
|
|
577
|
+
- [NOWPayments Support](https://nowpayments.io/help)
|
|
578
|
+
- [API Documentation](https://documenter.getpostman.com/view/7907941/2s93JusNJt)
|