paystack-gateway 0.1.0 → 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 +4 -4
- data/.rubocop.yml +12 -2
- data/CHANGELOG.md +57 -0
- data/README.md +88 -105
- data/lib/paystack_gateway/api_error.rb +12 -2
- data/lib/paystack_gateway/apple_pay.rb +92 -0
- data/lib/paystack_gateway/balance.rb +62 -0
- data/lib/paystack_gateway/bank.rb +181 -0
- data/lib/paystack_gateway/bulk_charge.rb +197 -0
- data/lib/paystack_gateway/charge.rb +346 -0
- data/lib/paystack_gateway/configuration.rb +5 -3
- data/lib/paystack_gateway/customer.rb +498 -0
- data/lib/paystack_gateway/customers.rb +1 -42
- data/lib/paystack_gateway/dedicated_virtual_account.rb +353 -0
- data/lib/paystack_gateway/dedicated_virtual_accounts.rb +1 -64
- data/lib/paystack_gateway/direct_debit.rb +64 -0
- data/lib/paystack_gateway/dispute.rb +380 -0
- data/lib/paystack_gateway/extensions/customer_extensions.rb +14 -0
- data/lib/paystack_gateway/extensions/plan_extensions.rb +36 -0
- data/lib/paystack_gateway/extensions/refund_extensions.rb +43 -0
- data/lib/paystack_gateway/extensions/transaction_extensions.rb +40 -0
- data/lib/paystack_gateway/extensions/transaction_response_extension.rb +31 -0
- data/lib/paystack_gateway/extensions/transfer_extensions.rb +34 -0
- data/lib/paystack_gateway/integration.rb +60 -0
- data/lib/paystack_gateway/legacy/customers.rb +48 -0
- data/lib/paystack_gateway/legacy/dedicated_virtual_accounts.rb +70 -0
- data/lib/paystack_gateway/legacy/miscellaneous.rb +30 -0
- data/lib/paystack_gateway/legacy/plans.rb +86 -0
- data/lib/paystack_gateway/legacy/refunds.rb +65 -0
- data/lib/paystack_gateway/legacy/subaccounts.rb +41 -0
- data/lib/paystack_gateway/legacy/transaction_response.rb +37 -0
- data/lib/paystack_gateway/legacy/transactions.rb +91 -0
- data/lib/paystack_gateway/legacy/transfer_recipients.rb +27 -0
- data/lib/paystack_gateway/legacy/transfers.rb +63 -0
- data/lib/paystack_gateway/legacy/verification.rb +37 -0
- data/lib/paystack_gateway/miscellaneous.rb +75 -13
- data/lib/paystack_gateway/order.rb +259 -0
- data/lib/paystack_gateway/page.rb +317 -0
- data/lib/paystack_gateway/payment_request.rb +454 -0
- data/lib/paystack_gateway/plan.rb +247 -0
- data/lib/paystack_gateway/plans.rb +1 -80
- data/lib/paystack_gateway/product.rb +303 -0
- data/lib/paystack_gateway/refund.rb +197 -0
- data/lib/paystack_gateway/refunds.rb +1 -59
- data/lib/paystack_gateway/request_module.rb +20 -15
- data/lib/paystack_gateway/settlement.rb +60 -0
- data/lib/paystack_gateway/split.rb +286 -0
- data/lib/paystack_gateway/storefront.rb +329 -0
- data/lib/paystack_gateway/subaccount.rb +263 -0
- data/lib/paystack_gateway/subaccounts.rb +1 -35
- data/lib/paystack_gateway/subscription.rb +256 -0
- data/lib/paystack_gateway/terminal.rb +236 -0
- data/lib/paystack_gateway/transaction.rb +592 -0
- data/lib/paystack_gateway/transaction_response.rb +1 -33
- data/lib/paystack_gateway/transactions.rb +1 -86
- data/lib/paystack_gateway/transfer.rb +410 -0
- data/lib/paystack_gateway/transfer_recipient.rb +237 -0
- data/lib/paystack_gateway/transfer_recipients.rb +1 -21
- data/lib/paystack_gateway/transfers.rb +1 -57
- data/lib/paystack_gateway/verification.rb +1 -31
- data/lib/paystack_gateway/version.rb +1 -1
- data/lib/paystack_gateway/virtual_terminal.rb +297 -0
- data/lib/paystack_gateway.rb +61 -11
- metadata +50 -7
|
@@ -1,82 +1,3 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
# Create and manage installment payment options
|
|
5
|
-
# https://paystack.com/docs/api/plan/#create
|
|
6
|
-
module Plans
|
|
7
|
-
include PaystackGateway::RequestModule
|
|
8
|
-
|
|
9
|
-
# Response from POST /plan endpoint.
|
|
10
|
-
class CreatePlanResponse < PaystackGateway::Response
|
|
11
|
-
delegate :id, to: :data, prefix: :plan
|
|
12
|
-
delegate :id, :plan_code, to: :data
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
api_method def self.create_plan(name:, amount:, interval:)
|
|
16
|
-
use_connection do |connection|
|
|
17
|
-
connection.post(
|
|
18
|
-
'/plan',
|
|
19
|
-
{
|
|
20
|
-
name:,
|
|
21
|
-
interval:,
|
|
22
|
-
amount: amount * 100,
|
|
23
|
-
send_invoices: false,
|
|
24
|
-
send_sms: false,
|
|
25
|
-
},
|
|
26
|
-
)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Response from GET /plan endpoint.
|
|
31
|
-
class ListPlansResponse < PaystackGateway::Response
|
|
32
|
-
def active_plans = data.select { |plan| !plan.is_deleted && !plan.is_archived }
|
|
33
|
-
|
|
34
|
-
def find_active_plan_by_name(name)
|
|
35
|
-
active_plans.sort_by { -Time.parse(_1.createdAt).to_i }.find { _1.name == name }
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
api_method def self.list_plans
|
|
40
|
-
use_connection do |connection|
|
|
41
|
-
connection.get('/plan')
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Response from GET /plan/:code endpoint.
|
|
46
|
-
class FetchPlanResponse < PaystackGateway::Response
|
|
47
|
-
delegate :subscriptions, to: :data
|
|
48
|
-
|
|
49
|
-
def active_subscriptions = subscriptions.select { _1.status.to_sym == :active }
|
|
50
|
-
|
|
51
|
-
def active_subscription_codes(email: nil)
|
|
52
|
-
subscriptions =
|
|
53
|
-
if email
|
|
54
|
-
active_subscriptions.select { _1.customer.email.casecmp?(email) }
|
|
55
|
-
else
|
|
56
|
-
active_subscriptions
|
|
57
|
-
end
|
|
58
|
-
subscriptions.map(&:subscription_code)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
api_method def self.fetch_plan(code:)
|
|
63
|
-
use_connection do |connection|
|
|
64
|
-
connection.get("/plan/#{code}")
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
class UpdatePlanResponse < PaystackGateway::Response; end
|
|
69
|
-
|
|
70
|
-
api_method def self.update_plan(code:, amount:, interval:)
|
|
71
|
-
use_connection do |connection|
|
|
72
|
-
connection.put(
|
|
73
|
-
"/plan/#{code}",
|
|
74
|
-
{
|
|
75
|
-
amount: amount * 100,
|
|
76
|
-
interval:,
|
|
77
|
-
},
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
3
|
+
require 'paystack_gateway/legacy/plans'
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaystackGateway
|
|
4
|
+
# https://paystack.com/docs/api/product
|
|
5
|
+
#
|
|
6
|
+
# Products
|
|
7
|
+
# A collection of endpoints for creating and managing inventories
|
|
8
|
+
module Product
|
|
9
|
+
include PaystackGateway::RequestModule
|
|
10
|
+
|
|
11
|
+
# Successful response from calling #list.
|
|
12
|
+
class ListResponse < PaystackGateway::Response; end
|
|
13
|
+
|
|
14
|
+
# Error response from #list.
|
|
15
|
+
class ListError < ApiError; end
|
|
16
|
+
|
|
17
|
+
# https://paystack.com/docs/api/product/#list
|
|
18
|
+
# List Products: GET /product
|
|
19
|
+
# List all previously created products
|
|
20
|
+
#
|
|
21
|
+
# @param per_page [Integer]
|
|
22
|
+
# Number of records to fetch per page
|
|
23
|
+
# @param page [Integer]
|
|
24
|
+
# The section to retrieve
|
|
25
|
+
# @param active [Boolean]
|
|
26
|
+
# The state of the product
|
|
27
|
+
# @param from [Time]
|
|
28
|
+
# The start date
|
|
29
|
+
# @param to [Time]
|
|
30
|
+
# The end date
|
|
31
|
+
#
|
|
32
|
+
# @return [ListResponse] successful response
|
|
33
|
+
# @raise [ListError] if the request fails
|
|
34
|
+
api_method def self.list(per_page: nil, page: nil, active: nil, from: nil, to: nil)
|
|
35
|
+
use_connection do |connection|
|
|
36
|
+
connection.get(
|
|
37
|
+
'/product',
|
|
38
|
+
{ perPage: per_page, page:, active:, from:, to: }.compact,
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Successful response from calling #create.
|
|
44
|
+
class CreateResponse < PaystackGateway::Response
|
|
45
|
+
delegate :variants_options,
|
|
46
|
+
:variants,
|
|
47
|
+
:name,
|
|
48
|
+
:description,
|
|
49
|
+
:currency,
|
|
50
|
+
:price,
|
|
51
|
+
:quantity,
|
|
52
|
+
:type,
|
|
53
|
+
:is_shippable,
|
|
54
|
+
:unlimited,
|
|
55
|
+
:files,
|
|
56
|
+
:shipping_fields,
|
|
57
|
+
:integration,
|
|
58
|
+
:domain,
|
|
59
|
+
:metadata,
|
|
60
|
+
:slug,
|
|
61
|
+
:product_code,
|
|
62
|
+
:quantity_sold,
|
|
63
|
+
:active,
|
|
64
|
+
:deleted_at,
|
|
65
|
+
:in_stock,
|
|
66
|
+
:minimum_orderable,
|
|
67
|
+
:maximum_orderable,
|
|
68
|
+
:low_stock_alert,
|
|
69
|
+
:id,
|
|
70
|
+
:createdAt,
|
|
71
|
+
:updatedAt, to: :data
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Error response from #create.
|
|
75
|
+
class CreateError < ApiError; end
|
|
76
|
+
|
|
77
|
+
# https://paystack.com/docs/api/product/#create
|
|
78
|
+
# Create Product: POST /product
|
|
79
|
+
# Create a new product on your integration
|
|
80
|
+
#
|
|
81
|
+
# @param name [String] (required)
|
|
82
|
+
# Name of product
|
|
83
|
+
# @param description [String] (required)
|
|
84
|
+
# The description of the product
|
|
85
|
+
# @param price [Integer] (required)
|
|
86
|
+
# Price should be in kobo if currency is NGN, pesewas, if currency is GHS, and cents,
|
|
87
|
+
# if currency is ZAR
|
|
88
|
+
# @param currency [String] (required)
|
|
89
|
+
# Currency in which price is set. Allowed values are: NGN, GHS, ZAR or USD
|
|
90
|
+
# @param unlimited [Boolean]
|
|
91
|
+
# Set to true if the product has unlimited stock. Leave as false if the product has
|
|
92
|
+
# limited stock
|
|
93
|
+
# @param quantity [Integer]
|
|
94
|
+
# Number of products in stock. Use if limited is true
|
|
95
|
+
# @param split_code [String]
|
|
96
|
+
# The split code if sharing the transaction with partners
|
|
97
|
+
# @param metadata [String]
|
|
98
|
+
# Stringified JSON object of custom data
|
|
99
|
+
#
|
|
100
|
+
# @return [CreateResponse] successful response
|
|
101
|
+
# @raise [CreateError] if the request fails
|
|
102
|
+
api_method def self.create(
|
|
103
|
+
name:,
|
|
104
|
+
description:,
|
|
105
|
+
price:,
|
|
106
|
+
currency:,
|
|
107
|
+
unlimited: nil,
|
|
108
|
+
quantity: nil,
|
|
109
|
+
split_code: nil,
|
|
110
|
+
metadata: nil
|
|
111
|
+
)
|
|
112
|
+
use_connection do |connection|
|
|
113
|
+
connection.post(
|
|
114
|
+
'/product',
|
|
115
|
+
{
|
|
116
|
+
name:,
|
|
117
|
+
description:,
|
|
118
|
+
price:,
|
|
119
|
+
currency:,
|
|
120
|
+
unlimited:,
|
|
121
|
+
quantity:,
|
|
122
|
+
split_code:,
|
|
123
|
+
metadata:,
|
|
124
|
+
}.compact,
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Successful response from calling #fetch.
|
|
130
|
+
class FetchResponse < PaystackGateway::Response
|
|
131
|
+
delegate :digital_assets,
|
|
132
|
+
:integration,
|
|
133
|
+
:name,
|
|
134
|
+
:description,
|
|
135
|
+
:product_code,
|
|
136
|
+
:price,
|
|
137
|
+
:currency,
|
|
138
|
+
:quantity,
|
|
139
|
+
:quantity_sold,
|
|
140
|
+
:type,
|
|
141
|
+
:files,
|
|
142
|
+
:file_path,
|
|
143
|
+
:is_shippable,
|
|
144
|
+
:shipping_fields,
|
|
145
|
+
:unlimited,
|
|
146
|
+
:domain,
|
|
147
|
+
:active,
|
|
148
|
+
:features,
|
|
149
|
+
:in_stock,
|
|
150
|
+
:metadata,
|
|
151
|
+
:slug,
|
|
152
|
+
:success_message,
|
|
153
|
+
:redirect_url,
|
|
154
|
+
:split_code,
|
|
155
|
+
:notification_emails,
|
|
156
|
+
:minimum_orderable,
|
|
157
|
+
:maximum_orderable,
|
|
158
|
+
:low_stock_alert,
|
|
159
|
+
:stock_threshold,
|
|
160
|
+
:expires_in,
|
|
161
|
+
:id,
|
|
162
|
+
:createdAt,
|
|
163
|
+
:updatedAt, to: :data
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Error response from #fetch.
|
|
167
|
+
class FetchError < ApiError; end
|
|
168
|
+
|
|
169
|
+
# https://paystack.com/docs/api/product/#fetch
|
|
170
|
+
# Fetch Product: GET /product/{id}
|
|
171
|
+
# Fetch a previously created product
|
|
172
|
+
#
|
|
173
|
+
# @param id [Integer] (required)
|
|
174
|
+
# The unique identifier of the product
|
|
175
|
+
#
|
|
176
|
+
# @return [FetchResponse] successful response
|
|
177
|
+
# @raise [FetchError] if the request fails
|
|
178
|
+
api_method def self.fetch(id:)
|
|
179
|
+
use_connection do |connection|
|
|
180
|
+
connection.get(
|
|
181
|
+
"/product/#{id}",
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Successful response from calling #update.
|
|
187
|
+
class UpdateResponse < PaystackGateway::Response
|
|
188
|
+
delegate :name,
|
|
189
|
+
:description,
|
|
190
|
+
:product_code,
|
|
191
|
+
:price,
|
|
192
|
+
:currency,
|
|
193
|
+
:quantity,
|
|
194
|
+
:quantity_sold,
|
|
195
|
+
:type,
|
|
196
|
+
:files,
|
|
197
|
+
:file_path,
|
|
198
|
+
:is_shippable,
|
|
199
|
+
:shipping_fields,
|
|
200
|
+
:unlimited,
|
|
201
|
+
:domain,
|
|
202
|
+
:active,
|
|
203
|
+
:features,
|
|
204
|
+
:in_stock,
|
|
205
|
+
:metadata,
|
|
206
|
+
:slug,
|
|
207
|
+
:success_message,
|
|
208
|
+
:redirect_url,
|
|
209
|
+
:split_code,
|
|
210
|
+
:notification_emails,
|
|
211
|
+
:minimum_orderable,
|
|
212
|
+
:maximum_orderable,
|
|
213
|
+
:low_stock_alert,
|
|
214
|
+
:stock_threshold,
|
|
215
|
+
:expires_in,
|
|
216
|
+
:id,
|
|
217
|
+
:integration,
|
|
218
|
+
:createdAt,
|
|
219
|
+
:updatedAt, to: :data
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Error response from #update.
|
|
223
|
+
class UpdateError < ApiError; end
|
|
224
|
+
|
|
225
|
+
# https://paystack.com/docs/api/product/#update
|
|
226
|
+
# Update product: PUT /product/{id}
|
|
227
|
+
# Update a previously created product
|
|
228
|
+
#
|
|
229
|
+
# @param id [Integer] (required)
|
|
230
|
+
# The unique identifier of the product
|
|
231
|
+
# @param name [String]
|
|
232
|
+
# Name of product
|
|
233
|
+
# @param description [String]
|
|
234
|
+
# The description of the product
|
|
235
|
+
# @param price [Integer]
|
|
236
|
+
# Price should be in kobo if currency is NGN, pesewas, if currency is GHS, and cents,
|
|
237
|
+
# if currency is ZAR
|
|
238
|
+
# @param currency [String]
|
|
239
|
+
# Currency in which price is set. Allowed values are: NGN, GHS, ZAR or USD
|
|
240
|
+
# @param unlimited [Boolean]
|
|
241
|
+
# Set to true if the product has unlimited stock. Leave as false if the product has
|
|
242
|
+
# limited stock
|
|
243
|
+
# @param quantity [Integer]
|
|
244
|
+
# Number of products in stock. Use if limited is true
|
|
245
|
+
# @param split_code [String]
|
|
246
|
+
# The split code if sharing the transaction with partners
|
|
247
|
+
# @param metadata [Hash]
|
|
248
|
+
# JSON object of custom data
|
|
249
|
+
#
|
|
250
|
+
# @return [UpdateResponse] successful response
|
|
251
|
+
# @raise [UpdateError] if the request fails
|
|
252
|
+
api_method def self.update(
|
|
253
|
+
id:,
|
|
254
|
+
name: nil,
|
|
255
|
+
description: nil,
|
|
256
|
+
price: nil,
|
|
257
|
+
currency: nil,
|
|
258
|
+
unlimited: nil,
|
|
259
|
+
quantity: nil,
|
|
260
|
+
split_code: nil,
|
|
261
|
+
metadata: nil
|
|
262
|
+
)
|
|
263
|
+
use_connection do |connection|
|
|
264
|
+
connection.put(
|
|
265
|
+
"/product/#{id}",
|
|
266
|
+
{
|
|
267
|
+
name:,
|
|
268
|
+
description:,
|
|
269
|
+
price:,
|
|
270
|
+
currency:,
|
|
271
|
+
unlimited:,
|
|
272
|
+
quantity:,
|
|
273
|
+
split_code:,
|
|
274
|
+
metadata:,
|
|
275
|
+
}.compact,
|
|
276
|
+
)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Successful response from calling #delete.
|
|
281
|
+
class DeleteResponse < PaystackGateway::Response; end
|
|
282
|
+
|
|
283
|
+
# Error response from #delete.
|
|
284
|
+
class DeleteError < ApiError; end
|
|
285
|
+
|
|
286
|
+
# https://paystack.com/docs/api/product/#delete
|
|
287
|
+
# Delete Product: DELETE /product/{id}
|
|
288
|
+
# Delete a previously created product
|
|
289
|
+
#
|
|
290
|
+
# @param id [Integer] (required)
|
|
291
|
+
# The unique identifier of the product
|
|
292
|
+
#
|
|
293
|
+
# @return [DeleteResponse] successful response
|
|
294
|
+
# @raise [DeleteError] if the request fails
|
|
295
|
+
api_method def self.delete(id:)
|
|
296
|
+
use_connection do |connection|
|
|
297
|
+
connection.delete(
|
|
298
|
+
"/product/#{id}",
|
|
299
|
+
)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaystackGateway
|
|
4
|
+
# https://paystack.com/docs/api/refund
|
|
5
|
+
#
|
|
6
|
+
# Refunds
|
|
7
|
+
# A collection of endpoints for creating and managing transaction reimbursement
|
|
8
|
+
module Refund
|
|
9
|
+
include PaystackGateway::RequestModule
|
|
10
|
+
|
|
11
|
+
# Successful response from calling #list.
|
|
12
|
+
class ListResponse < PaystackGateway::Response; end
|
|
13
|
+
|
|
14
|
+
# Error response from #list.
|
|
15
|
+
class ListError < ApiError; end
|
|
16
|
+
|
|
17
|
+
# https://paystack.com/docs/api/refund/#list
|
|
18
|
+
# List Refunds: GET /refund
|
|
19
|
+
# List previously created refunds
|
|
20
|
+
#
|
|
21
|
+
# @param per_page [Integer]
|
|
22
|
+
# Number of records to fetch per page
|
|
23
|
+
# @param page [Integer]
|
|
24
|
+
# The section to retrieve
|
|
25
|
+
# @param from [Time]
|
|
26
|
+
# The start date
|
|
27
|
+
# @param to [Time]
|
|
28
|
+
# The end date
|
|
29
|
+
#
|
|
30
|
+
# @return [ListResponse] successful response
|
|
31
|
+
# @raise [ListError] if the request fails
|
|
32
|
+
api_method def self.list(per_page: nil, page: nil, from: nil, to: nil)
|
|
33
|
+
use_connection do |connection|
|
|
34
|
+
connection.get(
|
|
35
|
+
'/refund',
|
|
36
|
+
{ perPage: per_page, page:, from:, to: }.compact,
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Successful response from calling #create.
|
|
42
|
+
class CreateResponse < PaystackGateway::Response
|
|
43
|
+
delegate :transaction,
|
|
44
|
+
:integration,
|
|
45
|
+
:deducted_amount,
|
|
46
|
+
:channel,
|
|
47
|
+
:merchant_note,
|
|
48
|
+
:customer_note,
|
|
49
|
+
:refunded_by,
|
|
50
|
+
:expected_at,
|
|
51
|
+
:currency,
|
|
52
|
+
:domain,
|
|
53
|
+
:amount,
|
|
54
|
+
:fully_deducted,
|
|
55
|
+
:id,
|
|
56
|
+
:createdAt,
|
|
57
|
+
:updatedAt, to: :data
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Error response from #create.
|
|
61
|
+
class CreateError < ApiError; end
|
|
62
|
+
|
|
63
|
+
# https://paystack.com/docs/api/refund/#create
|
|
64
|
+
# Create Refund: POST /refund
|
|
65
|
+
# Initiate a refund for a previously completed transaction
|
|
66
|
+
#
|
|
67
|
+
# @param transaction [String] (required)
|
|
68
|
+
# The reference of a previosuly completed transaction
|
|
69
|
+
# @param amount [Integer]
|
|
70
|
+
# Amount to be refunded to the customer. It cannot be more than the original transaction
|
|
71
|
+
# amount
|
|
72
|
+
# @param currency ["GHS", "KES", "NGN", "USD", "ZAR"]
|
|
73
|
+
# Three-letter ISO currency
|
|
74
|
+
# @param customer_note [String]
|
|
75
|
+
# Customer reason
|
|
76
|
+
# @param merchant_note [String]
|
|
77
|
+
# Merchant reason
|
|
78
|
+
#
|
|
79
|
+
# @return [CreateResponse] successful response
|
|
80
|
+
# @raise [CreateError] if the request fails
|
|
81
|
+
api_method def self.create(transaction:, amount: nil, currency: nil, customer_note: nil, merchant_note: nil)
|
|
82
|
+
use_connection do |connection|
|
|
83
|
+
connection.post(
|
|
84
|
+
'/refund',
|
|
85
|
+
{ transaction:, amount:, currency:, customer_note:, merchant_note: }.compact,
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Successful response from calling #retry.
|
|
91
|
+
class RetryResponse < PaystackGateway::Response
|
|
92
|
+
delegate :integration,
|
|
93
|
+
:transaction,
|
|
94
|
+
:dispute,
|
|
95
|
+
:settlement,
|
|
96
|
+
:id,
|
|
97
|
+
:domain,
|
|
98
|
+
:currency,
|
|
99
|
+
:amount,
|
|
100
|
+
:refunded_at,
|
|
101
|
+
:expected_at,
|
|
102
|
+
:channel,
|
|
103
|
+
:refunded_by,
|
|
104
|
+
:customer_note,
|
|
105
|
+
:merchant_note,
|
|
106
|
+
:deducted_amount,
|
|
107
|
+
:fully_deducted,
|
|
108
|
+
:bank_reference,
|
|
109
|
+
:reason,
|
|
110
|
+
:customer,
|
|
111
|
+
:initiated_by,
|
|
112
|
+
:reversed_at,
|
|
113
|
+
:session_id, to: :data
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Error response from #retry.
|
|
117
|
+
class RetryError < ApiError; end
|
|
118
|
+
|
|
119
|
+
# https://paystack.com/docs/api/refund/#retry
|
|
120
|
+
# Retry Refund: POST /refund/retry_with_customer_details/{id}
|
|
121
|
+
# Retry a refund with a `needs-attention` status by providing the bank account details
|
|
122
|
+
# of a customer.
|
|
123
|
+
#
|
|
124
|
+
# @param id [Integer] (required)
|
|
125
|
+
# The identifier of the refund
|
|
126
|
+
# @param refund_account_details [Hash] (required)
|
|
127
|
+
# An object that contains the customer’s account details for refund
|
|
128
|
+
# @option refund_account_details [String] :currency
|
|
129
|
+
# The currency of the customer's bank account. It should be the same as the currency
|
|
130
|
+
# the payment was made
|
|
131
|
+
# @option refund_account_details [String] :account_number
|
|
132
|
+
# The customer's account number
|
|
133
|
+
# @option refund_account_details [String] :bank_id
|
|
134
|
+
# The ID representing the customer's bank. You can get the list of bank IDs by calling
|
|
135
|
+
# the List Banks endpoint.
|
|
136
|
+
#
|
|
137
|
+
# @return [RetryResponse] successful response
|
|
138
|
+
# @raise [RetryError] if the request fails
|
|
139
|
+
api_method def self.retry(id:, refund_account_details:)
|
|
140
|
+
use_connection do |connection|
|
|
141
|
+
connection.post(
|
|
142
|
+
"/refund/retry_with_customer_details/#{id}",
|
|
143
|
+
{ refund_account_details: }.compact,
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Successful response from calling #fetch.
|
|
149
|
+
class FetchResponse < PaystackGateway::Response
|
|
150
|
+
delegate :integration,
|
|
151
|
+
:transaction,
|
|
152
|
+
:dispute,
|
|
153
|
+
:settlement,
|
|
154
|
+
:id,
|
|
155
|
+
:domain,
|
|
156
|
+
:currency,
|
|
157
|
+
:amount,
|
|
158
|
+
:refunded_at,
|
|
159
|
+
:refunded_by,
|
|
160
|
+
:customer_note,
|
|
161
|
+
:merchant_note,
|
|
162
|
+
:deducted_amount,
|
|
163
|
+
:fully_deducted,
|
|
164
|
+
:createdAt,
|
|
165
|
+
:bank_reference,
|
|
166
|
+
:transaction_reference,
|
|
167
|
+
:reason,
|
|
168
|
+
:customer,
|
|
169
|
+
:refund_type,
|
|
170
|
+
:transaction_amount,
|
|
171
|
+
:initiated_by,
|
|
172
|
+
:refund_channel,
|
|
173
|
+
:session_id,
|
|
174
|
+
:collect_account_number, to: :data
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Error response from #fetch.
|
|
178
|
+
class FetchError < ApiError; end
|
|
179
|
+
|
|
180
|
+
# https://paystack.com/docs/api/refund/#fetch
|
|
181
|
+
# Fetch Refund: GET /refund/{id}
|
|
182
|
+
# Get a previously created refund
|
|
183
|
+
#
|
|
184
|
+
# @param id [Integer] (required)
|
|
185
|
+
# The identifier of the refund
|
|
186
|
+
#
|
|
187
|
+
# @return [FetchResponse] successful response
|
|
188
|
+
# @raise [FetchError] if the request fails
|
|
189
|
+
api_method def self.fetch(id:)
|
|
190
|
+
use_connection do |connection|
|
|
191
|
+
connection.get(
|
|
192
|
+
"/refund/#{id}",
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -1,61 +1,3 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
# Create and manage transaction refunds.
|
|
5
|
-
# https://paystack.com/docs/api/refund
|
|
6
|
-
module Refunds
|
|
7
|
-
include PaystackGateway::RequestModule
|
|
8
|
-
|
|
9
|
-
# Common helpers for responses from refunds endpoints
|
|
10
|
-
module TransactionRefundResponse
|
|
11
|
-
def refund_success? = transaction_status == :processed
|
|
12
|
-
def refund_failed? = transaction_status == :failed
|
|
13
|
-
def refund_pending? = transaction_status.in?(%i[pending processing])
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Response from POST /refund endpoint.
|
|
17
|
-
class CreateResponse < PaystackGateway::Response
|
|
18
|
-
include TransactionResponse
|
|
19
|
-
include TransactionRefundResponse
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
api_method def self.create(transaction_reference_or_id:)
|
|
23
|
-
use_connection do |connection|
|
|
24
|
-
connection.post('/refund', { transaction: transaction_reference_or_id })
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Response from GET /refund endpoint.
|
|
29
|
-
class ListRefundsResponse < PaystackGateway::Response
|
|
30
|
-
def pending_or_successful
|
|
31
|
-
filtered = data.select { _1.status&.to_sym.in?(%i[processed pending processing]) }
|
|
32
|
-
|
|
33
|
-
ListRefundsResponse.new({ **self, data: filtered })
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def with_amount(amount)
|
|
37
|
-
filtered = data.select { _1.amount == amount * 100 }
|
|
38
|
-
|
|
39
|
-
ListRefundsResponse.new({ **self, data: filtered })
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
api_method def self.list_refunds(transaction_id:)
|
|
44
|
-
use_connection do |connection|
|
|
45
|
-
connection.get('/refund', { transaction: transaction_id })
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Response from GET /refund/:id endpoint.
|
|
50
|
-
class FetchRefundResponse < PaystackGateway::Response
|
|
51
|
-
include TransactionResponse
|
|
52
|
-
include TransactionRefundResponse
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
api_method def self.fetch_refund(refund_id:)
|
|
56
|
-
use_connection do |connection|
|
|
57
|
-
connection.get("/refund/#{refund_id}")
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
3
|
+
require 'paystack_gateway/legacy/refunds'
|
|
@@ -54,29 +54,28 @@ module PaystackGateway
|
|
|
54
54
|
def api_method(method_name)
|
|
55
55
|
@api_method_names ||= Set.new
|
|
56
56
|
@api_method_names << method_name
|
|
57
|
-
|
|
58
|
-
decorate_api_methods(method_name)
|
|
59
57
|
end
|
|
60
58
|
|
|
61
|
-
def decorate_api_methods(
|
|
59
|
+
def decorate_api_methods(method_names = api_methods)
|
|
62
60
|
singleton_class.class_exec do
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
method_names.flatten.each do |method_name|
|
|
62
|
+
implementation = instance_method(method_name)
|
|
63
|
+
remove_method(method_name)
|
|
64
|
+
|
|
65
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
66
|
+
PaystackGateway::Current.with(api_module: self, api_method_name: method_name) do
|
|
67
|
+
implementation.bind_call(self, *args, **kwargs, &block)
|
|
68
|
+
rescue Faraday::Error => e
|
|
69
|
+
handle_error(e)
|
|
71
70
|
end
|
|
72
71
|
end
|
|
73
|
-
end
|
|
72
|
+
end
|
|
74
73
|
end
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
def handle_error(error)
|
|
78
|
-
PaystackGateway.logger.error "#{Current.qualified_api_method_name}: #{error.message}"
|
|
79
|
-
PaystackGateway.logger.
|
|
77
|
+
PaystackGateway.logger.error { "#{Current.qualified_api_method_name}: #{error.message}" }
|
|
78
|
+
PaystackGateway.logger.debug { JSON.pretty_generate(filtered_response(error.response) || {}) } if error.response
|
|
80
79
|
|
|
81
80
|
raise Current.error_class.new(
|
|
82
81
|
"Paystack error: #{error.message}, status: #{error.response_status}, response: #{error.response_body}",
|
|
@@ -91,7 +90,13 @@ module PaystackGateway
|
|
|
91
90
|
request_method: response.dig(:request, :method),
|
|
92
91
|
request_url: response.dig(:request, :url),
|
|
93
92
|
request_headers: PaystackGateway.log_filter.call(response.dig(:request, :headers)),
|
|
94
|
-
request_body: PaystackGateway.log_filter.call(
|
|
93
|
+
request_body: PaystackGateway.log_filter.call(
|
|
94
|
+
begin
|
|
95
|
+
JSON.parse(response.dig(:request, :body) || '{}')
|
|
96
|
+
rescue JSON::ParserError
|
|
97
|
+
response.dig(:request, :body)
|
|
98
|
+
end,
|
|
99
|
+
),
|
|
95
100
|
|
|
96
101
|
response_status: response[:status],
|
|
97
102
|
response_headers: PaystackGateway.log_filter.call(response[:headers]),
|