airwallex 0.8.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.
@@ -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 networks/issuing banks.
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)
@@ -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
@@ -7,6 +7,9 @@ module Airwallex
7
7
  extend APIOperations::List
8
8
  include APIOperations::Update
9
9
 
10
+ # Sandbox-only — see https://www.airwallex.com/docs/api/simulation/shopper-actions
11
+ SIMULATION_SHOPPER_ACTION_PATH = "/api/v1/simulation/pa/shopper_actions"
12
+
10
13
  def self.resource_path
11
14
  "/api/v1/pa/payment_intents"
12
15
  end
@@ -40,5 +43,28 @@ module Airwallex
40
43
  refresh_from(response)
41
44
  self
42
45
  end
46
+
47
+ # Simulate the shopper completing a redirect/3DS challenge raised during
48
+ # #confirm, using the `url` from the confirm response's next_action
49
+ #
50
+ # @param url [String] the redirect URL from #confirm's next_action
51
+ # @return [PaymentIntent] self
52
+ def simulate_shopper_pay(url:)
53
+ response = Airwallex.client.post("#{SIMULATION_SHOPPER_ACTION_PATH}/pay", url: url)
54
+ refresh_from(response)
55
+ self
56
+ end
57
+
58
+ # Simulate the shopper abandoning/rejecting a redirect/3DS challenge
59
+ # raised during #confirm, using the `url` from the confirm response's
60
+ # next_action
61
+ #
62
+ # @param url [String] the redirect URL from #confirm's next_action
63
+ # @return [PaymentIntent] self
64
+ def simulate_shopper_reject(url:)
65
+ response = Airwallex.client.post("#{SIMULATION_SHOPPER_ACTION_PATH}/reject", url: url)
66
+ refresh_from(response)
67
+ self
68
+ end
43
69
  end
44
70
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airwallex
4
+ # Represents an in-person (POS) Terminal used for card-present payments.
5
+ #
6
+ # This gem currently only implements the sandbox Simulation endpoints for
7
+ # driving a terminal through a test payment; it does not yet implement the
8
+ # live terminal create/retrieve/list endpoints.
9
+ # See https://www.airwallex.com/docs/api/simulation/pos-terminals
10
+ #
11
+ # @example Simulate a terminal completing a PaymentIntent
12
+ # Airwallex::POSTerminal.simulate_turn_on(terminal_id: "term_123")
13
+ # Airwallex::POSTerminal.simulate_confirm_payment_intent(
14
+ # terminal_id: "term_123",
15
+ # payment_scenario_name: "approve"
16
+ # )
17
+ class POSTerminal < APIResource
18
+ # @return [String] API resource path for simulated POS terminal actions
19
+ def self.resource_path
20
+ "/api/v1/simulation/pa/pos/terminals"
21
+ end
22
+
23
+ # Simulate turning a terminal on
24
+ #
25
+ # @param terminal_id [String]
26
+ # @return [POSTerminal]
27
+ def self.simulate_turn_on(terminal_id:)
28
+ response = Airwallex.client.post("#{resource_path}/turn_on", terminal_id: terminal_id)
29
+ new(response)
30
+ end
31
+
32
+ # Simulate turning a terminal off
33
+ #
34
+ # @param terminal_id [String]
35
+ # @return [POSTerminal]
36
+ def self.simulate_turn_off(terminal_id:)
37
+ response = Airwallex.client.post("#{resource_path}/turn_off", terminal_id: terminal_id)
38
+ new(response)
39
+ end
40
+
41
+ # Simulate generating a terminal activation code
42
+ #
43
+ # @param request_id [String]
44
+ # @return [Hash] raw response (contains the generated activation code)
45
+ def self.simulate_generate_activation_code(request_id:)
46
+ Airwallex.client.post("#{resource_path}/generate_activation_code", request_id: request_id)
47
+ end
48
+
49
+ # Simulate a terminal confirming a PaymentIntent under a named test
50
+ # scenario (see .simulate_payment_scenarios for valid names)
51
+ #
52
+ # @param terminal_id [String]
53
+ # @param payment_scenario_name [String]
54
+ # @return [POSTerminal]
55
+ def self.simulate_confirm_payment_intent(terminal_id:, payment_scenario_name:)
56
+ response = Airwallex.client.post(
57
+ "#{resource_path}/confirm_payment_intent",
58
+ terminal_id: terminal_id,
59
+ payment_scenario_name: payment_scenario_name
60
+ )
61
+ new(response)
62
+ end
63
+
64
+ # List the test scenario names available to .simulate_confirm_payment_intent
65
+ #
66
+ # @return [Array, Hash] raw response
67
+ def self.simulate_payment_scenarios
68
+ Airwallex.client.get("#{resource_path}/payment_scenarios")
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airwallex
4
+ # Represents a Request for Information (RFI) — a compliance question
5
+ # Airwallex raises against your account (KYC, an ongoing KYC review, a
6
+ # cardholder, a transaction, payment enablement, or merchant risk).
7
+ #
8
+ # This gem currently only implements the sandbox Simulation endpoints
9
+ # (there is no live create — real RFIs originate from Airwallex's own
10
+ # compliance review, same as Dispute). See
11
+ # https://www.airwallex.com/docs/api/simulation/request-for-information
12
+ #
13
+ # @example Raise a KYC RFI, then close it
14
+ # rfi = Airwallex::RFI.simulate_create(
15
+ # type: "KYC",
16
+ # questions: [{ answer: { type: "TEXT" } }]
17
+ # )
18
+ # rfi.simulate_close
19
+ class RFI < APIResource
20
+ # @return [String] API resource path for simulated RFI actions
21
+ def self.resource_path
22
+ "/api/v1/simulation/rfis"
23
+ end
24
+
25
+ # Simulate Airwallex raising an RFI
26
+ #
27
+ # @param params [Hash] type: (required, one of "KYC", "KYC_ONGOING",
28
+ # "CARDHOLDER", "TRANSACTION", "PAYMENT_ENABLEMENT",
29
+ # "MERCHANT_RISK"); questions: (required, array of
30
+ # { answer: { type: }, sources: [] })
31
+ # @return [RFI]
32
+ def self.simulate_create(params = {})
33
+ response = Airwallex.client.post("#{resource_path}/create", params)
34
+ new(response)
35
+ end
36
+
37
+ # Simulate closing an RFI
38
+ #
39
+ # @param rfi_id [String]
40
+ # @return [RFI]
41
+ def self.simulate_close(rfi_id)
42
+ response = Airwallex.client.post("#{resource_path}/#{rfi_id}/close", {})
43
+ new(response)
44
+ end
45
+
46
+ # Simulate a follow-up on an RFI — reopen an existing answered question
47
+ # (by id) or append a new one
48
+ #
49
+ # @param rfi_id [String]
50
+ # @param params [Hash] questions: (required, array of
51
+ # { id: } or { answer: { type: }, sources: [] })
52
+ # @return [RFI]
53
+ def self.simulate_follow_up(rfi_id, params = {})
54
+ response = Airwallex.client.post("#{resource_path}/#{rfi_id}/follow_up", params)
55
+ new(response)
56
+ end
57
+
58
+ # Simulate closing this RFI
59
+ #
60
+ # @return [RFI] self
61
+ def simulate_close
62
+ response = Airwallex.client.post("#{self.class.resource_path}/#{id}/close", {})
63
+ refresh_from(response)
64
+ self
65
+ end
66
+
67
+ # Simulate a follow-up on this RFI
68
+ #
69
+ # @param params [Hash] questions: (required)
70
+ # @return [RFI] self
71
+ def simulate_follow_up(params = {})
72
+ response = Airwallex.client.post("#{self.class.resource_path}/#{id}/follow_up", params)
73
+ refresh_from(response)
74
+ self
75
+ end
76
+ end
77
+ end
@@ -6,6 +6,9 @@ module Airwallex
6
6
  extend APIOperations::Retrieve
7
7
  extend APIOperations::List
8
8
 
9
+ # Sandbox-only — see https://www.airwallex.com/docs/api/simulation/transfers
10
+ SIMULATION_PATH = "/api/v1/simulation/transfers"
11
+
9
12
  def self.resource_path
10
13
  "/api/v1/transfers"
11
14
  end
@@ -19,5 +22,38 @@ module Airwallex
19
22
  refresh_from(response)
20
23
  self
21
24
  end
25
+
26
+ # Simulate this transfer's status advancing one step. The standard
27
+ # lifecycle is SCHEDULED -> PROCESSING -> SENT -> PAID, progressed one
28
+ # step at a time; OVERDUE, FAILED, and CANCELLED can be jumped to
29
+ # directly.
30
+ #
31
+ # @param next_status [String] one of "OVERDUE", "PROCESSING", "SENT",
32
+ # "PAID", "FAILED", "CANCELLED"
33
+ # @param failure_type [String, nil] failure reason code, only
34
+ # meaningful when next_status is "FAILED"
35
+ # @return [Transfer] self
36
+ def simulate_transition(next_status:, failure_type: nil)
37
+ params = { next_status: next_status }
38
+ params[:failure_type] = failure_type if failure_type
39
+ response = Airwallex.client.post("#{SIMULATION_PATH}/#{id}/transition", params)
40
+ refresh_from(response)
41
+ self
42
+ end
43
+
44
+ # Simulate a transfer's status advancing one step
45
+ #
46
+ # @param transfer_id [String]
47
+ # @param next_status [String] one of "OVERDUE", "PROCESSING", "SENT",
48
+ # "PAID", "FAILED", "CANCELLED"
49
+ # @param failure_type [String, nil] failure reason code, only
50
+ # meaningful when next_status is "FAILED"
51
+ # @return [Transfer]
52
+ def self.simulate_transition(transfer_id, next_status:, failure_type: nil)
53
+ params = { next_status: next_status }
54
+ params[:failure_type] = failure_type if failure_type
55
+ response = Airwallex.client.post("#{SIMULATION_PATH}/#{transfer_id}/transition", params)
56
+ new(response)
57
+ end
22
58
  end
23
59
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Airwallex
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end
data/lib/airwallex.rb CHANGED
@@ -48,6 +48,13 @@ require_relative "airwallex/resources/connected_account"
48
48
  require_relative "airwallex/resources/account_amendment"
49
49
  require_relative "airwallex/resources/funds_split"
50
50
  require_relative "airwallex/resources/charge"
51
+ require_relative "airwallex/resources/deposit"
52
+ require_relative "airwallex/resources/issuing_transaction"
53
+ require_relative "airwallex/resources/cardholder"
54
+ require_relative "airwallex/resources/linked_account"
55
+ require_relative "airwallex/resources/account_offboarding"
56
+ require_relative "airwallex/resources/rfi"
57
+ require_relative "airwallex/resources/pos_terminal"
51
58
 
52
59
  module Airwallex
53
60
  class << self