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.
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
- Quick Start
- Authentication
- Configuration
- Core API Resources
- Customer Portal API
- Pagination
- Error Handling
- Webhook Verification
- Environment Support
- Examples
- Development
- Contributing
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: "customer@example.com",
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: "customer@example.com"
}
})
# 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.
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
License
The gem is available as open source under the terms of the MIT License.