airwallex 0.1.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 +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +377 -0
- data/Rakefile +12 -0
- data/docs/internal/20251125_iteration_1_quickstart.md +130 -0
- data/docs/internal/20251125_iteration_1_summary.md +342 -0
- data/docs/internal/20251125_sprint_1_completed.md +448 -0
- data/docs/internal/20251125_sprint_1_plan.md +389 -0
- data/docs/internal/20251125_sprint_2_completed.md +559 -0
- data/docs/internal/20251125_sprint_2_plan.md +531 -0
- data/docs/internal/20251125_sprint_2_unit_tests_completed.md +264 -0
- data/docs/research/Airwallex API Endpoint Research.md +410 -0
- data/docs/research/Airwallex API Research for Ruby Gem.md +383 -0
- data/lib/airwallex/api_operations/create.rb +16 -0
- data/lib/airwallex/api_operations/delete.rb +16 -0
- data/lib/airwallex/api_operations/list.rb +23 -0
- data/lib/airwallex/api_operations/retrieve.rb +16 -0
- data/lib/airwallex/api_operations/update.rb +44 -0
- data/lib/airwallex/api_resource.rb +96 -0
- data/lib/airwallex/client.rb +132 -0
- data/lib/airwallex/configuration.rb +67 -0
- data/lib/airwallex/errors.rb +64 -0
- data/lib/airwallex/list_object.rb +85 -0
- data/lib/airwallex/middleware/auth_refresh.rb +32 -0
- data/lib/airwallex/middleware/idempotency.rb +29 -0
- data/lib/airwallex/resources/beneficiary.rb +14 -0
- data/lib/airwallex/resources/payment_intent.rb +44 -0
- data/lib/airwallex/resources/transfer.rb +23 -0
- data/lib/airwallex/util.rb +58 -0
- data/lib/airwallex/version.rb +5 -0
- data/lib/airwallex/webhook.rb +67 -0
- data/lib/airwallex.rb +49 -0
- data/sig/airwallex.rbs +4 -0
- metadata +128 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7517d087f8337d63e00476a7be3e551164e97bd271c510ac37621db595a4c86f
|
|
4
|
+
data.tar.gz: 7b5a90c9a5a9ca677f48ce63d9a1bb50a9cedd030a2f13ae39c658b2fe8d63e9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9a6344fa43f9f38ae9950cf8c84a0770361f5448bcb365d6e44d20068986f831e23d628c41f07402d94d3c0bb71306095fd2fd46eca867a61abfb79f18b57fd9
|
|
7
|
+
data.tar.gz: '08d8be5e80b739ce7278af4f894744fa708d9aa755fda96b415a881c24218692c4336602b1c59c765d9ea0dd3a705f3cdc6e0b42108979d6ad6ce097f5b46e83'
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2025-11-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Core infrastructure: Configuration, Client, Error handling
|
|
7
|
+
- Authentication: Bearer token with automatic refresh
|
|
8
|
+
- API Resources:
|
|
9
|
+
- PaymentIntent (create, retrieve, list, update, confirm, cancel, capture)
|
|
10
|
+
- Transfer (create, retrieve, list, cancel)
|
|
11
|
+
- Beneficiary (create, retrieve, list, delete)
|
|
12
|
+
- API Operations: Reusable mixins for Create, Retrieve, List, Update, Delete
|
|
13
|
+
- Pagination: Unified ListObject with auto-paging support (cursor and offset-based)
|
|
14
|
+
- Idempotency: Automatic request_id generation for safe retries
|
|
15
|
+
- Webhook verification: HMAC-SHA256 signature validation
|
|
16
|
+
- Multi-environment support: Sandbox and Production
|
|
17
|
+
- Comprehensive test suite: 187 tests with 100% coverage
|
|
18
|
+
- Ruby 3.1+ support
|
|
19
|
+
|
|
20
|
+
### Notes
|
|
21
|
+
- This is an MVP release focusing on core payment acceptance and payout functionality
|
|
22
|
+
- Additional resources (FX, cards, refunds) will be added in future versions
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chayut Orapinpatipat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# Airwallex Ruby Gem
|
|
2
|
+
|
|
3
|
+
A Ruby client library for the [Airwallex API](https://www.airwallex.com/docs/api), providing access to payment acceptance and payout capabilities.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This gem provides a Ruby interface to Airwallex's payment infrastructure, designed for Ruby 3.1+ applications. It includes core functionality for authentication management, idempotency guarantees, webhook verification, and multi-environment support.
|
|
8
|
+
|
|
9
|
+
**Current Features (v0.1.0):**
|
|
10
|
+
|
|
11
|
+
- **Authentication**: Bearer token authentication with automatic refresh
|
|
12
|
+
- **Payment Acceptance**: Payment intent creation, confirmation, and management
|
|
13
|
+
- **Payouts**: Transfer creation and beneficiary management
|
|
14
|
+
- **Idempotency**: Automatic request deduplication for safe retries
|
|
15
|
+
- **Pagination**: Unified interface over cursor-based and offset-based pagination
|
|
16
|
+
- **Webhook Security**: HMAC-SHA256 signature verification with replay protection
|
|
17
|
+
- **Sandbox Support**: Full testing environment for development
|
|
18
|
+
|
|
19
|
+
**Note:** This is an initial MVP release. Additional resources (FX, cards, refunds, etc.) will be added in future versions.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Add this line to your application's Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'airwallex'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
And then execute:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bundle install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or install it yourself as:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
gem install airwallex
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### Configuration
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
require 'airwallex'
|
|
47
|
+
|
|
48
|
+
Airwallex.configure do |config|
|
|
49
|
+
config.api_key = 'your_api_key'
|
|
50
|
+
config.client_id = 'your_client_id'
|
|
51
|
+
config.environment = :sandbox # or :production
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Creating a Payment Intent
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# Create a payment intent
|
|
59
|
+
payment_intent = Airwallex::PaymentIntent.create(
|
|
60
|
+
amount: 100.00,
|
|
61
|
+
currency: 'USD',
|
|
62
|
+
merchant_order_id: 'order_123',
|
|
63
|
+
return_url: 'https://yoursite.com/return'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Confirm with card details
|
|
67
|
+
payment_intent.confirm(
|
|
68
|
+
payment_method: {
|
|
69
|
+
type: 'card',
|
|
70
|
+
card: {
|
|
71
|
+
number: '4242424242424242',
|
|
72
|
+
expiry_month: '12',
|
|
73
|
+
expiry_year: '2025',
|
|
74
|
+
cvc: '123'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Creating a Payout
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# Create a beneficiary
|
|
84
|
+
beneficiary = Airwallex::Beneficiary.create(
|
|
85
|
+
bank_details: {
|
|
86
|
+
account_number: '123456789',
|
|
87
|
+
account_routing_type1: 'aba',
|
|
88
|
+
account_routing_value1: '026009593',
|
|
89
|
+
bank_country_code: 'US'
|
|
90
|
+
},
|
|
91
|
+
beneficiary_type: 'BUSINESS',
|
|
92
|
+
company_name: 'Acme Corp'
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Execute transfer
|
|
96
|
+
transfer = Airwallex::Transfer.create(
|
|
97
|
+
beneficiary_id: beneficiary.id,
|
|
98
|
+
source_currency: 'USD',
|
|
99
|
+
transfer_method: 'LOCAL',
|
|
100
|
+
amount: 1000.00,
|
|
101
|
+
reason: 'Payment for services'
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Usage
|
|
106
|
+
|
|
107
|
+
### Authentication
|
|
108
|
+
|
|
109
|
+
The gem uses Bearer token authentication with automatic token refresh:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
Airwallex.configure do |config|
|
|
113
|
+
config.api_key = 'your_api_key'
|
|
114
|
+
config.client_id = 'your_client_id'
|
|
115
|
+
config.environment = :sandbox # or :production
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Tokens are automatically refreshed when they expire, and the gem handles thread-safe token management.
|
|
120
|
+
|
|
121
|
+
### Idempotency
|
|
122
|
+
|
|
123
|
+
The gem automatically handles idempotency for safe retries:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
# Automatic request_id generation
|
|
127
|
+
transfer = Airwallex::Transfer.create(
|
|
128
|
+
amount: 500.00,
|
|
129
|
+
beneficiary_id: 'ben_123'
|
|
130
|
+
# request_id automatically generated
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Or provide your own for reconciliation
|
|
134
|
+
transfer = Airwallex::Transfer.create(
|
|
135
|
+
amount: 500.00,
|
|
136
|
+
beneficiary_id: 'ben_123',
|
|
137
|
+
request_id: 'my_internal_id_789'
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Pagination
|
|
142
|
+
|
|
143
|
+
Unified interface across both cursor-based and offset-based endpoints:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
# Auto-pagination with enumerable
|
|
147
|
+
Airwallex::Transfer.list.auto_paging_each do |transfer|
|
|
148
|
+
puts transfer.id
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Manual pagination
|
|
152
|
+
transfers = Airwallex::Transfer.list(page_size: 50)
|
|
153
|
+
while transfers.has_more?
|
|
154
|
+
transfers.each { |t| process(t) }
|
|
155
|
+
transfers = transfers.next_page
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Webhook Handling
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# In your webhook controller
|
|
163
|
+
payload = request.body.read
|
|
164
|
+
signature = request.headers['x-signature']
|
|
165
|
+
timestamp = request.headers['x-timestamp']
|
|
166
|
+
|
|
167
|
+
begin
|
|
168
|
+
event = Airwallex::Webhook.construct_event(
|
|
169
|
+
payload,
|
|
170
|
+
signature,
|
|
171
|
+
timestamp,
|
|
172
|
+
tolerance: 300 # 5 minutes
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
case event.type
|
|
176
|
+
when 'payment_intent.succeeded'
|
|
177
|
+
handle_successful_payment(event.data)
|
|
178
|
+
when 'payout.transfer.failed'
|
|
179
|
+
handle_failed_payout(event.data)
|
|
180
|
+
end
|
|
181
|
+
rescue Airwallex::SignatureVerificationError => e
|
|
182
|
+
# Invalid signature
|
|
183
|
+
head :bad_request
|
|
184
|
+
end
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Error Handling
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
begin
|
|
191
|
+
transfer = Airwallex::Transfer.create(params)
|
|
192
|
+
rescue Airwallex::InsufficientFundsError => e
|
|
193
|
+
# Handle insufficient balance
|
|
194
|
+
notify_user("Insufficient funds: #{e.message}")
|
|
195
|
+
rescue Airwallex::RateLimitError => e
|
|
196
|
+
# Rate limit hit - automatic retry with backoff
|
|
197
|
+
retry_with_backoff
|
|
198
|
+
rescue Airwallex::AuthenticationError => e
|
|
199
|
+
# Invalid credentials
|
|
200
|
+
log_error("Auth failed: #{e.message}")
|
|
201
|
+
rescue Airwallex::APIError => e
|
|
202
|
+
# General API error
|
|
203
|
+
log_error("API error: #{e.code} - #{e.message}")
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Architecture
|
|
208
|
+
|
|
209
|
+
### Design Principles
|
|
210
|
+
|
|
211
|
+
- **Correctness First**: Automatic idempotency and type safety prevent duplicate transactions
|
|
212
|
+
- **Fail-Safe Defaults**: Sandbox environment default, automatic token refresh
|
|
213
|
+
- **Developer Experience**: Auto-pagination, dynamic schema validation, structured errors
|
|
214
|
+
- **Security**: HMAC webhook verification, constant-time signature comparison, SCA support
|
|
215
|
+
- **Resilience**: Exponential backoff, jittered retries, concurrent request limits
|
|
216
|
+
|
|
217
|
+
### Core Components
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
lib/airwallex/
|
|
221
|
+
├── api_operations/ # CRUD operation mixins (Create, Retrieve, List, Update, Delete)
|
|
222
|
+
├── resources/ # Implemented resources
|
|
223
|
+
│ ├── payment_intent.rb # Payment acceptance
|
|
224
|
+
│ ├── transfer.rb # Payouts
|
|
225
|
+
│ └── beneficiary.rb # Payout beneficiaries
|
|
226
|
+
├── api_resource.rb # Base resource class with dynamic attributes
|
|
227
|
+
├── list_object.rb # Pagination wrapper
|
|
228
|
+
├── errors.rb # Exception hierarchy
|
|
229
|
+
├── client.rb # HTTP client with authentication
|
|
230
|
+
├── configuration.rb # Environment and credentials
|
|
231
|
+
├── webhook.rb # Signature verification
|
|
232
|
+
├── util.rb # Helper methods
|
|
233
|
+
└── middleware/ # Faraday middleware
|
|
234
|
+
└── idempotency.rb # Automatic request_id injection
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
240
|
+
|
|
241
|
+
### Running Tests
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
bundle exec rspec
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Code Style
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
bundle exec rubocop
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Local Development
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
# In bin/console or irb
|
|
257
|
+
require 'airwallex'
|
|
258
|
+
|
|
259
|
+
Airwallex.configure do |config|
|
|
260
|
+
config.environment = :sandbox
|
|
261
|
+
config.api_key = ENV['AIRWALLEX_API_KEY']
|
|
262
|
+
config.client_id = ENV['AIRWALLEX_CLIENT_ID']
|
|
263
|
+
end
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## API Coverage (v0.1.0)
|
|
267
|
+
|
|
268
|
+
### Currently Implemented Resources
|
|
269
|
+
|
|
270
|
+
- **Payment Acceptance**: PaymentIntent (create, retrieve, list, update, confirm, cancel, capture)
|
|
271
|
+
- **Payouts**: Transfer (create, retrieve, list, cancel), Beneficiary (create, retrieve, list, delete)
|
|
272
|
+
- **Webhooks**: Event handling, HMAC-SHA256 signature verification
|
|
273
|
+
|
|
274
|
+
### Coming in Future Versions
|
|
275
|
+
|
|
276
|
+
- Refunds and disputes
|
|
277
|
+
- Foreign exchange (rates, quotes, conversions)
|
|
278
|
+
- Payment methods management
|
|
279
|
+
- Global accounts
|
|
280
|
+
- Card issuing
|
|
281
|
+
- Additional payout methods
|
|
282
|
+
|
|
283
|
+
## Environment Support
|
|
284
|
+
|
|
285
|
+
### Sandbox
|
|
286
|
+
|
|
287
|
+
Testing environment for development:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
Airwallex.configure do |config|
|
|
291
|
+
config.environment = :sandbox
|
|
292
|
+
config.api_key = ENV['AIRWALLEX_SANDBOX_API_KEY']
|
|
293
|
+
config.client_id = ENV['AIRWALLEX_SANDBOX_CLIENT_ID']
|
|
294
|
+
end
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Production
|
|
298
|
+
|
|
299
|
+
Live environment for real financial transactions:
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
Airwallex.configure do |config|
|
|
303
|
+
config.environment = :production
|
|
304
|
+
config.api_key = ENV['AIRWALLEX_API_KEY']
|
|
305
|
+
config.client_id = ENV['AIRWALLEX_CLIENT_ID']
|
|
306
|
+
end
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Rate Limits
|
|
310
|
+
|
|
311
|
+
The gem respects Airwallex API rate limits. If you encounter `Airwallex::RateLimitError`, implement retry logic with exponential backoff:
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
begin
|
|
315
|
+
transfer = Airwallex::Transfer.create(params)
|
|
316
|
+
rescue Airwallex::RateLimitError => e
|
|
317
|
+
sleep(2 ** retry_count)
|
|
318
|
+
retry_count += 1
|
|
319
|
+
retry if retry_count < 3
|
|
320
|
+
end
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Contributing
|
|
324
|
+
|
|
325
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/Sentia/airwallex>.
|
|
326
|
+
|
|
327
|
+
### Development Setup
|
|
328
|
+
|
|
329
|
+
1. Fork and clone the repository
|
|
330
|
+
2. Run `bin/setup` to install dependencies
|
|
331
|
+
3. Create a `.env` file with sandbox credentials
|
|
332
|
+
4. Run tests: `bundle exec rspec`
|
|
333
|
+
5. Check style: `bundle exec rubocop`
|
|
334
|
+
|
|
335
|
+
### Guidelines
|
|
336
|
+
|
|
337
|
+
- Write tests for new features
|
|
338
|
+
- Follow existing code style (enforced by Rubocop)
|
|
339
|
+
- Update documentation for API changes
|
|
340
|
+
- Ensure all tests pass before submitting PR
|
|
341
|
+
|
|
342
|
+
## Versioning
|
|
343
|
+
|
|
344
|
+
This gem follows [Semantic Versioning](https://semver.org/). The Airwallex API uses date-based versioning, which is handled internally by the gem.
|
|
345
|
+
|
|
346
|
+
## Security
|
|
347
|
+
|
|
348
|
+
If you discover a security vulnerability, please email security@sentia.com instead of using the issue tracker.
|
|
349
|
+
|
|
350
|
+
## Documentation
|
|
351
|
+
|
|
352
|
+
- [Airwallex API Documentation](https://www.airwallex.com/docs/api)
|
|
353
|
+
- [API Reference](https://www.airwallex.com/docs/api#overview)
|
|
354
|
+
|
|
355
|
+
## Requirements
|
|
356
|
+
|
|
357
|
+
- Ruby 3.1 or higher
|
|
358
|
+
- Bundler 2.0 or higher
|
|
359
|
+
|
|
360
|
+
## Dependencies
|
|
361
|
+
|
|
362
|
+
- `faraday` (~> 2.0) - HTTP client
|
|
363
|
+
- `faraday-retry` - Request retry logic
|
|
364
|
+
- `faraday-multipart` - File upload support
|
|
365
|
+
|
|
366
|
+
## License
|
|
367
|
+
|
|
368
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
369
|
+
|
|
370
|
+
## Support
|
|
371
|
+
|
|
372
|
+
- GitHub Issues: <https://github.com/Sentia/airwallex/issues>
|
|
373
|
+
- Airwallex Support: <https://www.airwallex.com/support>
|
|
374
|
+
|
|
375
|
+
## Acknowledgments
|
|
376
|
+
|
|
377
|
+
Built with comprehensive analysis of the Airwallex API ecosystem. Special thanks to the Airwallex team for their extensive documentation and developer resources.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Iteration 1 - Quick Start
|
|
2
|
+
|
|
3
|
+
**Sprint 1, Iteration 1**
|
|
4
|
+
**Date:** 25 November 2025
|
|
5
|
+
**Status:** ✅ Complete
|
|
6
|
+
|
|
7
|
+
## What Was Built
|
|
8
|
+
|
|
9
|
+
Core infrastructure for the Airwallex Ruby gem including:
|
|
10
|
+
|
|
11
|
+
1. ✅ Complete directory structure
|
|
12
|
+
2. ✅ Configuration management (sandbox/production)
|
|
13
|
+
3. ✅ HTTP client with Faraday
|
|
14
|
+
4. ✅ Bearer token authentication with auto-refresh
|
|
15
|
+
5. ✅ Comprehensive error handling
|
|
16
|
+
6. ✅ Automatic idempotency
|
|
17
|
+
7. ✅ Webhook signature verification
|
|
18
|
+
8. ✅ Utility helpers
|
|
19
|
+
9. ✅ Zero Rubocop offenses
|
|
20
|
+
|
|
21
|
+
## Quick Test
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require 'airwallex'
|
|
25
|
+
|
|
26
|
+
# Configure the gem
|
|
27
|
+
Airwallex.configure do |config|
|
|
28
|
+
config.api_key = 'your_api_key'
|
|
29
|
+
config.client_id = 'your_client_id'
|
|
30
|
+
config.environment = :sandbox
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Check configuration
|
|
34
|
+
puts Airwallex.configuration.api_url
|
|
35
|
+
# => https://api-demo.airwallex.com/api/v1
|
|
36
|
+
|
|
37
|
+
# Client is ready (authentication happens automatically on first request)
|
|
38
|
+
client = Airwallex.client
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Files Created
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
lib/airwallex.rb - Main module with configuration
|
|
45
|
+
lib/airwallex/version.rb - Version constant
|
|
46
|
+
lib/airwallex/configuration.rb - Configuration class
|
|
47
|
+
lib/airwallex/client.rb - HTTP client with Faraday
|
|
48
|
+
lib/airwallex/errors.rb - Exception hierarchy
|
|
49
|
+
lib/airwallex/util.rb - Helper utilities
|
|
50
|
+
lib/airwallex/webhook.rb - Webhook verification
|
|
51
|
+
lib/airwallex/middleware/idempotency.rb - Auto request_id
|
|
52
|
+
lib/airwallex/middleware/auth_refresh.rb - Token management
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Key Features
|
|
56
|
+
|
|
57
|
+
### Environment Safety
|
|
58
|
+
- Defaults to `:sandbox` to prevent accidental production transactions
|
|
59
|
+
- Validates environment selection
|
|
60
|
+
- Dynamic URL generation
|
|
61
|
+
|
|
62
|
+
### Authentication
|
|
63
|
+
- Automatic Bearer token exchange
|
|
64
|
+
- 30-minute token lifetime with 5-minute refresh buffer
|
|
65
|
+
- Thread-safe token management
|
|
66
|
+
- Transparent 401 retry
|
|
67
|
+
|
|
68
|
+
### Idempotency
|
|
69
|
+
- Automatic UUID v4 generation for `request_id`
|
|
70
|
+
- Injected into request body (Airwallex specification)
|
|
71
|
+
- Safe request retries
|
|
72
|
+
|
|
73
|
+
### Error Handling
|
|
74
|
+
- HTTP status mapped to specific exceptions
|
|
75
|
+
- Polymorphic error body parsing
|
|
76
|
+
- Detailed error information (`code`, `message`, `param`, `details`)
|
|
77
|
+
|
|
78
|
+
### Webhook Security
|
|
79
|
+
- HMAC-SHA256 signature verification
|
|
80
|
+
- Replay attack protection (5-minute tolerance)
|
|
81
|
+
- Constant-time comparison
|
|
82
|
+
|
|
83
|
+
## What's Next
|
|
84
|
+
|
|
85
|
+
**Iteration 2:** Testing Infrastructure
|
|
86
|
+
- Set up RSpec, WebMock, VCR
|
|
87
|
+
- Write comprehensive tests
|
|
88
|
+
- Achieve 90%+ coverage
|
|
89
|
+
|
|
90
|
+
**Sprint 2:** Resource Implementation
|
|
91
|
+
- APIResource base class
|
|
92
|
+
- Payment Intent resource
|
|
93
|
+
- Transfer resource
|
|
94
|
+
- Pagination system
|
|
95
|
+
|
|
96
|
+
## Validation
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Check for errors
|
|
100
|
+
bundle exec rubocop lib/
|
|
101
|
+
# => 9 files inspected, no offenses detected ✅
|
|
102
|
+
|
|
103
|
+
# Test gem loading
|
|
104
|
+
bundle exec ruby -e "require './lib/airwallex'; puts 'OK'"
|
|
105
|
+
# => OK ✅
|
|
106
|
+
|
|
107
|
+
# Test configuration
|
|
108
|
+
bundle exec ruby -e "
|
|
109
|
+
require './lib/airwallex'
|
|
110
|
+
Airwallex.configure { |c| c.api_key = 'test'; c.client_id = 'test' }
|
|
111
|
+
puts Airwallex.configuration.api_url
|
|
112
|
+
"
|
|
113
|
+
# => https://api-demo.airwallex.com/api/v1 ✅
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Time Spent
|
|
117
|
+
|
|
118
|
+
Approximately 4 hours for:
|
|
119
|
+
- Directory structure setup
|
|
120
|
+
- Core class implementation
|
|
121
|
+
- Faraday middleware
|
|
122
|
+
- Rubocop configuration
|
|
123
|
+
- Documentation
|
|
124
|
+
|
|
125
|
+
## Notes
|
|
126
|
+
|
|
127
|
+
- All code follows Ruby 3.1+ standards
|
|
128
|
+
- No external API calls made yet (no tests)
|
|
129
|
+
- Architecture matches research blueprints exactly
|
|
130
|
+
- Ready for test implementation
|