airwallex-ruby 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/.gitignore +15 -0
- data/.rspec +1 -0
- data/.rubocop.yml +67 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +433 -0
- data/Rakefile +12 -0
- data/airwallex-ruby.gemspec +54 -0
- data/docs/airwallex-research.md +78 -0
- data/docs/release.md +66 -0
- data/examples/basic_configuration.rb +21 -0
- data/examples/payment_intent_create.rb +28 -0
- data/examples/rails_webhook_controller.rb +38 -0
- data/examples/refund_create.rb +29 -0
- data/examples/webhook_verification.rb +23 -0
- data/lib/airwallex/client.rb +203 -0
- data/lib/airwallex/configuration.rb +38 -0
- data/lib/airwallex/errors.rb +77 -0
- data/lib/airwallex/railtie.rb +6 -0
- data/lib/airwallex/resources/authentication.rb +28 -0
- data/lib/airwallex/resources/base_resource.rb +47 -0
- data/lib/airwallex/resources/payment_intents.rb +33 -0
- data/lib/airwallex/resources/refunds.rb +22 -0
- data/lib/airwallex/version.rb +5 -0
- data/lib/airwallex/webhook.rb +94 -0
- data/lib/airwallex.rb +38 -0
- data/lib/generators/airwallex/install/install_generator.rb +13 -0
- data/lib/generators/airwallex/install/templates/airwallex.rb +10 -0
- metadata +195 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bdf272d514644f46fb58ec553ed1f19350adc59aa8c979b50be19cd040754726
|
|
4
|
+
data.tar.gz: 6ed20800b0e6496f18d8b27a4cd7d7ad1dbb06fb462b987e3d88f3cf6aa0ee34
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 68d2e8a8d125438c26ec2a687f48a13fa0f2b0c02cfcbc8d4045db3007852d1ed7b477f402caddd170adf81841591a720fe0fcdde6c870e8ca020414acd47d3d
|
|
7
|
+
data.tar.gz: a636cc93cc28b718a814684d1544150ff30a7c69c808eb2228541f822ab8a36d3db1e058ce5111bc0ae040c1196e62e80a33c8bcb1a5f114af98a5c434c1558b
|
data/.gitignore
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.1
|
|
4
|
+
Exclude:
|
|
5
|
+
- "vendor/**/*"
|
|
6
|
+
- "tmp/**/*"
|
|
7
|
+
|
|
8
|
+
Style/Documentation:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
11
|
+
Style/StringLiterals:
|
|
12
|
+
EnforcedStyle: double_quotes
|
|
13
|
+
|
|
14
|
+
Lint/EmptyClass:
|
|
15
|
+
Exclude:
|
|
16
|
+
- "lib/airwallex/client.rb"
|
|
17
|
+
- "lib/airwallex/resources/**/*"
|
|
18
|
+
|
|
19
|
+
Layout/LineLength:
|
|
20
|
+
Max: 120
|
|
21
|
+
|
|
22
|
+
Metrics/BlockLength:
|
|
23
|
+
Exclude:
|
|
24
|
+
- "spec/**/*"
|
|
25
|
+
- "airwallex-ruby.gemspec"
|
|
26
|
+
|
|
27
|
+
Gemspec/DevelopmentDependencies:
|
|
28
|
+
Exclude:
|
|
29
|
+
- "airwallex-ruby.gemspec"
|
|
30
|
+
|
|
31
|
+
Style/FetchEnvVar:
|
|
32
|
+
Exclude:
|
|
33
|
+
- "lib/generators/**/*"
|
|
34
|
+
- "examples/**/*"
|
|
35
|
+
|
|
36
|
+
Metrics/ClassLength:
|
|
37
|
+
Exclude:
|
|
38
|
+
- "lib/airwallex/client.rb"
|
|
39
|
+
|
|
40
|
+
Metrics/AbcSize:
|
|
41
|
+
Exclude:
|
|
42
|
+
- "lib/airwallex/client.rb"
|
|
43
|
+
- "lib/airwallex/webhook.rb"
|
|
44
|
+
|
|
45
|
+
Metrics/MethodLength:
|
|
46
|
+
Exclude:
|
|
47
|
+
- "lib/airwallex/client.rb"
|
|
48
|
+
|
|
49
|
+
Metrics/ParameterLists:
|
|
50
|
+
Exclude:
|
|
51
|
+
- "lib/airwallex/client.rb"
|
|
52
|
+
|
|
53
|
+
Naming/PredicateMethod:
|
|
54
|
+
Exclude:
|
|
55
|
+
- "lib/airwallex/webhook.rb"
|
|
56
|
+
|
|
57
|
+
Naming/MethodParameterName:
|
|
58
|
+
Exclude:
|
|
59
|
+
- "lib/airwallex/webhook.rb"
|
|
60
|
+
|
|
61
|
+
Metrics/CyclomaticComplexity:
|
|
62
|
+
Exclude:
|
|
63
|
+
- "lib/airwallex/webhook.rb"
|
|
64
|
+
|
|
65
|
+
Metrics/PerceivedComplexity:
|
|
66
|
+
Exclude:
|
|
67
|
+
- "lib/airwallex/webhook.rb"
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - Unreleased
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Configuration support
|
|
13
|
+
- Demo and production environments
|
|
14
|
+
- Faraday HTTP client
|
|
15
|
+
- Authentication and token caching
|
|
16
|
+
- PaymentIntents resource
|
|
17
|
+
- Refunds resource
|
|
18
|
+
- Idempotency key support
|
|
19
|
+
- Webhook signature verification
|
|
20
|
+
- Optional Rails initializer generator
|
|
21
|
+
- RSpec/WebMock test coverage
|
|
22
|
+
- Documentation and examples
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SenseiProject
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# airwallex-ruby
|
|
2
|
+
|
|
3
|
+
[](https://github.com/delacruzjames/airwallex-ruby/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE.txt)
|
|
5
|
+
|
|
6
|
+
Unofficial Ruby SDK for Airwallex APIs.
|
|
7
|
+
|
|
8
|
+
## Disclaimer
|
|
9
|
+
|
|
10
|
+
This is an unofficial Ruby SDK for Airwallex. It is not maintained, sponsored, or endorsed by Airwallex.
|
|
11
|
+
|
|
12
|
+
Requires **Ruby 3.1+**.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- Configuration support
|
|
17
|
+
- Demo and production environments
|
|
18
|
+
- Authentication and token caching
|
|
19
|
+
- Faraday-based HTTP client
|
|
20
|
+
- Typed error handling
|
|
21
|
+
- PaymentIntents resource
|
|
22
|
+
- Refunds resource
|
|
23
|
+
- Idempotency key support
|
|
24
|
+
- Webhook signature verification
|
|
25
|
+
- Optional Rails initializer generator
|
|
26
|
+
- RSpec/WebMock test coverage
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Add this line to your application's Gemfile:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
gem "airwallex-ruby"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then run:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bundle install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For local development against a checkout of this repository:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
gem "airwallex-ruby", path: "../airwallex-ruby"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Basic configuration
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
require "airwallex"
|
|
52
|
+
|
|
53
|
+
Airwallex.configure do |config|
|
|
54
|
+
config.client_id = ENV["AIRWALLEX_CLIENT_ID"]
|
|
55
|
+
config.api_key = ENV["AIRWALLEX_API_KEY"]
|
|
56
|
+
config.login_as = ENV["AIRWALLEX_LOGIN_AS"]
|
|
57
|
+
config.environment = :demo
|
|
58
|
+
config.timeout = 30
|
|
59
|
+
config.open_timeout = 10
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
client = Airwallex.client
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Direct client initialization
|
|
66
|
+
|
|
67
|
+
You can also construct a client directly without global configuration:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
client = Airwallex::Client.new(
|
|
71
|
+
client_id: ENV["AIRWALLEX_CLIENT_ID"],
|
|
72
|
+
api_key: ENV["AIRWALLEX_API_KEY"],
|
|
73
|
+
login_as: ENV["AIRWALLEX_LOGIN_AS"],
|
|
74
|
+
environment: :demo
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Environments
|
|
79
|
+
|
|
80
|
+
| Environment | Base URL |
|
|
81
|
+
|-------------|----------|
|
|
82
|
+
| `:demo` | `https://api-demo.airwallex.com/api/v1` |
|
|
83
|
+
| `:production` | `https://api.airwallex.com/api/v1` |
|
|
84
|
+
|
|
85
|
+
Set `config.environment` or pass `environment:` when creating a client.
|
|
86
|
+
|
|
87
|
+
## Authentication
|
|
88
|
+
|
|
89
|
+
Authentication is handled automatically. When an authenticated request is made, the client calls `POST /authentication/login` if no valid token is cached. The access token is stored in memory and reused until it expires. All authenticated requests include `Authorization: Bearer <token>`.
|
|
90
|
+
|
|
91
|
+
You can authenticate explicitly or check authentication state:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
client.authenticate
|
|
95
|
+
client.authenticated?
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## PaymentIntents
|
|
99
|
+
|
|
100
|
+
### Create
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
payment_intent = client.payment_intents.create(
|
|
104
|
+
{
|
|
105
|
+
amount: 1000,
|
|
106
|
+
currency: "PHP",
|
|
107
|
+
merchant_order_id: "ORDER-1001",
|
|
108
|
+
return_url: "https://example.com/return"
|
|
109
|
+
},
|
|
110
|
+
idempotency_key: "order-1001-create"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
puts payment_intent["id"]
|
|
114
|
+
puts payment_intent["client_secret"]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Retrieve
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
payment_intent = client.payment_intents.retrieve("int_123")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Update
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
payment_intent = client.payment_intents.update(
|
|
127
|
+
"int_123",
|
|
128
|
+
{
|
|
129
|
+
amount: 1500
|
|
130
|
+
},
|
|
131
|
+
idempotency_key: "order-1001-update"
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Cancel
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
client.payment_intents.cancel(
|
|
139
|
+
"int_123",
|
|
140
|
+
idempotency_key: "order-1001-cancel"
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### List
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
payment_intents = client.payment_intents.list(
|
|
148
|
+
currency: "PHP",
|
|
149
|
+
page_num: 0,
|
|
150
|
+
page_size: 20
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Refunds
|
|
155
|
+
|
|
156
|
+
### Create
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
refund = client.refunds.create(
|
|
160
|
+
{
|
|
161
|
+
payment_intent_id: "int_123",
|
|
162
|
+
amount: 500,
|
|
163
|
+
reason: "requested_by_customer",
|
|
164
|
+
metadata: {
|
|
165
|
+
order_id: "ORDER-1001"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
idempotency_key: "order-1001-refund-1"
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Retrieve
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
refund = client.refunds.retrieve("ref_123")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### List
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
refunds = client.refunds.list(
|
|
182
|
+
payment_intent_id: "int_123",
|
|
183
|
+
page_num: 0,
|
|
184
|
+
page_size: 20
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Idempotency
|
|
189
|
+
|
|
190
|
+
Use idempotency keys for payment creation, updates, cancellations, and refunds. The key should be unique per operation.
|
|
191
|
+
|
|
192
|
+
Good examples:
|
|
193
|
+
|
|
194
|
+
- `order-1001-create`
|
|
195
|
+
- `order-1001-update`
|
|
196
|
+
- `order-1001-refund-1`
|
|
197
|
+
|
|
198
|
+
Pass `idempotency_key:` to resource methods or lower-level `client.post` / `client.patch` calls. The SDK sends the key as the `x-idempotency-key` header.
|
|
199
|
+
|
|
200
|
+
## Webhook verification
|
|
201
|
+
|
|
202
|
+
Verify incoming webhook requests using the raw request body and Airwallex signature headers:
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
raw_body = request.body.read
|
|
206
|
+
|
|
207
|
+
event = Airwallex::Webhook.construct_event(
|
|
208
|
+
payload: raw_body,
|
|
209
|
+
signature: request.headers["x-signature"],
|
|
210
|
+
timestamp: request.headers["x-timestamp"],
|
|
211
|
+
secret: ENV.fetch("AIRWALLEX_WEBHOOK_SECRET")
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
case event["name"]
|
|
215
|
+
when "payment_intent.succeeded"
|
|
216
|
+
# handle payment success
|
|
217
|
+
when "refund.accepted"
|
|
218
|
+
# handle refund accepted
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Important notes:
|
|
223
|
+
|
|
224
|
+
- Always use the raw request body.
|
|
225
|
+
- Verify the signature before parsing JSON (`construct_event` does this for you).
|
|
226
|
+
- Old timestamps are rejected by default (300 second tolerance).
|
|
227
|
+
- Signature comparison is timing-safe.
|
|
228
|
+
|
|
229
|
+
## Rails installation
|
|
230
|
+
|
|
231
|
+
Run the generator:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
rails generate airwallex:install
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
This creates:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
config/initializers/airwallex.rb
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Credentials example:
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
airwallex:
|
|
247
|
+
client_id: your_client_id
|
|
248
|
+
api_key: your_api_key
|
|
249
|
+
login_as: optional_account_id
|
|
250
|
+
webhook_secret: your_webhook_secret
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Rails webhook controller example:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
class AirwallexWebhooksController < ApplicationController
|
|
257
|
+
skip_before_action :verify_authenticity_token
|
|
258
|
+
|
|
259
|
+
def create
|
|
260
|
+
event = Airwallex::Webhook.construct_event(
|
|
261
|
+
payload: request.body.read,
|
|
262
|
+
signature: request.headers["x-signature"],
|
|
263
|
+
timestamp: request.headers["x-timestamp"],
|
|
264
|
+
secret: Rails.application.credentials.dig(:airwallex, :webhook_secret) || ENV.fetch("AIRWALLEX_WEBHOOK_SECRET")
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
case event["name"]
|
|
268
|
+
when "payment_intent.succeeded"
|
|
269
|
+
# handle payment success
|
|
270
|
+
when "refund.accepted"
|
|
271
|
+
# handle refund accepted
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
head :ok
|
|
275
|
+
rescue Airwallex::WebhookSignatureError, Airwallex::InvalidResponseError
|
|
276
|
+
head :bad_request
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Route:
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
post "/webhooks/airwallex", to: "airwallex_webhooks#create"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
See also [examples/rails_webhook_controller.rb](examples/rails_webhook_controller.rb).
|
|
288
|
+
|
|
289
|
+
## Error handling
|
|
290
|
+
|
|
291
|
+
The SDK raises typed errors for configuration, authentication, HTTP status codes, timeouts, invalid responses, and webhook verification failures.
|
|
292
|
+
|
|
293
|
+
| Error | Description |
|
|
294
|
+
|-------|-------------|
|
|
295
|
+
| `Airwallex::Error` | Base error for all SDK errors |
|
|
296
|
+
| `Airwallex::ConfigurationError` | Missing or invalid configuration |
|
|
297
|
+
| `Airwallex::AuthenticationError` | Login or token handling failed |
|
|
298
|
+
| `Airwallex::ArgumentError` | Invalid method arguments |
|
|
299
|
+
| `Airwallex::HTTPError` | Base class for HTTP error responses |
|
|
300
|
+
| `Airwallex::BadRequestError` | HTTP 400 |
|
|
301
|
+
| `Airwallex::UnauthorizedError` | HTTP 401 |
|
|
302
|
+
| `Airwallex::ForbiddenError` | HTTP 403 |
|
|
303
|
+
| `Airwallex::NotFoundError` | HTTP 404 |
|
|
304
|
+
| `Airwallex::ConflictError` | HTTP 409 |
|
|
305
|
+
| `Airwallex::RateLimitError` | HTTP 429 |
|
|
306
|
+
| `Airwallex::ServerError` | HTTP 5xx |
|
|
307
|
+
| `Airwallex::TimeoutError` | Request timeout or connection failure |
|
|
308
|
+
| `Airwallex::InvalidResponseError` | Invalid JSON or webhook payload |
|
|
309
|
+
| `Airwallex::WebhookSignatureError` | Webhook signature or timestamp verification failed |
|
|
310
|
+
|
|
311
|
+
Example:
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
begin
|
|
315
|
+
client.payment_intents.retrieve("int_123")
|
|
316
|
+
rescue Airwallex::NotFoundError => e
|
|
317
|
+
puts e.status
|
|
318
|
+
puts e.code
|
|
319
|
+
puts e.message
|
|
320
|
+
rescue Airwallex::RateLimitError
|
|
321
|
+
# retry later
|
|
322
|
+
rescue Airwallex::Error => e
|
|
323
|
+
# generic Airwallex SDK error
|
|
324
|
+
end
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Release / Local Installation
|
|
328
|
+
|
|
329
|
+
Build the gem from a checkout:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
gem build airwallex-ruby.gemspec
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Or use the Rake task (output goes to `pkg/`):
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
bundle exec rake build
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Install locally:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
gem install ./airwallex-ruby-0.1.0.gem
|
|
345
|
+
# or, after rake build:
|
|
346
|
+
gem install ./pkg/airwallex-ruby-0.1.0.gem
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Test in IRB:
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
require "airwallex"
|
|
353
|
+
Airwallex::VERSION
|
|
354
|
+
# => "0.1.0"
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Publishing
|
|
358
|
+
|
|
359
|
+
Do not publish until the release checklist is complete and changes have been reviewed.
|
|
360
|
+
|
|
361
|
+
### Automated (recommended)
|
|
362
|
+
|
|
363
|
+
After review, tag and push. GitHub Actions publishes to RubyGems and creates a GitHub Release:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
git tag v0.1.0
|
|
367
|
+
git push origin main
|
|
368
|
+
git push origin v0.1.0
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Requires the `RUBYGEMS_API_KEY` repository secret. See [docs/release.md](docs/release.md).
|
|
372
|
+
|
|
373
|
+
### Manual
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
gem push airwallex-ruby-0.1.0.gem
|
|
377
|
+
# or, after rake build:
|
|
378
|
+
gem push pkg/airwallex-ruby-0.1.0.gem
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
See [docs/release.md](docs/release.md) for the full release checklist.
|
|
382
|
+
|
|
383
|
+
## Development
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
bundle install
|
|
387
|
+
bundle exec rspec
|
|
388
|
+
bundle exec rubocop
|
|
389
|
+
bundle exec rake build
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
See [docs/airwallex-research.md](docs/airwallex-research.md) for API notes and planned resources.
|
|
393
|
+
|
|
394
|
+
## Testing
|
|
395
|
+
|
|
396
|
+
- RSpec is used for the test suite.
|
|
397
|
+
- WebMock is used to stub Airwallex API requests.
|
|
398
|
+
- No real Airwallex credentials are required for unit tests.
|
|
399
|
+
|
|
400
|
+
## Roadmap
|
|
401
|
+
|
|
402
|
+
- PaymentIntent confirm/capture support
|
|
403
|
+
- PaymentAttempts resource
|
|
404
|
+
- Customers resource
|
|
405
|
+
- PaymentConsents resource
|
|
406
|
+
- Transfers resource
|
|
407
|
+
- Balances resource
|
|
408
|
+
- Transactions resource
|
|
409
|
+
- File uploads
|
|
410
|
+
- More Rails generators
|
|
411
|
+
- Integration test mode
|
|
412
|
+
|
|
413
|
+
## Contributing
|
|
414
|
+
|
|
415
|
+
1. Fork the repository
|
|
416
|
+
2. Create a feature branch
|
|
417
|
+
3. Add specs for your changes
|
|
418
|
+
4. Run the test suite (`bundle exec rspec`) and RuboCop (`bundle exec rubocop`)
|
|
419
|
+
5. Open a pull request
|
|
420
|
+
|
|
421
|
+
## Examples
|
|
422
|
+
|
|
423
|
+
Runnable and copy-paste examples live in the [examples/](examples/) directory:
|
|
424
|
+
|
|
425
|
+
- [basic_configuration.rb](examples/basic_configuration.rb)
|
|
426
|
+
- [payment_intent_create.rb](examples/payment_intent_create.rb)
|
|
427
|
+
- [refund_create.rb](examples/refund_create.rb)
|
|
428
|
+
- [webhook_verification.rb](examples/webhook_verification.rb)
|
|
429
|
+
- [rails_webhook_controller.rb](examples/rails_webhook_controller.rb)
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
MIT — see [LICENSE.txt](LICENSE.txt).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/airwallex/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "airwallex-ruby"
|
|
7
|
+
spec.version = Airwallex::VERSION
|
|
8
|
+
spec.authors = ["James Martin Dela Cruz"]
|
|
9
|
+
spec.email = ["delacruzjamesmartin@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Unofficial Ruby SDK for Airwallex APIs"
|
|
12
|
+
spec.description = "An unofficial Ruby client library for Airwallex payment, payout, and treasury APIs."
|
|
13
|
+
spec.homepage = "https://github.com/delacruzjames/airwallex-ruby"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.1.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
|
20
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
21
|
+
|
|
22
|
+
spec.files = Dir.chdir(__dir__) do
|
|
23
|
+
files = if system("git rev-parse --is-inside-work-tree >/dev/null 2>&1")
|
|
24
|
+
`git ls-files -z`.split("\x0").reject do |path|
|
|
25
|
+
path.start_with?("spec/", ".github/") ||
|
|
26
|
+
path == "Gemfile.lock" ||
|
|
27
|
+
path.end_with?(".gem") ||
|
|
28
|
+
path.start_with?(".env")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if files.nil? || files.empty?
|
|
33
|
+
files = Dir["{lib,docs}/**/*", "README.md", "CHANGELOG.md", "LICENSE.txt", "airwallex-ruby.gemspec"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
%w[README.md CHANGELOG.md LICENSE.txt docs/release.md].each do |path|
|
|
37
|
+
files << path if File.exist?(path) && !files.include?(path)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
files
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
spec.require_paths = ["lib"]
|
|
44
|
+
|
|
45
|
+
spec.add_dependency "faraday", "~> 2.0"
|
|
46
|
+
spec.add_dependency "json", "~> 2.0"
|
|
47
|
+
|
|
48
|
+
spec.add_development_dependency "dotenv", "~> 3.0"
|
|
49
|
+
spec.add_development_dependency "rails", ">= 7.2", "< 8"
|
|
50
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
51
|
+
spec.add_development_dependency "rspec", "~> 3.13"
|
|
52
|
+
spec.add_development_dependency "rubocop", "~> 1.75"
|
|
53
|
+
spec.add_development_dependency "webmock", "~> 3.23"
|
|
54
|
+
end
|