Polar Ruby SDK

Note: This SDK was generated with the help of AI and is not an official SDK from Polar.sh. It is a community project and not affiliated with or endorsed by Polar.sh. Contributions, suggestions, and improvements are very welcome!

A comprehensive Ruby SDK for Polar.sh, providing easy integration with their payment infrastructure, subscription management, and merchant services.

Gem Version Ruby License: MIT

Features

  • Complete API Coverage: Access all Polar.sh API endpoints
  • Type Safety: Well-structured response objects and error handling
  • Authentication: Support for Organization Access Tokens and Customer Sessions
  • Pagination: Built-in pagination support for list endpoints
  • Retry Logic: Automatic retries with exponential backoff
  • Webhook Verification: Secure webhook signature validation
  • Environment Support: Production and sandbox environments
  • Customer Portal: Dedicated customer-facing API endpoints
  • Ruby Compatibility: Supports Ruby 2.7+

Table of Contents

Installation

Add this line to your application's Gemfile:

gem 'polar-ruby'

And then execute:

bundle install

Or install it yourself as:

gem install polar-ruby

Quick Start

require 'polar'

# Initialize client with Organization Access Token
client = Polar.new(access_token: 'polar_oat_your_token_here')

# List organizations
organizations = client.organizations.list.auto_paginate
puts organizations.first

# Create a product
product = client.products.create({
  name: "Premium Plan",
  description: "Access to premium features",
  organization_id: "org_123"
})

# List customers with pagination
client.customers.list.each do |customer|
  puts "Customer: #{customer['name']} (#{customer['email']})"
end

Authentication

Organization Access Tokens (OAT)

Use an OAT to act on behalf of your organization. Create tokens in your organization settings.

# Via initialization
client = Polar.new(access_token: 'polar_oat_your_token_here')

# Via environment variable
ENV['POLAR_ACCESS_TOKEN'] = 'polar_oat_your_token_here'
client = Polar.new

# Via global configuration
Polar.configure do |config|
  config.access_token = 'polar_oat_your_token_here'
end
client = Polar.new

Customer Sessions

For customer-facing operations, use customer session tokens:

# Customer portal operations require customer session
customer_client = Polar.new(customer_session: 'customer_session_token')

# Or pass session token to individual calls
orders = client.customer_portal.orders.list(customer_session: 'customer_session_token')

Configuration

Global Configuration

Polar.configure do |config|
  config.access_token = 'polar_oat_your_token_here'
  config.server = :sandbox  # :production (default) or :sandbox
  config.timeout = 30       # Request timeout in seconds
  config.retries = 3        # Number of retry attempts
  config.logger = Logger.new(STDOUT)
  config.debug = true       # Enable debug logging
end

Per-Client Configuration

client = Polar.new(
  access_token: 'polar_oat_your_token_here',
  server: :sandbox,
  timeout: 60,
  retries: 5,
  debug: true
)

Environment Variables

The SDK respects the following environment variables:

  • POLAR_ACCESS_TOKEN: Default access token
  • POLAR_DEBUG: Enable debug logging (set to 'true')

Core API Resources

Organizations

# List organizations
organizations = client.organizations.list.auto_paginate

# Get organization
org = client.organizations.get('org_123')

# Create organization
new_org = client.organizations.create({
  name: "My Company",
  slug: "my-company"
})

# Update organization
updated_org = client.organizations.update('org_123', { name: "Updated Name" })

Products

# List products
products = client.products.list(organization_id: 'org_123').auto_paginate

# Create product
product = client.products.create({
  name: "Premium Plan",
  description: "Access to premium features",
  organization_id: "org_123",
  prices: [
    {
      type: "recurring",
      amount: 2000,  # $20.00 in cents
      currency: "USD",
      recurring: { interval: "month" }
    }
  ]
})

# Get product
product = client.products.get('prod_123')

# Update product
updated_product = client.products.update('prod_123', { name: "New Name" })

Customers

# List customers
customers = client.customers.list(organization_id: 'org_123')

# Create customer
customer = client.customers.create({
  email: "[email protected]",
  name: "John Doe",
  organization_id: "org_123"
})

# Get customer
customer = client.customers.get('cust_123')

# Update customer
updated_customer = client.customers.update('cust_123', { name: "Jane Doe" })

# Get customer by external ID
customer = client.customers.get_external('external_123', organization_id: 'org_123')

Orders

# List orders
orders = client.orders.list(organization_id: 'org_123')

# Get order
order = client.orders.get('order_123')

# Update order
updated_order = client.orders.update('order_123', { metadata: { key: "value" } })

# Generate invoice
invoice = client.orders.generate_invoice('order_123')

Payments

# List payments
payments = client.payments.list(organization_id: 'org_123')

# Get payment
payment = client.payments.get('pay_123')

Subscriptions

# List subscriptions
subscriptions = client.subscriptions.list(organization_id: 'org_123')

# Get subscription
subscription = client.subscriptions.get('sub_123')

# Update subscription
updated_sub = client.subscriptions.update('sub_123', { metadata: { key: "value" } })

# Cancel subscription
cancelled_sub = client.subscriptions.revoke('sub_123')

Checkouts

# Create checkout session
checkout = client.checkouts.create({
  product_price_id: "price_123",
  success_url: "https://yoursite.com/success",
  cancel_url: "https://yoursite.com/cancel",
  customer_data: {
    email: "[email protected]"
  }
})

# Get checkout session
checkout = client.checkouts.get('checkout_123')

# Client-side operations (no auth required)
checkout = client.checkouts.client_get('checkout_123')
updated_checkout = client.checkouts.client_update('checkout_123', { customer_data: { name: "John" } })
confirmed_checkout = client.checkouts.client_confirm('checkout_123')

Customer Portal API

The Customer Portal API provides customer-facing endpoints with proper scoping:

# Initialize with customer session
customer_client = Polar.new(customer_session: 'customer_session_token')

# Or pass session to individual calls
session_token = 'customer_session_token'

# Get customer information
customer = client.customer_portal.customers.get(customer_session: session_token)

# List customer orders
orders = client.customer_portal.orders.list(customer_session: session_token)

# Get specific order
order = client.customer_portal.orders.get('order_123', customer_session: session_token)

# List customer subscriptions
subscriptions = client.customer_portal.subscriptions.list(customer_session: session_token)

# Cancel subscription
client.customer_portal.subscriptions.cancel('sub_123', customer_session: session_token)

# List license keys
license_keys = client.customer_portal.license_keys.list(customer_session: session_token)

# Validate license key (no auth required)
validation = client.customer_portal.license_keys.validate('license_key_123')

# Activate license key
activation = client.customer_portal.license_keys.activate('license_key_123', {
  label: "Development Machine"
})

Pagination

The SDK provides automatic pagination support:

# Auto-paginate (loads all pages into memory)
all_customers = client.customers.list.auto_paginate

# Manual pagination
customers_paginated = client.customers.list(organization_id: 'org_123')

# Iterate through all pages
customers_paginated.each do |customer|
  puts "Customer: #{customer['name']}"
end

# Get specific page
page_2 = customers_paginated.page(2)

# Check pagination info
puts "Total: #{customers_paginated.count}"
puts "Pages: #{customers_paginated.total_pages}"

Error Handling

The SDK provides comprehensive error handling:

begin
  customer = client.customers.get('invalid_id')
rescue Polar::NotFoundError => e
  puts "Customer not found: #{e.message}"
rescue Polar::UnauthorizedError => e
  puts "Authentication failed: #{e.message}"
rescue Polar::ValidationError => e
  puts "Validation error: #{e.message}"
  puts "Status: #{e.status_code}"
  puts "Body: #{e.body}"
rescue Polar::HTTPError => e
  puts "HTTP error: #{e.status_code} - #{e.message}"
rescue Polar::ConnectionError => e
  puts "Connection error: #{e.message}"
rescue Polar::TimeoutError => e
  puts "Request timed out: #{e.message}"
rescue Polar::Error => e
  puts "Polar SDK error: #{e.message}"
end

Error Types

  • Polar::HTTPError - Base HTTP error class
  • Polar::BadRequestError - 400 Bad Request
  • Polar::UnauthorizedError - 401 Unauthorized
  • Polar::ForbiddenError - 403 Forbidden
  • Polar::NotFoundError - 404 Not Found
  • Polar::UnprocessableEntityError - 422 Validation Error
  • Polar::TooManyRequestsError - 429 Rate Limited
  • Polar::InternalServerError - 500 Server Error
  • Polar::ConnectionError - Network connection failed
  • Polar::TimeoutError - Request timeout
  • Polar::WebhookVerificationError - Webhook signature verification failed

Webhook Verification

Verify webhook signatures to ensure requests are from Polar:

# In your webhook endpoint (Rails example)
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def polar_webhook
    payload = request.body.read
    headers = request.headers
    secret = ENV['POLAR_WEBHOOK_SECRET']

    begin
      event = Polar::Webhooks.validate_event(payload, headers, secret)

      # Process the event
      case event['type']
      when 'order.created'
        handle_order_created(event['data'])
      when 'subscription.cancelled'
        handle_subscription_cancelled(event['data'])
      end

      render json: { status: 'ok' }
    rescue Polar::WebhookVerificationError => e
      render json: { error: 'Invalid signature' }, status: 403
    end
  end

  private

  def handle_order_created(order_data)
    # Process order creation
    puts "New order: #{order_data['id']}"
  end

  def handle_subscription_cancelled(subscription_data)
    # Process subscription cancellation
    puts "Cancelled subscription: #{subscription_data['id']}"
  end
end

Manual Webhook Verification

# Verify signature manually
valid = Polar::Webhooks.verify_signature(
  payload,
  timestamp,
  signature,
  secret
)

# Parse event type
event_type = Polar::Webhooks.parse_event_type(payload)

# Check specific event type
is_order_event = Polar::Webhooks.event_type?(payload, 'order.created')

# Create event wrapper
event = Polar::Webhooks::Event.new(payload)
puts "Event ID: #{event.id}"
puts "Event Type: #{event.type}"
puts "Is subscription event: #{event.subscription_event?}"

Environment Support

Production Environment

# Default environment
client = Polar.new(access_token: 'polar_oat_prod_token')

# Explicit production
client = Polar.new(
  access_token: 'polar_oat_prod_token',
  server: :production
)

Sandbox Environment

# Sandbox environment for testing
client = Polar.new(
  access_token: 'polar_oat_sandbox_token',
  server: :sandbox
)

# Custom base URL
client = Polar.new(
  access_token: 'polar_oat_token',
  base_url: 'https://custom-api.polar.sh/v1'
)

Examples

E-commerce Integration

class PolarService
  def initialize
    @client = Polar.new(access_token: ENV['POLAR_ACCESS_TOKEN'])
  end

  def create_checkout_for_product(product_id, customer_email, success_url, cancel_url)
    @client.checkouts.create({
      product_price_id: product_id,
      success_url: success_url,
      cancel_url: cancel_url,
      customer_data: {
        email: customer_email
      }
    })
  end

  def get_customer_orders(customer_id)
    @client.orders.list(customer_id: customer_id).auto_paginate
  end

  def create_subscription_checkout(price_id, customer_data)
    @client.checkouts.create({
      product_price_id: price_id,
      success_url: "#{ENV['APP_URL']}/subscription/success",
      cancel_url: "#{ENV['APP_URL']}/subscription/cancel",
      customer_data: customer_data
    })
  end
end

License Key Management

class LicenseManager
  def initialize
    @client = Polar.new(access_token: ENV['POLAR_ACCESS_TOKEN'])
  end

  def validate_license(license_key)
    result = @client.customer_portal.license_keys.validate(license_key)
    {
      valid: result['valid'],
      customer: result['customer'],
      product: result['product']
    }
  rescue Polar::Error => e
    { valid: false, error: e.message }
  end

  def activate_license(license_key, machine_info)
    @client.customer_portal.license_keys.activate(license_key, {
      label: machine_info[:name],
      metadata: {
        os: machine_info[:os],
        version: machine_info[:version]
      }
    })
  rescue Polar::Error => e
    { error: e.message }
  end
end

Subscription Management

class SubscriptionManager
  def initialize
    @client = Polar.new(access_token: ENV['POLAR_ACCESS_TOKEN'])
  end

  def get_customer_subscriptions(customer_session)
    @client.customer_portal.subscriptions.list(
      customer_session: customer_session
    ).auto_paginate
  end

  def cancel_subscription(subscription_id, customer_session)
    @client.customer_portal.subscriptions.cancel(
      subscription_id,
      customer_session: customer_session
    )
  end

  def update_subscription(subscription_id, updates, customer_session)
    @client.customer_portal.subscriptions.update(
      subscription_id,
      updates,
      customer_session: customer_session
    )
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Running Tests

# Run all tests
bundle exec rspec

# Run with coverage
bundle exec rspec --format documentation

# Run specific test file
bundle exec rspec spec/polar/client_spec.rb

Documentation

Generate documentation:

bundle exec yard doc

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/polarsource/polar-ruby. This project is intended to be a safe, welcoming space for collaboration.

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License

The gem is available as open source under the terms of the MIT License.

Support