airwallex 0.7.0 → 0.9.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 +92 -0
- data/README.md +449 -28
- data/lib/airwallex/api_resource.rb +22 -0
- data/lib/airwallex/errors.rb +6 -1
- data/lib/airwallex/resources/account_amendment.rb +39 -0
- data/lib/airwallex/resources/account_offboarding.rb +43 -0
- data/lib/airwallex/resources/beneficiary.rb +10 -10
- data/lib/airwallex/resources/billing_customer.rb +2 -2
- data/lib/airwallex/resources/cardholder.rb +28 -0
- data/lib/airwallex/resources/connected_account.rb +51 -2
- data/lib/airwallex/resources/deposit.rb +115 -0
- data/lib/airwallex/resources/dispute.rb +83 -1
- data/lib/airwallex/resources/global_account.rb +2 -2
- data/lib/airwallex/resources/issuing_transaction.rb +100 -0
- data/lib/airwallex/resources/linked_account.rb +64 -0
- data/lib/airwallex/resources/payment_consent.rb +15 -0
- data/lib/airwallex/resources/payment_intent.rb +26 -0
- data/lib/airwallex/resources/pos_terminal.rb +71 -0
- data/lib/airwallex/resources/rfi.rb +77 -0
- data/lib/airwallex/resources/transfer.rb +36 -0
- data/lib/airwallex/util.rb +8 -5
- data/lib/airwallex/version.rb +1 -1
- data/lib/airwallex.rb +7 -0
- metadata +10 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents an offboarding request for a Connected Account. Always
|
|
5
|
+
# accessed through its parent ConnectedAccount, since its API path is
|
|
6
|
+
# scoped by account_id.
|
|
7
|
+
#
|
|
8
|
+
# This gem currently only implements the sandbox Simulation endpoints for
|
|
9
|
+
# completing/cancelling an offboarding; it does not yet implement the live
|
|
10
|
+
# create/retrieve/list endpoints (`/api/v1/accounts/{account_id}/offboardings`).
|
|
11
|
+
# See https://www.airwallex.com/docs/api/simulation/connected-account-offboarding
|
|
12
|
+
#
|
|
13
|
+
# @example Simulate an offboarding finishing
|
|
14
|
+
# Airwallex::AccountOffboarding.simulate_complete("acct_123", "obd_456")
|
|
15
|
+
class AccountOffboarding < APIResource
|
|
16
|
+
# @param account_id [String] the parent ConnectedAccount's id
|
|
17
|
+
# @return [String] API resource path for this account's simulated
|
|
18
|
+
# offboarding actions
|
|
19
|
+
def self.resource_path(account_id)
|
|
20
|
+
"/api/v1/simulation/accounts/#{account_id}/offboardings"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Simulate an offboarding completing
|
|
24
|
+
#
|
|
25
|
+
# @param account_id [String]
|
|
26
|
+
# @param offboarding_id [String]
|
|
27
|
+
# @return [AccountOffboarding]
|
|
28
|
+
def self.simulate_complete(account_id, offboarding_id)
|
|
29
|
+
response = Airwallex.client.post("#{resource_path(account_id)}/#{offboarding_id}/complete", {})
|
|
30
|
+
new(response)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Simulate an offboarding being cancelled
|
|
34
|
+
#
|
|
35
|
+
# @param account_id [String]
|
|
36
|
+
# @param offboarding_id [String]
|
|
37
|
+
# @return [AccountOffboarding]
|
|
38
|
+
def self.simulate_cancel(account_id, offboarding_id)
|
|
39
|
+
response = Airwallex.client.post("#{resource_path(account_id)}/#{offboarding_id}/cancel", {})
|
|
40
|
+
new(response)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -64,36 +64,36 @@ module Airwallex
|
|
|
64
64
|
# method) before attempting to create the beneficiary.
|
|
65
65
|
#
|
|
66
66
|
# @param params [Hash] the same shape of params you'd pass to .create
|
|
67
|
-
# @return [Hash]
|
|
67
|
+
# @return [Hash] symbolized validation result
|
|
68
68
|
def self.validate(params = {})
|
|
69
|
-
|
|
69
|
+
symbolized_post("#{resource_path}/validate", params)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
# Verify ownership of a beneficiary's bank account via Confirmation of
|
|
73
73
|
# Payee (CoP) before creation.
|
|
74
74
|
#
|
|
75
75
|
# @param params [Hash] beneficiary bank account details to verify
|
|
76
|
-
# @return [Hash]
|
|
76
|
+
# @return [Hash] symbolized verification result (status, account_name_match_result)
|
|
77
77
|
def self.verify_account(params = {})
|
|
78
|
-
|
|
78
|
+
symbolized_post("#{resource_path}/verify_account", params)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
# Retrieve the API schema (field validation rules) for beneficiary bank
|
|
82
82
|
# details, keyed by beneficiary type / entity type / bank country.
|
|
83
83
|
#
|
|
84
84
|
# @param params [Hash] e.g. beneficiary_type:, bank_country_code:
|
|
85
|
-
# @return [Hash]
|
|
85
|
+
# @return [Hash] symbolized schema response
|
|
86
86
|
def self.api_schema(params = {})
|
|
87
|
-
|
|
87
|
+
symbolized_post(API_SCHEMA_PATH, params)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# Retrieve the dynamic form schema used to render beneficiary bank-detail
|
|
91
91
|
# forms in the UI, keyed by beneficiary type / entity type / bank country.
|
|
92
92
|
#
|
|
93
93
|
# @param params [Hash] e.g. beneficiary_type:, bank_country_code:
|
|
94
|
-
# @return [Hash]
|
|
94
|
+
# @return [Hash] symbolized schema response
|
|
95
95
|
def self.form_schema(params = {})
|
|
96
|
-
|
|
96
|
+
symbolized_post(FORM_SCHEMA_PATH, params)
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
# Search financial institutions supported for a given country, currency,
|
|
@@ -102,9 +102,9 @@ module Airwallex
|
|
|
102
102
|
# @param params [Hash] required: bank_country_code:, account_currency:,
|
|
103
103
|
# entity_type:, transfer_method:, keyword: (a bank-name search term,
|
|
104
104
|
# min 3 chars)
|
|
105
|
-
# @return [Hash]
|
|
105
|
+
# @return [Hash] symbolized response listing supported institutions
|
|
106
106
|
def self.supported_financial_institutions(params = {})
|
|
107
|
-
|
|
107
|
+
symbolized_get(SUPPORTED_FINANCIAL_INSTITUTIONS_PATH, params)
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
end
|
|
@@ -40,9 +40,9 @@ module Airwallex
|
|
|
40
40
|
# Retrieve the bank transfer instructions for funding this customer's
|
|
41
41
|
# subscriptions (e.g. for a BT-funded instalment plan).
|
|
42
42
|
#
|
|
43
|
-
# @return [Hash]
|
|
43
|
+
# @return [Hash] symbolized bank transfer instructions
|
|
44
44
|
def bank_transfer_instructions
|
|
45
|
-
|
|
45
|
+
self.class.symbolized_get("#{self.class.resource_path}/#{id}/bank_transfer_instructions")
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents an Issuing Cardholder.
|
|
5
|
+
#
|
|
6
|
+
# This gem currently only implements the sandbox Simulation endpoint for
|
|
7
|
+
# bypassing a cardholder's RFI review stage; it does not yet implement the
|
|
8
|
+
# live create/retrieve/list endpoints.
|
|
9
|
+
# See https://www.airwallex.com/docs/api/simulation/issuing-cardholders
|
|
10
|
+
#
|
|
11
|
+
# @example Bypass a cardholder's pending review
|
|
12
|
+
# Airwallex::Cardholder.simulate_pass_review("chd_123")
|
|
13
|
+
class Cardholder < APIResource
|
|
14
|
+
# @return [String] API resource path for simulated cardholder actions
|
|
15
|
+
def self.resource_path
|
|
16
|
+
"/api/v1/simulation/issuing/cardholders"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Simulate bypassing this cardholder's RFI review stage
|
|
20
|
+
#
|
|
21
|
+
# @param cardholder_id [String]
|
|
22
|
+
# @return [Cardholder]
|
|
23
|
+
def self.simulate_pass_review(cardholder_id)
|
|
24
|
+
response = Airwallex.client.post("#{resource_path}/#{cardholder_id}/pass_review", {})
|
|
25
|
+
new(response)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -57,11 +57,29 @@ module Airwallex
|
|
|
57
57
|
CURRENT_ACCOUNT_PATH = "/api/v1/account"
|
|
58
58
|
WALLET_INFO_PATH = "/api/v1/account/wallet_info"
|
|
59
59
|
|
|
60
|
+
# Sandbox-only — see https://www.airwallex.com/docs/api/simulation/connected-accounts
|
|
61
|
+
SIMULATION_PATH = "/api/v1/simulation/accounts"
|
|
62
|
+
|
|
60
63
|
# @return [String] API resource path for connected accounts
|
|
61
64
|
def self.resource_path
|
|
62
65
|
"/api/v1/accounts"
|
|
63
66
|
end
|
|
64
67
|
|
|
68
|
+
# Simulate this account's status transitioning. The account must
|
|
69
|
+
# currently be SUBMITTED.
|
|
70
|
+
#
|
|
71
|
+
# @param account_id [String]
|
|
72
|
+
# @param next_status [String] one of "ACTIVE", "SUSPENDED",
|
|
73
|
+
# "ACTION_REQUIRED"
|
|
74
|
+
# @param force [Boolean] defaults to true
|
|
75
|
+
# @return [ConnectedAccount]
|
|
76
|
+
def self.simulate_update_status(account_id, next_status:, force: true)
|
|
77
|
+
response = Airwallex.client.post(
|
|
78
|
+
"#{SIMULATION_PATH}/#{account_id}/update_status", next_status: next_status, force: force
|
|
79
|
+
)
|
|
80
|
+
new(response)
|
|
81
|
+
end
|
|
82
|
+
|
|
65
83
|
# Retrieve whichever account the current request is authenticated as
|
|
66
84
|
#
|
|
67
85
|
# @param params [Hash] additional params
|
|
@@ -75,9 +93,9 @@ module Airwallex
|
|
|
75
93
|
# authenticated as
|
|
76
94
|
#
|
|
77
95
|
# @param params [Hash] additional params
|
|
78
|
-
# @return [Hash]
|
|
96
|
+
# @return [Hash] symbolized wallet info response
|
|
79
97
|
def self.wallet_info(params = {})
|
|
80
|
-
|
|
98
|
+
symbolized_get(WALLET_INFO_PATH, params)
|
|
81
99
|
end
|
|
82
100
|
|
|
83
101
|
# The legal_entity_id needed by BillingCustomer.create's
|
|
@@ -132,5 +150,36 @@ module Airwallex
|
|
|
132
150
|
refresh_from(response)
|
|
133
151
|
self
|
|
134
152
|
end
|
|
153
|
+
|
|
154
|
+
# Simulate this account's status transitioning. The account must
|
|
155
|
+
# currently be SUBMITTED.
|
|
156
|
+
#
|
|
157
|
+
# @param next_status [String] one of "ACTIVE", "SUSPENDED",
|
|
158
|
+
# "ACTION_REQUIRED"
|
|
159
|
+
# @param force [Boolean] defaults to true
|
|
160
|
+
# @return [ConnectedAccount] self
|
|
161
|
+
def simulate_update_status(next_status:, force: true)
|
|
162
|
+
response = Airwallex.client.post(
|
|
163
|
+
"#{self.class::SIMULATION_PATH}/#{id}/update_status", next_status: next_status, force: force
|
|
164
|
+
)
|
|
165
|
+
refresh_from(response)
|
|
166
|
+
self
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Simulate a pending offboarding on this account completing
|
|
170
|
+
#
|
|
171
|
+
# @param offboarding_id [String]
|
|
172
|
+
# @return [AccountOffboarding]
|
|
173
|
+
def simulate_complete_offboarding(offboarding_id)
|
|
174
|
+
AccountOffboarding.simulate_complete(id, offboarding_id)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Simulate a pending offboarding on this account being cancelled
|
|
178
|
+
#
|
|
179
|
+
# @param offboarding_id [String]
|
|
180
|
+
# @return [AccountOffboarding]
|
|
181
|
+
def simulate_cancel_offboarding(offboarding_id)
|
|
182
|
+
AccountOffboarding.simulate_cancel(id, offboarding_id)
|
|
183
|
+
end
|
|
135
184
|
end
|
|
136
185
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Deposit — funds arriving in your Airwallex account, either
|
|
5
|
+
# as an inbound bank transfer into a Global Account (type: BANK_TRANSFER)
|
|
6
|
+
# or a Direct Debit pull from a verified LinkedAccount (type:
|
|
7
|
+
# DIRECT_DEBIT, made via .create).
|
|
8
|
+
#
|
|
9
|
+
# The sandbox Simulation actions split cleanly along that same line:
|
|
10
|
+
# .simulate_create simulates a BANK_TRANSFER deposit landing, and it
|
|
11
|
+
# auto-settles on its own within a few seconds — same as a real bank
|
|
12
|
+
# transfer, there's no PENDING state to force through. .simulate_settle/
|
|
13
|
+
# .simulate_reject/.simulate_reverse only operate on DIRECT_DEBIT
|
|
14
|
+
# deposits made via .create, since a real direct debit pull takes days
|
|
15
|
+
# to clear and the sandbox has no other way to resolve it. Calling
|
|
16
|
+
# settle/reject/reverse on a BANK_TRANSFER deposit's id returns a 404
|
|
17
|
+
# "Deposit does not exist" — confirmed against the real sandbox.
|
|
18
|
+
# See https://www.airwallex.com/docs/api/simulation/deposits
|
|
19
|
+
#
|
|
20
|
+
# @example Simulate an inbound bank transfer landing (settles on its own)
|
|
21
|
+
# Airwallex::Deposit.simulate_create(amount: 100.00, global_account_id: "gacc_123")
|
|
22
|
+
#
|
|
23
|
+
# @example Pull via Direct Debit, then force it through the sandbox lifecycle
|
|
24
|
+
# deposit = Airwallex::Deposit.create(
|
|
25
|
+
# funding_source_id: "la_123", # a verified LinkedAccount id
|
|
26
|
+
# amount: 50.00,
|
|
27
|
+
# currency: "AUD"
|
|
28
|
+
# )
|
|
29
|
+
# deposit.simulate_settle
|
|
30
|
+
class Deposit < APIResource
|
|
31
|
+
extend APIOperations::Create
|
|
32
|
+
extend APIOperations::Retrieve
|
|
33
|
+
extend APIOperations::List
|
|
34
|
+
|
|
35
|
+
# Airwallex's own API is inconsistent here: simulating a bank-transfer
|
|
36
|
+
# deposit landing uses the singular "deposit", while every other
|
|
37
|
+
# simulation action on an existing deposit uses the plural "deposits".
|
|
38
|
+
SIMULATE_CREATE_PATH = "/api/v1/simulation/deposit/create"
|
|
39
|
+
SIMULATION_PATH = "/api/v1/simulation/deposits"
|
|
40
|
+
|
|
41
|
+
# @return [String] API resource path for deposits
|
|
42
|
+
def self.resource_path
|
|
43
|
+
"/api/v1/deposits"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Simulate an inbound bank-transfer deposit landing in a Global
|
|
47
|
+
# Account
|
|
48
|
+
#
|
|
49
|
+
# @param params [Hash] amount:, global_account_id: (required);
|
|
50
|
+
# payer_bankname:, payer_country:, payer_name:, reference:,
|
|
51
|
+
# statement_ref:, status: ("PENDING", "REJECTED", or "SETTLED",
|
|
52
|
+
# defaults to "SETTLED") (optional)
|
|
53
|
+
# @return [Deposit]
|
|
54
|
+
def self.simulate_create(params = {})
|
|
55
|
+
response = Airwallex.client.post(SIMULATE_CREATE_PATH, params)
|
|
56
|
+
new(response)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Simulate a PENDING Direct Debit deposit (made via .create) settling
|
|
60
|
+
#
|
|
61
|
+
# @param deposit_id [String]
|
|
62
|
+
# @return [Deposit]
|
|
63
|
+
def self.simulate_settle(deposit_id)
|
|
64
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{deposit_id}/settle", {})
|
|
65
|
+
new(response)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Simulate a PENDING Direct Debit deposit (made via .create) being
|
|
69
|
+
# rejected
|
|
70
|
+
#
|
|
71
|
+
# @param deposit_id [String]
|
|
72
|
+
# @return [Deposit]
|
|
73
|
+
def self.simulate_reject(deposit_id)
|
|
74
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{deposit_id}/reject", {})
|
|
75
|
+
new(response)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Simulate reversing a SETTLED Direct Debit deposit (creates an
|
|
79
|
+
# offsetting settled deposit and deactivates the LinkedAccount)
|
|
80
|
+
#
|
|
81
|
+
# @param deposit_id [String]
|
|
82
|
+
# @return [Deposit]
|
|
83
|
+
def self.simulate_reverse(deposit_id)
|
|
84
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{deposit_id}/reverse", {})
|
|
85
|
+
new(response)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Simulate this Direct Debit deposit settling
|
|
89
|
+
#
|
|
90
|
+
# @return [Deposit] self
|
|
91
|
+
def simulate_settle
|
|
92
|
+
response = Airwallex.client.post("#{self.class::SIMULATION_PATH}/#{id}/settle", {})
|
|
93
|
+
refresh_from(response)
|
|
94
|
+
self
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Simulate this Direct Debit deposit being rejected
|
|
98
|
+
#
|
|
99
|
+
# @return [Deposit] self
|
|
100
|
+
def simulate_reject
|
|
101
|
+
response = Airwallex.client.post("#{self.class::SIMULATION_PATH}/#{id}/reject", {})
|
|
102
|
+
refresh_from(response)
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Simulate reversing this Direct Debit deposit
|
|
107
|
+
#
|
|
108
|
+
# @return [Deposit] self
|
|
109
|
+
def simulate_reverse
|
|
110
|
+
response = Airwallex.client.post("#{self.class::SIMULATION_PATH}/#{id}/reverse", {})
|
|
111
|
+
refresh_from(response)
|
|
112
|
+
self
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -5,7 +5,11 @@ module Airwallex
|
|
|
5
5
|
#
|
|
6
6
|
# Disputes represent chargebacks or payment disputes initiated by cardholders.
|
|
7
7
|
# Merchants can view disputes, challenge them with evidence, or accept them.
|
|
8
|
-
# There is no create — disputes originate from card
|
|
8
|
+
# There is no live create — real disputes originate from card
|
|
9
|
+
# networks/issuing banks. In the sandbox, `.simulate_create` (and
|
|
10
|
+
# `#simulate_escalate`/`#simulate_resolve`) stand in for the card
|
|
11
|
+
# network/issuing bank to drive the full dispute lifecycle for testing.
|
|
12
|
+
# See https://www.airwallex.com/docs/api/simulation/payment-disputes
|
|
9
13
|
#
|
|
10
14
|
# @example List open disputes
|
|
11
15
|
# disputes = Airwallex::Dispute.list(status: 'OPEN')
|
|
@@ -18,15 +22,70 @@ module Airwallex
|
|
|
18
22
|
#
|
|
19
23
|
# @example Challenge a dispute
|
|
20
24
|
# dispute.challenge(...)
|
|
25
|
+
#
|
|
26
|
+
# @example Simulate the full sandbox lifecycle
|
|
27
|
+
# dispute = Airwallex::Dispute.simulate_create(
|
|
28
|
+
# payment_intent_id: "int_123",
|
|
29
|
+
# reason_code: "4853",
|
|
30
|
+
# stage: "CHARGEBACK",
|
|
31
|
+
# due_at: "2026-12-01T23:59:59Z"
|
|
32
|
+
# )
|
|
33
|
+
# dispute.challenge(customer_communication: "Email thread")
|
|
34
|
+
# dispute.simulate_resolve(in_favor_of: "MERCHANT")
|
|
21
35
|
class Dispute < APIResource
|
|
22
36
|
extend APIOperations::Retrieve
|
|
23
37
|
extend APIOperations::List
|
|
24
38
|
include APIOperations::Update
|
|
25
39
|
|
|
40
|
+
# Sandbox-only — see https://www.airwallex.com/docs/api/simulation/payment-disputes
|
|
41
|
+
SIMULATION_PATH = "/api/v1/simulation/pa/payment_disputes"
|
|
42
|
+
|
|
26
43
|
def self.resource_path
|
|
27
44
|
"/api/v1/pa/payment_disputes"
|
|
28
45
|
end
|
|
29
46
|
|
|
47
|
+
# Simulate a card network/issuing bank raising a dispute against a
|
|
48
|
+
# PaymentIntent
|
|
49
|
+
#
|
|
50
|
+
# @param params [Hash] payment_intent_id:, reason_code: (card-brand
|
|
51
|
+
# specific, e.g. Mastercard "4853", Visa "10.4"), stage: (one of
|
|
52
|
+
# "RFI", "PRE_CHARGEBACK", "CHARGEBACK", "PRE_ARBITRATION",
|
|
53
|
+
# "ARBITRATION"), due_at: (required); amount:, comment:, documents:
|
|
54
|
+
# (optional)
|
|
55
|
+
# @return [Dispute]
|
|
56
|
+
def self.simulate_create(params = {})
|
|
57
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/create", params)
|
|
58
|
+
new(response)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Simulate the issuing bank rejecting the merchant's challenge evidence
|
|
62
|
+
# and advancing the dispute to the next stage (e.g. Chargeback ->
|
|
63
|
+
# Pre-arbitration). Not valid while status is REQUIRES_RESPONSE — the
|
|
64
|
+
# merchant must #accept or #challenge first.
|
|
65
|
+
#
|
|
66
|
+
# @param dispute_id [String]
|
|
67
|
+
# @param params [Hash] due_at: (required); amount:, comment:,
|
|
68
|
+
# documents: (optional)
|
|
69
|
+
# @return [Dispute]
|
|
70
|
+
def self.simulate_escalate(dispute_id, params = {})
|
|
71
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{dispute_id}/escalate", params)
|
|
72
|
+
new(response)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Simulate the issuing bank's final decision on a dispute.
|
|
76
|
+
# in_favor_of: "MERCHANT" resolves it WON/REVERSED; "CUSTOMER" resolves
|
|
77
|
+
# it LOST. Not valid while status is REQUIRES_RESPONSE — the merchant
|
|
78
|
+
# must #accept or #challenge first.
|
|
79
|
+
#
|
|
80
|
+
# @param dispute_id [String]
|
|
81
|
+
# @param params [Hash] in_favor_of: ("MERCHANT" or "CUSTOMER",
|
|
82
|
+
# required); amount: (optional, defaults to the full disputed amount)
|
|
83
|
+
# @return [Dispute]
|
|
84
|
+
def self.simulate_resolve(dispute_id, params = {})
|
|
85
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{dispute_id}/resolve", params)
|
|
86
|
+
new(response)
|
|
87
|
+
end
|
|
88
|
+
|
|
30
89
|
# Accept a dispute without challenging it
|
|
31
90
|
#
|
|
32
91
|
# @return [Airwallex::Dispute] self
|
|
@@ -47,6 +106,29 @@ module Airwallex
|
|
|
47
106
|
self
|
|
48
107
|
end
|
|
49
108
|
|
|
109
|
+
# Simulate the issuing bank rejecting this dispute's challenge evidence
|
|
110
|
+
# and advancing it to the next stage
|
|
111
|
+
#
|
|
112
|
+
# @param params [Hash] due_at: (required); amount:, comment:,
|
|
113
|
+
# documents: (optional)
|
|
114
|
+
# @return [Airwallex::Dispute] self
|
|
115
|
+
def simulate_escalate(params = {})
|
|
116
|
+
response = Airwallex.client.post("#{self.class::SIMULATION_PATH}/#{id}/escalate", params)
|
|
117
|
+
refresh_from(response)
|
|
118
|
+
self
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Simulate the issuing bank's final decision on this dispute
|
|
122
|
+
#
|
|
123
|
+
# @param params [Hash] in_favor_of: ("MERCHANT" or "CUSTOMER",
|
|
124
|
+
# required); amount: (optional)
|
|
125
|
+
# @return [Airwallex::Dispute] self
|
|
126
|
+
def simulate_resolve(params = {})
|
|
127
|
+
response = Airwallex.client.post("#{self.class::SIMULATION_PATH}/#{id}/resolve", params)
|
|
128
|
+
refresh_from(response)
|
|
129
|
+
self
|
|
130
|
+
end
|
|
131
|
+
|
|
50
132
|
# List payment intents related to this dispute
|
|
51
133
|
#
|
|
52
134
|
# @param params [Hash] additional query params (e.g. pagination)
|
|
@@ -45,9 +45,9 @@ module Airwallex
|
|
|
45
45
|
# @param params [Hash] account_statement_type: (e.g. "AMAZON"),
|
|
46
46
|
# registration_info: { agreement:, registered_name:, registered_email:,
|
|
47
47
|
# registered_address: { address:, city:, state:, postcode:, country: } }
|
|
48
|
-
# @return [Hash]
|
|
48
|
+
# @return [Hash] symbolized response (the generated letter/document reference)
|
|
49
49
|
def generate_statement_letter(params = {})
|
|
50
|
-
|
|
50
|
+
self.class.symbolized_post("#{self.class.resource_path}/#{id}/generate_statement_letter", params)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# List inbound transactions (deposits) received into this account
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a card (Issuing) transaction event.
|
|
5
|
+
#
|
|
6
|
+
# This gem currently only implements the sandbox Simulation endpoints for
|
|
7
|
+
# issuing transactions — creating an authorization, capturing/reversing a
|
|
8
|
+
# PENDING one, refunding a CAPTURED one, and delivering a 3DS delegation
|
|
9
|
+
# notification. It does not yet implement the live Issuing resources
|
|
10
|
+
# (Card, Cardholder) or the live transaction retrieve/list endpoints.
|
|
11
|
+
# See https://www.airwallex.com/docs/api/simulation/issuing-transactions
|
|
12
|
+
#
|
|
13
|
+
# @example Authorize, then capture in full
|
|
14
|
+
# txn = Airwallex::IssuingTransaction.simulate_create(
|
|
15
|
+
# card_id: "card_123",
|
|
16
|
+
# transaction_amount: 25.00,
|
|
17
|
+
# transaction_currency: "USD"
|
|
18
|
+
# )
|
|
19
|
+
# Airwallex::IssuingTransaction.simulate_capture(txn.transaction_id)
|
|
20
|
+
#
|
|
21
|
+
# @example Authorize and clear in a single step
|
|
22
|
+
# Airwallex::IssuingTransaction.simulate_create(
|
|
23
|
+
# card_id: "card_123",
|
|
24
|
+
# transaction_amount: 25.00,
|
|
25
|
+
# transaction_currency: "USD",
|
|
26
|
+
# single_phase: true
|
|
27
|
+
# )
|
|
28
|
+
class IssuingTransaction < APIResource
|
|
29
|
+
# @return [String] API resource path for simulated issuing transactions
|
|
30
|
+
def self.resource_path
|
|
31
|
+
"/api/v1/simulation/issuing"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Simulate a card authorization (or, with single_phase: true, an
|
|
35
|
+
# authorization cleared in one step)
|
|
36
|
+
#
|
|
37
|
+
# @param params [Hash] card_id: (or card_number:), transaction_amount:,
|
|
38
|
+
# transaction_currency: (required); single_phase:, auth_code:,
|
|
39
|
+
# merchant_category_code:, merchant_info:, transaction_failure_reason:
|
|
40
|
+
# (optional)
|
|
41
|
+
# @return [IssuingTransaction] the resulting transaction — read its
|
|
42
|
+
# `transaction_id` to pass to .simulate_capture/.simulate_reverse
|
|
43
|
+
def self.simulate_create(params = {})
|
|
44
|
+
response = Airwallex.client.post("#{resource_path}/create", params)
|
|
45
|
+
new(response)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Simulate capturing a PENDING transaction
|
|
49
|
+
#
|
|
50
|
+
# @param transaction_id [String] the `transaction_id` from
|
|
51
|
+
# .simulate_create's response
|
|
52
|
+
# @param params [Hash] merchant_info:, transaction_amount: (optional —
|
|
53
|
+
# a partial capture if less than the authorized amount, full amount
|
|
54
|
+
# if omitted)
|
|
55
|
+
# @return [IssuingTransaction]
|
|
56
|
+
def self.simulate_capture(transaction_id, params = {})
|
|
57
|
+
response = Airwallex.client.post(
|
|
58
|
+
"#{resource_path}/card_transaction_lifecycles/#{transaction_id}/capture", params
|
|
59
|
+
)
|
|
60
|
+
new(response)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Simulate reversing a PENDING transaction
|
|
64
|
+
#
|
|
65
|
+
# @param transaction_id [String] the `transaction_id` from
|
|
66
|
+
# .simulate_create's response
|
|
67
|
+
# @param params [Hash] transaction_amount: (optional — a partial
|
|
68
|
+
# reversal if less than the authorized amount, full reversal if
|
|
69
|
+
# omitted)
|
|
70
|
+
# @return [IssuingTransaction]
|
|
71
|
+
def self.simulate_reverse(transaction_id, params = {})
|
|
72
|
+
response = Airwallex.client.post(
|
|
73
|
+
"#{resource_path}/card_transaction_lifecycles/#{transaction_id}/reverse", params
|
|
74
|
+
)
|
|
75
|
+
new(response)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Simulate refunding a CAPTURED, not-fully-refunded transaction back to
|
|
79
|
+
# the card
|
|
80
|
+
#
|
|
81
|
+
# @param params [Hash] card_id: (or card_number:), transaction_amount:,
|
|
82
|
+
# transaction_currency: (required); merchant_category_code:,
|
|
83
|
+
# merchant_info: (optional)
|
|
84
|
+
# @return [IssuingTransaction]
|
|
85
|
+
def self.simulate_refund(params = {})
|
|
86
|
+
response = Airwallex.client.post("#{resource_path}/refund", params)
|
|
87
|
+
new(response)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Simulate a 3DS delegation-mode notification for a card
|
|
91
|
+
#
|
|
92
|
+
# @param params [Hash] card_number: (required); merchant_info: (object,
|
|
93
|
+
# optional: acquirer_id:, merchant_category_code:,
|
|
94
|
+
# merchant_country_code:, merchant_id:, merchant_name:, merchant_url:)
|
|
95
|
+
# @return [Hash] raw response
|
|
96
|
+
def self.simulate_notify_three_ds(params = {})
|
|
97
|
+
Airwallex.client.post("#{resource_path}/threeds/notify", params)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Airwallex
|
|
4
|
+
# Represents a Linked Account — a customer's external bank account
|
|
5
|
+
# authorized for Direct Debit pulls.
|
|
6
|
+
#
|
|
7
|
+
# This gem currently only implements the sandbox Simulation endpoints for
|
|
8
|
+
# the mandate and micro-deposit lifecycle; it does not yet implement the
|
|
9
|
+
# live create/retrieve/list endpoints. All four actions below return
|
|
10
|
+
# HTTP 200 with an empty body on success, so they return `true` rather
|
|
11
|
+
# than a resource instance.
|
|
12
|
+
# See https://www.airwallex.com/docs/api/simulation/linked-accounts
|
|
13
|
+
#
|
|
14
|
+
# MANDATE STATUS LIFECYCLE:
|
|
15
|
+
# PROCESSING -> ACTIVE (simulate_accept_mandate)
|
|
16
|
+
# PROCESSING -> INACTIVE (simulate_reject_mandate)
|
|
17
|
+
# PROCESSING or ACTIVE -> INACTIVE (simulate_cancel_mandate)
|
|
18
|
+
#
|
|
19
|
+
# @example Approve a mandate that's awaiting the customer's bank
|
|
20
|
+
# Airwallex::LinkedAccount.simulate_accept_mandate("la_123")
|
|
21
|
+
class LinkedAccount < APIResource
|
|
22
|
+
# @return [String] API resource path for simulated linked account actions
|
|
23
|
+
def self.resource_path
|
|
24
|
+
"/api/v1/simulation/linked_accounts"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Simulate the mandate transitioning PROCESSING -> ACTIVE
|
|
28
|
+
#
|
|
29
|
+
# @param linked_account_id [String]
|
|
30
|
+
# @return [true]
|
|
31
|
+
def self.simulate_accept_mandate(linked_account_id)
|
|
32
|
+
Airwallex.client.post("#{resource_path}/#{linked_account_id}/mandate/accept", {})
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Simulate the mandate transitioning PROCESSING -> INACTIVE
|
|
37
|
+
#
|
|
38
|
+
# @param linked_account_id [String]
|
|
39
|
+
# @return [true]
|
|
40
|
+
def self.simulate_reject_mandate(linked_account_id)
|
|
41
|
+
Airwallex.client.post("#{resource_path}/#{linked_account_id}/mandate/reject", {})
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Simulate the mandate transitioning PROCESSING or ACTIVE -> INACTIVE
|
|
46
|
+
#
|
|
47
|
+
# @param linked_account_id [String]
|
|
48
|
+
# @return [true]
|
|
49
|
+
def self.simulate_cancel_mandate(linked_account_id)
|
|
50
|
+
Airwallex.client.post("#{resource_path}/#{linked_account_id}/mandate/cancel", {})
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Simulate a failed micro-deposit verification, transitioning the
|
|
55
|
+
# linked account itself from REQUIRES_ACTION to FAILED
|
|
56
|
+
#
|
|
57
|
+
# @param linked_account_id [String]
|
|
58
|
+
# @return [true]
|
|
59
|
+
def self.simulate_fail_microdeposits(linked_account_id)
|
|
60
|
+
Airwallex.client.post("#{resource_path}/#{linked_account_id}/fail_microdeposits", {})
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -33,6 +33,9 @@ module Airwallex
|
|
|
33
33
|
"/api/v1/pa/payment_consents"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Sandbox-only — see https://www.airwallex.com/docs/api/simulation/shopper-actions
|
|
37
|
+
SIMULATION_SHOPPER_ACTION_PATH = "/api/v1/simulation/pa/shopper_actions"
|
|
38
|
+
|
|
36
39
|
# Verify this consent (e.g. via a zero/low-value authorization) before
|
|
37
40
|
# it can be used for off-session charges
|
|
38
41
|
#
|
|
@@ -64,5 +67,17 @@ module Airwallex
|
|
|
64
67
|
refresh_from(response)
|
|
65
68
|
self
|
|
66
69
|
end
|
|
70
|
+
|
|
71
|
+
# Simulate the shopper completing a redirect/3DS challenge raised
|
|
72
|
+
# during #verify, using the `url` from the verify response's
|
|
73
|
+
# next_action
|
|
74
|
+
#
|
|
75
|
+
# @param url [String] the redirect URL from #verify's next_action
|
|
76
|
+
# @return [PaymentConsent] self
|
|
77
|
+
def simulate_shopper_verify(url:)
|
|
78
|
+
response = Airwallex.client.post("#{SIMULATION_SHOPPER_ACTION_PATH}/verify", url: url)
|
|
79
|
+
refresh_from(response)
|
|
80
|
+
self
|
|
81
|
+
end
|
|
67
82
|
end
|
|
68
83
|
end
|