Skip to content

delacruzjames/airwallex-ruby

Repository files navigation

airwallex-ruby

CI License: MIT

Unofficial Ruby SDK for Airwallex APIs.

Disclaimer

This is an unofficial Ruby SDK for Airwallex. It is not maintained, sponsored, or endorsed by Airwallex.

Requires Ruby 3.1+.

Features

  • Configuration support
  • Demo and production environments
  • Authentication and token caching
  • Faraday-based HTTP client
  • Typed error handling
  • PaymentIntents resource
  • Refunds resource
  • Idempotency key support
  • Webhook signature verification
  • Optional Rails initializer generator
  • RSpec/WebMock test coverage

Installation

Add this line to your application's Gemfile:

gem "airwallex-ruby"

Then run:

bundle install

For local development against a checkout of this repository:

gem "airwallex-ruby", path: "../airwallex-ruby"

Basic configuration

require "airwallex"

Airwallex.configure do |config|
  config.client_id = ENV["AIRWALLEX_CLIENT_ID"]
  config.api_key = ENV["AIRWALLEX_API_KEY"]
  config.login_as = ENV["AIRWALLEX_LOGIN_AS"]
  config.environment = :demo
  config.timeout = 30
  config.open_timeout = 10
end

client = Airwallex.client

Direct client initialization

You can also construct a client directly without global configuration:

client = Airwallex::Client.new(
  client_id: ENV["AIRWALLEX_CLIENT_ID"],
  api_key: ENV["AIRWALLEX_API_KEY"],
  login_as: ENV["AIRWALLEX_LOGIN_AS"],
  environment: :demo
)

Environments

Environment Base URL
:demo https://api-demo.airwallex.com/api/v1
:production https://api.airwallex.com/api/v1

Set config.environment or pass environment: when creating a client.

Authentication

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>.

You can authenticate explicitly or check authentication state:

client.authenticate
client.authenticated?

PaymentIntents

Create

payment_intent = client.payment_intents.create(
  {
    amount: 1000,
    currency: "PHP",
    merchant_order_id: "ORDER-1001",
    return_url: "https://example.com/return"
  },
  idempotency_key: "order-1001-create"
)

puts payment_intent["id"]
puts payment_intent["client_secret"]

Retrieve

payment_intent = client.payment_intents.retrieve("int_123")

Update

payment_intent = client.payment_intents.update(
  "int_123",
  {
    amount: 1500
  },
  idempotency_key: "order-1001-update"
)

Cancel

client.payment_intents.cancel(
  "int_123",
  idempotency_key: "order-1001-cancel"
)

List

payment_intents = client.payment_intents.list(
  currency: "PHP",
  page_num: 0,
  page_size: 20
)

Refunds

Create

refund = client.refunds.create(
  {
    payment_intent_id: "int_123",
    amount: 500,
    reason: "requested_by_customer",
    metadata: {
      order_id: "ORDER-1001"
    }
  },
  idempotency_key: "order-1001-refund-1"
)

Retrieve

refund = client.refunds.retrieve("ref_123")

List

refunds = client.refunds.list(
  payment_intent_id: "int_123",
  page_num: 0,
  page_size: 20
)

Idempotency

Use idempotency keys for payment creation, updates, cancellations, and refunds. The key should be unique per operation.

Good examples:

  • order-1001-create
  • order-1001-update
  • order-1001-refund-1

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.

Webhook verification

Verify incoming webhook requests using the raw request body and Airwallex signature headers:

raw_body = request.body.read

event = Airwallex::Webhook.construct_event(
  payload: raw_body,
  signature: request.headers["x-signature"],
  timestamp: request.headers["x-timestamp"],
  secret: ENV.fetch("AIRWALLEX_WEBHOOK_SECRET")
)

case event["name"]
when "payment_intent.succeeded"
  # handle payment success
when "refund.accepted"
  # handle refund accepted
end

Important notes:

  • Always use the raw request body.
  • Verify the signature before parsing JSON (construct_event does this for you).
  • Old timestamps are rejected by default (300 second tolerance).
  • Signature comparison is timing-safe.

Rails installation

Run the generator:

rails generate airwallex:install

This creates:

config/initializers/airwallex.rb

Credentials example:

airwallex:
  client_id: your_client_id
  api_key: your_api_key
  login_as: optional_account_id
  webhook_secret: your_webhook_secret

Rails webhook controller example:

class AirwallexWebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    event = Airwallex::Webhook.construct_event(
      payload: request.body.read,
      signature: request.headers["x-signature"],
      timestamp: request.headers["x-timestamp"],
      secret: Rails.application.credentials.dig(:airwallex, :webhook_secret) || ENV.fetch("AIRWALLEX_WEBHOOK_SECRET")
    )

    case event["name"]
    when "payment_intent.succeeded"
      # handle payment success
    when "refund.accepted"
      # handle refund accepted
    end

    head :ok
  rescue Airwallex::WebhookSignatureError, Airwallex::InvalidResponseError
    head :bad_request
  end
end

Route:

post "/webhooks/airwallex", to: "airwallex_webhooks#create"

See also examples/rails_webhook_controller.rb.

Sample Rails App

A lightweight Rails integration example is available in:

examples/rails_app

It demonstrates:

  • Airwallex initializer configuration
  • PaymentIntent creation
  • Refund creation
  • Webhook verification
  • Example routes

See docs/examples.md for more usage notes.

Error handling

The SDK raises typed errors for configuration, authentication, HTTP status codes, timeouts, invalid responses, and webhook verification failures.

Error Description
Airwallex::Error Base error for all SDK errors
Airwallex::ConfigurationError Missing or invalid configuration
Airwallex::AuthenticationError Login or token handling failed
Airwallex::ArgumentError Invalid method arguments
Airwallex::HTTPError Base class for HTTP error responses
Airwallex::BadRequestError HTTP 400
Airwallex::UnauthorizedError HTTP 401
Airwallex::ForbiddenError HTTP 403
Airwallex::NotFoundError HTTP 404
Airwallex::ConflictError HTTP 409
Airwallex::RateLimitError HTTP 429
Airwallex::ServerError HTTP 5xx
Airwallex::TimeoutError Request timeout or connection failure
Airwallex::InvalidResponseError Invalid JSON or webhook payload
Airwallex::WebhookSignatureError Webhook signature or timestamp verification failed

Example:

begin
  client.payment_intents.retrieve("int_123")
rescue Airwallex::NotFoundError => e
  puts e.status
  puts e.code
  puts e.message
rescue Airwallex::RateLimitError
  # retry later
rescue Airwallex::Error => e
  # generic Airwallex SDK error
end

Release / Local Installation

Build the gem from a checkout:

gem build airwallex-ruby.gemspec

Or use the Rake task (output goes to pkg/):

bundle exec rake build

Install locally:

gem install ./airwallex-ruby-0.1.0.gem
# or, after rake build:
gem install ./pkg/airwallex-ruby-0.1.0.gem

Test in IRB:

require "airwallex"
Airwallex::VERSION
# => "0.1.0"

Publishing

Do not publish until the release checklist is complete and changes have been reviewed.

Automated (recommended)

After review, tag and push. GitHub Actions publishes to RubyGems and creates a GitHub Release:

git tag v0.1.0
git push origin main
git push origin v0.1.0

Requires the RUBYGEMS_API_KEY repository secret. See docs/release.md.

Manual

gem push airwallex-ruby-0.1.0.gem
# or, after rake build:
gem push pkg/airwallex-ruby-0.1.0.gem

See docs/release.md for the full release checklist.

Sandbox Integration Checks

Manual sandbox verification scripts are available under:

scripts/sandbox

See:

docs/sandbox.md

Example:

cp .env.example .env
bundle exec rspec
ruby scripts/sandbox/authenticate.rb
ruby scripts/sandbox/create_payment_intent.rb

These scripts require Airwallex sandbox credentials and are not part of the automated unit test suite.

Development

bundle install
bundle exec rspec
bundle exec rubocop
bundle exec rake build

See docs/airwallex-research.md for API notes and planned resources.

Testing

  • RSpec is used for the test suite.
  • WebMock is used to stub Airwallex API requests.
  • No real Airwallex credentials are required for unit tests.

Roadmap

  • PaymentIntent confirm/capture support
  • PaymentAttempts resource
  • Customers resource
  • PaymentConsents resource
  • Transfers resource
  • Balances resource
  • Transactions resource
  • File uploads
  • More Rails generators
  • Integration test mode

Repository

  • Issues: use GitHub Issues for bugs and feature requests.
  • Pull requests: contributions are welcome. See CONTRIBUTING.md.
  • Security: do not report vulnerabilities in public issues. See SECURITY.md.
  • CI: GitHub Actions runs specs, RuboCop, and gem build.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add specs for your changes
  4. Run the test suite (bundle exec rspec) and RuboCop (bundle exec rubocop)
  5. Open a pull request

Examples

Runnable and copy-paste examples live in the examples/ directory:

License

MIT — see LICENSE.txt.

About

Unofficial Ruby SDK for Airwallex payment, payout, and treasury APIs

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors

Languages