Skip to content

Sentia/nowpayments

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NOWPayments Ruby SDK

Gem Version License: MIT

Production-ready Ruby wrapper for the NOWPayments API. Accept cryptocurrency payments with minimal code.

Why NOWPayments?

  • 150+ cryptocurrencies - Bitcoin, Ethereum, USDT, and more
  • No KYC required - Accept payments immediately
  • Instant settlement - Real-time payment processing
  • Low fees - Competitive transaction costs
  • Global reach - Accept payments from anywhere

Installation

Add to your Gemfile:

gem 'nowpayments', git: 'https://github.com/Sentia/nowpayments'

Or install directly:

gem install nowpayments

Quick Start

require 'nowpayments'

# Initialize client (sandbox for testing, production when ready)
client = NOWPayments::Client.new(
  api_key: ENV['NOWPAYMENTS_API_KEY'],
  sandbox: true
)

# Create a payment
payment = client.create_payment(
  price_amount: 100.0,
  price_currency: 'usd',
  pay_currency: 'btc',
  order_id: 'order-123',
  ipn_callback_url: 'https://yourdomain.com/webhooks/nowpayments'
)

puts "Payment address: #{payment['pay_address']}"
puts "Amount: #{payment['pay_amount']} BTC"
puts "Status: #{payment['payment_status']}"

Features

🎉 Complete API Coverage - 57 Methods, 100% Coverage!

11 API Modules with Full Implementation:

  1. Authentication (5 methods) - JWT token management for protected endpoints
  2. Status (1 method) - API health checks
  3. Currencies (3 methods) - Available cryptocurrencies and details
  4. Payments (4 methods) - Create and track cryptocurrency payments
  5. Invoices (3 methods) - Hosted payment pages with status tracking
  6. Estimates (2 methods) - Price calculations and minimum amounts
  7. Mass Payouts (8 methods) - Batch withdrawals with 2FA verification
  8. Conversions (3 methods) - Currency conversions at market rates
  9. Subscriptions (9 methods) - Recurring payment plans and billing
  10. Custody/Sub-accounts (11 methods) - User wallet management for marketplaces
  11. Fiat Payouts (8 methods) - Beta: Crypto to fiat withdrawals

Security & Production Ready:

  • JWT Authentication - Bearer token support for sensitive operations
  • Webhook Verification - HMAC-SHA512 signature validation
  • Constant-time comparison - Prevents timing attacks
  • Comprehensive error handling - 8 exception classes with detailed messages
  • 100% tested - 23 passing tests, RuboCop clean

Complete Method List (57 Methods)

Authentication (5 methods) - JWT token management
  • authenticate(email:, password:) - Get JWT token (5-min expiry)
  • jwt_token(email:, password:) - Get token with auto-refresh
  • jwt_expired? - Check if token is expired
  • clear_jwt_token - Clear stored token
  • jwt_time_remaining - Seconds until expiry
Status & Currencies (4 methods) - API health and currency info
  • status - Check API status
  • currencies(fixed_rate: nil) - Get available currencies
  • full_currencies - Detailed currency information
  • merchant_coins - Your enabled currencies
Payments (4 methods) - Standard cryptocurrency payments
  • create_payment(...) - Create new payment
  • payment(payment_id) - Get payment status
  • payments(limit:, page:, ...) - List payments with filters
  • update_payment_estimate(payment_id) - Update exchange rate
Invoices (3 methods) - Hosted payment pages
  • create_invoice(...) - Create invoice with payment page
  • create_invoice_payment(...) - Create payment by invoice ID
  • invoice(invoice_id) - Get invoice status
Estimates (2 methods) - Price calculations
  • estimate(amount:, currency_from:, currency_to:) - Price estimate
  • min_amount(currency_from:, currency_to:) - Minimum payment amount
Mass Payouts (8 methods) - Batch withdrawals (JWT required)
  • balance - Get account balance
  • create_payout(withdrawals:, ...) - Create batch payout (JWT)
  • verify_payout(batch_withdrawal_id:, verification_code:) - 2FA verify (JWT)
  • payout_status(payout_id) - Get payout status
  • list_payouts(limit:, offset:) - List all payouts (JWT)
  • validate_payout_address(address:, currency:, ...) - Validate address
  • min_payout_amount(currency:) - Minimum payout amount
  • payout_fee(currency:, amount:) - Calculate payout fee
Conversions (3 methods) - Currency conversions (JWT required)
  • create_conversion(from_currency:, to_currency:, amount:) - Convert crypto (JWT)
  • conversion_status(conversion_id) - Check conversion status (JWT)
  • list_conversions(limit:, offset:) - List all conversions (JWT)
Subscriptions (9 methods) - Recurring payments
  • subscription_plans - List all subscription plans
  • create_subscription_plan(plan_data) - Create new plan
  • update_subscription_plan(plan_id, plan_data) - Update plan
  • subscription_plan(plan_id) - Get plan details
  • create_subscription(plan_id:, email:) - Create subscription
  • list_recurring_payments(...) - List recurring payments with filters
  • recurring_payment(subscription_id) - Get subscription details
  • delete_recurring_payment(subscription_id) - Cancel subscription (JWT)
  • subscription_payments(subscription_id) - List subscription payments
Custody/Sub-accounts (11 methods) - User wallet management
  • create_sub_account(user_id:) - Create user account
  • sub_account_balance(user_id) - Get user balance
  • sub_account_balances - Get all balances
  • list_sub_accounts(...) - List all sub-accounts
  • transfer_between_sub_accounts(...) - Transfer between users (JWT)
  • create_sub_account_deposit(user_id:, currency:, ...) - Generate deposit address
  • create_sub_account_payment_deposit(...) - Payment to sub-account
  • transfer_to_sub_account(user_id:, currency:, amount:) - Deposit to user
  • withdraw_from_sub_account(user_id:, currency:, amount:) - Withdraw from user (JWT)
  • sub_account_transfer(transfer_id) - Get transfer details
  • sub_account_transfers(...) - List all transfers
Fiat Payouts (8 methods) - Beta: Crypto to fiat (JWT required)
  • fiat_payout_payment_methods(fiat_currency: nil) - Available payment methods (JWT)
  • create_fiat_payout_account(...) - Create payout account (JWT)
  • fiat_payout_accounts(...) - List payout accounts (JWT)
  • update_fiat_payout_account(account_id:, ...) - Update account (JWT)
  • create_fiat_payout(...) - Create fiat payout (JWT)
  • fiat_payout_status(payout_id) - Get payout status (JWT)
  • fiat_payouts(...) - List all fiat payouts with filters (JWT)
  • fiat_payout_rates(...) - Get conversion rates (JWT)

Built for Production

  • Comprehensive error handling - 8 exception classes with detailed messages
  • Faraday middleware - Automatic error mapping and retries
  • Tested - 23 passing tests with VCR cassettes for integration
  • Rails-ready - Drop-in Rack middleware for webhook verification
  • Type-safe - All responses return Ruby Hashes from parsed JSON

Usage Examples

Accept Payment on Your Site

# 1. Create payment
payment = client.create_payment(
  price_amount: 49.99,
  price_currency: 'usd',
  pay_currency: 'btc',
  order_id: "order-#{order.id}",
  order_description: 'Pro Plan - Annual',
  ipn_callback_url: 'https://example.com/webhooks/nowpayments'
)

# 2. Show payment address to customer
@payment_address = payment['pay_address']
@payment_amount = payment['pay_amount']

# 3. Check status
status = client.payment(payment['payment_id'])
# => {"payment_status"=>"finished", ...}

Hosted Invoice Page

# Create invoice with hosted payment page
invoice = client.create_invoice(
  price_amount: 99.0,
  price_currency: 'usd',
  order_id: "inv-#{invoice.id}",
  success_url: 'https://example.com/thank-you',
  cancel_url: 'https://example.com/checkout'
)

# Redirect customer to payment page
redirect_to invoice['invoice_url']
# Customer can choose from 150+ cryptocurrencies

Custody API - Sub-accounts (Marketplaces & Casinos)

# Create sub-account for a user
sub_account = client.create_sub_account(user_id: user.id)
# => {"id"=>123, "user_id"=>456, "created_at"=>"2025-11-01T..."}

# Generate deposit address for user's BTC wallet
deposit = client.create_sub_account_deposit(
  user_id: user.id,
  currency: 'btc'
)
# => {"address"=>"bc1q...", "currency"=>"btc"}

# Check user's balance
balances = client.sub_account_balances(user_id: user.id)
# => {"balances"=>{"btc"=>0.05, "eth"=>1.2}}

# Transfer funds to sub-account
transfer = client.transfer_to_sub_account(
  user_id: user.id,
  currency: 'btc',
  amount: 0.01
)

# Process withdrawal
withdrawal = client.withdraw_from_sub_account(
  user_id: user.id,
  currency: 'btc',
  amount: 0.005
)

JWT Authentication (Required for Advanced Features)

Some endpoints require JWT authentication (expires every 5 minutes):

# Authenticate to get JWT token
client.authenticate(
  email: '[email protected]',
  password: 'your_password'
)
# Token is automatically stored and injected in subsequent requests

# Check token status
client.jwt_expired? # => false
client.jwt_time_remaining # => 287 (seconds)

# JWT is required for these endpoints:
# - Mass Payouts (create_payout, verify_payout, list_payouts)
# - Conversions (create_conversion, conversion_status, list_conversions)
# - Custody Operations (transfer_between_sub_accounts, write_off_sub_account_balance)
# - Recurring Payments (delete_recurring_payment)

# Example: Create payout (requires JWT)
client.authenticate(email: '[email protected]', password: 'password')
payout = client.create_payout(
  withdrawals: [
    {
      address: 'TEmGwPeRTPiLFLVfBxXkSP91yc5GMNQhfS',
      currency: 'trx',
      amount: 10
    }
  ],
  payout_description: 'Weekly payouts'
)

# Verify payout with 2FA code (from Google Authenticator)
client.verify_payout(
  batch_withdrawal_id: payout['id'],
  verification_code: '123456'
)

# Token auto-refresh pattern
def ensure_authenticated(client, email, password)
  return unless client.jwt_expired?
  client.authenticate(email: email, password: password)
end

# Before JWT-required operations
ensure_authenticated(client, EMAIL, PASSWORD)
payouts = client.list_payouts(limit: 10, offset: 0)

# Clear token when done (optional, for security)
client.clear_jwt_token

See examples/jwt_authentication_example.rb for complete usage patterns.

Currency Conversions (JWT Required)

Convert between cryptocurrencies at market rates:

# Authenticate first
client.authenticate(email: '[email protected]', password: 'password')

# Create conversion
conversion = client.create_conversion(
  from_currency: 'btc',
  to_currency: 'eth',
  amount: 0.1
)
# => {"conversion_id" => "conv_123", "status" => "processing", ...}

# Check conversion status
status = client.conversion_status(conversion['conversion_id'])
# => {"status" => "completed", "from_amount" => 0.1, "to_amount" => 2.5, ...}

# List all conversions
conversions = client.list_conversions(limit: 10, offset: 0)

Fiat Payouts (Beta - JWT Required)

Withdraw cryptocurrency to fiat bank accounts:

# Authenticate first
client.authenticate(email: '[email protected]', password: 'password')

# Get available payment methods
methods = client.fiat_payout_payment_methods(fiat_currency: 'EUR')
# => {"result" => [{"provider" => "transfi", "methods" => [...]}]}

# Create payout account
account = client.create_fiat_payout_account(
  provider: 'transfi',
  fiat_currency: 'EUR',
  account_data: {
    accountHolderName: 'John Doe',
    iban: 'DE89370400440532013000'
  }
)
# => {"result" => {"id" => "acc_123", ...}}

# Get conversion rates
rates = client.fiat_payout_rates(
  crypto_currency: 'btc',
  fiat_currency: 'EUR',
  crypto_amount: 0.1
)
# => {"result" => {"fiatAmount" => "2500.00", "rate" => "25000.00", ...}}

# Create fiat payout
payout = client.create_fiat_payout(
  account_id: account['result']['id'],
  crypto_currency: 'btc',
  crypto_amount: 0.1
)
# => {"result" => {"id" => "payout_123", "status" => "PENDING", ...}}

# Check payout status
status = client.fiat_payout_status(payout['result']['id'])
# => {"result" => {"status" => "FINISHED", ...}}

# List all fiat payouts with filters
payouts = client.fiat_payouts(
  status: 'FINISHED',
  fiat_currency: 'EUR',
  limit: 10,
  page: 0
)

Webhook Verification (Critical!)

Always verify webhook signatures to prevent fraud:

# app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token
  
  def nowpayments
    # Verify signature - raises SecurityError if invalid
    payload = NOWPayments::Rack.verify_webhook(
      request,
      ENV['NOWPAYMENTS_IPN_SECRET']
    )
    
    # Process payment status
    order = Order.find_by(id: payload['order_id'])
    
    case payload['payment_status']
    when 'finished'
      order.mark_paid!
      OrderMailer.payment_received(order).deliver_later
    when 'failed', 'expired'
      order.cancel!
    when 'partially_paid'
      # Customer sent wrong amount
      logger.warn "Underpaid: #{payload['actually_paid']} vs #{payload['pay_amount']}"
    end
    
    head :ok
    
  rescue NOWPayments::SecurityError => e
    logger.error "Invalid webhook signature: #{e.message}"
    head :forbidden
  end
end

# config/routes.rb
post '/webhooks/nowpayments', to: 'webhooks#nowpayments'

Error Handling

begin
  payment = client.create_payment(...)
  
rescue NOWPayments::AuthenticationError
  # Invalid API key
  
rescue NOWPayments::BadRequestError => e
  # Invalid parameters
  puts "Error: #{e.message}"
  puts "Details: #{e.body}"
  
rescue NOWPayments::RateLimitError => e
  # Too many requests
  retry_after = e.headers['Retry-After']
  
rescue NOWPayments::ServerError
  # NOWPayments server error
  
rescue NOWPayments::ConnectionError
  # Network error
end

Documentation

Testing with Sandbox

# Use sandbox for development
client = NOWPayments::Client.new(
  api_key: ENV['NOWPAYMENTS_SANDBOX_API_KEY'],
  ipn_secret: ENV['NOWPAYMENTS_SANDBOX_IPN_SECRET'],
  sandbox: true
)

# All API calls go to sandbox environment
payment = client.create_payment(...)

Get sandbox credentials:

  1. Create account at https://account-sandbox.nowpayments.io/
  2. Generate API key from dashboard
  3. Generate IPN secret for webhooks
  4. Add to .env file

Configuration

# .env
NOWPAYMENTS_API_KEY=your_production_api_key
NOWPAYMENTS_IPN_SECRET=your_ipn_secret

# Testing
NOWPAYMENTS_SANDBOX_API_KEY=your_sandbox_api_key
NOWPAYMENTS_SANDBOX_IPN_SECRET=your_sandbox_ipn_secret

Examples

See the examples/ directory:

# API usage demo
cp .env.example .env
# Add your sandbox credentials to .env
ruby examples/simple_demo.rb

# Webhook receiver (Sinatra)
ruby examples/webhook_server.rb
# Use ngrok to expose: ngrok http 4567

Development

# Install dependencies
bundle install

# Run tests
bundle exec rspec

# Run tests with coverage
COVERAGE=true bundle exec rspec

# Lint code
bundle exec rubocop

# Interactive console
bundle exec rake console

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Run tests (bundle exec rspec)
  4. Commit your changes (git commit -am 'Add feature')
  5. Push to the branch (git push origin feature/my-feature)
  6. Create a Pull Request

Security

Report security vulnerabilities to: [email protected]

Never commit API keys or secrets. Always use environment variables.

License

MIT License - see LICENSE.txt

Support

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published