/epaybg

Gem for interacting with epay.bg services

Primary LanguageRubyMIT LicenseMIT

Epaybg

Code Climate Build Status

Rails-specific library for working with the Epay.bg API. More information at https://demo.epay.bg/?page=login

Installation

Add this line to your application's Gemfile:

gem 'epaybg'

And then execute:

$ bundle

Or install it yourself as:

$ gem install epaybg

Configuration

Create the config file config/epaybg.yml with the following contents:

production:
  secret: YOUR-SECRET-KEY
  min: YOUR-MIN
  url: "https://epay.bg"
  url_idn: "https://epay.bg/ezp/reg_bill.cgi"

test:
  secret: YOUR-SECRET-KEY
  min: YOUR-MIN
  url: "https://demo.epay.bg"
  url_idn: "https://demo.epay.bg/ezp/reg_bill.cgi"

Set the mode to production in your config/environments/production.rb file:

# Add to bottom of the file
Epaybg.mode = :production

Usage

Handle a response callback from EpayBG:

response = Epaybg::Response.new params[:encoded], params[:checksum]
response.valid?
# => true

response.status
# => "PAID"

Respond to their callback:

response = Epaybg::Response.new(params[:encoded], params[:checksum])

response.invoice
=> "f5b1eaf"

response.response_for(:ok)
=> "INVOICE=f5b1eaf:STATUS=PAID"

Recurring payments

There is no testing environment. You're on your own.

Your best bet is to contact EpayBG and ask for the technical specification because it can not be found on their site.

Workflow

If your application has a subscription feature, you may consider implementing recurrent payments. EpayBG has a rather strange way of doing this.

Here are the steps in their recurring payment cycle.

  • Your user subscribes or makes a purchase.
  • You provide them with an unique 'subscription' number.
  • The user registers this number with EpayBG.
  • From now on, every month EpayBG will 'ask' your application (on a TCP server provided by you, explained below), if the registered subscription has any outstanding debts.
  • Your application searches for a debt related to the number and returns an answer.
  • If a debt is returned, the user will receive a notification from EpayBG that there is a pending payment.
  • The user pays this pending payment.
  • On the TCP server you will then receive a payment notification, process it and return a response to EpayBG.
  • The next month you will get another 'debt request' for this number.

It is rather hard to find this information anywhere in the web. It's unclear even in the official documentation that EpayBG send to the developers.

Implementing a TCP server

In order to accept recurring payment you have to implement a TCP server.

Here is an example server for accepting incoming TCP requests.

require 'socket'

server = TCPServer.new 2000

loop do
  #  The code in this block has to be threadsafe
  Thread.start(server.accept) do |client|
    begin
      # This line will read the incoming data stream until the tcp client on the other side sends a
      # 'shutdown' message.
      message = client.read

      # Handle the payment.
    rescue => e
      # Handle an unexpected error
    ensure
      client.close
    end
  end
end

Handling debt requests

The example will work with the TCP server implementation described above. EpayBG sends a list of messages separated by new lines \n. After that they stop sending data and now only wait for a response in the same TCP session.

An example request

XTYPE=QBN
AID=700021
ACSID=0000900
BORIKAID=0000900
CLIENTID=67600000000000000
LANG=1
IDN=000000000001
TID=20111010103406700021592704

Refer to the technical documentation for further details.

Handling the request

The gem provides a method which turns the message into a hash.

message = client.read
data = Epaybg::Recurring.parse_request_body(message)
request = Epaybg::Recurring::Debt::Request.new data

After you do the processing of the request and find out that this number has a pending payment, you have to build a response object.

This is done through a Epaybg::Recurring::Debt::Request object. It accepts a hash with parameters, validates them and builds a response array.

# ...

subscription = Subscription.find_by_epay_number request.idn

response_params = {
  xvalidto: (Time.now + 5.days), # Due date of the subscription
  secondid: 45, # Custom id for this debt (Optional)
  amount: 5000, # Debt in coins (bulgarian stotinki)
  status: '00', # If the status is not '00' (debt found) the other fields will be ignored.
                # Look up the documentation for the other status codes.
  shortdesc: 'Debt description',
  longdesc: 'Debt details' # Optional
}

response = Epaybg::Recurring::Debt::Response.new response_params

Now that you have a response object, you can send back an answer in the current TCP session.

  response.body_array.each do |element|
    client.puts element.encode('cp1251') # EpayBG requires that responses are windows-1251 encoded
  end

We have notified EpayBG that this subscription has a pending payment. Now we wait for a payment request.

Payments

After the user pays their debt through EpayBG's system, EpayBG will send a payment notification on the same TCP server used for debt request processing.

This is an example payment request:

XTYPE=QBC
AID=700021
ACSID=0000900
BORIKAID=0000900
CLIENTID=67600000000000000
IDN=000000000001
NEWAMOUNT=000000003000
AMOUNT=5000
TID=20111010103406700021592705
REF=592460592460
TDATE=20111010103409

Different request types are identified by the XTYPE parameter. Refer to the technical documentation for further details.

message = client.read
data = Epaybg::Recurring.parse_request_body(message)
epay_recurrent_payment = Epaybg::Recurring::Payment.new data

# Handle the payment

epay_recurrent_payment.respond_with(:ok) # This will generate a response for this session.

epay_recurrent_payment.response_array.each do |element|
  client.puts element
end

The payment is accepted and EpayBG has been notified that the payment has been processed.

Contributing

  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