paystack-gateway 0.1.1 → 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 +81 -102
- data/lib/paystack_gateway/api_error.rb +3 -1
- 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 +13 -2
- 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 +12 -11
- 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 +50 -18
- metadata +50 -7
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# The Dedicated Virtual Account API enables Nigerian merchants to manage unique
|
|
7
|
+
# payment accounts of their customers.
|
|
8
|
+
# https://paystack.com/docs/api/dedicated-virtual-account
|
|
9
|
+
#
|
|
10
|
+
# @deprecated Use PaystackGateway::DedicatedVirtualAccount instead.
|
|
11
|
+
module DedicatedVirtualAccounts
|
|
12
|
+
include PaystackGateway::RequestModule
|
|
13
|
+
|
|
14
|
+
# Response from POST /dedicated_account endpoint.
|
|
15
|
+
class CreateDedicatedVirtualAccountResponse < PaystackGateway::Response; end
|
|
16
|
+
|
|
17
|
+
api_method def self.create_dedicated_virtual_account(customer_id_or_code:, subaccount_code:, preferred_bank:)
|
|
18
|
+
use_connection do |connection|
|
|
19
|
+
connection.post(
|
|
20
|
+
'/dedicated_account',
|
|
21
|
+
{
|
|
22
|
+
customer: customer_id_or_code,
|
|
23
|
+
preferred_bank:,
|
|
24
|
+
subaccount: subaccount_code,
|
|
25
|
+
phone: '+2348011111111', # phone number is required by paystack for some reason, use fake phone number
|
|
26
|
+
},
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Response from POST /dedicated_account/assign endpoint.
|
|
32
|
+
class AssignDedicatedVirtualAccountResponse < PaystackGateway::Response; end
|
|
33
|
+
|
|
34
|
+
api_method def self.assign_dedicated_virtual_account(
|
|
35
|
+
email:, first_name:, last_name:, subaccount_code:, preferred_bank:
|
|
36
|
+
)
|
|
37
|
+
use_connection do |connection|
|
|
38
|
+
connection.post(
|
|
39
|
+
'/dedicated_account/assign',
|
|
40
|
+
{
|
|
41
|
+
email:,
|
|
42
|
+
first_name:,
|
|
43
|
+
last_name:,
|
|
44
|
+
preferred_bank:,
|
|
45
|
+
country: :NG,
|
|
46
|
+
subaccount: subaccount_code,
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Response from POST /dedicated_account/split endpoint
|
|
53
|
+
class SplitDedicatedAccountTransactionResponse < PaystackGateway::Response; end
|
|
54
|
+
|
|
55
|
+
api_method def self.split_dedicated_account_transaction(customer_id_or_code:, subaccount_code:)
|
|
56
|
+
use_connection do |connection|
|
|
57
|
+
connection.post('/dedicated_account/split', { customer: customer_id_or_code, subaccount: subaccount_code })
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Response from GET /dedicated_account/requery/:account_number endpoint
|
|
62
|
+
class RequeryDedicatedAccountResponse < PaystackGateway::Response; end
|
|
63
|
+
|
|
64
|
+
api_method def self.requery_dedicated_account(account_number:, bank:)
|
|
65
|
+
use_connection do |connection|
|
|
66
|
+
connection.get('/dedicated_account/requery', { account_number:, provider_slug: bank })
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaystackGateway
|
|
4
|
+
# Backwards-compatible API methods retained from releases before 0.2.0.
|
|
5
|
+
module Miscellaneous
|
|
6
|
+
include PaystackGateway::RequestModule
|
|
7
|
+
|
|
8
|
+
# Successful response from GET /bank.
|
|
9
|
+
class ListBanksResponse < PaystackGateway::Response
|
|
10
|
+
def bank_names = data.map(&:name)
|
|
11
|
+
def bank_slugs = data.map(&:slug)
|
|
12
|
+
|
|
13
|
+
def bank_details(*attributes) = data.map { _1.slice(*attributes) }
|
|
14
|
+
|
|
15
|
+
def by_bank_names = data.index_by(&:name)
|
|
16
|
+
def by_bank_codes = data.index_by(&:code)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Error response from GET /bank.
|
|
20
|
+
class ListBanksError < ApiError; end
|
|
21
|
+
|
|
22
|
+
# https://paystack.com/docs/api/miscellaneous/#bank
|
|
23
|
+
# @deprecated Use PaystackGateway::Bank.list instead.
|
|
24
|
+
api_method def self.list_banks(use_cache: true, pay_with_bank_transfer: false)
|
|
25
|
+
use_connection(cache_options: use_cache ? {} : nil) do |connection|
|
|
26
|
+
connection.get('/bank', pay_with_bank_transfer ? { pay_with_bank_transfer: } : {})
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Create and manage installment payment options
|
|
7
|
+
# https://paystack.com/docs/api/plan/#create
|
|
8
|
+
#
|
|
9
|
+
# @deprecated Use PaystackGateway::Plan instead.
|
|
10
|
+
module Plans
|
|
11
|
+
include PaystackGateway::RequestModule
|
|
12
|
+
|
|
13
|
+
# Response from POST /plan endpoint.
|
|
14
|
+
class CreatePlanResponse < PaystackGateway::Response
|
|
15
|
+
delegate :id, to: :data, prefix: :plan
|
|
16
|
+
delegate :id, :plan_code, to: :data
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
api_method def self.create_plan(name:, amount:, interval:)
|
|
20
|
+
use_connection do |connection|
|
|
21
|
+
connection.post(
|
|
22
|
+
'/plan',
|
|
23
|
+
{
|
|
24
|
+
name:,
|
|
25
|
+
interval:,
|
|
26
|
+
amount: amount * 100,
|
|
27
|
+
send_invoices: false,
|
|
28
|
+
send_sms: false,
|
|
29
|
+
},
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Response from GET /plan endpoint.
|
|
35
|
+
class ListPlansResponse < PaystackGateway::Response
|
|
36
|
+
def active_plans = data.select { |plan| !plan.is_deleted && !plan.is_archived }
|
|
37
|
+
|
|
38
|
+
def find_active_plan_by_name(name)
|
|
39
|
+
active_plans.sort_by { -Time.parse(_1.createdAt).to_i }.find { _1.name == name }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
api_method def self.list_plans
|
|
44
|
+
use_connection do |connection|
|
|
45
|
+
connection.get('/plan')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Response from GET /plan/:code endpoint.
|
|
50
|
+
class FetchPlanResponse < PaystackGateway::Response
|
|
51
|
+
delegate :subscriptions, to: :data
|
|
52
|
+
|
|
53
|
+
def active_subscriptions = subscriptions.select { _1.status.to_sym == :active }
|
|
54
|
+
|
|
55
|
+
def active_subscription_codes(email: nil)
|
|
56
|
+
subscriptions =
|
|
57
|
+
if email
|
|
58
|
+
active_subscriptions.select { _1.customer.email.casecmp?(email) }
|
|
59
|
+
else
|
|
60
|
+
active_subscriptions
|
|
61
|
+
end
|
|
62
|
+
subscriptions.map(&:subscription_code)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
api_method def self.fetch_plan(code:)
|
|
67
|
+
use_connection do |connection|
|
|
68
|
+
connection.get("/plan/#{code}")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class UpdatePlanResponse < PaystackGateway::Response; end
|
|
73
|
+
|
|
74
|
+
api_method def self.update_plan(code:, amount:, interval:)
|
|
75
|
+
use_connection do |connection|
|
|
76
|
+
connection.put(
|
|
77
|
+
"/plan/#{code}",
|
|
78
|
+
{
|
|
79
|
+
amount: amount * 100,
|
|
80
|
+
interval:,
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Create and manage transaction refunds.
|
|
7
|
+
# https://paystack.com/docs/api/refund
|
|
8
|
+
#
|
|
9
|
+
# @deprecated Use PaystackGateway::Refund instead.
|
|
10
|
+
module Refunds
|
|
11
|
+
include PaystackGateway::RequestModule
|
|
12
|
+
|
|
13
|
+
# Common helpers for responses from refunds endpoints
|
|
14
|
+
module TransactionRefundResponse
|
|
15
|
+
def refund_success? = transaction_status == :processed
|
|
16
|
+
def refund_failed? = transaction_status == :failed
|
|
17
|
+
def refund_pending? = transaction_status.in?(%i[pending processing])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Response from POST /refund endpoint.
|
|
21
|
+
class CreateResponse < PaystackGateway::Response
|
|
22
|
+
include TransactionResponse
|
|
23
|
+
include TransactionRefundResponse
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
api_method def self.create(transaction_reference_or_id:)
|
|
27
|
+
use_connection do |connection|
|
|
28
|
+
connection.post('/refund', { transaction: transaction_reference_or_id })
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Response from GET /refund endpoint.
|
|
33
|
+
class ListRefundsResponse < PaystackGateway::Response
|
|
34
|
+
def pending_or_successful
|
|
35
|
+
filtered = data.select { _1.status&.to_sym.in?(%i[processed pending processing]) }
|
|
36
|
+
|
|
37
|
+
ListRefundsResponse.new({ **self, data: filtered })
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def with_amount(amount)
|
|
41
|
+
filtered = data.select { _1.amount == amount * 100 }
|
|
42
|
+
|
|
43
|
+
ListRefundsResponse.new({ **self, data: filtered })
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
api_method def self.list_refunds(transaction_id:)
|
|
48
|
+
use_connection do |connection|
|
|
49
|
+
connection.get('/refund', { transaction: transaction_id })
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Response from GET /refund/:id endpoint.
|
|
54
|
+
class FetchRefundResponse < PaystackGateway::Response
|
|
55
|
+
include TransactionResponse
|
|
56
|
+
include TransactionRefundResponse
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
api_method def self.fetch_refund(refund_id:)
|
|
60
|
+
use_connection do |connection|
|
|
61
|
+
connection.get("/refund/#{refund_id}")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Create and manage subaccounts https://paystack.com/docs/api/subaccount/
|
|
7
|
+
#
|
|
8
|
+
# @deprecated Use PaystackGateway::Subaccount instead.
|
|
9
|
+
module Subaccounts
|
|
10
|
+
include PaystackGateway::RequestModule
|
|
11
|
+
|
|
12
|
+
# Response from POST /subaccount
|
|
13
|
+
class CreateSubaccountResponse < PaystackGateway::Response
|
|
14
|
+
delegate :subaccount_code, to: :data
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
api_method def self.create_subaccount(business_name:, settlement_bank:, account_number:, percentage_charge:)
|
|
18
|
+
use_connection do |connection|
|
|
19
|
+
connection.post(
|
|
20
|
+
'/subaccount',
|
|
21
|
+
{ business_name:, settlement_bank:, account_number:, percentage_charge: }.compact,
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Response from PUT /subaccount/:id_or_code
|
|
27
|
+
class UpdateSubaccountResponse < CreateSubaccountResponse; end
|
|
28
|
+
|
|
29
|
+
api_method def self.update_subaccount(
|
|
30
|
+
subaccount_code,
|
|
31
|
+
business_name: nil, settlement_bank: nil, account_number: nil, percentage_charge: nil
|
|
32
|
+
)
|
|
33
|
+
use_connection do |connection|
|
|
34
|
+
connection.put(
|
|
35
|
+
"/subaccount/#{subaccount_code}",
|
|
36
|
+
{ business_name:, settlement_bank:, account_number:, percentage_charge: }.compact,
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Common helpers for responses from transaction endpoints
|
|
7
|
+
module TransactionResponse
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
delegate :id, :amount, :subaccount, :fees_split, to: :data
|
|
12
|
+
|
|
13
|
+
attr_writer :completed_at
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def transaction_success? = transaction_status.in?(%i[success reversed reversal_pending])
|
|
17
|
+
def transaction_abandoned? = transaction_status == :abandoned
|
|
18
|
+
def transaction_failed? = transaction_status == :failed
|
|
19
|
+
def transaction_pending? = transaction_status.in?(%i[pending ongoing])
|
|
20
|
+
|
|
21
|
+
def transaction_status = data.status.to_sym
|
|
22
|
+
def transaction_amount = amount / BigDecimal('100')
|
|
23
|
+
def transaction_completed_at = data[:updatedAt] || @completed_at
|
|
24
|
+
|
|
25
|
+
def subaccount_amount
|
|
26
|
+
return if !subaccount || !fees_split
|
|
27
|
+
|
|
28
|
+
fees_split.subaccount / BigDecimal('100')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def failure_reason
|
|
32
|
+
return if !transaction_failed? && !transaction_abandoned?
|
|
33
|
+
|
|
34
|
+
data.gateway_response || transaction_status || message
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Create and manage payments https://paystack.com/docs/api/#transaction
|
|
7
|
+
#
|
|
8
|
+
# @deprecated Use PaystackGateway::Transaction instead.
|
|
9
|
+
module Transactions
|
|
10
|
+
include PaystackGateway::RequestModule
|
|
11
|
+
|
|
12
|
+
# Response from POST /transaction/initialize endpoint.
|
|
13
|
+
class InitializeTransactionResponse < PaystackGateway::Response
|
|
14
|
+
delegate :authorization_url, to: :data
|
|
15
|
+
|
|
16
|
+
alias payment_url authorization_url
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Raised when an error occurs while calling POST /transaction/initialize
|
|
20
|
+
class InitializeTransactionError < ApiError
|
|
21
|
+
def cancellable? = super || network_error?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Response looks like this.
|
|
25
|
+
# {
|
|
26
|
+
# "status": true,
|
|
27
|
+
# "message": "Authorization URL created",
|
|
28
|
+
# "data": {
|
|
29
|
+
# "authorization_url": "https://checkout.paystack.com/n4ysyedbuseog8c",
|
|
30
|
+
# "access_code": "n4ysyedbuseog8c",
|
|
31
|
+
# "reference": "hlr4bxhypt"
|
|
32
|
+
# }
|
|
33
|
+
# }
|
|
34
|
+
api_method def self.initialize_transaction(**transaction_data)
|
|
35
|
+
raise ApiError.new(:invalid_amount, cancellable: true) if transaction_data[:amount].blank?
|
|
36
|
+
raise ApiError.new(:invalid_reference, cancellable: true) if transaction_data[:reference].blank?
|
|
37
|
+
raise ApiError.new(:invalid_email, cancellable: true) if transaction_data[:email].blank?
|
|
38
|
+
|
|
39
|
+
transaction_data[:amount] = (transaction_data[:amount] * 100).to_i
|
|
40
|
+
|
|
41
|
+
use_connection do |connection|
|
|
42
|
+
connection.post('/transaction/initialize', transaction_data.compact)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Response from GET /transaction/verify/:reference endpoint.
|
|
47
|
+
class VerifyTransactionResponse < PaystackGateway::Response
|
|
48
|
+
include TransactionResponse
|
|
49
|
+
|
|
50
|
+
delegate :paid_at, to: :data
|
|
51
|
+
|
|
52
|
+
def transaction_completed_at
|
|
53
|
+
paid_at || super
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Raised when an error occurs while calling /transactions/verify/:reference
|
|
58
|
+
class VerifyTransactionError < ApiError
|
|
59
|
+
def transaction_not_found?
|
|
60
|
+
parsed_response_body[:status] == false &&
|
|
61
|
+
parsed_response_body[:message]&.match?(/transaction reference not found/i)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
api_method def self.verify_transaction(reference:)
|
|
66
|
+
raise VerifyTransactionError, :invalid_reference if reference.blank?
|
|
67
|
+
|
|
68
|
+
use_connection do |connection|
|
|
69
|
+
connection.get("/transaction/verify/#{reference}")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Response from POST /transaction/charge_authorization endpoint.
|
|
74
|
+
class ChargeAuthorizationResponse < PaystackGateway::Response
|
|
75
|
+
include TransactionResponse
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
api_method def self.charge_authorization(**transaction_data)
|
|
79
|
+
raise ApiError.new(:invalid_amount, cancellable: true) if transaction_data[:amount].blank?
|
|
80
|
+
raise ApiError.new(:invalid_reference, cancellable: true) if transaction_data[:reference].blank?
|
|
81
|
+
raise ApiError.new(:invalid_authorization_code, cancellable: true) if transaction_data[:authorization_code].blank?
|
|
82
|
+
raise ApiError.new(:invalid_email, cancellable: true) if transaction_data[:email].blank?
|
|
83
|
+
|
|
84
|
+
transaction_data[:amount] = (transaction_data[:amount] * 100).to_i
|
|
85
|
+
|
|
86
|
+
use_connection do |connection|
|
|
87
|
+
connection.post('/transaction/charge_authorization', transaction_data.compact)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Create and manage beneficiaries that you send money to
|
|
7
|
+
# https://paystack.com/docs/api/#transfer-recipient
|
|
8
|
+
#
|
|
9
|
+
# @deprecated Use PaystackGateway::TransferRecipient instead.
|
|
10
|
+
module TransferRecipients
|
|
11
|
+
include PaystackGateway::RequestModule
|
|
12
|
+
|
|
13
|
+
# Response from POST /transferrecipient endpoint.
|
|
14
|
+
class CreateTransferRecipientResponse < PaystackGateway::Response
|
|
15
|
+
delegate :id, :recipient_code, to: :data
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
api_method def self.create_transfer_recipient(name:, account_number:, bank_code:)
|
|
19
|
+
use_connection do |connection|
|
|
20
|
+
connection.post(
|
|
21
|
+
'/transferrecipient',
|
|
22
|
+
{ type: :nuban, name: name, account_number: account_number, bank_code: bank_code, currency: :NGN },
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Automate sending money https://paystack.com/docs/api/#transfer
|
|
7
|
+
#
|
|
8
|
+
# @deprecated Use PaystackGateway::Transfer instead.
|
|
9
|
+
module Transfers
|
|
10
|
+
include PaystackGateway::RequestModule
|
|
11
|
+
|
|
12
|
+
# Response from POST /transfer endpoint.
|
|
13
|
+
class InitiateTransferResponse < PaystackGateway::Response
|
|
14
|
+
include TransactionResponse
|
|
15
|
+
|
|
16
|
+
delegate :transfer_code, :transferred_at, to: :data
|
|
17
|
+
|
|
18
|
+
def transaction_completed_at
|
|
19
|
+
transferred_at || super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Raised when an error occurs while calling POST /transfer
|
|
24
|
+
class InitiateTransferError < ApiError
|
|
25
|
+
def transaction_failed?
|
|
26
|
+
parsed_response_body[:status] == false && http_code == 400
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def failure_reason
|
|
30
|
+
parsed_response_body[:message]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
api_method def self.initiate_transfer(amount:, recipient_code:, reference:, reason: nil)
|
|
35
|
+
response = use_connection do |connection|
|
|
36
|
+
connection.post(
|
|
37
|
+
'/transfer',
|
|
38
|
+
{
|
|
39
|
+
source: :balance, amount: (amount * 100).to_i, recipient: recipient_code,
|
|
40
|
+
reference: reference, reason: reason,
|
|
41
|
+
}.compact,
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
response.tap { |r| r.completed_at = Time.current }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Response from GET /transfer/verify/:reference endpoint.
|
|
48
|
+
class VerifyTransferResponse < InitiateTransferResponse; end
|
|
49
|
+
|
|
50
|
+
# Raised when an error occurs while calling GET /transfer/verify/:reference
|
|
51
|
+
class VerifyTransferError < ApiError
|
|
52
|
+
def transaction_not_found?
|
|
53
|
+
parsed_response_body[:status] == false && parsed_response_body[:message]&.match?(/transfer not found/i)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
api_method def self.verify_transfer(reference:)
|
|
58
|
+
use_connection do |connection|
|
|
59
|
+
connection.get("/transfer/verify/#{reference}")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Legacy implementation retained for backwards compatibility.
|
|
4
|
+
|
|
5
|
+
module PaystackGateway
|
|
6
|
+
# Perform KYC processes https://paystack.com/docs/api/#verification
|
|
7
|
+
#
|
|
8
|
+
# @deprecated Use PaystackGateway::Bank instead.
|
|
9
|
+
module Verification
|
|
10
|
+
include PaystackGateway::RequestModule
|
|
11
|
+
|
|
12
|
+
# Response from GET /bank/resolve endpoint.
|
|
13
|
+
class ResolveAccountNumberResponse < PaystackGateway::Response
|
|
14
|
+
delegate :account_name, :bank_id, to: :data
|
|
15
|
+
|
|
16
|
+
def account_valid?
|
|
17
|
+
status && account_name.present?
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Raised when an error occurs while calling /bank/resolve
|
|
22
|
+
class ResolveAccountNumberError < ApiError
|
|
23
|
+
def invalid_account?
|
|
24
|
+
http_code == 422 && parsed_response_body[:status] == false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
api_method def self.resolve_account_number(account_number:, bank_code:)
|
|
29
|
+
use_connection(cache_options: {}) do |connection|
|
|
30
|
+
connection.get(
|
|
31
|
+
'/bank/resolve',
|
|
32
|
+
{ account_number: account_number, bank_code: bank_code },
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,26 +1,88 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PaystackGateway
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# https://paystack.com/docs/api/miscellaneous
|
|
5
|
+
#
|
|
6
|
+
# Miscellaneous
|
|
7
|
+
# A collection of endpoints that provides utility functions
|
|
6
8
|
module Miscellaneous
|
|
7
9
|
include PaystackGateway::RequestModule
|
|
8
10
|
|
|
9
|
-
#
|
|
10
|
-
class
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
# Successful response from calling #resolve_card_bin.
|
|
12
|
+
class ResolveCardBinResponse < PaystackGateway::Response
|
|
13
|
+
delegate :bin,
|
|
14
|
+
:brand,
|
|
15
|
+
:sub_brand,
|
|
16
|
+
:country_code,
|
|
17
|
+
:country_name,
|
|
18
|
+
:card_type,
|
|
19
|
+
:bank,
|
|
20
|
+
:currency,
|
|
21
|
+
:linked_bank_id, to: :data
|
|
22
|
+
end
|
|
13
23
|
|
|
14
|
-
|
|
24
|
+
# Error response from #resolve_card_bin.
|
|
25
|
+
class ResolveCardBinError < ApiError; end
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
# https://paystack.com/docs/api/miscellaneous/#resolve_card_bin
|
|
28
|
+
# Resolve Card BIN: GET /decision/bin/{bin}
|
|
29
|
+
# Get the details of a card BIN
|
|
30
|
+
#
|
|
31
|
+
# @param bin [Integer] (required)
|
|
32
|
+
# The card bank identification number
|
|
33
|
+
#
|
|
34
|
+
# @return [ResolveCardBinResponse] successful response
|
|
35
|
+
# @raise [ResolveCardBinError] if the request fails
|
|
36
|
+
api_method def self.resolve_card_bin(bin:)
|
|
37
|
+
use_connection do |connection|
|
|
38
|
+
connection.get(
|
|
39
|
+
"/decision/bin/#{bin}",
|
|
40
|
+
)
|
|
41
|
+
end
|
|
18
42
|
end
|
|
19
43
|
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
44
|
+
# Successful response from calling #list_countries.
|
|
45
|
+
class ListCountriesResponse < PaystackGateway::Response; end
|
|
46
|
+
|
|
47
|
+
# Error response from #list_countries.
|
|
48
|
+
class ListCountriesError < ApiError; end
|
|
49
|
+
|
|
50
|
+
# https://paystack.com/docs/api/miscellaneous/#list_countries
|
|
51
|
+
# List Countries: GET /country
|
|
52
|
+
# List all supported countries on Paystack
|
|
53
|
+
#
|
|
54
|
+
#
|
|
55
|
+
# @return [ListCountriesResponse] successful response
|
|
56
|
+
# @raise [ListCountriesError] if the request fails
|
|
57
|
+
api_method def self.list_countries
|
|
58
|
+
use_connection do |connection|
|
|
59
|
+
connection.get(
|
|
60
|
+
'/country',
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Successful response from calling #avs.
|
|
66
|
+
class AvsResponse < PaystackGateway::Response; end
|
|
67
|
+
|
|
68
|
+
# Error response from #avs.
|
|
69
|
+
class AvsError < ApiError; end
|
|
70
|
+
|
|
71
|
+
# https://paystack.com/docs/api/miscellaneous/#avs
|
|
72
|
+
# List States (AVS): GET /address_verification/states
|
|
73
|
+
# Get a list of states for a country for address verification
|
|
74
|
+
#
|
|
75
|
+
# @param country [String]
|
|
76
|
+
# The country code of the states to list. It is gotten after the charge request
|
|
77
|
+
#
|
|
78
|
+
# @return [AvsResponse] successful response
|
|
79
|
+
# @raise [AvsError] if the request fails
|
|
80
|
+
api_method def self.avs(country: nil)
|
|
81
|
+
use_connection do |connection|
|
|
82
|
+
connection.get(
|
|
83
|
+
'/address_verification/states',
|
|
84
|
+
{ country: }.compact,
|
|
85
|
+
)
|
|
24
86
|
end
|
|
25
87
|
end
|
|
26
88
|
end
|