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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Estimation and calculation endpoints
|
|
6
|
+
module Estimation
|
|
7
|
+
# Get minimum payment amount for currency pair
|
|
8
|
+
# GET /v1/min-amount
|
|
9
|
+
# @param currency_from [String] Source currency code
|
|
10
|
+
# @param currency_to [String] Target currency code
|
|
11
|
+
# @return [Hash] Minimum amount info
|
|
12
|
+
def min_amount(currency_from:, currency_to:)
|
|
13
|
+
get("min-amount", params: {
|
|
14
|
+
currency_from: currency_from,
|
|
15
|
+
currency_to: currency_to
|
|
16
|
+
}).body
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Estimate price for currency pair
|
|
20
|
+
# GET /v1/estimate
|
|
21
|
+
# @param amount [Numeric] Amount to estimate
|
|
22
|
+
# @param currency_from [String] Source currency
|
|
23
|
+
# @param currency_to [String] Target currency
|
|
24
|
+
# @return [Hash] Price estimate
|
|
25
|
+
def estimate(amount:, currency_from:, currency_to:)
|
|
26
|
+
get("estimate", params: {
|
|
27
|
+
amount: amount,
|
|
28
|
+
currency_from: currency_from,
|
|
29
|
+
currency_to: currency_to
|
|
30
|
+
}).body
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Fiat Payouts API endpoints (Beta)
|
|
6
|
+
# All endpoints require JWT authentication
|
|
7
|
+
# Note: This is a Beta feature with limited availability
|
|
8
|
+
module FiatPayouts
|
|
9
|
+
# Get available fiat payment methods
|
|
10
|
+
# GET /v1/fiat-payouts/payment-methods
|
|
11
|
+
# @param fiat_currency [String, nil] Optional filter by fiat currency (e.g., "EUR", "USD")
|
|
12
|
+
# @return [Hash] Available payment methods
|
|
13
|
+
def fiat_payout_payment_methods(fiat_currency: nil)
|
|
14
|
+
params = {}
|
|
15
|
+
params[:fiatCurrency] = fiat_currency if fiat_currency
|
|
16
|
+
get("fiat-payouts/payment-methods", params: params).body
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Create a fiat payout account
|
|
20
|
+
# POST /v1/fiat-payouts/account
|
|
21
|
+
# @param provider [String] Payment provider (e.g., "transfi")
|
|
22
|
+
# @param fiat_currency [String] Fiat currency code (e.g., "EUR", "USD")
|
|
23
|
+
# @param account_data [Hash] Provider-specific account data
|
|
24
|
+
# @return [Hash] Created account details
|
|
25
|
+
def create_fiat_payout_account(provider:, fiat_currency:, account_data:)
|
|
26
|
+
params = {
|
|
27
|
+
provider: provider,
|
|
28
|
+
fiatCurrency: fiat_currency,
|
|
29
|
+
accountData: account_data
|
|
30
|
+
}
|
|
31
|
+
post("fiat-payouts/account", body: params).body
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get list of fiat payout accounts
|
|
35
|
+
# GET /v1/fiat-payouts/account
|
|
36
|
+
# @param provider [String, nil] Optional filter by provider
|
|
37
|
+
# @param fiat_currency [String, nil] Optional filter by fiat currency
|
|
38
|
+
# @param limit [Integer] Number of results per page (default: 10)
|
|
39
|
+
# @param page [Integer] Page number (default: 0)
|
|
40
|
+
# @return [Hash] List of payout accounts
|
|
41
|
+
def fiat_payout_accounts(provider: nil, fiat_currency: nil, limit: 10, page: 0)
|
|
42
|
+
params = { limit: limit, page: page }
|
|
43
|
+
params[:provider] = provider if provider
|
|
44
|
+
params[:fiatCurrency] = fiat_currency if fiat_currency
|
|
45
|
+
get("fiat-payouts/account", params: params).body
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Update a fiat payout account
|
|
49
|
+
# PATCH /v1/fiat-payouts/account/:account_id
|
|
50
|
+
# @param account_id [String, Integer] Account ID
|
|
51
|
+
# @param account_data [Hash] Updated account data
|
|
52
|
+
# @return [Hash] Updated account details
|
|
53
|
+
def update_fiat_payout_account(account_id:, account_data:)
|
|
54
|
+
params = { accountData: account_data }
|
|
55
|
+
patch("fiat-payouts/account/#{account_id}", body: params).body
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Create a fiat payout
|
|
59
|
+
# POST /v1/fiat-payouts
|
|
60
|
+
# @param account_id [String, Integer] Payout account ID
|
|
61
|
+
# @param crypto_currency [String] Cryptocurrency code (e.g., "btc", "eth")
|
|
62
|
+
# @param crypto_amount [Numeric] Amount in cryptocurrency
|
|
63
|
+
# @param request_id [String, nil] Optional unique request ID
|
|
64
|
+
# @return [Hash] Created payout details
|
|
65
|
+
def create_fiat_payout(account_id:, crypto_currency:, crypto_amount:, request_id: nil)
|
|
66
|
+
params = {
|
|
67
|
+
accountId: account_id,
|
|
68
|
+
cryptoCurrency: crypto_currency,
|
|
69
|
+
cryptoAmount: crypto_amount
|
|
70
|
+
}
|
|
71
|
+
params[:requestId] = request_id if request_id
|
|
72
|
+
post("fiat-payouts", body: params).body
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get fiat payout status
|
|
76
|
+
# GET /v1/fiat-payouts/:payout_id
|
|
77
|
+
# @param payout_id [String, Integer] Payout ID
|
|
78
|
+
# @return [Hash] Payout details and status
|
|
79
|
+
def fiat_payout_status(payout_id)
|
|
80
|
+
get("fiat-payouts/#{payout_id}").body
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get list of fiat payouts
|
|
84
|
+
# GET /v1/fiat-payouts
|
|
85
|
+
# @param id [String, Integer, nil] Optional filter by payout ID
|
|
86
|
+
# @param provider [String, nil] Optional filter by provider
|
|
87
|
+
# @param request_id [String, nil] Optional filter by request ID
|
|
88
|
+
# @param fiat_currency [String, nil] Optional filter by fiat currency
|
|
89
|
+
# @param crypto_currency [String, nil] Optional filter by crypto currency
|
|
90
|
+
# @param status [String, nil] Optional filter by status (e.g., "FINISHED", "PENDING")
|
|
91
|
+
# @param filter [String, nil] Optional text filter
|
|
92
|
+
# @param provider_payout_id [String, nil] Optional filter by provider's payout ID
|
|
93
|
+
# @param limit [Integer] Number of results per page (default: 10)
|
|
94
|
+
# @param page [Integer] Page number (default: 0)
|
|
95
|
+
# @param order_by [String, nil] Optional field to order by
|
|
96
|
+
# @param sort_by [String, nil] Optional sort direction ("asc" or "desc")
|
|
97
|
+
# @param date_from [String, nil] Optional start date (ISO 8601)
|
|
98
|
+
# @param date_to [String, nil] Optional end date (ISO 8601)
|
|
99
|
+
# @return [Hash] List of payouts
|
|
100
|
+
def fiat_payouts(
|
|
101
|
+
id: nil,
|
|
102
|
+
provider: nil,
|
|
103
|
+
request_id: nil,
|
|
104
|
+
fiat_currency: nil,
|
|
105
|
+
crypto_currency: nil,
|
|
106
|
+
status: nil,
|
|
107
|
+
filter: nil,
|
|
108
|
+
provider_payout_id: nil,
|
|
109
|
+
limit: 10,
|
|
110
|
+
page: 0,
|
|
111
|
+
order_by: nil,
|
|
112
|
+
sort_by: nil,
|
|
113
|
+
date_from: nil,
|
|
114
|
+
date_to: nil
|
|
115
|
+
)
|
|
116
|
+
params = { limit: limit, page: page }
|
|
117
|
+
params[:id] = id if id
|
|
118
|
+
params[:provider] = provider if provider
|
|
119
|
+
params[:requestId] = request_id if request_id
|
|
120
|
+
params[:fiatCurrency] = fiat_currency if fiat_currency
|
|
121
|
+
params[:cryptoCurrency] = crypto_currency if crypto_currency
|
|
122
|
+
params[:status] = status if status
|
|
123
|
+
params[:filter] = filter if filter
|
|
124
|
+
params[:provider_payout_id] = provider_payout_id if provider_payout_id
|
|
125
|
+
params[:orderBy] = order_by if order_by
|
|
126
|
+
params[:sortBy] = sort_by if sort_by
|
|
127
|
+
params[:dateFrom] = date_from if date_from
|
|
128
|
+
params[:dateTo] = date_to if date_to
|
|
129
|
+
get("fiat-payouts", params: params).body
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Get fiat conversion rates
|
|
133
|
+
# POST /v1/fiat-payouts/rates
|
|
134
|
+
# @param crypto_currency [String] Cryptocurrency code (e.g., "btc", "eth")
|
|
135
|
+
# @param fiat_currency [String] Fiat currency code (e.g., "EUR", "USD")
|
|
136
|
+
# @param crypto_amount [Numeric, nil] Optional crypto amount to convert
|
|
137
|
+
# @param fiat_amount [Numeric, nil] Optional fiat amount to convert
|
|
138
|
+
# @return [Hash] Conversion rates and amounts
|
|
139
|
+
def fiat_payout_rates(crypto_currency:, fiat_currency:, crypto_amount: nil, fiat_amount: nil)
|
|
140
|
+
params = {
|
|
141
|
+
cryptoCurrency: crypto_currency,
|
|
142
|
+
fiatCurrency: fiat_currency
|
|
143
|
+
}
|
|
144
|
+
params[:cryptoAmount] = crypto_amount if crypto_amount
|
|
145
|
+
params[:fiatAmount] = fiat_amount if fiat_amount
|
|
146
|
+
post("fiat-payouts/rates", body: params).body
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Invoice-related endpoints
|
|
6
|
+
module Invoices
|
|
7
|
+
# Create an invoice (hosted payment page)
|
|
8
|
+
# POST /v1/invoice
|
|
9
|
+
# @param price_amount [Numeric] Fiat amount
|
|
10
|
+
# @param price_currency [String] Fiat currency
|
|
11
|
+
# @param pay_currency [String, nil] Optional crypto (if nil, customer chooses)
|
|
12
|
+
# @param order_id [String, nil] Optional merchant order ID
|
|
13
|
+
# @param order_description [String, nil] Optional description
|
|
14
|
+
# @param ipn_callback_url [String, nil] Optional webhook URL
|
|
15
|
+
# @param success_url [String, nil] Optional redirect after success
|
|
16
|
+
# @param cancel_url [String, nil] Optional redirect after cancel
|
|
17
|
+
# @return [Hash] Invoice with invoice_url
|
|
18
|
+
def create_invoice(
|
|
19
|
+
price_amount:,
|
|
20
|
+
price_currency:,
|
|
21
|
+
pay_currency: nil,
|
|
22
|
+
order_id: nil,
|
|
23
|
+
order_description: nil,
|
|
24
|
+
ipn_callback_url: nil,
|
|
25
|
+
success_url: nil,
|
|
26
|
+
cancel_url: nil
|
|
27
|
+
)
|
|
28
|
+
params = {
|
|
29
|
+
price_amount: price_amount,
|
|
30
|
+
price_currency: price_currency
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
params[:pay_currency] = pay_currency if pay_currency
|
|
34
|
+
params[:order_id] = order_id if order_id
|
|
35
|
+
params[:order_description] = order_description if order_description
|
|
36
|
+
params[:ipn_callback_url] = ipn_callback_url if ipn_callback_url
|
|
37
|
+
params[:success_url] = success_url if success_url
|
|
38
|
+
params[:cancel_url] = cancel_url if cancel_url
|
|
39
|
+
|
|
40
|
+
post("invoice", body: params).body
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create payment by invoice
|
|
44
|
+
# POST /v1/invoice-payment
|
|
45
|
+
# @param iid [Integer, String] Invoice ID
|
|
46
|
+
# @param pay_currency [String] Crypto currency
|
|
47
|
+
# @param purchase_id [String, Integer, nil] Optional purchase ID
|
|
48
|
+
# @param order_description [String, nil] Optional description
|
|
49
|
+
# @param customer_email [String, nil] Optional customer email
|
|
50
|
+
# @param payout_address [String, nil] Optional custom payout address
|
|
51
|
+
# @param payout_extra_id [String, nil] Optional extra ID for payout
|
|
52
|
+
# @param payout_currency [String, nil] Required if payout_address set
|
|
53
|
+
# @return [Hash] Payment details
|
|
54
|
+
def create_invoice_payment(
|
|
55
|
+
iid:,
|
|
56
|
+
pay_currency:,
|
|
57
|
+
purchase_id: nil,
|
|
58
|
+
order_description: nil,
|
|
59
|
+
customer_email: nil,
|
|
60
|
+
payout_address: nil,
|
|
61
|
+
payout_extra_id: nil,
|
|
62
|
+
payout_currency: nil
|
|
63
|
+
)
|
|
64
|
+
params = {
|
|
65
|
+
iid: iid,
|
|
66
|
+
pay_currency: pay_currency
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
params[:purchase_id] = purchase_id if purchase_id
|
|
70
|
+
params[:order_description] = order_description if order_description
|
|
71
|
+
params[:customer_email] = customer_email if customer_email
|
|
72
|
+
params[:payout_address] = payout_address if payout_address
|
|
73
|
+
params[:payout_extra_id] = payout_extra_id if payout_extra_id
|
|
74
|
+
params[:payout_currency] = payout_currency if payout_currency
|
|
75
|
+
|
|
76
|
+
post("invoice-payment", body: params).body
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Get invoice details and status
|
|
80
|
+
# GET /v1/invoice/:invoice_id
|
|
81
|
+
# @param invoice_id [String, Integer] Invoice ID
|
|
82
|
+
# @return [Hash] Invoice details with payment status
|
|
83
|
+
def invoice(invoice_id)
|
|
84
|
+
get("invoice/#{invoice_id}").body
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Payment-related endpoints
|
|
6
|
+
module Payments
|
|
7
|
+
# Create a new payment
|
|
8
|
+
# POST /v1/payment
|
|
9
|
+
# @param price_amount [Numeric] Fiat amount
|
|
10
|
+
# @param price_currency [String] Fiat currency
|
|
11
|
+
# @param pay_currency [String] Crypto currency customer pays with
|
|
12
|
+
# @param pay_amount [Numeric, nil] Optional crypto amount (alternative to price_amount)
|
|
13
|
+
# @param order_id [String, nil] Optional merchant order ID
|
|
14
|
+
# @param order_description [String, nil] Optional description
|
|
15
|
+
# @param ipn_callback_url [String, nil] Optional webhook URL
|
|
16
|
+
# @param payout_address [String, nil] Optional custom payout address
|
|
17
|
+
# @param payout_currency [String, nil] Required if payout_address set
|
|
18
|
+
# @param payout_extra_id [String, nil] Optional extra ID for payout
|
|
19
|
+
# @param is_fixed_rate [Boolean, nil] Fixed rate flag
|
|
20
|
+
# @param is_fee_paid_by_user [Boolean, nil] Whether user pays network fees
|
|
21
|
+
# @return [Hash] Payment details
|
|
22
|
+
def create_payment(
|
|
23
|
+
price_amount:,
|
|
24
|
+
price_currency:,
|
|
25
|
+
pay_currency:,
|
|
26
|
+
pay_amount: nil,
|
|
27
|
+
order_id: nil,
|
|
28
|
+
order_description: nil,
|
|
29
|
+
ipn_callback_url: nil,
|
|
30
|
+
payout_address: nil,
|
|
31
|
+
payout_currency: nil,
|
|
32
|
+
payout_extra_id: nil,
|
|
33
|
+
is_fixed_rate: nil,
|
|
34
|
+
is_fee_paid_by_user: nil
|
|
35
|
+
)
|
|
36
|
+
params = {
|
|
37
|
+
price_amount: price_amount,
|
|
38
|
+
price_currency: price_currency,
|
|
39
|
+
pay_currency: pay_currency
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
params[:pay_amount] = pay_amount if pay_amount
|
|
43
|
+
params[:order_id] = order_id if order_id
|
|
44
|
+
params[:order_description] = order_description if order_description
|
|
45
|
+
params[:ipn_callback_url] = ipn_callback_url if ipn_callback_url
|
|
46
|
+
params[:payout_address] = payout_address if payout_address
|
|
47
|
+
params[:payout_currency] = payout_currency if payout_currency
|
|
48
|
+
params[:payout_extra_id] = payout_extra_id if payout_extra_id
|
|
49
|
+
params[:is_fixed_rate] = is_fixed_rate unless is_fixed_rate.nil?
|
|
50
|
+
params[:is_fee_paid_by_user] = is_fee_paid_by_user unless is_fee_paid_by_user.nil?
|
|
51
|
+
|
|
52
|
+
validate_payment_params!(params)
|
|
53
|
+
|
|
54
|
+
post("payment", body: params).body
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Get payment status
|
|
58
|
+
# GET /v1/payment/:payment_id
|
|
59
|
+
# @param payment_id [Integer, String] Payment ID
|
|
60
|
+
# @return [Hash] Payment status
|
|
61
|
+
def payment(payment_id)
|
|
62
|
+
get("payment/#{payment_id}").body
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# List payments with pagination and filters
|
|
66
|
+
# GET /v1/payment
|
|
67
|
+
# @param limit [Integer] Results per page
|
|
68
|
+
# @param page [Integer] Page number
|
|
69
|
+
# @param sort_by [String, nil] Sort field
|
|
70
|
+
# @param order_by [String, nil] Order direction (asc/desc)
|
|
71
|
+
# @param date_from [String, nil] Start date filter
|
|
72
|
+
# @param date_to [String, nil] End date filter
|
|
73
|
+
# @return [Hash] List of payments
|
|
74
|
+
def payments(limit: 10, page: 0, sort_by: nil, order_by: nil, date_from: nil, date_to: nil)
|
|
75
|
+
params = { limit: limit, page: page }
|
|
76
|
+
params[:sortBy] = sort_by if sort_by
|
|
77
|
+
params[:orderBy] = order_by if order_by
|
|
78
|
+
params[:dateFrom] = date_from if date_from
|
|
79
|
+
params[:dateTo] = date_to if date_to
|
|
80
|
+
|
|
81
|
+
get("payment", params: params).body
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Update payment estimate
|
|
85
|
+
# PATCH /v1/payment/:payment_id
|
|
86
|
+
# @param payment_id [Integer, String] Payment ID
|
|
87
|
+
# @return [Hash] Updated payment
|
|
88
|
+
def update_payment_estimate(payment_id)
|
|
89
|
+
patch("payment/#{payment_id}").body
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Payout and mass payout endpoints (requires JWT auth)
|
|
6
|
+
module Payouts
|
|
7
|
+
# Get account balance
|
|
8
|
+
# GET /v1/balance
|
|
9
|
+
# @return [Hash] Balance information with available and pending amounts per currency
|
|
10
|
+
def balance
|
|
11
|
+
get("balance").body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Validate payout address
|
|
15
|
+
# POST /v1/payout/validate-address
|
|
16
|
+
# @param address [String] Payout address to validate
|
|
17
|
+
# @param currency [String] Currency code
|
|
18
|
+
# @param extra_id [String, nil] Optional memo/tag/destination tag
|
|
19
|
+
# @return [Hash] Validation result
|
|
20
|
+
def validate_payout_address(address:, currency:, extra_id: nil)
|
|
21
|
+
params = {
|
|
22
|
+
address: address,
|
|
23
|
+
currency: currency
|
|
24
|
+
}
|
|
25
|
+
params[:extra_id] = extra_id if extra_id
|
|
26
|
+
|
|
27
|
+
post("payout/validate-address", body: params).body
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Create payout
|
|
31
|
+
# POST /v1/payout
|
|
32
|
+
# Note: This endpoint requires JWT authentication
|
|
33
|
+
# @param withdrawals [Array<Hash>] Array of withdrawal objects
|
|
34
|
+
# Each withdrawal should have:
|
|
35
|
+
# - address (String, required): Crypto address
|
|
36
|
+
# - currency (String, required): Currency code
|
|
37
|
+
# - amount (Numeric, required): Amount to send
|
|
38
|
+
# - ipn_callback_url (String, optional): Individual callback URL
|
|
39
|
+
# - extra_id (String, optional): Payment extra ID (memo/tag)
|
|
40
|
+
# - fiat_amount (Numeric, optional): Fiat equivalent amount
|
|
41
|
+
# - fiat_currency (String, optional): Fiat currency code
|
|
42
|
+
# - unique_external_id (String, optional): External reference ID
|
|
43
|
+
# - payout_description (String, optional): Individual description
|
|
44
|
+
# @param payout_description [String, nil] Description for entire batch
|
|
45
|
+
# @param ipn_callback_url [String, nil] Callback URL for entire batch
|
|
46
|
+
# @return [Hash] Payout result
|
|
47
|
+
def create_payout(withdrawals:, payout_description: nil, ipn_callback_url: nil)
|
|
48
|
+
params = { withdrawals: withdrawals }
|
|
49
|
+
params[:payout_description] = payout_description if payout_description
|
|
50
|
+
params[:ipn_callback_url] = ipn_callback_url if ipn_callback_url
|
|
51
|
+
|
|
52
|
+
post("payout", body: params).body
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Verify payout with 2FA code
|
|
56
|
+
# POST /v1/payout/:batch_withdrawal_id/verify
|
|
57
|
+
# @param batch_withdrawal_id [String, Integer] Batch withdrawal ID from create_payout
|
|
58
|
+
# @param verification_code [String] 2FA code from Google Auth app or email
|
|
59
|
+
# @return [Hash] Verification result
|
|
60
|
+
def verify_payout(batch_withdrawal_id:, verification_code:)
|
|
61
|
+
post("payout/#{batch_withdrawal_id}/verify", body: {
|
|
62
|
+
verification_code: verification_code
|
|
63
|
+
}).body
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get payout status
|
|
67
|
+
# GET /v1/payout/:payout_id
|
|
68
|
+
# @param payout_id [String, Integer] Payout ID
|
|
69
|
+
# @return [Hash] Payout status and details
|
|
70
|
+
def payout_status(payout_id)
|
|
71
|
+
get("payout/#{payout_id}").body
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# List payouts with pagination
|
|
75
|
+
# GET /v1/payout
|
|
76
|
+
# @param limit [Integer] Results per page
|
|
77
|
+
# @param offset [Integer] Offset for pagination
|
|
78
|
+
# @return [Hash] List of payouts
|
|
79
|
+
def list_payouts(limit: 10, offset: 0)
|
|
80
|
+
get("payout", params: {
|
|
81
|
+
limit: limit,
|
|
82
|
+
offset: offset
|
|
83
|
+
}).body
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Get minimum payout amount
|
|
87
|
+
# GET /v1/payout/min-amount
|
|
88
|
+
# @param currency [String] Currency code
|
|
89
|
+
# @return [Hash] Minimum payout amount for currency
|
|
90
|
+
def min_payout_amount(currency:)
|
|
91
|
+
get("payout/min-amount", params: { currency: currency }).body
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Get payout fee estimate
|
|
95
|
+
# GET /v1/payout/fee
|
|
96
|
+
# @param currency [String] Currency code
|
|
97
|
+
# @param amount [Numeric] Payout amount
|
|
98
|
+
# @return [Hash] Fee estimate
|
|
99
|
+
def payout_fee(currency:, amount:)
|
|
100
|
+
get("payout/fee", params: {
|
|
101
|
+
currency: currency,
|
|
102
|
+
amount: amount
|
|
103
|
+
}).body
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Status and utility endpoints
|
|
6
|
+
module Status
|
|
7
|
+
# Check API status
|
|
8
|
+
# GET /v1/status
|
|
9
|
+
# @return [Hash] Status response
|
|
10
|
+
def status
|
|
11
|
+
get("status").body
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NOWPayments
|
|
4
|
+
module API
|
|
5
|
+
# Subscription and recurring payment endpoints
|
|
6
|
+
module Subscriptions
|
|
7
|
+
# Get subscription plans
|
|
8
|
+
# GET /v1/subscriptions/plans
|
|
9
|
+
# @return [Hash] List of subscription plans
|
|
10
|
+
def subscription_plans
|
|
11
|
+
get("subscriptions/plans").body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Create subscription plan
|
|
15
|
+
# POST /v1/subscriptions/plans
|
|
16
|
+
# @param plan_data [Hash] Plan configuration
|
|
17
|
+
# @return [Hash] Created plan
|
|
18
|
+
def create_subscription_plan(plan_data)
|
|
19
|
+
post("subscriptions/plans", body: plan_data).body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Update subscription plan
|
|
23
|
+
# PATCH /v1/subscriptions/plans/:plan_id
|
|
24
|
+
# @param plan_id [String, Integer] Plan ID
|
|
25
|
+
# @param plan_data [Hash] Updated plan configuration
|
|
26
|
+
# @return [Hash] Updated plan details
|
|
27
|
+
def update_subscription_plan(plan_id, plan_data)
|
|
28
|
+
patch("subscriptions/plans/#{plan_id}", body: plan_data).body
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get specific subscription plan
|
|
32
|
+
# GET /v1/subscriptions/plans/:plan_id
|
|
33
|
+
# @param plan_id [String, Integer] Plan ID
|
|
34
|
+
# @return [Hash] Plan details
|
|
35
|
+
def subscription_plan(plan_id)
|
|
36
|
+
get("subscriptions/plans/#{plan_id}").body
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Create email subscription
|
|
40
|
+
# POST /v1/subscriptions
|
|
41
|
+
# @param plan_id [String] Subscription plan ID
|
|
42
|
+
# @param email [String] Customer email
|
|
43
|
+
# @return [Hash] Subscription result
|
|
44
|
+
def create_subscription(plan_id:, email:)
|
|
45
|
+
post("subscriptions", body: {
|
|
46
|
+
plan_id: plan_id,
|
|
47
|
+
email: email
|
|
48
|
+
}).body
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# List recurring payments with filters
|
|
52
|
+
# GET /v1/subscriptions
|
|
53
|
+
# @param limit [Integer] Results per page
|
|
54
|
+
# @param offset [Integer] Offset for pagination
|
|
55
|
+
# @param status [String, nil] Filter by status (WAITING_PAY, PAID, PARTIALLY_PAID, EXPIRED)
|
|
56
|
+
# @param subscription_plan_id [String, Integer, nil] Filter by plan ID
|
|
57
|
+
# @param is_active [Boolean, nil] Filter by active status
|
|
58
|
+
# @return [Hash] List of recurring payments
|
|
59
|
+
def list_recurring_payments(limit: 10, offset: 0, status: nil, subscription_plan_id: nil, is_active: nil)
|
|
60
|
+
params = { limit: limit, offset: offset }
|
|
61
|
+
params[:status] = status if status
|
|
62
|
+
params[:subscription_plan_id] = subscription_plan_id if subscription_plan_id
|
|
63
|
+
params[:is_active] = is_active unless is_active.nil?
|
|
64
|
+
|
|
65
|
+
get("subscriptions", params: params).body
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get specific recurring payment
|
|
69
|
+
# GET /v1/subscriptions/:subscription_id
|
|
70
|
+
# @param subscription_id [String, Integer] Subscription ID
|
|
71
|
+
# @return [Hash] Recurring payment details
|
|
72
|
+
def recurring_payment(subscription_id)
|
|
73
|
+
get("subscriptions/#{subscription_id}").body
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Delete recurring payment
|
|
77
|
+
# DELETE /v1/subscriptions/:subscription_id
|
|
78
|
+
# @param subscription_id [String, Integer] Subscription ID
|
|
79
|
+
# @return [Hash] Deletion result
|
|
80
|
+
def delete_recurring_payment(subscription_id)
|
|
81
|
+
delete("subscriptions/#{subscription_id}").body
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Get subscription payments
|
|
85
|
+
# GET /v1/subscriptions/:subscription_id/payments
|
|
86
|
+
# @param subscription_id [String, Integer] Subscription ID
|
|
87
|
+
# @return [Hash] Subscription payments
|
|
88
|
+
def subscription_payments(subscription_id)
|
|
89
|
+
get("subscriptions/#{subscription_id}/payments").body
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "api/status"
|
|
6
|
+
require_relative "api/authentication"
|
|
7
|
+
require_relative "api/currencies"
|
|
8
|
+
require_relative "api/estimation"
|
|
9
|
+
require_relative "api/payments"
|
|
10
|
+
require_relative "api/invoices"
|
|
11
|
+
require_relative "api/payouts"
|
|
12
|
+
require_relative "api/subscriptions"
|
|
13
|
+
require_relative "api/conversions"
|
|
14
|
+
require_relative "api/custody"
|
|
15
|
+
require_relative "api/fiat_payouts"
|
|
16
|
+
|
|
17
|
+
module NOWPayments
|
|
18
|
+
# Main client for interacting with the NOWPayments API
|
|
19
|
+
class Client
|
|
20
|
+
include API::Status
|
|
21
|
+
include API::Authentication
|
|
22
|
+
include API::Currencies
|
|
23
|
+
include API::Estimation
|
|
24
|
+
include API::Payments
|
|
25
|
+
include API::Invoices
|
|
26
|
+
include API::Payouts
|
|
27
|
+
include API::Subscriptions
|
|
28
|
+
include API::Conversions
|
|
29
|
+
include API::Custody
|
|
30
|
+
include API::FiatPayouts
|
|
31
|
+
|
|
32
|
+
attr_reader :api_key, :ipn_secret, :sandbox
|
|
33
|
+
attr_accessor :jwt_token, :jwt_expires_at
|
|
34
|
+
|
|
35
|
+
BASE_URL = "https://api.nowpayments.io/v1"
|
|
36
|
+
SANDBOX_URL = "https://api-sandbox.nowpayments.io/v1"
|
|
37
|
+
|
|
38
|
+
def initialize(api_key:, ipn_secret: nil, sandbox: false)
|
|
39
|
+
@api_key = api_key
|
|
40
|
+
@ipn_secret = ipn_secret
|
|
41
|
+
@sandbox = sandbox
|
|
42
|
+
@jwt_token = nil
|
|
43
|
+
@jwt_expires_at = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def base_url
|
|
47
|
+
sandbox ? SANDBOX_URL : BASE_URL
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def connection
|
|
53
|
+
@connection ||= Faraday.new(url: base_url) do |faraday|
|
|
54
|
+
faraday.request :json
|
|
55
|
+
faraday.response :json, content_type: /\bjson$/
|
|
56
|
+
faraday.adapter Faraday.default_adapter
|
|
57
|
+
faraday.headers["x-api-key"] = @api_key
|
|
58
|
+
|
|
59
|
+
# Add JWT Bearer token if available and not expired
|
|
60
|
+
faraday.headers["Authorization"] = "Bearer #{@jwt_token}" if @jwt_token && !jwt_expired?
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Reset connection when JWT token changes
|
|
65
|
+
def reset_connection!
|
|
66
|
+
@connection = nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def get(path, params: {})
|
|
70
|
+
connection.get(path, params)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def post(path, body: {})
|
|
74
|
+
connection.post(path, body.to_json)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def patch(path, body: {})
|
|
78
|
+
connection.patch(path, body.to_json)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def delete(path, params: {})
|
|
82
|
+
connection.delete(path, params)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate_payment_params!(params)
|
|
86
|
+
return unless params[:payout_address] && !params[:payout_currency]
|
|
87
|
+
|
|
88
|
+
raise ValidationError, "payout_currency required when payout_address is set"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|