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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -8
  3. data/README.md +2 -2
  4. data/lib/airwallex/api_operations/delete.rb +3 -3
  5. data/lib/airwallex/api_operations/update.rb +4 -4
  6. data/lib/airwallex/client.rb +15 -11
  7. data/lib/airwallex/configuration.rb +6 -2
  8. data/lib/airwallex/middleware/auth_refresh.rb +21 -7
  9. data/lib/airwallex/resources/account_amendment.rb +29 -0
  10. data/lib/airwallex/resources/beneficiary.rb +96 -0
  11. data/lib/airwallex/resources/billing_customer.rb +48 -0
  12. data/lib/airwallex/resources/billing_price.rb +30 -0
  13. data/lib/airwallex/resources/billing_product.rb +23 -0
  14. data/lib/airwallex/resources/billing_subscription.rb +93 -0
  15. data/lib/airwallex/resources/billing_subscription_item.rb +7 -0
  16. data/lib/airwallex/resources/charge.rb +19 -0
  17. data/lib/airwallex/resources/connected_account.rb +136 -0
  18. data/lib/airwallex/resources/conversion.rb +1 -1
  19. data/lib/airwallex/resources/customer.rb +5 -2
  20. data/lib/airwallex/resources/dispute.rb +48 -28
  21. data/lib/airwallex/resources/funds_split.rb +37 -0
  22. data/lib/airwallex/resources/global_account.rb +137 -0
  23. data/lib/airwallex/resources/global_account_alias.rb +62 -0
  24. data/lib/airwallex/resources/global_account_mandate.rb +26 -0
  25. data/lib/airwallex/resources/global_account_transaction.rb +7 -0
  26. data/lib/airwallex/resources/payment_consent.rb +68 -0
  27. data/lib/airwallex/resources/payment_method.rb +13 -5
  28. data/lib/airwallex/resources/payment_source.rb +52 -0
  29. data/lib/airwallex/resources/quote.rb +5 -3
  30. data/lib/airwallex/resources/rate.rb +2 -7
  31. data/lib/airwallex/version.rb +1 -1
  32. data/lib/airwallex/webhook.rb +6 -2
  33. data/lib/airwallex.rb +15 -0
  34. 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
@@ -37,7 +37,7 @@ module Airwallex
37
37
  extend APIOperations::List
38
38
 
39
39
  def self.resource_path
40
- "/api/v1/conversions"
40
+ "/api/v1/fx/conversions"
41
41
  end
42
42
  end
43
43
  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
- # metadata: { internal_id: "user_789" }
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, submit evidence to challenge them, or accept them.
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/disputes"
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] The updated dispute object
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
- # Submit evidence to challenge a dispute
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
- # @return [Airwallex::Dispute] The updated dispute object
56
- def submit_evidence(evidence)
57
- response = Airwallex.client.post("#{resource_path}/#{id}/evidence", evidence)
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 resource_path
65
- self.class.resource_path
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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airwallex
4
+ # Read-only representation of a single Global Account inbound transaction
5
+ class GlobalAccountTransaction < APIResource
6
+ end
7
+ 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