From aae0f18b355db01274fcb7bc281cf7bcf23feaaa Mon Sep 17 00:00:00 2001 From: adrian-y1 <80251505+adrian-y1@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:39:15 +1000 Subject: [PATCH 1/3] resolve webhook timestamp issue --- lib/airwallex/webhook.rb | 4 ++++ spec/airwallex/webhook_spec.rb | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/airwallex/webhook.rb b/lib/airwallex/webhook.rb index 8cea1b3..e27603f 100644 --- a/lib/airwallex/webhook.rb +++ b/lib/airwallex/webhook.rb @@ -5,6 +5,8 @@ module Airwallex module Webhook DEFAULT_TOLERANCE = 300 # 5 minutes + # Timestamps below this are seconds, at/above are milliseconds. Valid until year 2286. + MS_THRESHOLD = 10_000_000_000 module_function @@ -37,6 +39,8 @@ def compute_signature(timestamp, payload, secret) def verify_timestamp(timestamp, tolerance) current_time = Time.now.to_i timestamp_int = timestamp.to_i + # Airwallex sends x-timestamp in milliseconds; normalize to seconds before comparing. + timestamp_int /= 1000 if timestamp_int > MS_THRESHOLD if (current_time - timestamp_int).abs > tolerance raise SignatureVerificationError, "Timestamp outside tolerance (#{tolerance}s)" diff --git a/spec/airwallex/webhook_spec.rb b/spec/airwallex/webhook_spec.rb index a7f7db2..40a5b87 100644 --- a/spec/airwallex/webhook_spec.rb +++ b/spec/airwallex/webhook_spec.rb @@ -27,6 +27,23 @@ end.to raise_error(Airwallex::SignatureVerificationError, /Timestamp outside tolerance/) end + it "accepts a millisecond-precision timestamp within tolerance" do + ms_timestamp = (Time.now.to_f * 1000).to_i.to_s + ms_signature = described_class.send(:compute_signature, ms_timestamp, payload, secret) + + event = described_class.construct_event(payload, ms_signature, ms_timestamp, secret: secret) + expect(event).to be_a(Airwallex::Webhook::Event) + end + + it "raises error with an old millisecond-precision timestamp" do + old_ms_timestamp = ((Time.now.to_i - 400) * 1000).to_s + old_ms_signature = described_class.send(:compute_signature, old_ms_timestamp, payload, secret) + + expect do + described_class.construct_event(payload, old_ms_signature, old_ms_timestamp, secret: secret) + end.to raise_error(Airwallex::SignatureVerificationError, /Timestamp outside tolerance/) + end + it "accepts custom tolerance" do old_timestamp = (Time.now.to_i - 400).to_s old_signature = described_class.send(:compute_signature, old_timestamp, payload, secret) From 19da70c55f9b132d14a568a0bff6f4ac7d3e7b94 Mon Sep 17 00:00:00 2001 From: adrian-y1 <80251505+adrian-y1@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:40:36 +1000 Subject: [PATCH 2/3] update version --- lib/airwallex/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/airwallex/version.rb b/lib/airwallex/version.rb index 34e6626..a15568d 100644 --- a/lib/airwallex/version.rb +++ b/lib/airwallex/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Airwallex - VERSION = "0.3.0" + VERSION = "0.5.0" end From cf3100bfd266a5ba1a14f62f2270c76f11c1c5e1 Mon Sep 17 00:00:00 2001 From: adrian-y1 <80251505+adrian-y1@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:41:30 +1000 Subject: [PATCH 3/3] update gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e0d6a0a..5997ad8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - airwallex (0.3.0) + airwallex (0.5.0) faraday (~> 2.0) faraday-multipart (~> 1.0) faraday-retry (~> 2.0)