airwallex 0.9.0 → 0.10.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 +20 -0
- data/README.md +33 -3
- data/lib/airwallex/list_object.rb +10 -3
- data/lib/airwallex/resources/rfi.rb +73 -15
- data/lib/airwallex/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '018417ed2b3091ca1c5eafdf99df79c1a81be1d13ddda2524a917d2bc9a2c3ff'
|
|
4
|
+
data.tar.gz: 0abdb483ebe91136353cc77efb5023cb1d0a05b3fd986c8c3ba59750dfeccfdd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b48a815ca9ca948b4695cfa28b12e70c5dc21a7c6ff37781d20fe0b2f219b12daf205ec997eaaf51eca19bce49d3ffd0fbb890773cd99f5573c4733680e1ec6
|
|
7
|
+
data.tar.gz: ff66015473fee32cd8ec4bd7cdbb03072b340111d1c354db181c07e0722c936d7b14565210eabd6583f8cd223be9f4c96889b1e329a79b41709ed4626b474427
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.10.0] - 2026-09-29
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `RFI.retrieve` (`GET /api/v1/rfis/{id}`) and `RFI.list` (`GET /api/v1/rfis`, filterable by
|
|
8
|
+
`statuses`, `types`, `from_created_at`, `end_created_at`, `page_size`). Both take
|
|
9
|
+
`headers:` for `x-on-behalf-of`
|
|
10
|
+
- `RFI.list` supports bookmark pagination: `has_more`/`next_cursor` come from `page_after`, and
|
|
11
|
+
`next_page`/`auto_paging_each` request `page: <page_after>`
|
|
12
|
+
- `ListObject` accepts `cursor_param:` (defaults to `:next_cursor`) and `opts:`, and forwards
|
|
13
|
+
the original `opts` (e.g. `headers:`) to subsequent pages. Resources that don't pass these
|
|
14
|
+
behave exactly as before
|
|
15
|
+
- `RFI.simulate_create`/`.simulate_close`/`.simulate_follow_up` and the instance
|
|
16
|
+
`#simulate_close`/`#simulate_follow_up` accept an optional trailing `opts` hash with `headers:`
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `RFI.resource_path` is now `/api/v1/rfis`; the simulation path moved to `RFI::SIMULATION_PATH`
|
|
21
|
+
(`/api/v1/simulation/rfis`). Simulation methods hit the same URLs as before
|
|
22
|
+
|
|
3
23
|
## [0.9.0] - 2026-09-09
|
|
4
24
|
|
|
5
25
|
### Added
|
data/README.md
CHANGED
|
@@ -16,6 +16,7 @@ This gem provides a Ruby interface to Airwallex's payment infrastructure, design
|
|
|
16
16
|
- **Billing**: Products, prices, billing customers, and subscriptions for recurring/instalment billing
|
|
17
17
|
- **Recurring Payments**: Payment consents and payment sources for merchant-initiated (off-session) charges
|
|
18
18
|
- **Scale**: Connected accounts, funds splits, and charges for platforms onboarding sub-merchants
|
|
19
|
+
- **Compliance**: Retrieve and list Requests for Information (RFIs), including for connected accounts
|
|
19
20
|
- **Sandbox Simulations**: Deposits, issuing transactions, disputes, transfer status, Direct Debit mandates, connected account/offboarding review outcomes, RFIs, and POS terminals — see [Sandbox Simulations](#sandbox-simulations)
|
|
20
21
|
- **Idempotency**: Automatic request deduplication for safe retries
|
|
21
22
|
- **Pagination**: Unified interface over cursor-based and offset-based pagination
|
|
@@ -417,6 +418,26 @@ split.release
|
|
|
417
418
|
charge = Airwallex::Charge.retrieve('charge_id')
|
|
418
419
|
```
|
|
419
420
|
|
|
421
|
+
### Requests for Information (RFIs)
|
|
422
|
+
|
|
423
|
+
RFIs are compliance questions Airwallex raises against an account. They use
|
|
424
|
+
bookmark pagination (`page_after`/`page`), which `ListObject` handles for you,
|
|
425
|
+
and connected accounts are addressed with `x-on-behalf-of`, which is kept
|
|
426
|
+
across pages. The API key needs the **Risk → Request for Information (RFI):
|
|
427
|
+
Read** permission, or Airwallex responds with 401 (`Airwallex::AuthenticationError`).
|
|
428
|
+
|
|
429
|
+
```ruby
|
|
430
|
+
headers = { 'x-on-behalf-of' => 'acct_123' }
|
|
431
|
+
|
|
432
|
+
rfi = Airwallex::RFI.retrieve('rfi_123', headers: headers)
|
|
433
|
+
rfi.status # => "ACTION_REQUIRED"
|
|
434
|
+
rfi.active_request[:questions].first # => { id: ..., title: { en: ... }, answer: { type: "ATTACHMENT" }, ... }
|
|
435
|
+
|
|
436
|
+
# statuses/types are comma-separated strings; page_size is 1-2000 (default 100)
|
|
437
|
+
Airwallex::RFI.list({ statuses: 'ACTION_REQUIRED', types: 'KYC,KYC_ONGOING' }, headers: headers)
|
|
438
|
+
.auto_paging_each { |r| puts r.id }
|
|
439
|
+
```
|
|
440
|
+
|
|
420
441
|
### Sandbox Simulations
|
|
421
442
|
|
|
422
443
|
Sandbox-only endpoints (`/api/v1/simulation/...`) that stand in for the bank,
|
|
@@ -621,6 +642,14 @@ rfi.simulate_follow_up(questions: [{ answer: { type: 'TEXT' } }])
|
|
|
621
642
|
# Class-level equivalents take an rfi_id directly instead of an instance
|
|
622
643
|
Airwallex::RFI.simulate_close('rfi_123')
|
|
623
644
|
Airwallex::RFI.simulate_follow_up('rfi_123', questions: [{ answer: { type: 'TEXT' } }])
|
|
645
|
+
|
|
646
|
+
# Raise an RFI against a connected account — params need braces when
|
|
647
|
+
# followed by opts. Fails with BadRequestError ("ongoing RFI case exists")
|
|
648
|
+
# if the account already has an open RFI.
|
|
649
|
+
Airwallex::RFI.simulate_create(
|
|
650
|
+
{ type: 'KYC', questions: [{ answer: { type: 'TEXT' } }] },
|
|
651
|
+
headers: { 'x-on-behalf-of' => 'acct_123' }
|
|
652
|
+
)
|
|
624
653
|
```
|
|
625
654
|
|
|
626
655
|
#### POS Terminals
|
|
@@ -679,7 +708,8 @@ transfer = Airwallex::Transfer.create(
|
|
|
679
708
|
|
|
680
709
|
### Pagination
|
|
681
710
|
|
|
682
|
-
Unified interface across
|
|
711
|
+
Unified interface across cursor-based, offset-based, and bookmark-based (RFI) endpoints.
|
|
712
|
+
Any `headers:` passed to `.list` (e.g. `x-on-behalf-of`) are kept on every page:
|
|
683
713
|
|
|
684
714
|
```ruby
|
|
685
715
|
# Auto-pagination with enumerable
|
|
@@ -852,8 +882,8 @@ end
|
|
|
852
882
|
- Cardholder (simulate_pass_review)
|
|
853
883
|
- **In-Person Payments** (sandbox Simulation only):
|
|
854
884
|
- POSTerminal (simulate_turn_on, simulate_turn_off, simulate_generate_activation_code, simulate_confirm_payment_intent, simulate_payment_scenarios)
|
|
855
|
-
- **Compliance
|
|
856
|
-
- RFI (simulate_create, simulate_close, simulate_follow_up)
|
|
885
|
+
- **Compliance**:
|
|
886
|
+
- RFI (retrieve, list, plus sandbox simulate_create, simulate_close, simulate_follow_up)
|
|
857
887
|
- **Webhooks**: Event handling, HMAC-SHA256 signature verification
|
|
858
888
|
|
|
859
889
|
### Coming in Future Versions
|
|
@@ -6,12 +6,19 @@ module Airwallex
|
|
|
6
6
|
|
|
7
7
|
attr_reader :data, :has_more, :next_cursor
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# @param cursor_param [Symbol] query param the next cursor is sent as
|
|
10
|
+
# (e.g. :page for bookmark-paginated endpoints like RFI)
|
|
11
|
+
# @param opts [Hash] the original request opts (e.g. headers:), forwarded
|
|
12
|
+
# to every subsequent page so x-on-behalf-of etc. are kept
|
|
13
|
+
def initialize(data:, has_more:, resource_class:, next_cursor: nil, params: {}, # rubocop:disable Metrics/ParameterLists
|
|
14
|
+
cursor_param: :next_cursor, opts: {})
|
|
10
15
|
@data = data.map { |item| resource_class.new(item) }
|
|
11
16
|
@has_more = has_more
|
|
12
17
|
@next_cursor = next_cursor
|
|
13
18
|
@resource_class = resource_class
|
|
14
19
|
@params = params
|
|
20
|
+
@cursor_param = cursor_param
|
|
21
|
+
@opts = opts
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
def each(&)
|
|
@@ -49,7 +56,7 @@ module Airwallex
|
|
|
49
56
|
|
|
50
57
|
if @next_cursor
|
|
51
58
|
# Cursor-based pagination
|
|
52
|
-
next_params[
|
|
59
|
+
next_params[@cursor_param] = @next_cursor
|
|
53
60
|
else
|
|
54
61
|
# Offset-based pagination
|
|
55
62
|
page_size = @params[:page_size] || @params[:limit] || 20
|
|
@@ -57,7 +64,7 @@ module Airwallex
|
|
|
57
64
|
next_params[:offset] = current_offset + page_size
|
|
58
65
|
end
|
|
59
66
|
|
|
60
|
-
@resource_class.list(next_params)
|
|
67
|
+
@opts.empty? ? @resource_class.list(next_params) : @resource_class.list(next_params, @opts)
|
|
61
68
|
end
|
|
62
69
|
|
|
63
70
|
# Automatically iterate through all pages
|
|
@@ -5,11 +5,31 @@ module Airwallex
|
|
|
5
5
|
# Airwallex raises against your account (KYC, an ongoing KYC review, a
|
|
6
6
|
# cardholder, a transaction, payment enablement, or merchant risk).
|
|
7
7
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
8
|
+
# Supports retrieving and listing real RFIs, plus the sandbox Simulation
|
|
9
|
+
# endpoints for raising, closing and following up on them (there is no
|
|
10
|
+
# live create — real RFIs originate from Airwallex's own compliance
|
|
11
|
+
# review, same as Dispute). See
|
|
12
|
+
# https://www.airwallex.com/docs/api/risk/request_for_information_rfi and
|
|
11
13
|
# https://www.airwallex.com/docs/api/simulation/request-for-information
|
|
12
14
|
#
|
|
15
|
+
# Statuses: ACTION_REQUIRED, ANSWERED, CLOSED. Types: KYC, KYC_ONGOING,
|
|
16
|
+
# CARDHOLDER, TRANSACTION, PAYMENT_ENABLEMENT, MERCHANT_RISK.
|
|
17
|
+
#
|
|
18
|
+
# Reading RFIs requires the API key's "Risk → Request for Information
|
|
19
|
+
# (RFI): Read" permission — without it Airwallex returns 401
|
|
20
|
+
# (AuthenticationError).
|
|
21
|
+
#
|
|
22
|
+
# @example Retrieve an RFI for a connected account
|
|
23
|
+
# rfi = Airwallex::RFI.retrieve("rfi_123", headers: { "x-on-behalf-of" => "acct_123" })
|
|
24
|
+
# rfi.status # => "ACTION_REQUIRED"
|
|
25
|
+
# rfi.active_request[:questions].first # => { id: ..., title: { en: ... }, ... }
|
|
26
|
+
#
|
|
27
|
+
# @example List open RFIs, following every page
|
|
28
|
+
# Airwallex::RFI.list(
|
|
29
|
+
# { statuses: "ACTION_REQUIRED" },
|
|
30
|
+
# headers: { "x-on-behalf-of" => "acct_123" }
|
|
31
|
+
# ).auto_paging_each { |rfi| puts rfi.id }
|
|
32
|
+
#
|
|
13
33
|
# @example Raise a KYC RFI, then close it
|
|
14
34
|
# rfi = Airwallex::RFI.simulate_create(
|
|
15
35
|
# type: "KYC",
|
|
@@ -17,9 +37,42 @@ module Airwallex
|
|
|
17
37
|
# )
|
|
18
38
|
# rfi.simulate_close
|
|
19
39
|
class RFI < APIResource
|
|
20
|
-
|
|
40
|
+
extend APIOperations::Retrieve
|
|
41
|
+
extend APIOperations::List
|
|
42
|
+
|
|
43
|
+
# Sandbox-only — see https://www.airwallex.com/docs/api/simulation/request-for-information
|
|
44
|
+
SIMULATION_PATH = "/api/v1/simulation/rfis"
|
|
45
|
+
|
|
46
|
+
# @return [String] API resource path for RFIs
|
|
21
47
|
def self.resource_path
|
|
22
|
-
"/api/v1/
|
|
48
|
+
"/api/v1/rfis"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# List RFIs. Unlike most resources, RFIs use bookmark pagination: the
|
|
52
|
+
# response carries page_after/page_before instead of has_more/next_cursor,
|
|
53
|
+
# and the next page is requested with page: <page_after>.
|
|
54
|
+
#
|
|
55
|
+
# @param params [Hash] page: (bookmark), page_size: (1-2000, default
|
|
56
|
+
# 100), statuses: / types: (comma-separated strings, e.g.
|
|
57
|
+
# "ACTION_REQUIRED,ANSWERED"), from_created_at: / end_created_at:
|
|
58
|
+
# (ISO8601)
|
|
59
|
+
# @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id }),
|
|
60
|
+
# kept on every subsequent page
|
|
61
|
+
# @return [ListObject<RFI>]
|
|
62
|
+
def self.list(params = {}, opts = {})
|
|
63
|
+
response = Airwallex.client.get(resource_path, params, opts[:headers] || {})
|
|
64
|
+
items = extract_data(response)
|
|
65
|
+
page_after = response.is_a?(Hash) ? response[:page_after] || response["page_after"] : nil
|
|
66
|
+
|
|
67
|
+
ListObject.new(
|
|
68
|
+
data: items,
|
|
69
|
+
has_more: !page_after.nil? && !items.empty?,
|
|
70
|
+
next_cursor: page_after,
|
|
71
|
+
cursor_param: :page,
|
|
72
|
+
resource_class: self,
|
|
73
|
+
params: params,
|
|
74
|
+
opts: opts
|
|
75
|
+
)
|
|
23
76
|
end
|
|
24
77
|
|
|
25
78
|
# Simulate Airwallex raising an RFI
|
|
@@ -28,18 +81,20 @@ module Airwallex
|
|
|
28
81
|
# "CARDHOLDER", "TRANSACTION", "PAYMENT_ENABLEMENT",
|
|
29
82
|
# "MERCHANT_RISK"); questions: (required, array of
|
|
30
83
|
# { answer: { type: }, sources: [] })
|
|
84
|
+
# @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id })
|
|
31
85
|
# @return [RFI]
|
|
32
|
-
def self.simulate_create(params = {})
|
|
33
|
-
response = Airwallex.client.post("#{
|
|
86
|
+
def self.simulate_create(params = {}, opts = {})
|
|
87
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/create", params, opts[:headers] || {})
|
|
34
88
|
new(response)
|
|
35
89
|
end
|
|
36
90
|
|
|
37
91
|
# Simulate closing an RFI
|
|
38
92
|
#
|
|
39
93
|
# @param rfi_id [String]
|
|
94
|
+
# @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id })
|
|
40
95
|
# @return [RFI]
|
|
41
|
-
def self.simulate_close(rfi_id)
|
|
42
|
-
response = Airwallex.client.post("#{
|
|
96
|
+
def self.simulate_close(rfi_id, opts = {})
|
|
97
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{rfi_id}/close", {}, opts[:headers] || {})
|
|
43
98
|
new(response)
|
|
44
99
|
end
|
|
45
100
|
|
|
@@ -49,17 +104,19 @@ module Airwallex
|
|
|
49
104
|
# @param rfi_id [String]
|
|
50
105
|
# @param params [Hash] questions: (required, array of
|
|
51
106
|
# { id: } or { answer: { type: }, sources: [] })
|
|
107
|
+
# @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id })
|
|
52
108
|
# @return [RFI]
|
|
53
|
-
def self.simulate_follow_up(rfi_id, params = {})
|
|
54
|
-
response = Airwallex.client.post("#{
|
|
109
|
+
def self.simulate_follow_up(rfi_id, params = {}, opts = {})
|
|
110
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{rfi_id}/follow_up", params, opts[:headers] || {})
|
|
55
111
|
new(response)
|
|
56
112
|
end
|
|
57
113
|
|
|
58
114
|
# Simulate closing this RFI
|
|
59
115
|
#
|
|
116
|
+
# @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id })
|
|
60
117
|
# @return [RFI] self
|
|
61
|
-
def simulate_close
|
|
62
|
-
response = Airwallex.client.post("#{
|
|
118
|
+
def simulate_close(opts = {})
|
|
119
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{id}/close", {}, opts[:headers] || {})
|
|
63
120
|
refresh_from(response)
|
|
64
121
|
self
|
|
65
122
|
end
|
|
@@ -67,9 +124,10 @@ module Airwallex
|
|
|
67
124
|
# Simulate a follow-up on this RFI
|
|
68
125
|
#
|
|
69
126
|
# @param params [Hash] questions: (required)
|
|
127
|
+
# @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id })
|
|
70
128
|
# @return [RFI] self
|
|
71
|
-
def simulate_follow_up(params = {})
|
|
72
|
-
response = Airwallex.client.post("#{
|
|
129
|
+
def simulate_follow_up(params = {}, opts = {})
|
|
130
|
+
response = Airwallex.client.post("#{SIMULATION_PATH}/#{id}/follow_up", params, opts[:headers] || {})
|
|
73
131
|
refresh_from(response)
|
|
74
132
|
self
|
|
75
133
|
end
|
data/lib/airwallex/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: airwallex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chayut Orapinpatipat
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -144,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
145
|
version: '0'
|
|
146
146
|
requirements: []
|
|
147
|
-
rubygems_version: 3.
|
|
147
|
+
rubygems_version: 3.5.16
|
|
148
148
|
signing_key:
|
|
149
149
|
specification_version: 4
|
|
150
150
|
summary: Production-grade Ruby client for the Airwallex API
|