PayHub Ruby SDK

Official PayHub SDK for Ruby. Synchronous Net::HTTP transport, idempotent retries, typed error hierarchy, webhook verifier, and a discriminated NextAction you case-match on.

Install

gem install payhub

Or in Gemfile:

gem "payhub", "~> 1.0"

Quickstart — Sadad OTP

require "payhub"

client = Payhub::Client.new(ENV.fetch("PAYHUB_API_KEY"))

payment = client.payments.create(
  psp: "sadad",
  merchant_order_ref: "ord-42",
  amount_minor: 4500,
  currency: "LYD",
  customer: {msisdn: "218910000001", birth_year: 1990}
)

case payment.next_action
in Payhub::NextAction::OtpRequired => otp
  puts "Sadad sent OTP to #{otp.masked_destination}"
end

confirmed = client.payments.confirm_otp(payment.id, "111111")
puts confirmed.status # "succeeded"

Webhook verification (Rails / Sinatra / Rack)

The single most important rule: verify the raw request body, not a parsed Hash. Re-serializing the JSON before HMAC will corrupt the signature.

# Rack / Sinatra
post "/webhooks/payhub" do
  body = request.body.read

  ev = Payhub::WebhookEvent.verify(
    secret: ENV.fetch("PAYHUB_WEBHOOK_SECRET"),
    body: body,
    header: request.env["HTTP_HUB_SIGNATURE"]
  )

  # ev.type ∈ "payment.succeeded" | "payment.failed" | "payment.expired" | "payment.refunded"
  status 200
end
# Rails controller
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def payhub
    body = request.raw_post
    ev = Payhub::WebhookEvent.verify(
      ENV.fetch("PAYHUB_WEBHOOK_SECRET"),
      body,
      request.headers["Hub-Signature"]
    )
    head :ok
  end
end

Errors

Class When
Payhub::Errors::AuthenticationError 401
Payhub::Errors::PermissionError 403
Payhub::Errors::NotFoundError 404
Payhub::Errors::IdempotencyConflictError 409
Payhub::Errors::ValidationError 422
Payhub::Errors::RateLimitedError 429 (#retry_after)
Payhub::Errors::GatewayError 5xx + gateway.<psp>.*
Payhub::Errors::ServerError other 5xx
Payhub::Errors::TimeoutError timeout
Payhub::Errors::ConnectionError TCP / TLS / DNS failure
Payhub::Errors::DecodeError malformed response
Payhub::MalformedHeaderError webhook header missing t=/v1=
Payhub::TimestampOutOfToleranceError webhook clock skew > 300 s
Payhub::InvalidSignatureError webhook HMAC mismatch

License

MIT — see LICENSE.