Skip to content

nanafox/paystack_sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Paystack Ruby SDK: Simplify Payments

The paystack_sdk gem provides a simple and intuitive interface for interacting with Paystack's payment gateway API. It allows developers to easily integrate Paystack's payment processing features into their Ruby applications. With support for various endpoints, this SDK simplifies tasks such as initiating transactions, verifying payments, managing customers, and more.

Table of Contents

Installation

Add this line to your application's Gemfile:

gem 'paystack_sdk'

And then execute:

bundle install

Or install it yourself as:

gem install paystack_sdk

Quick Start

require 'paystack_sdk'

# Initialize the client with your secret key
paystack = PaystackSdk::Client.new(secret_key: "sk_test_xxx")

# Initialize a transaction
params = {
  email: "[email protected]",
  amount: 2300,  # Amount in the smallest currency unit (kobo for NGN)
  currency: "NGN"
}

begin
  response = paystack.transactions.initiate(params)

  if response.success?
    puts "Visit this URL to complete payment: #{response.authorization_url}"
    puts "Transaction reference: #{response.reference}"
  else
    puts "Error: #{response.error_message}"
  end
rescue PaystackSdk::MissingParamError => e
  puts "Missing required data: #{e.message}"
rescue PaystackSdk::InvalidFormatError => e
  puts "Invalid data format: #{e.message}"
end

# Create a customer
customer_params = {
  email: "[email protected]",
  first_name: "John",
  last_name: "Doe"
}

begin
  customer_response = paystack.customers.create(customer_params)

  if customer_response.success?
    puts "Customer created: #{customer_response.data.customer_code}"
  else
    puts "Error: #{customer_response.error_message}"
  end
rescue PaystackSdk::ValidationError => e
  puts "Validation error: #{e.message}"
end

Response Format

The SDK handles API responses that use string keys (as returned by Paystack) and provides seamless access through both string and symbol notation. All response data maintains the original string key format from the API while offering convenient dot notation access.

Error Handling

The SDK uses a two-tier error handling approach:

  1. Validation Errors (thrown as exceptions) - for missing or invalid input data
  2. API Response Errors (returned as unsuccessful Response objects) - for API-level issues

Input Validation

The SDK validates your parameters before making API calls and throws exceptions immediately for data issues:

begin
  # This will throw an exception before making any API call
  response = paystack.transactions.initiate({amount: 1000}) # Missing required email
rescue PaystackSdk::MissingParamError => e
  puts "Fix your data: #{e.message}"
end

API Response Handling

All successful API calls return a Response object that you can check for success:

response = paystack.transactions.initiate(valid_params)

if response.success?
  puts "Transaction created: #{response.authorization_url}"
else
  puts "Transaction failed: #{response.error_message}"

  # Get detailed error information
  error_details = response.error_details
  puts "Status code: #{error_details[:status_code]}"
  puts "Error message: #{error_details[:message]}"
end

Note: The SDK raises exceptions for:

  • Validation errors - when required parameters are missing or have invalid formats
  • Authentication errors (401) - usually configuration issues
  • Rate limiting (429) - requires retry logic
  • Server errors (5xx) - Paystack infrastructure issues
  • Network errors - connection failures

All other API errors (resource not found, business logic errors, etc.) are returned as unsuccessful Response objects.

Usage

Client Initialization

# Initialize with your Paystack secret key
paystack = PaystackSdk::Client.new(secret_key: "sk_test_xxx")

# Or set the PAYSTACK_SECRET_KEY in your environment and do this instead
paystack = PaystackSdk::Client.new # => This will dynamically fetch the secret key

# You can access the connection directly if needed
connection = paystack.connection

Transactions

The SDK provides comprehensive support for Paystack's Transaction API.

Initialize a Transaction

# Prepare transaction parameters
params = {
  email: "[email protected]",
  amount: 10000,  # Amount in the smallest currency unit (e.g., kobo, pesewas, cents)
  currency: "GHS",
  callback_url: "https://example.com/callback"
}

# Initialize the transaction
response = paystack.transactions.initiate(params)

if response.success?
  puts "Transaction initialized successfully!"
  puts "Authorization URL: #{response.authorization_url}"
  puts "Access Code: #{response.access_code}"
  puts "Reference: #{response.reference}"
else
  puts "Error: #{response.error_message}"
end

Verify a Transaction

# Verify using transaction reference
response = paystack.transactions.verify(reference: "transaction_reference")

if response.success?
  transaction = response.data
  puts "Transaction verified successfully!"
  puts "Status: #{transaction.status}"
  puts "Amount: #{transaction.amount}"
  puts "Currency: #{transaction.currency}"
  puts "Customer Email: #{transaction.customer.email}"

  # Check specific transaction status
  case transaction.status
  when "success"
    puts "Payment successful!"
  when "pending"
    puts "Payment is pending."
  else
    puts "Current status: #{transaction.status}"
  end
else
  puts "Verification failed: #{response.error_message}"
end

List Transactions

# Get all transactions (default pagination: 50 per page)
response = paystack.transactions.list

# With custom pagination
response = paystack.transactions.list(per_page: 20, page: 2)

# With additional filters
response = paystack.transactions.list(
  per_page: 10,
  page: 1,
  from: "2025-01-01",
  to: "2025-04-30",
  status: "success"
)

if response.success?
  puts "Total transactions: #{response.count}" # response.size is another way

  response.data.each do |transaction|
    puts "ID: #{transaction.id}"
    puts "Reference: #{transaction.reference}"
    puts "Amount: #{transaction.amount}"
    puts "----------------"
  end

  # Get the first transaction
  first_transaction = response.data.first
  puts "First transaction reference: #{first_transaction.reference}"

  # Get the last transaction
  last_transaction = response.data.last
  puts "Last transaction amount: #{last_transaction.amount}"
else
  puts "Error: #{response.error_message}"
end

Fetch a Transaction

# Fetch a specific transaction by ID
transaction_id = "12345"
response = paystack.transactions.fetch(transaction_id)

if response.success?
  transaction = response.data
  puts "Transaction details:"
  puts "ID: #{transaction.id}"
  puts "Reference: #{transaction.reference}"
  puts "Amount: #{transaction.amount}"
  puts "Status: #{transaction.status}"

  # Access customer information
  puts "Customer Email: #{transaction.customer.email}"
  puts "Customer Name: #{transaction.customer.name}"
else
  puts "Error: #{response.error_message}"
end

Get Transaction Totals

# Get transaction volume and success metrics
response = paystack.transactions.totals

if response.success?
  puts "Total Transactions: #{response.data.total_transactions}"
  puts "Total Volume: #{response.data.total_volume}"
  puts "Pending Transfers: #{response.data.pending_transfers}"
else
  puts "Error: #{response.error_message}"
end

Customers

The SDK provides comprehensive support for Paystack's Customer API, allowing you to manage customer records and their associated data.

Create a Customer

# Prepare customer parameters
params = {
  email: "[email protected]",
  first_name: "John",
  last_name: "Doe",
  phone: "+2348123456789"
}

# Create the customer
response = paystack.customers.create(params)

if response.success?
  puts "Customer created successfully!"
  puts "Customer Code: #{response.data.customer_code}"
  puts "Email: #{response.data.email}"
  puts "Name: #{response.data.first_name} #{response.data.last_name}"
else
  puts "Error: #{response.error_message}"
end

List Customers

# Get all customers (default pagination: 50 per page)
response = paystack.customers.list

# With custom pagination
response = paystack.customers.list(per_page: 20, page: 2)

# With date filters
response = paystack.customers.list(
  per_page: 10,
  page: 1,
  from: "2025-01-01",
  to: "2025-06-10"
)

if response.success?
  puts "Total customers: #{response.data.size}"

  response.data.each do |customer|
    puts "Code: #{customer.customer_code}"
    puts "Email: #{customer.email}"
    puts "Name: #{customer.first_name} #{customer.last_name}"
    puts "----------------"
  end
else
  puts "Error: #{response.error_message}"
end

Fetch a Customer

# Fetch by customer code
customer_code = "CUS_xr58yrr2ujlft9k"
response = paystack.customers.fetch(customer_code)

# Or fetch by email
response = paystack.customers.fetch("[email protected]")

if response.success?
  customer = response.data
  puts "Customer details:"
  puts "Code: #{customer.customer_code}"
  puts "Email: #{customer.email}"
  puts "Name: #{customer.first_name} #{customer.last_name}"
  puts "Phone: #{customer.phone}"
else
  puts "Error: #{response.error_message}"
end

Update a Customer

customer_code = "CUS_xr58yrr2ujlft9k"
update_params = {
  first_name: "Jane",
  last_name: "Smith",
  phone: "+2348987654321"
}

response = paystack.customers.update(customer_code, update_params)

if response.success?
  puts "Customer updated successfully!"
  puts "Updated Name: #{response.data.first_name} #{response.data.last_name}"
else
  puts "Error: #{response.error_message}"
end

Validate a Customer

customer_code = "CUS_xr58yrr2ujlft9k"
validation_params = {
  country: "NG",
  type: "bank_account",
  account_number: "0123456789",
  bvn: "20012345677",
  bank_code: "007",
  first_name: "John",
  last_name: "Doe"
}

response = paystack.customers.validate(customer_code, validation_params)

if response.success?
  puts "Customer validation initiated: #{response.message}"
else
  puts "Error: #{response.error_message}"
end

Set Risk Action

params = {
  customer: "CUS_xr58yrr2ujlft9k",
  risk_action: "allow"  # Options: "default", "allow", "deny"
}

response = paystack.customers.set_risk_action(params)

if response.success?
  puts "Risk action set successfully!"
  puts "Customer: #{response.data.customer_code}"
  puts "Risk Action: #{response.data.risk_action}"
else
  puts "Error: #{response.error_message}"
end

Deactivate Authorization

params = {
  authorization_code: "AUTH_72btv547"
}

response = paystack.customers.deactivate_authorization(params)

if response.success?
  puts "Authorization deactivated: #{response.message}"
else
  puts "Error: #{response.error_message}"
end

Response Handling

Working with Response Objects

All API requests return a PaystackSdk::Response object that provides easy access to the response data.

response = paystack.transactions.initiate(params)

# Check if the request was successful
response.success?  # => true or false

# Access response message
response.api_message  # => "Authorization URL created"

# Access data using dot notation
response.data.authorization_url
response.data.access_code

# Access data directly from the response
response.authorization_url  # Same as response.data.authorization_url

# Access nested data
response.data.customer.email

# For arrays, use array methods
response.data.first  # First item in an array
response.data.last   # Last item in an array
response.data.size   # Size of the array

# Iterate through array data
response.data.each do |item|
  puts item.id
end

Accessing the Original Response

Sometimes you may need access to the original API response:

response = paystack.transactions.list

# Access the original response body
original = response.original_response

# Access metadata from the original response
total_count = original.dig("meta", "total")
current_page = original.dig("meta", "page")

Exception Handling

The SDK provides specific error classes for different types of failures, making it easier to handle errors appropriately:

begin
  response = paystack.transactions.verify(reference: "invalid_reference")
rescue PaystackSdk::AuthenticationError => e
  puts "Authentication failed: #{e.message}"
rescue PaystackSdk::RateLimitError => e
  puts "Rate limit exceeded. Retry after: #{e.retry_after} seconds"
rescue PaystackSdk::ServerError => e
  puts "Server error: #{e.message}"
rescue PaystackSdk::APIError => e
  puts "API error: #{e.message}"
rescue PaystackSdk::Error => e
  puts "General error: #{e.message}"
end

# Alternatively, check response success without exceptions
response = paystack.transactions.verify(reference: "invalid_reference")

unless response.success?
  puts "Error: #{response.error_message}"

  # Take action based on the error
  if response.error_message.include?("not found")
    puts "The transaction reference was not found."
  elsif response.error_message.include?("Invalid key")
    puts "API authentication failed. Check your API key."
  end
end
Error Types

The SDK includes several specific error classes:

  • PaystackSdk::ValidationError - Base class for all validation errors

    • PaystackSdk::MissingParamError - Raised when required parameters are missing
    • PaystackSdk::InvalidFormatError - Raised when parameters have invalid format (e.g., invalid email)
    • PaystackSdk::InvalidValueError - Raised when parameters have invalid values (e.g., not in allowed list)
  • PaystackSdk::APIError - Base class for API-related errors

    • PaystackSdk::AuthenticationError - Authentication failures
    • PaystackSdk::ResourceNotFoundError - Resource not found (404 errors)
    • PaystackSdk::RateLimitError - Rate limiting encountered
    • PaystackSdk::ServerError - Server errors (5xx responses)
Validation Error Examples

The SDK validates your input data before making API calls and will throw exceptions immediately if required data is missing or incorrectly formatted:

# Missing required parameter
begin
  paystack.transactions.initiate({amount: 1000}) # Missing email
rescue PaystackSdk::MissingParamError => e
  puts e.message # => "Missing required parameter: email"
end

# Invalid format
begin
  paystack.transactions.initiate({
    email: "invalid-email",  # Not a valid email format
    amount: 1000
  })
rescue PaystackSdk::InvalidFormatError => e
  puts e.message # => "Invalid format for Email. Expected format: valid email address"
end

# Invalid value
begin
  paystack.customers.set_risk_action({
    customer: "CUS_123",
    risk_action: "invalid_action"  # Not in allowed values
  })
rescue PaystackSdk::InvalidValueError => e
  puts e.message # => "Invalid value for risk_action: must be one of [default, allow, deny]"
end

These validation errors are thrown immediately and prevent the API call from being made, helping you catch data issues early in development.

Advanced Usage

Environment Variables

You can use environment variables to configure the SDK:

# Set the PAYSTACK_SECRET_KEY environment variable
ENV["PAYSTACK_SECRET_KEY"] = "sk_test_xxx"

# Then initialize resources without specifying the key
transactions = PaystackSdk::Resources::Transactions.new
customers = PaystackSdk::Resources::Customers.new

Direct Resource Instantiation

For more advanced usage, you can instantiate resource classes directly:

# With a secret key
transactions = PaystackSdk::Resources::Transactions.new(secret_key: "sk_test_xxx")
customers = PaystackSdk::Resources::Customers.new(secret_key: "sk_test_xxx")

# With an existing Faraday connection
connection = Faraday.new(url: "https://api.paystack.co") do |conn|
  # Configure the connection
end

# The secret key can be omitted if set in an environment
transactions = PaystackSdk::Resources::Transactions.new(connection, secret_key:)
customers = PaystackSdk::Resources::Customers.new(connection, secret_key:)

For more detailed documentation on specific resources, please refer to the following guides:

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.

Testing

The SDK includes comprehensive test coverage with consistent response format handling. All test specifications use string keys with hashrocket notation (=>) to match the actual format returned by the Paystack API:

# Example test response format
.and_return(Faraday::Response.new(status: 200, body: {
  "status" => true,
  "message" => "Transaction initialized",
  "data" => {
    "authorization_url" => "https://checkout.paystack.com/abc123",
    "access_code" => "access_code_123",
    "reference" => "ref_123"
  }
}))

Tests also validate specific error types to ensure proper exception handling:

# Testing specific error types
expect { customers.set_risk_action(invalid_params) }
  .to raise_error(PaystackSdk::InvalidValueError, /risk_action/i)

Installation and Release

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

This will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/nanafox/paystack_sdk. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

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

Code of Conduct

Everyone interacting in the PaystackSdk project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the code of conduct.

About

Supercharge Your Ruby App with Paystack's Easy Payment APIs

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks