Project

qpay-sdk

0.0
The project is in a healthy, maintained state
Ruby SDK for QPay V2 payment gateway API with auto token management.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13.0
~> 3.0
~> 3.0

Runtime

~> 2.0
 Project Readme

QPay Ruby SDK

CI Gem Version License: MIT

Ruby SDK for the QPay V2 payment gateway API. Provides a simple, idiomatic Ruby interface with automatic token management, typed request/response structs, and comprehensive error handling.

Installation

Add to your Gemfile:

gem "qpay"

Then run:

bundle install

Or install directly:

gem install qpay

Quick Start

require "qpay"

# Configure from environment variables
config = QPay::Config.from_env

# Or configure manually
config = QPay::Config.new(
  base_url: "https://merchant.qpay.mn",
  username: "YOUR_USERNAME",
  password: "YOUR_PASSWORD",
  invoice_code: "YOUR_INVOICE_CODE",
  callback_url: "https://yoursite.com/payments/callback"
)

# Create a client (handles authentication automatically)
client = QPay::Client.new(config: config)

# Create an invoice
invoice = client.create_simple_invoice(
  QPay::CreateSimpleInvoiceRequest.new(
    invoice_code: config.invoice_code,
    sender_invoice_no: "ORDER-001",
    invoice_receiver_code: "terminal",
    invoice_description: "Payment for Order #001",
    amount: 10_000,
    callback_url: config.callback_url
  )
)

puts invoice.invoice_id      # => "abc123..."
puts invoice.qpay_short_url  # => "https://qpay.mn/s/..."
puts invoice.qr_text         # => QR code data for rendering

Configuration

Environment Variables

Variable Description
QPAY_BASE_URL QPay API base URL (e.g., https://merchant.qpay.mn)
QPAY_USERNAME QPay merchant username
QPAY_PASSWORD QPay merchant password
QPAY_INVOICE_CODE Default invoice code
QPAY_CALLBACK_URL Payment callback URL

Load from environment:

config = QPay::Config.from_env

All five environment variables are required. A missing or empty variable raises ArgumentError listing the missing variables.

Manual Configuration

config = QPay::Config.new(
  base_url: "https://merchant.qpay.mn",
  username: "YOUR_USERNAME",
  password: "YOUR_PASSWORD",
  invoice_code: "YOUR_INVOICE_CODE",
  callback_url: "https://yoursite.com/callback"
)

Usage

Authentication

The client manages tokens automatically. You never need to call get_token manually -- every API method calls ensure_token internally. However, you can manage tokens explicitly if needed:

# Get a new token pair
token = client.get_token
puts token.access_token
puts token.refresh_token
puts token.expires_in

# Refresh the current token
token = client.refresh_token

Create Invoice (Full)

Use CreateInvoiceRequest for invoices with full options including branch data, line items, and transactions:

invoice = client.create_invoice(
  QPay::CreateInvoiceRequest.new(
    invoice_code: "INV_CODE",
    sender_invoice_no: "ORDER-001",
    sender_branch_code: "BRANCH01",
    sender_branch_data: QPay::SenderBranchData.new(
      register: "REG001",
      name: "Main Branch",
      email: "branch@company.mn",
      phone: "77001234",
      address: QPay::Address.new(
        city: "Ulaanbaatar",
        district: "Khan-Uul",
        street: "Peace Avenue 123"
      )
    ),
    sender_staff_data: QPay::SenderStaffData.new(
      name: "John",
      email: "john@company.mn",
      phone: "88001234"
    ),
    invoice_receiver_code: "terminal",
    invoice_receiver_data: QPay::InvoiceReceiverData.new(
      register: "AA12345678",
      name: "Customer Name",
      email: "customer@example.com",
      phone: "99001234"
    ),
    invoice_description: "Order #001 - 2 items",
    amount: 50_000,
    callback_url: "https://yoursite.com/callback",
    lines: [
      QPay::InvoiceLine.new(
        tax_product_code: "TAX001",
        line_description: "Product A",
        line_quantity: 2,
        line_unit_price: 25_000
      )
    ],
    transactions: [
      QPay::Transaction.new(
        description: "Payment for order",
        amount: 50_000,
        accounts: [
          QPay::Account.new(
            account_bank_code: "050000",
            account_number: "1234567890",
            account_name: "Company Account",
            account_currency: "MNT",
            is_default: true
          )
        ]
      )
    ]
  )
)

puts invoice.invoice_id
puts invoice.qr_text
puts invoice.qr_image         # Base64-encoded QR image
puts invoice.qpay_short_url

# Deep links for mobile bank apps
invoice.urls.each do |url|
  puts "#{url.name}: #{url.link}"
end

Create Simple Invoice

For basic invoices with minimal fields:

invoice = client.create_simple_invoice(
  QPay::CreateSimpleInvoiceRequest.new(
    invoice_code: "INV_CODE",
    sender_invoice_no: "ORDER-002",
    invoice_receiver_code: "terminal",
    invoice_description: "Quick payment",
    sender_branch_code: "BRANCH01",
    amount: 5_000,
    callback_url: "https://yoursite.com/callback"
  )
)

Create Ebarimt Invoice

For invoices that include tax (ebarimt) information:

invoice = client.create_ebarimt_invoice(
  QPay::CreateEbarimtInvoiceRequest.new(
    invoice_code: "INV_CODE",
    sender_invoice_no: "ORDER-003",
    sender_branch_code: "BRANCH01",
    invoice_receiver_code: "terminal",
    invoice_description: "Tax invoice",
    tax_type: "1",
    district_code: "001",
    callback_url: "https://yoursite.com/callback",
    lines: [
      QPay::EbarimtInvoiceLine.new(
        tax_product_code: "TAX001",
        line_description: "Taxable product",
        line_quantity: 1,
        line_unit_price: 10_000,
        classification_code: "CLS001"
      )
    ]
  )
)

Cancel Invoice

client.cancel_invoice("INVOICE_ID")

Get Payment Details

payment = client.get_payment("PAYMENT_ID")

puts payment.payment_id
puts payment.payment_status    # => "PAID"
puts payment.payment_amount    # => 10000
puts payment.payment_currency  # => "MNT"
puts payment.payment_wallet
puts payment.payment_date
puts payment.transaction_type
puts payment.object_type
puts payment.obj_id

# Card transactions
payment.card_transactions.each do |ct|
  puts "Card: #{ct.card_number} - #{ct.amount} #{ct.currency}"
end

# P2P transactions
payment.p2p_transactions.each do |pt|
  puts "Bank: #{pt.account_bank_name} - #{pt.amount} #{pt.currency}"
end

Check Payment

Check if a payment has been made for an invoice:

result = client.check_payment(
  QPay::PaymentCheckRequest.new(
    object_type: "INVOICE",
    obj_id: "INVOICE_ID",
    offset: QPay::Offset.new(page_number: 1, page_limit: 10)
  )
)

puts result.count        # Number of payments
puts result.paid_amount  # Total paid amount

result.rows.each do |row|
  puts "#{row.payment_id}: #{row.payment_status} - #{row.payment_amount}"
end

List Payments

payments = client.list_payments(
  QPay::PaymentListRequest.new(
    object_type: "INVOICE",
    obj_id: "INVOICE_ID",
    start_date: "2024-01-01",
    end_date: "2024-12-31",
    offset: QPay::Offset.new(page_number: 1, page_limit: 20)
  )
)

puts payments.count

payments.rows.each do |item|
  puts "#{item.payment_id}: #{item.payment_amount} #{item.payment_currency} (#{item.payment_status})"
end

Cancel Payment

Cancel a card payment:

# Without request body
client.cancel_payment("PAYMENT_ID")

# With callback and note
client.cancel_payment("PAYMENT_ID", request: QPay::PaymentCancelRequest.new(
  callback_url: "https://yoursite.com/cancel-callback",
  note: "Customer requested cancellation"
))

Refund Payment

Refund a card payment:

# Without request body
client.refund_payment("PAYMENT_ID")

# With callback and note
client.refund_payment("PAYMENT_ID", request: QPay::PaymentRefundRequest.new(
  callback_url: "https://yoursite.com/refund-callback",
  note: "Refund for defective product"
))

Create Ebarimt (Tax Receipt)

Create an electronic tax receipt for a completed payment:

ebarimt = client.create_ebarimt(
  QPay::CreateEbarimtRequest.new(
    payment_id: "PAYMENT_ID",
    ebarimt_receiver_type: "CITIZEN",
    ebarimt_receiver: "AA12345678",
    district_code: "001",
    classification_code: "CLS001"
  )
)

puts ebarimt.id
puts ebarimt.amount
puts ebarimt.vat_amount
puts ebarimt.barimt_status
puts ebarimt.ebarimt_qr_data
puts ebarimt.ebarimt_lottery

ebarimt.barimt_items.each do |item|
  puts "#{item.name}: #{item.amount}"
end

Cancel Ebarimt

ebarimt = client.cancel_ebarimt("PAYMENT_ID")

puts ebarimt.barimt_status  # => "CANCELED"

Error Handling

All API errors raise QPay::Error, which is a subclass of StandardError:

begin
  client.create_simple_invoice(request)
rescue QPay::Error => e
  puts e.message      # => "qpay: INVOICE_LINE_REQUIRED - Invoice lines are required (status 400)"
  puts e.status_code  # => 400
  puts e.code         # => "INVOICE_LINE_REQUIRED"
  puts e.raw_body     # => Raw JSON response body
end

Checking Error Type

Use the QPay.qpay_error? helper to distinguish QPay errors from other exceptions:

begin
  client.get_payment("some_id")
rescue => e
  qpay_err, is_qpay = QPay.qpay_error?(e)
  if is_qpay
    puts "QPay error: #{qpay_err.code}"
  else
    puts "Other error: #{e.message}"
  end
end

Error Code Constants

The SDK defines all QPay error codes as constants for easy comparison:

rescue QPay::Error => e
  case e.code
  when QPay::AUTHENTICATION_FAILED
    # Re-authenticate
  when QPay::INVOICE_NOTFOUND
    # Handle missing invoice
  when QPay::INVOICE_PAID
    # Invoice already paid
  when QPay::PAYMENT_NOTFOUND
    # Handle missing payment
  when QPay::PAYMENT_ALREADY_CANCELED
    # Payment was already canceled
  when QPay::INVALID_AMOUNT
    # Amount out of range
  when QPay::PERMISSION_DENIED
    # Access denied
  when QPay::EBARIMT_NOT_REGISTERED
    # Ebarimt not registered
  end
end

Full list of error constants:

Constant Value
QPay::ACCOUNT_BANK_DUPLICATED "ACCOUNT_BANK_DUPLICATED"
QPay::ACCOUNT_SELECTION_INVALID "ACCOUNT_SELECTION_INVALID"
QPay::AUTHENTICATION_FAILED "AUTHENTICATION_FAILED"
QPay::BANK_ACCOUNT_NOTFOUND "BANK_ACCOUNT_NOTFOUND"
QPay::BANK_MCC_ALREADY_ADDED "BANK_MCC_ALREADY_ADDED"
QPay::BANK_MCC_NOT_FOUND "BANK_MCC_NOT_FOUND"
QPay::CARD_TERMINAL_NOTFOUND "CARD_TERMINAL_NOTFOUND"
QPay::CLIENT_NOTFOUND "CLIENT_NOTFOUND"
QPay::CLIENT_USERNAME_DUPLICATED "CLIENT_USERNAME_DUPLICATED"
QPay::CUSTOMER_DUPLICATE "CUSTOMER_DUPLICATE"
QPay::CUSTOMER_NOTFOUND "CUSTOMER_NOTFOUND"
QPay::CUSTOMER_REGISTER_INVALID "CUSTOMER_REGISTER_INVALID"
QPay::EBARIMT_CANCEL_NOTSUPPERDED "EBARIMT_CANCEL_NOTSUPPERDED"
QPay::EBARIMT_NOT_REGISTERED "EBARIMT_NOT_REGISTERED"
QPay::EBARIMT_QR_CODE_INVALID "EBARIMT_QR_CODE_INVALID"
QPay::INFORM_NOTFOUND "INFORM_NOTFOUND"
QPay::INPUT_CODE_REGISTERED "INPUT_CODE_REGISTERED"
QPay::INPUT_NOTFOUND "INPUT_NOTFOUND"
QPay::INVALID_AMOUNT "INVALID_AMOUNT"
QPay::INVALID_OBJECT_TYPE "INVALID_OBJECT_TYPE"
QPay::INVOICE_ALREADY_CANCELED "INVOICE_ALREADY_CANCELED"
QPay::INVOICE_CODE_INVALID "INVOICE_CODE_INVALID"
QPay::INVOICE_CODE_REGISTERED "INVOICE_CODE_REGISTERED"
QPay::INVOICE_LINE_REQUIRED "INVOICE_LINE_REQUIRED"
QPay::INVOICE_NOTFOUND "INVOICE_NOTFOUND"
QPay::INVOICE_PAID "INVOICE_PAID"
QPay::INVOICE_RECEIVER_DATA_ADDRESS_REQUIRED "INVOICE_RECEIVER_DATA_ADDRESS_REQUIRED"
QPay::INVOICE_RECEIVER_DATA_EMAIL_REQUIRED "INVOICE_RECEIVER_DATA_EMAIL_REQUIRED"
QPay::INVOICE_RECEIVER_DATA_PHONE_REQUIRED "INVOICE_RECEIVER_DATA_PHONE_REQUIRED"
QPay::INVOICE_RECEIVER_DATA_REQUIRED "INVOICE_RECEIVER_DATA_REQUIRED"
QPay::MAX_AMOUNT_ERR "MAX_AMOUNT_ERR"
QPay::MCC_NOTFOUND "MCC_NOTFOUND"
QPay::MERCHANT_ALREADY_REGISTERED "MERCHANT_ALREADY_REGISTERED"
QPay::MERCHANT_INACTIVE "MERCHANT_INACTIVE"
QPay::MERCHANT_NOTFOUND "MERCHANT_NOTFOUND"
QPay::MIN_AMOUNT_ERR "MIN_AMOUNT_ERR"
QPay::NO_CREDENDIALS "NO_CREDENDIALS"
QPay::OBJECT_DATA_ERROR "OBJECT_DATA_ERROR"
QPay::P2P_TERMINAL_NOTFOUND "P2P_TERMINAL_NOTFOUND"
QPay::PAYMENT_ALREADY_CANCELED "PAYMENT_ALREADY_CANCELED"
QPay::PAYMENT_NOT_PAID "PAYMENT_NOT_PAID"
QPay::PAYMENT_NOTFOUND "PAYMENT_NOTFOUND"
QPay::PERMISSION_DENIED "PERMISSION_DENIED"
QPay::QRACCOUNT_INACTIVE "QRACCOUNT_INACTIVE"
QPay::QRACCOUNT_NOTFOUND "QRACCOUNT_NOTFOUND"
QPay::QRCODE_NOTFOUND "QRCODE_NOTFOUND"
QPay::QRCODE_USED "QRCODE_USED"
QPay::SENDER_BRANCH_DATA_REQUIRED "SENDER_BRANCH_DATA_REQUIRED"
QPay::TAX_LINE_REQUIRED "TAX_LINE_REQUIRED"
QPay::TAX_PRODUCT_CODE_REQUIRED "TAX_PRODUCT_CODE_REQUIRED"
QPay::TRANSACTION_NOT_APPROVED "TRANSACTION_NOT_APPROVED"
QPay::TRANSACTION_REQUIRED "TRANSACTION_REQUIRED"

API Reference

Client Methods

Method Parameters Returns Description
get_token -- TokenResponse Authenticate and get token pair
refresh_token -- TokenResponse Refresh access token
create_invoice CreateInvoiceRequest InvoiceResponse Create full invoice
create_simple_invoice CreateSimpleInvoiceRequest InvoiceResponse Create simple invoice
create_ebarimt_invoice CreateEbarimtInvoiceRequest InvoiceResponse Create invoice with tax info
cancel_invoice invoice_id (String) nil Cancel an invoice
get_payment payment_id (String) PaymentDetail Get payment details
check_payment PaymentCheckRequest PaymentCheckResponse Check payment status
list_payments PaymentListRequest PaymentListResponse List payments
cancel_payment payment_id, request: (optional) nil Cancel a payment
refund_payment payment_id, request: (optional) nil Refund a payment
create_ebarimt CreateEbarimtRequest EbarimtResponse Create tax receipt
cancel_ebarimt payment_id (String) EbarimtResponse Cancel tax receipt

Request/Response Structs

All structs use keyword_init: true and can be created with named parameters:

Auth: TokenResponse

Invoice: CreateInvoiceRequest, CreateSimpleInvoiceRequest, CreateEbarimtInvoiceRequest, InvoiceResponse

Payment: PaymentCheckRequest, PaymentCheckResponse, PaymentCheckRow, PaymentDetail, PaymentListRequest, PaymentListResponse, PaymentListItem, PaymentCancelRequest, PaymentRefundRequest

Ebarimt: CreateEbarimtRequest, EbarimtResponse, EbarimtItem, EbarimtHistory

Shared: Address, SenderBranchData, SenderStaffData, InvoiceReceiverData, Account, Transaction, InvoiceLine, EbarimtInvoiceLine, TaxEntry, Deeplink, Offset

Requirements

Development

# Install dependencies
bundle install

# Run tests
bundle exec rake spec

# Run tests (alternative)
bundle exec rspec

License

MIT License. See LICENSE for details.