ridebuilder-affiliate (Ruby)

Server-side SDK for RideBuilder's FirstParty affiliate program. It does two things:

  1. Capture the click_id a shopper arrives with, so your backend can bind it to the cart/order.
  2. Report checkout and return postbacks to RideBuilder (auth, retries, idempotency handled).

Mirrors the Node/.NET/Python/PHP/Java SDKs — same contract, verified by the shared conformance suite.

Install

gem install ridebuilder-affiliate

or in a Gemfile: gem "ridebuilder-affiliate". Requires Ruby 3.0+. No runtime dependencies — the default transport uses the stdlib net/http.

The pattern: capture at landing, bind to the order

require "ridebuilder/affiliate"

# 1. On landing, read a validated click_id off the request URL and persist it onto YOUR cart record.
click_id = RideBuilder::Affiliate::Capture.from_url(request.fullpath)
cart.ridebuilder_click_id = click_id if click_id

# 2. At order time, send the postback from your backend.
rb = RideBuilder::Affiliate::Client.new(api_key: ENV.fetch("RIDEBUILDER_API_KEY"))
rb.report_checkout(
  order_id: order.id,
  subtotal: "199.99",   # major units; a string keeps it exact
  currency: "USD",
  click_id: order.ridebuilder_click_id
)

Store the API key server-side (env/secrets) — never in frontend code.

Decoupled frontend (e.g. React) + separate Ruby backend

The browser snippet captures the click_id into a first-party cookie; get it to your backend one of two ways:

# Same registrable domain — the cookie rides along; read it off the Cookie header:
click_id = RideBuilder::Affiliate::Capture.from_cookie_header(request.get_header("HTTP_COOKIE"))

# Cross-domain / mobile — the frontend forwards it in the checkout call:
click_id = RideBuilder::Affiliate::Capture.from_headers(request.headers)  # default: X-RideBuilder-Click-Id

Either way, report_checkout is unchanged — that's the SDK's real value in a decoupled setup.

Refunds

rb.report_return(return_id: refund.id, order_id: order.id, refund_amount: "49.95", currency: "USD")

Integration protocol (register / verify / heartbeat)

rb = RideBuilder::Affiliate::Client.new(api_key: api_key, environment: "production") # or "sandbox"

reg = rb.register   # handshake on install/startup; returns a stable integration id
rb.verify           # deploy/CI self-test — raises RideBuilder::Affiliate::Error on a bad/rotated key
rb.heartbeat        # periodic liveness (call on a schedule)

The SDK reports its own type (ruby_sdk), version, and default capabilities.

Capture helpers

All validate ref == "ridebuilder" and the UUID-v4 click_id, returning nil otherwise:

  • Capture.from_url(url) — from an absolute or relative URL.
  • Capture.from_query(hash) — from a decoded query map.
  • Capture.from_cookie_header(cookie_header) — recover it from the ridebuilder_attribution cookie.
  • Capture.from_headers(headers, name = "X-RideBuilder-Click-Id") — from a forwarding header (decoupled path).

Client options

RideBuilder::Affiliate::Client.new(
  api_key:,               # required
  base_url: nil,          # defaults to https://api.ridebuilder.com/v1
  max_retries: 3,         # retries on network errors, timeouts, 5xx, 429
  timeout_ms: 10_000,     # per-attempt timeout
  environment: "production",
  transport: nil          # inject an object responding to #call(method, url, headers, body)
)

report_checkout / report_return return PostbackResult (.accepted, .status; 202 = accepted, validated asynchronously). Invalid input raises a non-retryable RideBuilder::Affiliate::Error; auth/size failures (401, 413) raise with .status and .error_code. Amounts must be > 0 with at most 2 decimal places (pass a string to avoid float rounding) or the call raises up front.

Tests

Plain Ruby, no test framework required:

ruby test/conformance.rb   # the shared cross-language fixtures
ruby test/unit.rb          # validation, capture, money, retry/error, identity

Contract

Wraps the RideBuilder affiliate REST contract — POST /v1/postback/checkout, /postback/return, /postback/health, the /integration/* endpoints, the /redirect link format, and API-key provisioning. Verified byte-for-byte against the Node/.NET/Python/PHP/Java SDKs by the shared conformance fixtures.