airwallex 0.9.0 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fc42ac04f0af0416fc2fe200f747a17ec326ea5b071a40d5965043da1ebfd7c
4
- data.tar.gz: bfc8c907e1ce9d0e702e124bdf0c85f1fb8827f7c90b0b4382665b5a4f1ade00
3
+ metadata.gz: f1288d75bff8a6f00a8c0dfffc318b6df1821cb26c945e062930de6035e83822
4
+ data.tar.gz: 5452a4a8aed172764129f05043646e9f074c1c9e62df84246ffe6ff575ba0140
5
5
  SHA512:
6
- metadata.gz: 952b7120270923df264d701e631570a673584fd08f73502dc3ec36ff19bdd47577d6d799928cdae40781813cbd5f881de7ce4dcca3488e10ee5ff4d46f67da76
7
- data.tar.gz: 440bfe68c53e17f2a5b30856f6de51257fffdc32c38e459ebe82d8604c9b018d58ee6369249bc2e2907701f0c79f43bdd9005c08eaaf19aa9ceb81486a2e9999
6
+ metadata.gz: 02e3eea25661113f96af469d3154ede00aae144c671c42fcdf48d2e08d900c74ff2291b9b901ebc2b2072688b840d07d58f6b7eb21bd14aaf2c75d2e4625e6f2
7
+ data.tar.gz: 31bb06a6c4a99f4d433c63b5eb9e209e31deeec63dd3d98a516d259801530e874b66b70d4680d386eba0e569589a55e51c59640af3907fbade0e1da9171b6698
data/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.11.0] - 2026-10-01
4
+
5
+ ### Added
6
+
7
+ - `FundsSplitReversal` resource (`.create`, `.retrieve`, `.list`) for
8
+ `/api/v1/pa/funds_split_reversals`, which moves split funds back from a
9
+ connected account to the platform. `.list` takes the required `funds_split_id:`.
10
+ Partial reversals are allowed until the full split is reversed
11
+ - `ConnectedAccountTransfer` resource (`.create`, `.retrieve`, `.list`) for
12
+ `/api/v1/connected_account_transfers`, which moves funds between the platform
13
+ and a connected account. Pass `headers: { "x-on-behalf-of" => ... }` to
14
+ debit a connected account instead of the platform
15
+ - `Transfer.simulate_transition`, `Transfer#simulate_transition` and
16
+ `Transfer#cancel` accept `headers:`. A transfer created `x-on-behalf-of` a
17
+ connected account can only be found with that same header
18
+
19
+ ### Fixed
20
+
21
+ - `FundsSplit` docs (README and class comment) now show the real create body:
22
+ `request_id`, `source_id`, `source_type: "PAYMENT_INTENT"`, `amount` (string),
23
+ `destination`, `auto_release`, `metadata`, with one split per destination.
24
+ The docs previously showed `payment_intent_id:` and `splits: [...]`
25
+ - Documented that `funds_split.*` webhook payloads use `split_id` (not `id`)
26
+ and carry no `request_id`, and that the sandbox rejects a PROCESSING -> PAID
27
+ transfer transition (go through SENT)
28
+
29
+ ## [0.10.0] - 2026-09-29
30
+
31
+ ### Added
32
+
33
+ - `RFI.retrieve` (`GET /api/v1/rfis/{id}`) and `RFI.list` (`GET /api/v1/rfis`, filterable by
34
+ `statuses`, `types`, `from_created_at`, `end_created_at`, `page_size`). Both take
35
+ `headers:` for `x-on-behalf-of`
36
+ - `RFI.list` supports bookmark pagination: `has_more`/`next_cursor` come from `page_after`, and
37
+ `next_page`/`auto_paging_each` request `page: <page_after>`
38
+ - `ListObject` accepts `cursor_param:` (defaults to `:next_cursor`) and `opts:`, and forwards
39
+ the original `opts` (e.g. `headers:`) to subsequent pages. Resources that don't pass these
40
+ behave exactly as before
41
+ - `RFI.simulate_create`/`.simulate_close`/`.simulate_follow_up` and the instance
42
+ `#simulate_close`/`#simulate_follow_up` accept an optional trailing `opts` hash with `headers:`
43
+
44
+ ### Changed
45
+
46
+ - `RFI.resource_path` is now `/api/v1/rfis`; the simulation path moved to `RFI::SIMULATION_PATH`
47
+ (`/api/v1/simulation/rfis`). Simulation methods hit the same URLs as before
48
+
3
49
  ## [0.9.0] - 2026-09-09
4
50
 
5
51
  ### 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
@@ -374,7 +375,7 @@ payment_source = Airwallex::PaymentSource.create(
374
375
  )
375
376
  ```
376
377
 
377
- ### Scale (Connected Accounts, Funds Splits, Charges)
378
+ ### Scale (Connected Accounts, Funds Splits, Reversals, Connected Account Transfers, Charges)
378
379
 
379
380
  For platforms onboarding sub-merchants:
380
381
 
@@ -406,17 +407,76 @@ connected_account.submit
406
407
  Airwallex::ConnectedAccount.current
407
408
  Airwallex::ConnectedAccount.wallet_info
408
409
 
409
- # Split a charge's funds between the platform and a connected account
410
+ # Split part of a PaymentIntent's funds to a connected account. One split per
411
+ # destination: create several splits to pay several connected accounts.
412
+ # A reused request_id raises BadRequestError (duplicate_request); splitting
413
+ # more than the PaymentIntent amount raises amount_above_limit.
410
414
  split = Airwallex::FundsSplit.create(
411
- payment_intent_id: payment_intent.id,
412
- splits: [{ account_id: connected_account.id, amount: 10.00 }]
415
+ request_id: 'split-order-123', # required, max 64
416
+ source_id: payment_intent.id, # required
417
+ source_type: 'PAYMENT_INTENT', # required
418
+ amount: '10.00', # required, string
419
+ destination: connected_account.id,
420
+ auto_release: false, # optional, default true
421
+ metadata: { order_id: '123' } # optional
422
+ )
423
+ split.release # only needed when auto_release: false
424
+
425
+ # Reverse split funds back to the platform. Partial reversals are allowed
426
+ # until the full split is reversed. Statuses: CREATED, RELEASED, SETTLED
427
+ reversal = Airwallex::FundsSplitReversal.create(
428
+ request_id: 'rev-order-123', # required, max 64
429
+ funds_split_id: split.id, # required
430
+ amount: '5.00', # required, string
431
+ metadata: { reason: 'refund' } # optional
413
432
  )
414
- split.release
433
+ Airwallex::FundsSplitReversal.retrieve(reversal.id)
434
+ Airwallex::FundsSplitReversal.list(funds_split_id: split.id) # funds_split_id is required
435
+
436
+ # Move funds between the platform and a connected account's wallet. Pass
437
+ # x-on-behalf-of to debit a connected account instead of the platform.
438
+ # Statuses: NEW, PENDING, SETTLED, SUSPENDED, FAILED
439
+ cat = Airwallex::ConnectedAccountTransfer.create(
440
+ request_id: 'top-up-123', # required, 1-50
441
+ amount: '100.00',
442
+ currency: 'AUD',
443
+ destination: connected_account.id,
444
+ reason: 'transfer_to_own_account',
445
+ reference: 'Ledger top-up' # required, 1-140
446
+ )
447
+ Airwallex::ConnectedAccountTransfer.retrieve(cat.id)
448
+ Airwallex::ConnectedAccountTransfer.list(destination: connected_account.id, status: 'SETTLED')
449
+
450
+ # Pay out from a connected account: create the Transfer on its behalf
451
+ Airwallex::Transfer.create(transfer_params, headers: { 'x-on-behalf-of' => connected_account.id })
415
452
 
416
453
  # Charges (create, retrieve, list)
417
454
  charge = Airwallex::Charge.retrieve('charge_id')
418
455
  ```
419
456
 
457
+ `funds_split.*` webhook payloads name the split `split_id`, not `id`, and carry
458
+ no `request_id`: `{ split_id, status, amount, currency, source_id, source_type, destination, ... }`.
459
+
460
+ ### Requests for Information (RFIs)
461
+
462
+ RFIs are compliance questions Airwallex raises against an account. They use
463
+ bookmark pagination (`page_after`/`page`), which `ListObject` handles for you,
464
+ and connected accounts are addressed with `x-on-behalf-of`, which is kept
465
+ across pages. The API key needs the **Risk → Request for Information (RFI):
466
+ Read** permission, or Airwallex responds with 401 (`Airwallex::AuthenticationError`).
467
+
468
+ ```ruby
469
+ headers = { 'x-on-behalf-of' => 'acct_123' }
470
+
471
+ rfi = Airwallex::RFI.retrieve('rfi_123', headers: headers)
472
+ rfi.status # => "ACTION_REQUIRED"
473
+ rfi.active_request[:questions].first # => { id: ..., title: { en: ... }, answer: { type: "ATTACHMENT" }, ... }
474
+
475
+ # statuses/types are comma-separated strings; page_size is 1-2000 (default 100)
476
+ Airwallex::RFI.list({ statuses: 'ACTION_REQUIRED', types: 'KYC,KYC_ONGOING' }, headers: headers)
477
+ .auto_paging_each { |r| puts r.id }
478
+ ```
479
+
420
480
  ### Sandbox Simulations
421
481
 
422
482
  Sandbox-only endpoints (`/api/v1/simulation/...`) that stand in for the bank,
@@ -534,8 +594,18 @@ transfer.simulate_transition(next_status: 'FAILED', failure_type: 'INSUFFICIENT_
534
594
 
535
595
  # Class-level equivalent takes a transfer_id directly instead of an instance
536
596
  Airwallex::Transfer.simulate_transition('tfr_123', next_status: 'CANCELLED')
597
+
598
+ # A transfer created x-on-behalf-of a connected account can only be found
599
+ # with the same header (cancel takes it too)
600
+ headers = { 'x-on-behalf-of' => 'acct_123' }
601
+ transfer.simulate_transition(next_status: 'SENT', headers: headers)
602
+ Airwallex::Transfer.simulate_transition('tfr_123', next_status: 'PAID', headers: headers)
603
+ transfer.cancel(headers: headers)
537
604
  ```
538
605
 
606
+ The sandbox rejects PROCESSING -> PAID with a 500 `operation_failed`. Move the
607
+ transfer to SENT first.
608
+
539
609
  #### Connected Accounts & Offboarding
540
610
 
541
611
  ```ruby
@@ -621,6 +691,14 @@ rfi.simulate_follow_up(questions: [{ answer: { type: 'TEXT' } }])
621
691
  # Class-level equivalents take an rfi_id directly instead of an instance
622
692
  Airwallex::RFI.simulate_close('rfi_123')
623
693
  Airwallex::RFI.simulate_follow_up('rfi_123', questions: [{ answer: { type: 'TEXT' } }])
694
+
695
+ # Raise an RFI against a connected account — params need braces when
696
+ # followed by opts. Fails with BadRequestError ("ongoing RFI case exists")
697
+ # if the account already has an open RFI.
698
+ Airwallex::RFI.simulate_create(
699
+ { type: 'KYC', questions: [{ answer: { type: 'TEXT' } }] },
700
+ headers: { 'x-on-behalf-of' => 'acct_123' }
701
+ )
624
702
  ```
625
703
 
626
704
  #### POS Terminals
@@ -679,7 +757,8 @@ transfer = Airwallex::Transfer.create(
679
757
 
680
758
  ### Pagination
681
759
 
682
- Unified interface across both cursor-based and offset-based endpoints:
760
+ Unified interface across cursor-based, offset-based, and bookmark-based (RFI) endpoints.
761
+ Any `headers:` passed to `.list` (e.g. `x-on-behalf-of`) are kept on every page:
683
762
 
684
763
  ```ruby
685
764
  # Auto-pagination with enumerable
@@ -716,6 +795,10 @@ begin
716
795
  handle_successful_payment(event.data)
717
796
  when 'payout.transfer.failed'
718
797
  handle_failed_payout(event.data)
798
+ when 'funds_split.settled'
799
+ # funds_split.* payloads identify the split by split_id (not id) and
800
+ # carry no request_id — see the Scale section
801
+ handle_settled_split(event.data)
719
802
  end
720
803
  rescue Airwallex::SignatureVerificationError => e
721
804
  # Invalid signature
@@ -819,7 +902,7 @@ end
819
902
  - Customer (create, retrieve, list, update, delete)
820
903
  - Dispute (retrieve, list, accept, challenge, related_payment_intents, simulate_create, simulate_escalate, simulate_resolve)
821
904
  - **Payouts**:
822
- - Transfer (create, retrieve, list, cancel, simulate_transition)
905
+ - Transfer (create, retrieve, list, cancel, simulate_transition; cancel/simulate_transition take headers: for x-on-behalf-of)
823
906
  - Beneficiary (create, retrieve, list, update, delete, validate, verify_account, api_schema, form_schema, supported_financial_institutions)
824
907
  - BatchTransfer (create, retrieve, list)
825
908
  - **Foreign Exchange & Multi-Currency**:
@@ -846,14 +929,16 @@ end
846
929
  - AccountAmendment (create, retrieve, simulate_approve, simulate_reject) - requires Admin-level API key permissions
847
930
  - AccountOffboarding — sandbox Simulation only, scoped to a parent ConnectedAccount (simulate_complete, simulate_cancel)
848
931
  - FundsSplit (create, retrieve, list, release)
932
+ - FundsSplitReversal (create, retrieve, list)
933
+ - ConnectedAccountTransfer (create, retrieve, list)
849
934
  - Charge (create, retrieve, list)
850
935
  - **Issuing** (sandbox Simulation only — no live Card/Cardholder resources yet):
851
936
  - IssuingTransaction (simulate_create with single_phase, simulate_capture, simulate_reverse, simulate_refund, simulate_notify_three_ds)
852
937
  - Cardholder (simulate_pass_review)
853
938
  - **In-Person Payments** (sandbox Simulation only):
854
939
  - POSTerminal (simulate_turn_on, simulate_turn_off, simulate_generate_activation_code, simulate_confirm_payment_intent, simulate_payment_scenarios)
855
- - **Compliance** (sandbox Simulation only):
856
- - RFI (simulate_create, simulate_close, simulate_follow_up)
940
+ - **Compliance**:
941
+ - RFI (retrieve, list, plus sandbox simulate_create, simulate_close, simulate_follow_up)
857
942
  - **Webhooks**: Event handling, HMAC-SHA256 signature verification
858
943
 
859
944
  ### Coming in Future Versions
@@ -6,12 +6,19 @@ module Airwallex
6
6
 
7
7
  attr_reader :data, :has_more, :next_cursor
8
8
 
9
- def initialize(data:, has_more:, resource_class:, next_cursor: nil, params: {})
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[:next_cursor] = @next_cursor
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
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airwallex
4
+ # Represents a Connected Account Transfer — moves funds between the platform
5
+ # account and a connected account's wallet. Send x-on-behalf-of to debit a
6
+ # connected account instead of the platform.
7
+ #
8
+ # Statuses: NEW, PENDING, SETTLED, SUSPENDED, FAILED. Create errors include
9
+ # insufficient_fund and request_id_duplicate.
10
+ #
11
+ # @example Fund a connected account from the platform
12
+ # transfer = Airwallex::ConnectedAccountTransfer.create(
13
+ # request_id: "cat-req-123", # required, 1-50
14
+ # amount: "100.00",
15
+ # currency: "AUD",
16
+ # destination: "acct_123", # connected account id
17
+ # reason: "transfer_to_own_account",
18
+ # reference: "Ledger top-up" # required, 1-140
19
+ # )
20
+ #
21
+ # @example List settled transfers to an account
22
+ # Airwallex::ConnectedAccountTransfer.list(destination: "acct_123", status: "SETTLED")
23
+ class ConnectedAccountTransfer < APIResource
24
+ extend APIOperations::Create
25
+ extend APIOperations::Retrieve
26
+ extend APIOperations::List
27
+
28
+ # @return [String] API resource path for connected account transfers
29
+ def self.resource_path
30
+ "/api/v1/connected_account_transfers"
31
+ end
32
+ end
33
+ end
@@ -1,18 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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).
4
+ # Represents a Funds Split — moves part of a PaymentIntent's funds from the
5
+ # platform account to a Connected Account. Each split has exactly one
6
+ # destination; create one split per connected account.
7
+ #
8
+ # Create errors include duplicate_request (reused request_id) and
9
+ # amount_above_limit (splits exceed the PaymentIntent amount).
10
+ #
11
+ # funds_split.* webhook payloads name the split split_id, not id, and carry
12
+ # no request_id: { split_id:, status:, amount:, currency:, source_id:,
13
+ # source_type:, destination:, ... }.
7
14
  #
8
15
  # @example Create a split
9
16
  # split = Airwallex::FundsSplit.create(
10
- # payment_intent_id: intent.id,
11
- # splits: [{ account_id: connected_account.id, amount: 50.00 }]
17
+ # request_id: "split-req-123", # required, max 64
18
+ # source_id: intent.id, # required, PaymentIntent id
19
+ # source_type: "PAYMENT_INTENT", # required
20
+ # amount: "50.00", # required, string
21
+ # destination: connected_account.id,
22
+ # auto_release: true, # optional, default true
23
+ # metadata: { order_id: "123" } # optional
12
24
  # )
13
25
  #
14
- # @example Release the split funds
26
+ # @example Release the split funds (when created with auto_release: false)
15
27
  # split.release
28
+ #
29
+ # @see FundsSplitReversal to move split funds back to the platform
16
30
  class FundsSplit < APIResource
17
31
  extend APIOperations::Create
18
32
  extend APIOperations::Retrieve
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airwallex
4
+ # Represents a Funds Split Reversal — moves previously split funds back from
5
+ # the connected account (the split's destination) to the platform account.
6
+ # Partial reversals are allowed until the full split amount is reversed.
7
+ #
8
+ # Statuses: CREATED, RELEASED, SETTLED.
9
+ #
10
+ # @example Reverse part of a split
11
+ # reversal = Airwallex::FundsSplitReversal.create(
12
+ # request_id: "rev-req-123", # required, max 64
13
+ # funds_split_id: split.id, # required
14
+ # amount: "5.00", # required, string
15
+ # metadata: { reason: "refund" }
16
+ # )
17
+ # reversal.status # => "CREATED"
18
+ #
19
+ # @example List the reversals of a split
20
+ # Airwallex::FundsSplitReversal.list(funds_split_id: split.id)
21
+ class FundsSplitReversal < APIResource
22
+ extend APIOperations::Create
23
+ extend APIOperations::Retrieve
24
+ extend APIOperations::List
25
+
26
+ # @return [String] API resource path for funds split reversals
27
+ def self.resource_path
28
+ "/api/v1/pa/funds_split_reversals"
29
+ end
30
+ end
31
+ end
@@ -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
- # 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
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
- # @return [String] API resource path for simulated RFI actions
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/simulation/rfis"
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("#{resource_path}/create", params)
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("#{resource_path}/#{rfi_id}/close", {})
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("#{resource_path}/#{rfi_id}/follow_up", params)
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("#{self.class.resource_path}/#{id}/close", {})
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("#{self.class.resource_path}/#{id}/follow_up", params)
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
@@ -14,10 +14,14 @@ module Airwallex
14
14
  end
15
15
 
16
16
  # Cancel a pending transfer
17
- def cancel
17
+ #
18
+ # @param opts [Hash] headers: (e.g. { "x-on-behalf-of" => account_id })
19
+ # @return [Transfer] self
20
+ def cancel(opts = {})
18
21
  response = Airwallex.client.post(
19
22
  "#{self.class.resource_path}/#{id}/cancel",
20
- {}
23
+ {},
24
+ opts[:headers] || {}
21
25
  )
22
26
  refresh_from(response)
23
27
  self
@@ -25,18 +29,21 @@ module Airwallex
25
29
 
26
30
  # Simulate this transfer's status advancing one step. The standard
27
31
  # lifecycle is SCHEDULED -> PROCESSING -> SENT -> PAID, progressed one
28
- # step at a time; OVERDUE, FAILED, and CANCELLED can be jumped to
29
- # directly.
32
+ # step at a time (the sandbox rejects PROCESSING -> PAID with a 500
33
+ # operation_failed; go through SENT); OVERDUE, FAILED, and CANCELLED can
34
+ # be jumped to directly.
30
35
  #
31
36
  # @param next_status [String] one of "OVERDUE", "PROCESSING", "SENT",
32
37
  # "PAID", "FAILED", "CANCELLED"
33
38
  # @param failure_type [String, nil] failure reason code, only
34
39
  # meaningful when next_status is "FAILED"
40
+ # @param headers [Hash] e.g. { "x-on-behalf-of" => account_id } — required
41
+ # for a transfer created on behalf of a connected account
35
42
  # @return [Transfer] self
36
- def simulate_transition(next_status:, failure_type: nil)
43
+ def simulate_transition(next_status:, failure_type: nil, headers: {})
37
44
  params = { next_status: next_status }
38
45
  params[:failure_type] = failure_type if failure_type
39
- response = Airwallex.client.post("#{SIMULATION_PATH}/#{id}/transition", params)
46
+ response = Airwallex.client.post("#{SIMULATION_PATH}/#{id}/transition", params, headers)
40
47
  refresh_from(response)
41
48
  self
42
49
  end
@@ -48,11 +55,13 @@ module Airwallex
48
55
  # "PAID", "FAILED", "CANCELLED"
49
56
  # @param failure_type [String, nil] failure reason code, only
50
57
  # meaningful when next_status is "FAILED"
58
+ # @param headers [Hash] e.g. { "x-on-behalf-of" => account_id } — required
59
+ # for a transfer created on behalf of a connected account
51
60
  # @return [Transfer]
52
- def self.simulate_transition(transfer_id, next_status:, failure_type: nil)
61
+ def self.simulate_transition(transfer_id, next_status:, failure_type: nil, headers: {})
53
62
  params = { next_status: next_status }
54
63
  params[:failure_type] = failure_type if failure_type
55
- response = Airwallex.client.post("#{SIMULATION_PATH}/#{transfer_id}/transition", params)
64
+ response = Airwallex.client.post("#{SIMULATION_PATH}/#{transfer_id}/transition", params, headers)
56
65
  new(response)
57
66
  end
58
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Airwallex
4
- VERSION = "0.9.0"
4
+ VERSION = "0.11.0"
5
5
  end
data/lib/airwallex.rb CHANGED
@@ -47,6 +47,8 @@ require_relative "airwallex/resources/payment_source"
47
47
  require_relative "airwallex/resources/connected_account"
48
48
  require_relative "airwallex/resources/account_amendment"
49
49
  require_relative "airwallex/resources/funds_split"
50
+ require_relative "airwallex/resources/funds_split_reversal"
51
+ require_relative "airwallex/resources/connected_account_transfer"
50
52
  require_relative "airwallex/resources/charge"
51
53
  require_relative "airwallex/resources/deposit"
52
54
  require_relative "airwallex/resources/issuing_transaction"
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.9.0
4
+ version: 0.11.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-09 00:00:00.000000000 Z
11
+ date: 2026-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -93,11 +93,13 @@ files:
93
93
  - lib/airwallex/resources/cardholder.rb
94
94
  - lib/airwallex/resources/charge.rb
95
95
  - lib/airwallex/resources/connected_account.rb
96
+ - lib/airwallex/resources/connected_account_transfer.rb
96
97
  - lib/airwallex/resources/conversion.rb
97
98
  - lib/airwallex/resources/customer.rb
98
99
  - lib/airwallex/resources/deposit.rb
99
100
  - lib/airwallex/resources/dispute.rb
100
101
  - lib/airwallex/resources/funds_split.rb
102
+ - lib/airwallex/resources/funds_split_reversal.rb
101
103
  - lib/airwallex/resources/global_account.rb
102
104
  - lib/airwallex/resources/global_account_alias.rb
103
105
  - lib/airwallex/resources/global_account_mandate.rb
@@ -144,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
146
  - !ruby/object:Gem::Version
145
147
  version: '0'
146
148
  requirements: []
147
- rubygems_version: 3.4.10
149
+ rubygems_version: 3.5.22
148
150
  signing_key:
149
151
  specification_version: 4
150
152
  summary: Production-grade Ruby client for the Airwallex API