Skip to content

fix: strict compliance with API and webhook security spec#6

Open
danieldenis01 wants to merge 1 commit into
AbacatePay:mainfrom
academia-ruby:fix/strict-spec-compliance
Open

fix: strict compliance with API and webhook security spec#6
danieldenis01 wants to merge 1 commit into
AbacatePay:mainfrom
academia-ruby:fix/strict-spec-compliance

Conversation

@danieldenis01
Copy link
Copy Markdown

Three small fixes uncovered while building a pay-rails/pay adapter on top of the SDK and exercising it against the AbacatePay sandbox. All low-risk, no public API changes — defaults align the SDK with what the API and the docs actually require today.

1. Compact nil keys before sending JSON payloads

The current API rejects requests like { \"cellphone\": null } or { \"description\": null } with HTTP 400:

Expected property 'cellphone' to be string but found: null

The clients build payloads with optional fields and pass nil straight through. Adding .compact on the request hash matches what the API actually accepts. Affected:

  • clients/customer_client.rb#create
  • clients/product_client.rb#create

This was hit in production-style flows where a Pay::Customer had no phone_number set, and where one-time products were created without a description.

2. HMAC webhook signature: hex → base64

docs.abacatepay.com/pages/webhooks/security specifies HMAC-SHA256 base64-encoded in the X-Webhook-Signature header. The previous implementation used OpenSSL::HMAC.hexdigest, which never matched the signatures the AbacatePay platform actually sends, so verify! rejected every real delivery as invalid.

3. Default the HMAC key to AbacatePay's documented PUBLIC_KEY

The same security doc states webhooks are signed with a fixed public key (not the per-webhook secret you configure in the dashboard — that one goes in the ?webhookSecret= query parameter). The constant is exposed as AbacatePay::Webhooks::PUBLIC_KEY and is now the default for verify! / valid?, so callers that just want to verify a delivery don't have to know about it. The secret: kwarg still works for tests and for any future per-webhook signing scheme.

Other

Adds base64 as an explicit runtime dependency. Ruby 3.4 stopped shipping it as a default gem, so require \"base64\" started warning (and would eventually break).

Test plan

  • bundle exec rspec — 327 examples, 0 failures
  • Tested end-to-end against the AbacatePay sandbox: customer create, product create, hosted checkout, webhook receipt with both query-parameter auth and HMAC header

🤖 Generated with Claude Code

Three fixes uncovered while integrating the SDK in a real-world Pay
adapter against the AbacatePay sandbox. All low-risk, no public API
changes:

1. Compact nil keys before sending JSON payloads.
   The current API rejects requests like { cellphone: null } or
   { description: null } with HTTP 400 ("Expected property X to be
   string but found: null"). The clients construct payloads with
   optional fields and let nil through. Adding .compact matches what
   the API actually accepts.
   - clients/customer_client.rb (#create)
   - clients/product_client.rb  (#create)

2. HMAC webhook signature: switch from hex to base64.
   https://docs.abacatepay.com/pages/webhooks/security specifies
   HMAC-SHA256 base64-encoded. The previous verify! used
   OpenSSL::HMAC.hexdigest, which never matched real deliveries.

3. Default the HMAC key to AbacatePay's documented PUBLIC_KEY.
   The doc says webhooks are signed with a fixed public key (not the
   per-webhook secret). Exposed as AbacatePay::Webhooks::PUBLIC_KEY;
   verify!/valid? now default to it. Callers can still override
   secret: for tests.

Adds base64 as an explicit runtime dependency (Ruby 3.4 no longer
ships it with the default gems).

Suite: 327 examples, 0 failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ChristoPy ChristoPy requested a review from matheuscarddoso May 14, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant