Pago Ruby SDK

Ruby client for the Pago API, generated from its OpenAPI specification. Requires Ruby 3.1 or newer and has no runtime dependencies — only net/http, json and openssl from the standard library.

gem "pago-sdk"

Usage

require "pago"

client = Pago::Client.new(access_token: ENV.fetch("PAGO_ACCESS_TOKEN"))

Pago::Client always tracks the latest API version (2026-04). Pin a version explicitly to opt out of that:

client = Pago::V2026_04::Client.new(access_token: token)

The base URL defaults to https://api.pago.sh and reads PAGO_BASE_URL when set; the sandbox lives at Pago::BaseClient::SANDBOX_BASE_URL.

Models

Models are immutable value objects built with keyword arguments. Required properties are required keyword arguments; everything else defaults to unset.

order = Pago::Models::Order.from_json(payload)
order.id
order.to_json_hash          # Hash ready for JSON.generate
order.to_json               # String

Only the attributes you actually set are serialized, so a partial update never sends null for the fields it does not touch. Properties this SDK release does not know about survive a from_json / to_json round trip untouched.

Pagination

Every paginated operation gets an _each companion returning a Pago::Paginator, a plain Enumerable over the items that fetches pages on demand:

client.products.list_each(organization_id: id).each { |product| puts product.name }
client.products.list_each(organization_id: id).first(50)
client.products.list_each(organization_id: id) { |product| puts product.name }
client.products.list_each(organization_id: id).pages.count   # page objects

Errors

Every declared error response has its own class, inheriting from the generic error matching its status code:

begin
  client.products.get(product_id)
rescue Pago::Errors::ResourceNotFound => e
  e.status_code   # 404
  e.data          # deserialized error payload
rescue Pago::NotFoundError
  # any 404 of the API
rescue Pago::APIError => e
  # anything the API rejected
rescue Pago::NetworkError
  # the request never reached the API
end

Webhooks

Signature verification follows the Standard Webhooks specification, and the payload comes back as the model matching its event type:

event = Pago::Webhooks.validate_event(
  body: request.body.read,
  headers: request.headers,
  secret: ENV.fetch("PAGO_WEBHOOK_SECRET")
)

case event
when Pago::Models::WebhookOrderPaidPayload then fulfill(event.data)
end

The secret is the one shown when the endpoint is created — the whsec_ prefix followed by the base64 encoded signing key. Pass it verbatim: the SDK strips the prefix and base64 decodes the rest to obtain the HMAC key, exactly like every other Standard Webhooks implementation.

It raises Pago::WebhookVerificationError on a bad signature or a timestamp outside the five minute window, Pago::WebhookSecretError (a subclass of it) when the secret is not in whsec_<base64> form, and Pago::WebhookUnknownTypeError for an event type this SDK release does not know.

Custom HTTP adapter

The client talks to any object answering call(Pago::HTTP::Request) and returning a Pago::HTTP::Response — useful for instrumentation, retries or tests:

class LoggingAdapter
  def initialize(inner) = @inner = inner

  def call(request)
    warn "#{request.http_method} #{request.url}"
    @inner.call(request)
  end
end

Pago::Client.new(access_token: token, adapter: LoggingAdapter.new(Pago::HTTP::NetHTTPAdapter.new))

Types

The gem ships RBS signatures under sig/, and every public method carries YARD documentation.

License

MIT