Cost+Docs

Ruby

Official Ruby SDK for the Cost+ payment gateway

Official Ruby SDK for the Cost+ payment gateway. Simplifies the HPP (Hosted Payment Page) redirect flow, HMAC payload signing, and webhook verification.

Features

  • HMAC-SHA256 signature generation and constant-time verification
  • Automatic snake_case mapping from the API to Ruby-friendly OpenStruct objects
  • Webhook parsing + API-based order verification
  • Tested across Ruby 3.1, 3.2, and 3.3
  • Sinatra-based demo merchant app included

Requirements

Installation

Add to your Gemfile:

gem "nopayn"

Then run:

bundle install

Or install directly:

gem install nopayn

Quick Start

1. Initialise the Client

require "nopayn"

nopayn = NoPayn::Client.new(
  api_key:     "your-api-key",
  merchant_id: "your-project"
)

2. Create a Payment and Redirect to the HPP

result = nopayn.generate_payment_url(
  amount:            1295,           # €12.95 in cents
  currency:          "EUR",
  merchant_order_id: "ORDER-001",
  description:       "Premium Widget",
  return_url:        "https://shop.example.com/success",
  failure_url:       "https://shop.example.com/failure",
  webhook_url:       "https://shop.example.com/webhook",
  locale:            "en-GB",
  expiration_period: "PT30M"
)

# Redirect the customer
# result.order_url   → HPP (customer picks payment method)
# result.payment_url → direct link to the first transaction's payment method
# result.signature   → HMAC-SHA256 for verification
# result.order_id    → NoPayn order UUID

3. Handle the Webhook

post "/webhook" do
  request.body.rewind
  raw_body = request.body.read

  verified = nopayn.verify_webhook(raw_body)

  puts verified.order.status  # "completed", "cancelled", etc.
  puts verified.is_final      # true when the order won't change

  if verified.order.status == "completed"
    # Fulfil the order
  end

  status 200
end

API Reference

NoPayn::Client.new(api_key:, merchant_id:, base_url:)

ParameterTypeRequiredDefault
api_keyStringYes
merchant_idStringYes
base_urlStringNohttps://api.nopayn.co.uk

client.create_order(params) → OpenStruct

Creates an order via POST /v1/orders/.

ParameterTypeRequiredDescription
:amountIntegerYesAmount in smallest currency unit (cents)
:currencyStringYesISO 4217 code (EUR, GBP, USD, NOK, SEK)
:merchant_order_idStringNoYour internal order reference
:descriptionStringNoOrder description
:return_urlStringNoRedirect after successful payment
:failure_urlStringNoRedirect on cancel/expiry/error
:webhook_urlStringNoAsync status-change notifications
:localeStringNoHPP language (en-GB, de-DE, nl-NL, etc.)
:payment_methodsArray<String>NoFilter HPP methods
:expiration_periodStringNoISO 8601 duration (PT30M)

Available payment methods: credit-card, apple-pay, google-pay, vipps-mobilepay

client.get_order(order_id) → OpenStruct

Fetches order details via GET /v1/orders/{id}/.

client.create_refund(order_id, amount, description: nil) → OpenStruct

Issues a full or partial refund via POST /v1/orders/{id}/refunds/.

client.generate_payment_url(params) → OpenStruct

Convenience method that creates an order and returns:

result.order_id     # NoPayn order UUID
result.order_url    # HPP URL
result.payment_url  # Direct payment URL (first transaction)
result.signature    # HMAC-SHA256 of amount:currency:order_id
result.order        # Full order OpenStruct

client.generate_signature(amount, currency, order_id) → String

Generates an HMAC-SHA256 hex signature.

client.verify_signature(amount, currency, order_id, signature) → Boolean

Constant-time verification of an HMAC-SHA256 signature.

client.verify_webhook(raw_body) → OpenStruct

Parses the webhook body, then calls GET /v1/orders/{id}/ to verify the actual status.

Standalone HMAC Utilities

require "nopayn"

sig = NoPayn::Signature.generate("your-api-key", 1295, "EUR", "order-uuid")
ok  = NoPayn::Signature.verify("your-api-key", 1295, "EUR", "order-uuid", sig)

Error Handling

begin
  nopayn.create_order(amount: 100, currency: "EUR")
rescue NoPayn::ApiError => e
  puts e.status_code  # 401, 400, etc.
  puts e.error_body   # Raw API error response
rescue NoPayn::Error => e
  puts e.message      # Network or parsing error
end
ExceptionParentDescription
NoPayn::ErrorStandardErrorBase error for all SDK errors
NoPayn::ApiErrorNoPayn::ErrorHTTP error from the API
NoPayn::WebhookErrorNoPayn::ErrorInvalid webhook payload

Order Statuses

StatusFinal?Description
newNoOrder created
processingNoPayment in progress
completedYesPayment successful — deliver the goods
cancelledYesPayment cancelled by customer
expiredYesPayment link timed out
errorYesTechnical failure

Webhook Best Practices

  1. Always verify via the API — the webhook payload only contains the order ID, never the status. The SDK's verify_webhook does this automatically.
  2. Return HTTP 200 to acknowledge receipt. Any other code triggers up to 10 retries (2 minutes apart).
  3. Implement a backup poller — for orders older than 10 minutes that haven't reached a final status, poll get_order as a safety net.
  4. Be idempotent — you may receive the same webhook multiple times.

Test Cards

Use these cards in Cost+ test mode (sandbox website):

CardNumberNotes
Visa (success)4111 1111 1111 1111Any CVV
Mastercard (success)5544 3300 0003 7Any CVV
Visa (declined)4111 1111 1111 1105Do Not Honor
Visa (insufficient funds)4111 1111 1111 1151Insufficient Funds

Use any future expiry date and any 3-digit CVC.

Demo App

A Sinatra-based demo app is included in the GitHub repository for testing the full payment flow.

Support

Need help? Reach out to our support team at support@costplus.io.

On this page