airwallex 0.3.0 → 0.7.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/CHANGELOG.md +90 -8
- data/README.md +2 -2
- data/lib/airwallex/api_operations/delete.rb +3 -3
- data/lib/airwallex/api_operations/update.rb +4 -4
- data/lib/airwallex/client.rb +15 -11
- data/lib/airwallex/configuration.rb +6 -2
- data/lib/airwallex/middleware/auth_refresh.rb +21 -7
- data/lib/airwallex/resources/account_amendment.rb +29 -0
- data/lib/airwallex/resources/beneficiary.rb +96 -0
- data/lib/airwallex/resources/billing_customer.rb +48 -0
- data/lib/airwallex/resources/billing_price.rb +30 -0
- data/lib/airwallex/resources/billing_product.rb +23 -0
- data/lib/airwallex/resources/billing_subscription.rb +93 -0
- data/lib/airwallex/resources/billing_subscription_item.rb +7 -0
- data/lib/airwallex/resources/charge.rb +19 -0
- data/lib/airwallex/resources/connected_account.rb +136 -0
- data/lib/airwallex/resources/conversion.rb +1 -1
- data/lib/airwallex/resources/customer.rb +5 -2
- data/lib/airwallex/resources/dispute.rb +48 -28
- data/lib/airwallex/resources/funds_split.rb +37 -0
- data/lib/airwallex/resources/global_account.rb +137 -0
- data/lib/airwallex/resources/global_account_alias.rb +62 -0
- data/lib/airwallex/resources/global_account_mandate.rb +26 -0
- data/lib/airwallex/resources/global_account_transaction.rb +7 -0
- data/lib/airwallex/resources/payment_consent.rb +68 -0
- data/lib/airwallex/resources/payment_method.rb +13 -5
- data/lib/airwallex/resources/payment_source.rb +52 -0
- data/lib/airwallex/resources/quote.rb +5 -3
- data/lib/airwallex/resources/rate.rb +2 -7
- data/lib/airwallex/version.rb +1 -1
- data/lib/airwallex/webhook.rb +6 -2
- data/lib/airwallex.rb +15 -0
- metadata +18 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Charge (Scale product) — funds attributed to a Connected
|
|
5
|
+
# Account, tracked before payout.
|
|
6
|
+
#
|
|
7
|
+
# @example Retrieve a charge
|
|
8
|
+
# charge = Airwallex::Charge.retrieve("chg_123")
|
|
9
|
+
class Charge < APIResource
|
|
10
|
+
extend APIOperations::Create
|
|
11
|
+
extend APIOperations::Retrieve
|
|
12
|
+
extend APIOperations::List
|
|
13
|
+
|
|
14
|
+
# @return [String] API resource path for charges
|
|
15
|
+
def self.resource_path
|
|
16
|
+
"/api/v1/charges"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Connected Account (Scale product) — a sub-account container
|
|
5
|
+
# used to onboard and track funds for a platform's connected entities.
|
|
6
|
+
#
|
|
7
|
+
# @example Create a connected account (business entity)
|
|
8
|
+
# # account_details is a required top-level wrapper. customer_agreements,
|
|
9
|
+
# # nickname, and primary_contact are top-level siblings, not nested
|
|
10
|
+
# # inside it. legal_entity_type is "BUSINESS" or "INDIVIDUAL", not
|
|
11
|
+
# # "COMPANY" (business_structure below is the field that uses "COMPANY").
|
|
12
|
+
# # This is a minimal subset of Airwallex's real KYB schema — the full
|
|
13
|
+
# # schema also supports trustee entities and much more optional detail
|
|
14
|
+
# # (identity documents, store details, business person details, etc).
|
|
15
|
+
# account = Airwallex::ConnectedAccount.create(
|
|
16
|
+
# nickname: "Acme Advertiser",
|
|
17
|
+
# primary_contact: { email: "contact@acme.example" },
|
|
18
|
+
# customer_agreements: {
|
|
19
|
+
# agreed_to_terms_and_conditions: true,
|
|
20
|
+
# agreed_to_data_usage: true,
|
|
21
|
+
# terms_and_conditions: { service_agreement_type: "FULL" }
|
|
22
|
+
# },
|
|
23
|
+
# account_details: {
|
|
24
|
+
# legal_entity_type: "BUSINESS",
|
|
25
|
+
# business_details: {
|
|
26
|
+
# business_name: "Acme Corp",
|
|
27
|
+
# business_structure: "COMPANY",
|
|
28
|
+
# business_address: {
|
|
29
|
+
# address_line1: "200 Collins Street",
|
|
30
|
+
# country_code: "AU",
|
|
31
|
+
# postcode: "3000",
|
|
32
|
+
# state: "VIC",
|
|
33
|
+
# suburb: "Melbourne"
|
|
34
|
+
# }
|
|
35
|
+
# }
|
|
36
|
+
# }
|
|
37
|
+
# )
|
|
38
|
+
#
|
|
39
|
+
# @example Trigger KYC/KYB verification
|
|
40
|
+
# account.submit
|
|
41
|
+
#
|
|
42
|
+
# @example Look up whichever account you're currently authenticated as
|
|
43
|
+
# Airwallex::ConnectedAccount.current
|
|
44
|
+
#
|
|
45
|
+
# @example Get the legal_entity_id needed by BillingCustomer/BillingSubscription
|
|
46
|
+
# account = Airwallex::ConnectedAccount.retrieve("acct_123")
|
|
47
|
+
# account.legal_entity_id
|
|
48
|
+
class ConnectedAccount < APIResource
|
|
49
|
+
extend APIOperations::Create
|
|
50
|
+
extend APIOperations::Retrieve
|
|
51
|
+
extend APIOperations::List
|
|
52
|
+
include APIOperations::Update
|
|
53
|
+
|
|
54
|
+
# Not nested under /accounts/{id} — these endpoints are implicitly
|
|
55
|
+
# scoped to whichever account the request is authenticated as (the
|
|
56
|
+
# platform's own account, or a connected account via x-on-behalf-of).
|
|
57
|
+
CURRENT_ACCOUNT_PATH = "/api/v1/account"
|
|
58
|
+
WALLET_INFO_PATH = "/api/v1/account/wallet_info"
|
|
59
|
+
|
|
60
|
+
# @return [String] API resource path for connected accounts
|
|
61
|
+
def self.resource_path
|
|
62
|
+
"/api/v1/accounts"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Retrieve whichever account the current request is authenticated as
|
|
66
|
+
#
|
|
67
|
+
# @param params [Hash] additional params
|
|
68
|
+
# @return [ConnectedAccount]
|
|
69
|
+
def self.current(params = {})
|
|
70
|
+
response = Airwallex.client.get(CURRENT_ACCOUNT_PATH, params)
|
|
71
|
+
new(response)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Retrieve wallet info for whichever account the current request is
|
|
75
|
+
# authenticated as
|
|
76
|
+
#
|
|
77
|
+
# @param params [Hash] additional params
|
|
78
|
+
# @return [Hash] raw wallet info response
|
|
79
|
+
def self.wallet_info(params = {})
|
|
80
|
+
Airwallex.client.get(WALLET_INFO_PATH, params)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The legal_entity_id needed by BillingCustomer.create's
|
|
84
|
+
# default_legal_entity_id and BillingSubscription.create's
|
|
85
|
+
# legal_entity_id — nested inside account_details on this account's own
|
|
86
|
+
# response, not a separate resource.
|
|
87
|
+
#
|
|
88
|
+
# @return [String, nil]
|
|
89
|
+
def legal_entity_id
|
|
90
|
+
attributes.dig(:account_details, :legal_entity_id)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Submit the account for KYC/KYB verification
|
|
94
|
+
#
|
|
95
|
+
# @param params [Hash] additional submission params
|
|
96
|
+
# @return [ConnectedAccount] self
|
|
97
|
+
def submit(params = {})
|
|
98
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/submit", params)
|
|
99
|
+
refresh_from(response)
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Agree to Airwallex's terms and conditions on behalf of this account
|
|
104
|
+
#
|
|
105
|
+
# @param params [Hash] e.g. agreed_at:, service_agreement_type:,
|
|
106
|
+
# device_data: { ip_address:, user_agent: }
|
|
107
|
+
# @return [ConnectedAccount] self
|
|
108
|
+
def agree_to_terms_and_conditions(params = {})
|
|
109
|
+
response = Airwallex.client.post(
|
|
110
|
+
"#{self.class.resource_path}/#{id}/terms_and_conditions/agree", params
|
|
111
|
+
)
|
|
112
|
+
refresh_from(response)
|
|
113
|
+
self
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Suspend this account
|
|
117
|
+
#
|
|
118
|
+
# @param params [Hash] e.g. message: "reason for suspension"
|
|
119
|
+
# @return [ConnectedAccount] self
|
|
120
|
+
def suspend(params = {})
|
|
121
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/suspend", params)
|
|
122
|
+
refresh_from(response)
|
|
123
|
+
self
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Reactivate a suspended account
|
|
127
|
+
#
|
|
128
|
+
# @param params [Hash] e.g. message: "reason for reactivation"
|
|
129
|
+
# @return [ConnectedAccount] self
|
|
130
|
+
def reactivate(params = {})
|
|
131
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/reactivate", params)
|
|
132
|
+
refresh_from(response)
|
|
133
|
+
self
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -7,11 +7,15 @@ module Airwallex
|
|
|
7
7
|
# for individual users or accounts.
|
|
8
8
|
#
|
|
9
9
|
# @example Create a customer
|
|
10
|
+
# # merchant_customer_id is required — your own reference id for this
|
|
11
|
+
# # customer. request_id is optional (the gem's Idempotency middleware
|
|
12
|
+
# # injects one automatically).
|
|
10
13
|
# customer = Airwallex::Customer.create(
|
|
14
|
+
# merchant_customer_id: SecureRandom.uuid,
|
|
11
15
|
# email: "john@example.com",
|
|
12
16
|
# first_name: "John",
|
|
13
17
|
# last_name: "Doe",
|
|
14
|
-
#
|
|
18
|
+
# phone_number: "+1 1234567890"
|
|
15
19
|
# )
|
|
16
20
|
#
|
|
17
21
|
# @example List payment methods for a customer
|
|
@@ -20,7 +24,6 @@ module Airwallex
|
|
|
20
24
|
extend APIOperations::Create
|
|
21
25
|
extend APIOperations::Retrieve
|
|
22
26
|
extend APIOperations::List
|
|
23
|
-
extend APIOperations::Update
|
|
24
27
|
include APIOperations::Update
|
|
25
28
|
extend APIOperations::Delete
|
|
26
29
|
|
|
@@ -4,7 +4,8 @@ module Airwallex
|
|
|
4
4
|
# Dispute resource for handling chargebacks and payment disputes
|
|
5
5
|
#
|
|
6
6
|
# Disputes represent chargebacks or payment disputes initiated by cardholders.
|
|
7
|
-
# Merchants can view disputes,
|
|
7
|
+
# Merchants can view disputes, challenge them with evidence, or accept them.
|
|
8
|
+
# There is no create — disputes originate from card networks/issuing banks.
|
|
8
9
|
#
|
|
9
10
|
# @example List open disputes
|
|
10
11
|
# disputes = Airwallex::Dispute.list(status: 'OPEN')
|
|
@@ -12,57 +13,76 @@ module Airwallex
|
|
|
12
13
|
# @example Retrieve a dispute
|
|
13
14
|
# dispute = Airwallex::Dispute.retrieve('dis_123')
|
|
14
15
|
#
|
|
15
|
-
# @example Submit evidence
|
|
16
|
-
# dispute = Airwallex::Dispute.retrieve('dis_123')
|
|
17
|
-
# dispute.submit_evidence(
|
|
18
|
-
# customer_communication: "Email showing delivery confirmation",
|
|
19
|
-
# shipping_tracking_number: "1Z999AA10123456784"
|
|
20
|
-
# )
|
|
21
|
-
#
|
|
22
16
|
# @example Accept a dispute
|
|
23
|
-
# dispute = Airwallex::Dispute.retrieve('dis_123')
|
|
24
17
|
# dispute.accept
|
|
25
18
|
#
|
|
19
|
+
# @example Challenge a dispute
|
|
20
|
+
# dispute.challenge(...)
|
|
26
21
|
class Dispute < APIResource
|
|
27
22
|
extend APIOperations::Retrieve
|
|
28
23
|
extend APIOperations::List
|
|
24
|
+
include APIOperations::Update
|
|
29
25
|
|
|
30
26
|
def self.resource_path
|
|
31
|
-
"/api/v1/
|
|
27
|
+
"/api/v1/pa/payment_disputes"
|
|
32
28
|
end
|
|
33
29
|
|
|
34
30
|
# Accept a dispute without challenging it
|
|
35
31
|
#
|
|
36
|
-
# @return [Airwallex::Dispute]
|
|
32
|
+
# @return [Airwallex::Dispute] self
|
|
37
33
|
def accept
|
|
38
|
-
response = Airwallex.client.post("#{resource_path}/#{id}/accept", {})
|
|
34
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/accept", {})
|
|
39
35
|
refresh_from(response)
|
|
40
36
|
self
|
|
41
37
|
end
|
|
42
38
|
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# @param evidence [Hash] Evidence details
|
|
46
|
-
# @option evidence [String] :customer_communication Email or chat logs
|
|
47
|
-
# @option evidence [String] :shipping_tracking_number Tracking number
|
|
48
|
-
# @option evidence [String] :shipping_documentation Proof of shipping
|
|
49
|
-
# @option evidence [String] :customer_signature Signed receipt
|
|
50
|
-
# @option evidence [String] :receipt Proof of purchase
|
|
51
|
-
# @option evidence [String] :refund_policy Refund policy document
|
|
52
|
-
# @option evidence [String] :cancellation_policy Cancellation policy
|
|
53
|
-
# @option evidence [String] :additional_information Other relevant info
|
|
39
|
+
# Challenge a dispute with evidence
|
|
54
40
|
#
|
|
55
|
-
# @
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
# @param params [Hash] challenge params — exact shape unconfirmed, pass
|
|
42
|
+
# through whatever Airwallex's challenge schema requires
|
|
43
|
+
# @return [Airwallex::Dispute] self
|
|
44
|
+
def challenge(params = {})
|
|
45
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/challenge", params)
|
|
58
46
|
refresh_from(response)
|
|
59
47
|
self
|
|
60
48
|
end
|
|
61
49
|
|
|
50
|
+
# List payment intents related to this dispute
|
|
51
|
+
#
|
|
52
|
+
# @param params [Hash] additional query params (e.g. pagination)
|
|
53
|
+
# @return [ListObject<PaymentIntent>]
|
|
54
|
+
def related_payment_intents(params = {})
|
|
55
|
+
response = Airwallex.client.get(
|
|
56
|
+
"#{self.class.resource_path}/#{id}/related_payment_intents", params
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
ListObject.new(
|
|
60
|
+
data: extract_items(response),
|
|
61
|
+
has_more: extract_has_more(response),
|
|
62
|
+
next_cursor: extract_next_cursor(response),
|
|
63
|
+
resource_class: PaymentIntent,
|
|
64
|
+
params: params
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
62
68
|
private
|
|
63
69
|
|
|
64
|
-
def
|
|
65
|
-
|
|
70
|
+
def extract_items(response)
|
|
71
|
+
return response if response.is_a?(Array)
|
|
72
|
+
|
|
73
|
+
response[:items] || response["items"] || response[:data] || response["data"] || []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def extract_has_more(response)
|
|
77
|
+
return false unless response.is_a?(Hash)
|
|
78
|
+
|
|
79
|
+
response[:has_more] || response["has_more"] || false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def extract_next_cursor(response)
|
|
83
|
+
return nil unless response.is_a?(Hash)
|
|
84
|
+
|
|
85
|
+
response[:next_cursor] || response["next_cursor"]
|
|
66
86
|
end
|
|
67
87
|
end
|
|
68
88
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Funds Split — how a single PaymentIntent/inbound transaction
|
|
5
|
+
# is divided between the platform and a Connected Account at collection
|
|
6
|
+
# time (the Scale-product mechanism for splitting deposits).
|
|
7
|
+
#
|
|
8
|
+
# @example Create a split
|
|
9
|
+
# split = Airwallex::FundsSplit.create(
|
|
10
|
+
# payment_intent_id: intent.id,
|
|
11
|
+
# splits: [{ account_id: connected_account.id, amount: 50.00 }]
|
|
12
|
+
# )
|
|
13
|
+
#
|
|
14
|
+
# @example Release the split funds
|
|
15
|
+
# split.release
|
|
16
|
+
class FundsSplit < APIResource
|
|
17
|
+
extend APIOperations::Create
|
|
18
|
+
extend APIOperations::Retrieve
|
|
19
|
+
extend APIOperations::List
|
|
20
|
+
|
|
21
|
+
# @return [String] API resource path for funds splits
|
|
22
|
+
def self.resource_path
|
|
23
|
+
"/api/v1/pa/funds_splits"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Release this funds split (make the split amount available to the
|
|
27
|
+
# connected account)
|
|
28
|
+
#
|
|
29
|
+
# @param params [Hash] additional params
|
|
30
|
+
# @return [FundsSplit] self
|
|
31
|
+
def release(params = {})
|
|
32
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/release", params)
|
|
33
|
+
refresh_from(response)
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Global Account (Virtual Account Number) used to collect
|
|
5
|
+
# local bank transfers into a specific currency and jurisdiction.
|
|
6
|
+
#
|
|
7
|
+
# @example Provision a VAN
|
|
8
|
+
# account = Airwallex::GlobalAccount.create(
|
|
9
|
+
# country_code: "AU",
|
|
10
|
+
# nick_name: "booking_12345",
|
|
11
|
+
# required_features: [{ transfer_method: "LOCAL" }]
|
|
12
|
+
# )
|
|
13
|
+
#
|
|
14
|
+
# @example Reconcile inbound deposits
|
|
15
|
+
# account.transactions.each { |txn| puts "#{txn.amount} from #{txn.remitter}" }
|
|
16
|
+
#
|
|
17
|
+
# @example Add and verify an alias (e.g. a phone number or email VAN)
|
|
18
|
+
# alias_record = account.create_alias(type: "PAYID_PHONE", value: "+61400000000")
|
|
19
|
+
# alias_record.submit_verification_code(code: "123456")
|
|
20
|
+
class GlobalAccount < APIResource
|
|
21
|
+
extend APIOperations::Create
|
|
22
|
+
extend APIOperations::Retrieve
|
|
23
|
+
extend APIOperations::List
|
|
24
|
+
include APIOperations::Update
|
|
25
|
+
|
|
26
|
+
# @return [String] API resource path for global accounts
|
|
27
|
+
def self.resource_path
|
|
28
|
+
"/api/v1/global_accounts"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Close this global account
|
|
32
|
+
#
|
|
33
|
+
# @param params [Hash] additional closure params
|
|
34
|
+
# @return [GlobalAccount] self
|
|
35
|
+
def close(params = {})
|
|
36
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/close", params)
|
|
37
|
+
refresh_from(response)
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Generate a bank-ownership statement letter for this account — e.g. for
|
|
42
|
+
# marketplace seller verification (known value: "AMAZON"; other
|
|
43
|
+
# marketplace-specific values likely exist).
|
|
44
|
+
#
|
|
45
|
+
# @param params [Hash] account_statement_type: (e.g. "AMAZON"),
|
|
46
|
+
# registration_info: { agreement:, registered_name:, registered_email:,
|
|
47
|
+
# registered_address: { address:, city:, state:, postcode:, country: } }
|
|
48
|
+
# @return [Hash] raw response (the generated letter/document reference)
|
|
49
|
+
def generate_statement_letter(params = {})
|
|
50
|
+
Airwallex.client.post("#{self.class.resource_path}/#{id}/generate_statement_letter", params)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# List inbound transactions (deposits) received into this account
|
|
54
|
+
#
|
|
55
|
+
# @param params [Hash] additional query params (e.g. pagination)
|
|
56
|
+
# @return [ListObject<GlobalAccountTransaction>] list of transactions
|
|
57
|
+
def transactions(params = {})
|
|
58
|
+
response = Airwallex.client.get("#{self.class.resource_path}/#{id}/transactions", params)
|
|
59
|
+
build_list(response, GlobalAccountTransaction, params)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Create an alias (e.g. PayID, email, or phone-linked VAN) on this account
|
|
63
|
+
#
|
|
64
|
+
# @param params [Hash] alias attributes
|
|
65
|
+
# @return [GlobalAccountAlias]
|
|
66
|
+
def create_alias(params = {})
|
|
67
|
+
response = Airwallex.client.post("#{GlobalAccountAlias.resource_path(id)}/create", params)
|
|
68
|
+
GlobalAccountAlias.new(response)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Retrieve a single alias on this account
|
|
72
|
+
#
|
|
73
|
+
# @param alias_id [String]
|
|
74
|
+
# @return [GlobalAccountAlias]
|
|
75
|
+
def alias(alias_id)
|
|
76
|
+
response = Airwallex.client.get("#{GlobalAccountAlias.resource_path(id)}/#{alias_id}")
|
|
77
|
+
GlobalAccountAlias.new(response)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# List aliases on this account
|
|
81
|
+
#
|
|
82
|
+
# @param params [Hash] additional query params (e.g. pagination)
|
|
83
|
+
# @return [ListObject<GlobalAccountAlias>] list of aliases
|
|
84
|
+
def aliases(params = {})
|
|
85
|
+
response = Airwallex.client.get(GlobalAccountAlias.resource_path(id), params)
|
|
86
|
+
build_list(response, GlobalAccountAlias, params)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Retrieve a single direct debit mandate on this account
|
|
90
|
+
#
|
|
91
|
+
# @param mandate_id [String]
|
|
92
|
+
# @return [GlobalAccountMandate]
|
|
93
|
+
def mandate(mandate_id)
|
|
94
|
+
response = Airwallex.client.get("#{GlobalAccountMandate.resource_path(id)}/#{mandate_id}")
|
|
95
|
+
GlobalAccountMandate.new(response)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# List direct debit mandates on this account
|
|
99
|
+
#
|
|
100
|
+
# @param params [Hash] additional query params (e.g. pagination)
|
|
101
|
+
# @return [ListObject<GlobalAccountMandate>] list of mandates
|
|
102
|
+
def mandates(params = {})
|
|
103
|
+
response = Airwallex.client.get(GlobalAccountMandate.resource_path(id), params)
|
|
104
|
+
build_list(response, GlobalAccountMandate, params)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def build_list(response, resource_class, params)
|
|
110
|
+
ListObject.new(
|
|
111
|
+
data: extract_items(response),
|
|
112
|
+
has_more: extract_has_more(response),
|
|
113
|
+
next_cursor: extract_next_cursor(response),
|
|
114
|
+
resource_class: resource_class,
|
|
115
|
+
params: params
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def extract_items(response)
|
|
120
|
+
return response if response.is_a?(Array)
|
|
121
|
+
|
|
122
|
+
response[:items] || response["items"] || response[:data] || response["data"] || []
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def extract_has_more(response)
|
|
126
|
+
return false unless response.is_a?(Hash)
|
|
127
|
+
|
|
128
|
+
response[:has_more] || response["has_more"] || false
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def extract_next_cursor(response)
|
|
132
|
+
return nil unless response.is_a?(Hash)
|
|
133
|
+
|
|
134
|
+
response[:next_cursor] || response["next_cursor"]
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents an alias (e.g. PayID, email, or phone-linked VAN) attached to
|
|
5
|
+
# a GlobalAccount. Always accessed through its parent GlobalAccount, since
|
|
6
|
+
# its API path is scoped by global_account_id.
|
|
7
|
+
class GlobalAccountAlias < APIResource
|
|
8
|
+
# @param global_account_id [String] the parent GlobalAccount's id
|
|
9
|
+
# @return [String] API resource path for this account's aliases
|
|
10
|
+
def self.resource_path(global_account_id)
|
|
11
|
+
"/api/v1/global_accounts/#{global_account_id}/aliases"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Begin porting this alias in from another provider
|
|
15
|
+
#
|
|
16
|
+
# @param params [Hash] additional porting params
|
|
17
|
+
# @return [GlobalAccountAlias] self
|
|
18
|
+
def initiate_port(params = {})
|
|
19
|
+
response = Airwallex.client.post(
|
|
20
|
+
"#{self.class.resource_path(global_account_id)}/#{id}/initiate_port", params
|
|
21
|
+
)
|
|
22
|
+
refresh_from(response)
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Submit the verification code sent to confirm this alias
|
|
27
|
+
#
|
|
28
|
+
# @param params [Hash] e.g. code:
|
|
29
|
+
# @return [GlobalAccountAlias] self
|
|
30
|
+
def submit_verification_code(params = {})
|
|
31
|
+
response = Airwallex.client.post(
|
|
32
|
+
"#{self.class.resource_path(global_account_id)}/#{id}/submit_verification_code", params
|
|
33
|
+
)
|
|
34
|
+
refresh_from(response)
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Request a new verification code for this alias
|
|
39
|
+
#
|
|
40
|
+
# @param params [Hash] additional params
|
|
41
|
+
# @return [GlobalAccountAlias] self
|
|
42
|
+
def request_new_verification_code(params = {})
|
|
43
|
+
response = Airwallex.client.post(
|
|
44
|
+
"#{self.class.resource_path(global_account_id)}/#{id}/request_new_verification_code", params
|
|
45
|
+
)
|
|
46
|
+
refresh_from(response)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Cancel this alias
|
|
51
|
+
#
|
|
52
|
+
# @param params [Hash] additional params
|
|
53
|
+
# @return [GlobalAccountAlias] self
|
|
54
|
+
def cancel(params = {})
|
|
55
|
+
response = Airwallex.client.post(
|
|
56
|
+
"#{self.class.resource_path(global_account_id)}/#{id}/cancel", params
|
|
57
|
+
)
|
|
58
|
+
refresh_from(response)
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a direct debit mandate on a GlobalAccount. Always accessed
|
|
5
|
+
# through its parent GlobalAccount, since its API path is scoped by
|
|
6
|
+
# global_account_id.
|
|
7
|
+
class GlobalAccountMandate < APIResource
|
|
8
|
+
# @param global_account_id [String] the parent GlobalAccount's id
|
|
9
|
+
# @return [String] API resource path for this account's mandates
|
|
10
|
+
def self.resource_path(global_account_id)
|
|
11
|
+
"/api/v1/global_accounts/#{global_account_id}/mandates"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Cancel this mandate
|
|
15
|
+
#
|
|
16
|
+
# @param params [Hash] additional params
|
|
17
|
+
# @return [GlobalAccountMandate] self
|
|
18
|
+
def cancel(params = {})
|
|
19
|
+
response = Airwallex.client.post(
|
|
20
|
+
"#{self.class.resource_path(global_account_id)}/#{id}/cancel", params
|
|
21
|
+
)
|
|
22
|
+
refresh_from(response)
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Payment Consent — a saved card/payment method authorized for
|
|
5
|
+
# future off-session charges without re-prompting the customer.
|
|
6
|
+
#
|
|
7
|
+
# Does NOT attach directly to a BillingSubscription — a subscription's
|
|
8
|
+
# payment_source_id references a PaymentSource (see Airwallex::PaymentSource),
|
|
9
|
+
# a separate Billing object created FROM a verified, merchant-initiated
|
|
10
|
+
# PaymentConsent. See PaymentSource's docstring for the full chain.
|
|
11
|
+
#
|
|
12
|
+
# @example Create and verify a consent for merchant-initiated (unscheduled) charges
|
|
13
|
+
# # next_triggered_by/merchant_trigger_reason are required for the
|
|
14
|
+
# # consent to later be usable to create a PaymentSource.
|
|
15
|
+
# consent = Airwallex::PaymentConsent.create(
|
|
16
|
+
# customer_id: customer.id,
|
|
17
|
+
# payment_method: { type: "card", card: { ... } },
|
|
18
|
+
# next_triggered_by: "merchant",
|
|
19
|
+
# merchant_trigger_reason: "unscheduled"
|
|
20
|
+
# )
|
|
21
|
+
# consent.verify(payment_method: { card: { cvc: "123" } })
|
|
22
|
+
#
|
|
23
|
+
# @example Disable a consent
|
|
24
|
+
# consent.disable
|
|
25
|
+
class PaymentConsent < APIResource
|
|
26
|
+
extend APIOperations::Create
|
|
27
|
+
extend APIOperations::Retrieve
|
|
28
|
+
extend APIOperations::List
|
|
29
|
+
include APIOperations::Update
|
|
30
|
+
|
|
31
|
+
# @return [String] API resource path for payment consents
|
|
32
|
+
def self.resource_path
|
|
33
|
+
"/api/v1/pa/payment_consents"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Verify this consent (e.g. via a zero/low-value authorization) before
|
|
37
|
+
# it can be used for off-session charges
|
|
38
|
+
#
|
|
39
|
+
# @param params [Hash] verification params (e.g. payment_method details)
|
|
40
|
+
# @return [PaymentConsent] self
|
|
41
|
+
def verify(params = {})
|
|
42
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/verify", params)
|
|
43
|
+
refresh_from(response)
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Continue a verification that requires further customer action
|
|
48
|
+
# (e.g. completing 3DS)
|
|
49
|
+
#
|
|
50
|
+
# @param params [Hash] continuation params
|
|
51
|
+
# @return [PaymentConsent] self
|
|
52
|
+
def verify_continue(params = {})
|
|
53
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/verify_continue", params)
|
|
54
|
+
refresh_from(response)
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Disable this consent so it can no longer be charged
|
|
59
|
+
#
|
|
60
|
+
# @param params [Hash] additional params
|
|
61
|
+
# @return [PaymentConsent] self
|
|
62
|
+
def disable(params = {})
|
|
63
|
+
response = Airwallex.client.post("#{self.class.resource_path}/#{id}/disable", params)
|
|
64
|
+
refresh_from(response)
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|