Consulting. Training. Development.

ActiveMerchant integration for Robokassa

When you have a project written using Rails and you want to add payment system to it (e.g PayPal, Moneybookers, etc. or Robokassa in our case) the first gem you should think about is active_merchant by Shopify:

ActiveMerchant is a simple payment abstraction library used in and sponsored by Shopify.

So when I had to add Robokassa payment system to our project I opened a list or the supported integrations and was kinda disappointed because active_merchant didn’t have it. Latter I found a fork that had Robokassa support but it was outdated and some tests failed ec801d3d4f8. So I decided to look at this code and fix it instead of writing all this stuff from scratch.

Actually to make that tests work I had to fix a small typo 07fb5494134. Yeah, it was easy. The next thing I decided to add to that implementation was different urls for sandbox and production environments (yeah, Robokassa has a rule that you should test your code in sandbox at first and when all your stuff work you can use production environment). You can see this code here - c2ec85d53cb

And then it’s time to add active_merchant to the project. Add it to your Gemfile:

1
gem 'activemerchant', :require => 'active_merchant'

To use ActionView helpers like payment_service_for you should add this to activemerchant.rb file and put in in the initializers folder:

1
2
3
4
require 'active_merchant'
require 'active_merchant/billing/integrations/action_view_helper'

ActionView::Base.send(:include, ActiveMerchant::Billing::Integrations::ActionViewHelper)

If you want to use production mode add this line to initializers too:

1
ActiveMerchant::Billing::Base.integration_mode = :production # :test for sandbox

The next step is routes. Robokassa makes a push request to our application when transaction was made. So let’s add these routes:

1
2
3
4
5
6
  scope 'robokassa' do
    match 'paid'    => 'robokassa#paid',    :as => :robokassa_paid # to handle Robokassa push request

    match 'success' => 'robokassa#success', :as => :robokassa_success # to handle Robokassa success redirect
    match 'fail'    => 'robokassa#fail',    :as => :robokassa_fail # to handle Robokassa fail redirect
  end

To make this work we should create controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class RobokassaController < ApplicationController
  include ActiveMerchant::Billing::Integrations

  skip_before_filter :verify_authenticity_token # skip before filter if you chosen POST request for callbacks

  before_filter :create_notification
  before_filter :find_payment

  # Robokassa call this action after transaction
  def paid
    if @notification.acknowledge # check if it’s genuine Robokassa request
      @payment.approve! # project-specific code
      render :text => @notification.success_response
    else
      head :bad_request
    end
  end

  # Robokassa redirect user to this action if it’s all ok
  def success
    if !@payment.approved? && @notification.acknowledge
      @payment.approve!
    end

    redirect_to @payment, :notice => I18n.t("notice.robokassa.success")
  end
  # Robokassa redirect user to this action if it’s not
  def fail
    redirect_to @payment, :notice => I18n.t("notice.robokassa.fail")
  end

  private

  def create_notification
    @notification = Robokassa::Notification.new(request.raw_post, :secret => AppConfig.robokassa_secret)
  end

  def find_payment
    @payment = Payment.find(@notification.item_id)
  end
end

And the last thing we need is to add form to our page:

1
2
3
4
5
6
<%= payment_service_for @payment.id, AppConfig.robokassa_login,
                        :amount =>  @payment.amount,
                        :service => :robokassa,
                        :secret => AppConfig.robokassa_secret do |s| %>
  <%= submit_tag "Submit" %>
<% end %>

That’s it! Now if we have @payment object and we submitting this form we will be redirected to the Robokassa site where we can make payment with amount of @payment.amount.