robokassa 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/Gemfile +24 -0
- data/README.md +189 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/app/assets/images/robokassa/AlfaBank.gif +0 -0
- data/app/assets/images/robokassa/HandyBank.gif +0 -0
- data/app/assets/images/robokassa/Mastercard.gif +0 -0
- data/app/assets/images/robokassa/MoneyMail.gif +0 -0
- data/app/assets/images/robokassa/RBKMoney.gif +0 -0
- data/app/assets/images/robokassa/TeleMoney.gif +0 -0
- data/app/assets/images/robokassa/TerminalsAbsolutplat.gif +0 -0
- data/app/assets/images/robokassa/TerminalsElecsnet.gif +0 -0
- data/app/assets/images/robokassa/TerminalsPinpay.gif +0 -0
- data/app/assets/images/robokassa/TerminalsQiwi.gif +0 -0
- data/app/assets/images/robokassa/TerminalsUnikassa.gif +0 -0
- data/app/assets/images/robokassa/TerminalsmobilElement.gif +0 -0
- data/app/assets/images/robokassa/VKontakte.gif +0 -0
- data/app/assets/images/robokassa/VTB24.gif +0 -0
- data/app/assets/images/robokassa/Visa.gif +0 -0
- data/app/assets/images/robokassa/WMTrustID.gif +0 -0
- data/app/assets/images/robokassa/WebmoneyR.gif +0 -0
- data/app/assets/images/robokassa/YandexMoney.gif +0 -0
- data/app/assets/images/robokassa/iRobo.gif +0 -0
- data/app/assets/images/robokassa/logo.gif +0 -0
- data/app/controllers/robokassa_controller.rb +3 -0
- data/app/helpers/robokassa_helper.rb +130 -0
- data/app/views/payment_method/robokassa/_init.html.erb +32 -0
- data/autotest/discover.rb +1 -0
- data/config/locales/en.yml +3 -0
- data/config/locales/ru.yml +3 -0
- data/config/routes.rb +8 -0
- data/lib/robokassa.rb +20 -0
- data/lib/robokassa/controller.rb +19 -0
- data/lib/robokassa/interface.rb +378 -0
- data/robokassa.gemspec +35 -0
- data/spec/lib/interface_spec.rb +39 -0
- data/spec/routing/routes_spec.rb +23 -0
- data/spec/spec_helper.rb +14 -0
- metadata +199 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in robokassa.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'nokogiri'
|
7
|
+
|
8
|
+
group :test do
|
9
|
+
gem 'ZenTest'
|
10
|
+
gem 'rspec'
|
11
|
+
gem 'rspec-rails', '>= 2.5.0'
|
12
|
+
gem 'factory_girl', '>= 1.3.3'
|
13
|
+
gem 'factory_girl_rails', '>= 1.0.1'
|
14
|
+
# gem 'rcov'
|
15
|
+
gem 'shoulda'
|
16
|
+
gem 'faker'
|
17
|
+
# gem 'celerity'
|
18
|
+
# gem 'culerity'
|
19
|
+
if RUBY_VERSION < "1.9"
|
20
|
+
gem "ruby-debug"
|
21
|
+
else
|
22
|
+
gem "ruby-debug19"
|
23
|
+
end
|
24
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
SUMMARY
|
2
|
+
-------
|
3
|
+
|
4
|
+
This gem adds robokassa support to your app.
|
5
|
+
|
6
|
+
Robokassa is payment system, that provides a single simple interface for payment systems popular in Russia.
|
7
|
+
If you have customers in Russia you can use the gem.
|
8
|
+
|
9
|
+
The first thing about this gem, is that it was oribinally desgned for spree commerce. So keep it im mind.
|
10
|
+
|
11
|
+
Данный джем является форком джема: https://github.com/shaggyone/robokassa
|
12
|
+
|
13
|
+
Using the Gem
|
14
|
+
-------------
|
15
|
+
|
16
|
+
Add the following line to your app Gemfile
|
17
|
+
|
18
|
+
gem 'robokassa'
|
19
|
+
|
20
|
+
Update your bundle
|
21
|
+
|
22
|
+
bundle install
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
config/initializers/robokassa.rb:
|
26
|
+
|
27
|
+
ROBOKASSA_SETTINGS = {
|
28
|
+
:test_mode => true,
|
29
|
+
:login => 'LOGIN',
|
30
|
+
:password1 => 'PASSWORD1',
|
31
|
+
:password2 => 'PASSWORD2'
|
32
|
+
}
|
33
|
+
|
34
|
+
$robokassa = Robokassa::Interface.new(ROBOKASSA_SETTINGS)
|
35
|
+
|
36
|
+
module Robokassa
|
37
|
+
class Interface
|
38
|
+
def notify_implementation(invoice_id, *args); end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def get_options_by_notification_key(key)
|
42
|
+
ROBOKASSA_SETTINGS
|
43
|
+
end
|
44
|
+
|
45
|
+
def success_implementation(invoice_id, *args)
|
46
|
+
payment = Payment.find_by_id(invoice_id)
|
47
|
+
payment.to_success!
|
48
|
+
end
|
49
|
+
|
50
|
+
def fail_implementation(invoice_id, *args)
|
51
|
+
payment = Payment.find_by_id(invoice_id)
|
52
|
+
payment.to_fail!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
routes.rb:
|
59
|
+
|
60
|
+
...
|
61
|
+
controller :robokassa do
|
62
|
+
get "robokassa/:notification_key/notify" => :notify, :as => :robokassa_notification
|
63
|
+
get "robokassa/success" => :success, :as => :robokassa_on_success
|
64
|
+
get "robokassa/fail" => :fail, :as => :robokassa_on_fail
|
65
|
+
end
|
66
|
+
...
|
67
|
+
|
68
|
+
class Dashboard::PaymentsController < Dashboard::ApplicationController
|
69
|
+
...
|
70
|
+
def create
|
71
|
+
@payment = current_user.payments.create!(:amount => params[:payment][:amount])
|
72
|
+
pay_url = $robokassa.init_payment_url(
|
73
|
+
@payment.id, @payment.amount, "Платеж № #{@payment.id}",
|
74
|
+
'', 'ru', current_user.email, {}
|
75
|
+
)
|
76
|
+
redirect_to pay_url
|
77
|
+
end
|
78
|
+
...
|
79
|
+
|
80
|
+
class Payment < ActiveRecord::Base
|
81
|
+
include AASM
|
82
|
+
|
83
|
+
validates_presence_of :user_id, :amount
|
84
|
+
attr_accessible :amount
|
85
|
+
belongs_to :user
|
86
|
+
|
87
|
+
default_scope order("id desc")
|
88
|
+
|
89
|
+
aasm do
|
90
|
+
state :new, :initial => true
|
91
|
+
state :success
|
92
|
+
state :fail
|
93
|
+
|
94
|
+
event :to_success, :after => :give_money! do
|
95
|
+
transitions :to => :success
|
96
|
+
end
|
97
|
+
|
98
|
+
event :to_fail do
|
99
|
+
transitions :to => :fail
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def state
|
104
|
+
self.aasm_state
|
105
|
+
end
|
106
|
+
|
107
|
+
def give_money!
|
108
|
+
self.user.give_money!(self.amount)
|
109
|
+
end
|
110
|
+
|
111
|
+
def printable_amount
|
112
|
+
"#{self.amount.to_s} руб."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class User < ActiveRecord::Base
|
117
|
+
...
|
118
|
+
def give_money!(amount)
|
119
|
+
sql = "update users set balance='#{self.balance + amount.to_f}' where id='#{self.id}'"
|
120
|
+
connection.update(sql)
|
121
|
+
end
|
122
|
+
...
|
123
|
+
|
124
|
+
dashboarb/payments/_form.html.erb:
|
125
|
+
|
126
|
+
<%= semantic_form_for [:dashboard, @payment] do |f| %>
|
127
|
+
<%= f.error_messages %>
|
128
|
+
<%= f.input :amount %>
|
129
|
+
<%= actions_for f, "Пополнить" %>
|
130
|
+
<% end %>
|
131
|
+
|
132
|
+
app/controllers/robokassa.rb:
|
133
|
+
|
134
|
+
# coding: utf-8
|
135
|
+
class RobokassaController < Robokassa::Controller
|
136
|
+
def success
|
137
|
+
super
|
138
|
+
@payment = Payment.find_by_id(params[:InvId])
|
139
|
+
if @payment
|
140
|
+
redirect_to dashboard_payment_path(@payment),
|
141
|
+
:notice => "Ваш платеж на сумму #{@payment.amount.to_s} руб. успешно принят. Спасибо!"
|
142
|
+
else
|
143
|
+
redirect_to new_dashboard_payment_path,
|
144
|
+
:error => "Не могу найти платеж по данному идентификатору"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def fail
|
149
|
+
super
|
150
|
+
redirect_to dashboard_payments_path,
|
151
|
+
:error => "Во время принятия платежа возникла ошибка. Мы скоро разберемся!"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
```
|
156
|
+
|
157
|
+
In Robokassa account settings set:
|
158
|
+
|
159
|
+
Result URL: http://example.com/robokassa/default/notify
|
160
|
+
Success URL: http://example.com/robokassa/success
|
161
|
+
Fail URL: http://example.com/robokassa/fail
|
162
|
+
|
163
|
+
Testing
|
164
|
+
-----
|
165
|
+
In console:
|
166
|
+
|
167
|
+
Clone gem
|
168
|
+
```bash
|
169
|
+
git clone git://github.com/shaggyone/robokassa.git
|
170
|
+
```
|
171
|
+
|
172
|
+
Install gems and generate a dummy application (It'll be ignored by git):
|
173
|
+
```bash
|
174
|
+
cd robokassa
|
175
|
+
bundle install
|
176
|
+
bundle exec combust
|
177
|
+
```
|
178
|
+
|
179
|
+
Run specs:
|
180
|
+
```bash
|
181
|
+
rake spec
|
182
|
+
```
|
183
|
+
|
184
|
+
Generate a dummy test application
|
185
|
+
|
186
|
+
Plans
|
187
|
+
-----
|
188
|
+
|
189
|
+
I plan to add generators for views
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/packagetask'
|
5
|
+
require 'rubygems/package_task'
|
6
|
+
require 'bundler'
|
7
|
+
require 'rspec'
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
|
10
|
+
Bundler.setup
|
11
|
+
Bundler::GemHelper.install_tasks
|
12
|
+
|
13
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
14
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
15
|
+
spec.rspec_opts = ['--backtrace']
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'rdoc/task'
|
19
|
+
Rake::RDocTask.new do |rdoc|
|
20
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
21
|
+
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = "constantations #{version}"
|
24
|
+
rdoc.rdoc_files.include('README*')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module RobokassaHelper
|
2
|
+
include ActionView::Helpers::TagHelper
|
3
|
+
include ActionView::Helpers::CaptureHelper
|
4
|
+
include ActionView::Helpers::FormTagHelper
|
5
|
+
|
6
|
+
PAYMENT_SYSTEMS = {
|
7
|
+
:bank_card => 'BANKOCEANMR', # Банковской картой
|
8
|
+
:platezh_ru => 'OceanBankOceanR', # Банковской картой через Platezh.ru
|
9
|
+
:qiwi => 'QiwiR', # QIWI Кошелек
|
10
|
+
:yandex => 'PCR', # Яндекс.Деньги
|
11
|
+
:wmr => 'WMRM', # WMR
|
12
|
+
:wmz => 'WMZM', # WMZ
|
13
|
+
:wme => 'WMEM', # WME
|
14
|
+
:wmu => 'WMUM', # WMU
|
15
|
+
:wmb => 'WMBM', # WMB
|
16
|
+
:wmg => 'WMGM', # WMG
|
17
|
+
:money_mail_ru => 'MoneyMailR', # RUR MoneyMail
|
18
|
+
:rur_rbk_money => 'RuPayR', # RUR RBK Money
|
19
|
+
:w1r => 'W1R', # RUR Единый Кошелек
|
20
|
+
:easy_pay => 'EasyPayB', # EasyPay
|
21
|
+
:liq_pay_usd => 'LiqPayZ', # USD LiqPay
|
22
|
+
:money_mail_ru => 'MailRuR', # Деньги@Mail.Ru
|
23
|
+
:z_payment => 'ZPaymentR', # RUR Z-Payment
|
24
|
+
:tele_money => 'TeleMoneyR', # RUR TeleMoney
|
25
|
+
:alfabank => 'AlfaBankR', # Альфа-Клик
|
26
|
+
:pskbr => 'PSKBR', # Промсвязьбанк
|
27
|
+
:handy_bank => 'HandyBankMerchantR', # HandyBank
|
28
|
+
:innivation => 'BSSFederalBankForInnovationAndDevelopmentR', # АК ФБ Инноваций и Развития (ЗАО)
|
29
|
+
:energobank => 'BSSMezhtopenergobankR', # Межтопэнергобанк
|
30
|
+
:svyaznoy => 'RapidaOceanSvyaznoyR', # Через Связной
|
31
|
+
:euroset => 'RapidaOceanEurosetR', # Через Евросеть
|
32
|
+
:elecsnet_r => 'ElecsnetR', # Элекснет
|
33
|
+
:kassira_net => 'TerminalsUnikassaR', # Кассира.нет
|
34
|
+
:mobil_element => 'TerminalsMElementR', # Мобил Элемент
|
35
|
+
:baltika => 'TerminalsNovoplatR', # Банк Балтика
|
36
|
+
:absolut_plat => 'TerminalsAbsolutplatR', # Absolutplat
|
37
|
+
:pinpay => 'TerminalsPinpayR', # Pinpay
|
38
|
+
:money_money => 'TerminalsMoneyMoneyR', # Money-Money
|
39
|
+
:petrocommerce => 'TerminalsPkbR', # Петрокоммерц
|
40
|
+
:vtb24 => 'VTB24R', # ВТБ24
|
41
|
+
:mts => 'MtsR', # МТС
|
42
|
+
:megafon => 'MegafonR', # Мегафон
|
43
|
+
:iphone => 'BANKOCEANCHECKR', # Через iPhone
|
44
|
+
:contact => 'ContactR', # Переводом по системе Контакт
|
45
|
+
:online_credit => 'OnlineCreditR'
|
46
|
+
}
|
47
|
+
|
48
|
+
def payment_form(interface, invoice_id, amount, description, custom_options = {})
|
49
|
+
payment_base_url = interface.init_payment_base_url
|
50
|
+
payment_options = interface.init_payment_options(invoice_id, amount, description, custom_options)
|
51
|
+
|
52
|
+
@__robokassa_vars = {
|
53
|
+
:interface => interface,
|
54
|
+
:invoice_id => invoice_id,
|
55
|
+
:amount => amount,
|
56
|
+
:description => description,
|
57
|
+
:custom_options => custom_options,
|
58
|
+
:payment_base_url => payment_base_url,
|
59
|
+
:payment_options => payment_options
|
60
|
+
}
|
61
|
+
|
62
|
+
if block_given?
|
63
|
+
yield payment_base_url, payment_options
|
64
|
+
else
|
65
|
+
render 'payment_method/robokassa/init',
|
66
|
+
:interface => interface,
|
67
|
+
:invoice_id => invoice_id,
|
68
|
+
:amount => amount,
|
69
|
+
:description => description,
|
70
|
+
:custom_options => custom_options,
|
71
|
+
:payment_base_url => payment_base_url,
|
72
|
+
:payment_options => payment_options
|
73
|
+
end
|
74
|
+
|
75
|
+
@__robokassa_vars = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def robokassa_rates_hash
|
79
|
+
raise "rates_hash helper should be called inside of payment_form." if @__robokassa_vars.blank?
|
80
|
+
@__robokassa_vars[:rates_hash] ||= robokassa_interface.rates_linear(robokassa_amount)
|
81
|
+
end
|
82
|
+
|
83
|
+
def robokassa_payment_block currency
|
84
|
+
raise "payment_block should be called inside of payment_form." if @__robokassa_vars.blank?
|
85
|
+
|
86
|
+
currency = RobokassaHelper::PAYMENT_SYSTEMS[currency]
|
87
|
+
raise "wrong currency" unless currency
|
88
|
+
kept_rate = @__robokassa_vars[:currency_rate]
|
89
|
+
currency_rate = robokassa_rates_hash[currency]
|
90
|
+
return "" unless currency_rate
|
91
|
+
@__robokassa_vars[:currency_rate] = currency_rate
|
92
|
+
|
93
|
+
yield currency_rate
|
94
|
+
|
95
|
+
@__robokassa_vars[:currency_rate] = kept_rate
|
96
|
+
end
|
97
|
+
|
98
|
+
def robokassa_payment_link *args
|
99
|
+
raise "payment_link should be called inside of payment_form." if @__robokassa_vars.blank?
|
100
|
+
options = args.extract_options!
|
101
|
+
currency = options.delete(:currency)
|
102
|
+
if currency
|
103
|
+
return robokassa_currency currency do
|
104
|
+
payment_link args
|
105
|
+
end
|
106
|
+
end
|
107
|
+
payment_url = "#{ robokassa_payment_base_url }?#{ robokassa_payment_options.merge('IncCurrLabel' => robokassa_currency_rate[:name]).to_query}"
|
108
|
+
|
109
|
+
if block_given?
|
110
|
+
link_to payment_url, *args, options do
|
111
|
+
yield robokassa_currency_rate
|
112
|
+
end
|
113
|
+
else
|
114
|
+
title = args.shift
|
115
|
+
link_to title, payment_url, *args, options
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
[:interface, :invoice_id, :amount, :description, :custom_options, :payment_base_url, :payment_options].each do |method_name|
|
120
|
+
define_method "robokassa_#{method_name}".to_sym do
|
121
|
+
raise "robokassa_#{method_name} helper should be called inside of payment_form." if @__robokassa_vars.blank?
|
122
|
+
@__robokassa_vars[method_name]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def robokassa_currency_rate
|
127
|
+
raise "robokassa_currency_rate helper should be called inside of robokassa_payment_block." if @__robokassa_vars.blank? || @__robokassa_vars[:currency_rate].blank?
|
128
|
+
@__robokassa_vars[:currency_rate]
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div class='robokassa'>
|
2
|
+
<form action="<%=interface.init_payment_base_url %>" method="get">
|
3
|
+
<%
|
4
|
+
options = interface.init_payment_options(invoice_id, amount, description, custom_options)
|
5
|
+
options.delete 'IncCurrLabel'
|
6
|
+
%>
|
7
|
+
<% options.each do |name, value| %>
|
8
|
+
<%= hidden_field_tag name, value %>
|
9
|
+
<% end %>
|
10
|
+
<ul class='robokassa-groups'>
|
11
|
+
<% interface.rates(amount).each_with_index do |group, i| %>
|
12
|
+
<% group_name, group = group %>
|
13
|
+
<li class='robokassa-group robokassa-<%= group_name.underscore %>'>
|
14
|
+
<p class='robokassa-group-description'><%= group[:description] %></p>
|
15
|
+
<ul>
|
16
|
+
<% group[:currencies].each_with_index do |currency, j| %>
|
17
|
+
<% currency_name, currency = currency %>
|
18
|
+
<li class='robokassa-group-currency <%= "#{group_name.underscore}_#{currency_name.underscore}" %>'>
|
19
|
+
<%= label_tag "inc_curr_label_#{group_name.underscore}_#{currency_name.underscore}" do %>
|
20
|
+
<%= radio_button_tag "IncCurrLabel", currency_name, false, :id => "inc_curr_label_#{group_name.underscore}_#{currency_name.underscore}" %>
|
21
|
+
<span class="description"><%= currency[:currency_description] %></span>
|
22
|
+
<span class="amount"><%= number_to_currency currency[:amount], :delimiter => " ", :format=>'%n' %></span>
|
23
|
+
<% end %>
|
24
|
+
</li>
|
25
|
+
<% end %>
|
26
|
+
</ul>
|
27
|
+
</li>
|
28
|
+
<% end %>
|
29
|
+
</ul>
|
30
|
+
<input type="submit" class="continue button primary" value="<%=t("robokassa.proceed_to_payment") %>" />
|
31
|
+
</form>
|
32
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/config/routes.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
controller :robokassa do
|
3
|
+
get "robokassa/:notification_key/notify" => :notify, :as => :robokassa_notification
|
4
|
+
|
5
|
+
get "robokassa/success" => :success, :as => :robokassa_on_success
|
6
|
+
get "robokassa/fail" => :fail, :as => :robokassa_on_fail
|
7
|
+
end
|
8
|
+
end
|
data/lib/robokassa.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Robokassa
|
2
|
+
mattr_accessor :interface_class
|
3
|
+
|
4
|
+
# this allow use custom class for handeling api responces
|
5
|
+
# === Example
|
6
|
+
# Robokassa.interface_class = MyCustomInterface
|
7
|
+
# Robokassa.interface_class.new(options)
|
8
|
+
def self.interface_class
|
9
|
+
@@interface_class || ::Robokassa::Interface
|
10
|
+
end
|
11
|
+
|
12
|
+
class Engine < Rails::Engine #:nodoc:
|
13
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
14
|
+
|
15
|
+
def self.activate
|
16
|
+
end
|
17
|
+
|
18
|
+
config.to_prepare &method(:activate).to_proc
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Robokassa::Controller < ActionController::Base
|
2
|
+
protect_from_forgery :only => []
|
3
|
+
|
4
|
+
def notify
|
5
|
+
interface = Robokassa.interface_class.create_by_notification_key params[:notification_key]
|
6
|
+
params.delete :notification_key
|
7
|
+
render :text => interface.notify(params, self)
|
8
|
+
end
|
9
|
+
|
10
|
+
def success
|
11
|
+
retval = Robokassa.interface_class.success(params, self)
|
12
|
+
redirect_to retval if retval.is_a? String
|
13
|
+
end
|
14
|
+
|
15
|
+
def fail
|
16
|
+
retval = Robokassa.interface_class.fail(params, self)
|
17
|
+
redirect_to retval if retval.is_a? String
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,378 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'rexml/document'
|
6
|
+
|
7
|
+
class Robokassa::Interface
|
8
|
+
include ActionDispatch::Routing::UrlFor
|
9
|
+
include Rails.application.routes.url_helpers
|
10
|
+
|
11
|
+
cattr_accessor :config
|
12
|
+
|
13
|
+
@@default_options = {
|
14
|
+
:language => "ru"
|
15
|
+
}
|
16
|
+
@cache = {}
|
17
|
+
|
18
|
+
# Indicate if calling api in test mode
|
19
|
+
# === Returns
|
20
|
+
# true or false
|
21
|
+
def test_mode?
|
22
|
+
@options[:test_mode] || false
|
23
|
+
end
|
24
|
+
|
25
|
+
def owner
|
26
|
+
@options[:owner]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Takes options to access Robokassa API
|
30
|
+
#
|
31
|
+
# === Example
|
32
|
+
# Robokassa::Interface.new test_mode: true, login: 'demo', password1: '12345', password2: 'qweqwe123'
|
33
|
+
#
|
34
|
+
def initialize(options)
|
35
|
+
@options = @@default_options.merge(options.symbolize_keys)
|
36
|
+
@cache = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
# This method verificates request params recived from robocassa server
|
40
|
+
def notify(params, controller)
|
41
|
+
parsed_params = map_params(params, @@notification_params_map)
|
42
|
+
notify_implementation(
|
43
|
+
parsed_params[:invoice_id],
|
44
|
+
parsed_params[:amount],
|
45
|
+
parsed_params[:custom_options],
|
46
|
+
controller)
|
47
|
+
"OK#{parsed_params[:invoice_id]}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Handler for success api callback
|
51
|
+
# this method calls from RobokassaController
|
52
|
+
# It requires Robokassa::Interface.success_implementation to be inmplemented by user
|
53
|
+
def self.success(params, controller)
|
54
|
+
parsed_params = map_params(params, @@notification_params_map)
|
55
|
+
success_implementation(
|
56
|
+
parsed_params[:invoice_id],
|
57
|
+
parsed_params[:amount],
|
58
|
+
parsed_params[:language],
|
59
|
+
parsed_params[:custom_options],
|
60
|
+
controller)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fail callback requiest handler
|
64
|
+
# It requires Robokassa::Interface.fail_implementation to be inmplemented by user
|
65
|
+
def self.fail(params, controller)
|
66
|
+
parsed_params = map_params(params, @@notification_params_map)
|
67
|
+
fail_implementation(
|
68
|
+
parsed_params[:invoice_id],
|
69
|
+
parsed_params[:amount],
|
70
|
+
parsed_params[:language],
|
71
|
+
parsed_params[:custom_options],
|
72
|
+
controller)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Generates url for payment page
|
77
|
+
#
|
78
|
+
# === Example
|
79
|
+
# <%= link_to "Pay with Robokassa", interface.init_payment_url(order.id, order.amount, "Order #{order.id}", '', 'ru', order.user.email) %>
|
80
|
+
#
|
81
|
+
def init_payment_url(invoice_id, amount, description, currency='', language='ru', email='', custom_options={})
|
82
|
+
url_options = init_payment_options(invoice_id, amount, description, custom_options, currency, language, email)
|
83
|
+
"#{init_payment_base_url}?" + url_options.map do |k, v| "#{CGI::escape(k.to_s)}=#{CGI::escape(v.to_s)}" end.join('&')
|
84
|
+
end
|
85
|
+
|
86
|
+
def payment_methods # :nodoc:
|
87
|
+
return @cache[:payment_methods] if @cache[:payment_methods]
|
88
|
+
xml = get_remote_xml(payment_methods_url)
|
89
|
+
if xml.elements['PaymentMethodsList/Result/Code'].text != '0'
|
90
|
+
raise (a=xml.elements['PaymentMethodsList/Result/Description']) ? a.text : "Unknown error"
|
91
|
+
end
|
92
|
+
|
93
|
+
@cache[:payment_methods] ||= Hash[xml.elements.each('PaymentMethodsList/Methods/Method'){}.map do|g|
|
94
|
+
[g.attributes['Code'], g.attributes['Description']]
|
95
|
+
end]
|
96
|
+
end
|
97
|
+
|
98
|
+
def rates_long(amount, currency='')
|
99
|
+
cache_key = "rates_long_#{currency}_#{amount}"
|
100
|
+
return @cache[cache_key] if @cache[cache_key]
|
101
|
+
xml = get_remote_xml(rates_url(amount, currency))
|
102
|
+
if xml.elements['RatesList/Result/Code'].text != '0'
|
103
|
+
raise (a=xml.elements['RatesList/Result/Description']) ? a.text : "Unknown error"
|
104
|
+
end
|
105
|
+
|
106
|
+
@cache[cache_key] = Hash[xml.elements.each('RatesList/Groups/Group'){}.map do|g|
|
107
|
+
code = g.attributes['Code']
|
108
|
+
description = g.attributes['Description']
|
109
|
+
[
|
110
|
+
code,
|
111
|
+
{
|
112
|
+
:code => code,
|
113
|
+
:description => description,
|
114
|
+
:currencies => Hash[g.elements.each('Items/Currency'){}.map do|c|
|
115
|
+
label = c.attributes['Label']
|
116
|
+
name = c.attributes['Name']
|
117
|
+
[label, {
|
118
|
+
:currency => label,
|
119
|
+
:currency_description => name,
|
120
|
+
:group => code,
|
121
|
+
:group_description => description,
|
122
|
+
:amount => BigDecimal.new(c.elements['Rate'].attributes['IncSum'])
|
123
|
+
}]
|
124
|
+
end]
|
125
|
+
}
|
126
|
+
]
|
127
|
+
end]
|
128
|
+
end
|
129
|
+
|
130
|
+
def rates(amount, currency='')
|
131
|
+
cache_key = "rates_#{currency}_#{amount}"
|
132
|
+
@cache[cache_key] ||= Hash[rates_long(amount, currency).map do |key, value|
|
133
|
+
[key, {
|
134
|
+
:description => value[:description],
|
135
|
+
:currencies => Hash[(value[:currencies] || []).map do |k, v|
|
136
|
+
[k, v]
|
137
|
+
end]
|
138
|
+
}]
|
139
|
+
end]
|
140
|
+
end
|
141
|
+
|
142
|
+
def rates_linear(amount, currency='')
|
143
|
+
cache_key = "rates_linear#{currency}_#{amount}"
|
144
|
+
@cache[cache_key] ||= begin
|
145
|
+
retval = rates(amount, currency).map do |group|
|
146
|
+
group_name, group = group
|
147
|
+
group[:currencies].map do |currency|
|
148
|
+
currency_name, currency = currency
|
149
|
+
{
|
150
|
+
:name => currency_name,
|
151
|
+
:desc => currency[:currency_description],
|
152
|
+
:group_name => group[:name],
|
153
|
+
:group_desc => group[:description],
|
154
|
+
:amount => currency[:amount]
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
Hash[retval.flatten.map { |v| [v[:name], v] }]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def currencies_long
|
163
|
+
return @cache[:currencies_long] if @cache[:currencies_long]
|
164
|
+
xml = get_remote_xml(currencies_url)
|
165
|
+
if xml.elements['CurrenciesList/Result/Code'].text != '0'
|
166
|
+
raise (a=xml.elements['CurrenciesList/Result/Description']) ? a.text : "Unknown error"
|
167
|
+
end
|
168
|
+
@cache[:currencies_long] = Hash[xml.elements.each('CurrenciesList/Groups/Group'){}.map do|g|
|
169
|
+
code = g.attributes['Code']
|
170
|
+
description = g.attributes['Description']
|
171
|
+
[
|
172
|
+
code,
|
173
|
+
{
|
174
|
+
:code => code,
|
175
|
+
:description => description,
|
176
|
+
:currencies => Hash[g.elements.each('Items/Currency'){}.map do|c|
|
177
|
+
label = c.attributes['Label']
|
178
|
+
name = c.attributes['Name']
|
179
|
+
[label, {
|
180
|
+
:currency => label,
|
181
|
+
:currency_description => name,
|
182
|
+
:group => code,
|
183
|
+
:group_description => description
|
184
|
+
}]
|
185
|
+
end]
|
186
|
+
}
|
187
|
+
]
|
188
|
+
end]
|
189
|
+
end
|
190
|
+
|
191
|
+
def currencies
|
192
|
+
@cache[:currencies] ||= Hash[currencies_long.map do |key, value|
|
193
|
+
[key, {
|
194
|
+
:description => value[:description],
|
195
|
+
:currencies => value[:currencies]
|
196
|
+
}]
|
197
|
+
end]
|
198
|
+
end
|
199
|
+
|
200
|
+
# for testing
|
201
|
+
# === Example
|
202
|
+
# i.default_url_options = { :host => '127.0.0.1', :port => 3000 }
|
203
|
+
# i.notification_url # => 'http://127.0.0.1:3000/robokassa/asfadsf/notify'
|
204
|
+
def notification_url
|
205
|
+
robokassa_notification_url :notification_key => @options[:notification_key]
|
206
|
+
end
|
207
|
+
|
208
|
+
# for testing
|
209
|
+
def on_success_url
|
210
|
+
robokassa_on_success_url
|
211
|
+
end
|
212
|
+
|
213
|
+
# for testing
|
214
|
+
def on_fail_url
|
215
|
+
robokassa_on_fail_url
|
216
|
+
end
|
217
|
+
|
218
|
+
def parse_response_params(params)
|
219
|
+
parsed_params = map_params(params, @@notification_params_map)
|
220
|
+
parsed_params[:custom_options] = Hash[args.select do |k,v| o.starts_with?('shp') end.sort.map do|k, v| [k[3, k.size], v] end]
|
221
|
+
if response_signature(parsed_params)!=parsed_params[:signature].downcase
|
222
|
+
raise "Invalid signature"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def rates_url(amount, currency)
|
227
|
+
"#{xml_services_base_url}/GetRates?#{query_string(rates_options(amount, currency))}"
|
228
|
+
end
|
229
|
+
|
230
|
+
def rates_options(amount, currency)
|
231
|
+
map_params(subhash(@options.merge(:amount=>amount, :currency=>currency), %w{login language amount currency}), @@service_params_map)
|
232
|
+
end
|
233
|
+
|
234
|
+
def payment_methods_url
|
235
|
+
@cache[:get_currencies_url] ||= "#{xml_services_base_url}/GetPaymentMethods?#{query_string(payment_methods_options)}"
|
236
|
+
end
|
237
|
+
|
238
|
+
def payment_methods_options
|
239
|
+
map_params(subhash(@options, %w{login language}), @@service_params_map)
|
240
|
+
end
|
241
|
+
|
242
|
+
def currencies_url
|
243
|
+
@cache[:get_currencies_url] ||= "#{xml_services_base_url}/GetCurrencies?#{query_string(currencies_options)}"
|
244
|
+
end
|
245
|
+
|
246
|
+
def currencies_options
|
247
|
+
map_params(subhash(@options, %w{login language}), @@service_params_map)
|
248
|
+
end
|
249
|
+
|
250
|
+
# make hash of options for init_payment_url
|
251
|
+
def init_payment_options(invoice_id, amount, description, custom_options = {}, currency='', language='ru', email='')
|
252
|
+
options = {
|
253
|
+
:login => @options[:login],
|
254
|
+
:amount => amount.to_s,
|
255
|
+
:invoice_id => invoice_id,
|
256
|
+
:description => description[0, 100],
|
257
|
+
:signature => init_payment_signature(invoice_id, amount, description, custom_options),
|
258
|
+
:currency => currency,
|
259
|
+
:email => email,
|
260
|
+
:language => language
|
261
|
+
}.merge(Hash[custom_options.sort.map{|x| ["shp#{x[0]}", x[1]]}])
|
262
|
+
map_params(options, @@params_map)
|
263
|
+
end
|
264
|
+
|
265
|
+
# calculates signature to check params from Robokassa
|
266
|
+
def response_signature(parsed_params)
|
267
|
+
md5 response_signature_string(parsed_params)
|
268
|
+
end
|
269
|
+
|
270
|
+
# build signature string
|
271
|
+
def response_signature_string(parsed_params)
|
272
|
+
custom_options_fmt = custom_options.sort.map{|x|"shp#{x[0]}=x[1]]"}.join(":")
|
273
|
+
"#{parsed_params[:amount]}:#{parsed_params[:invoice_id]}:#{@options[:password2]}#{unless custom_options_fmt.blank? then ":" + custom_options_fmt else "" end}"
|
274
|
+
end
|
275
|
+
|
276
|
+
# calculates md5 from result of :init_payment_signature_string
|
277
|
+
def init_payment_signature(invoice_id, amount, description, custom_options={})
|
278
|
+
md5 init_payment_signature_string(invoice_id, amount, description, custom_options)
|
279
|
+
end
|
280
|
+
|
281
|
+
# generates signature string to calculate 'SignatureValue' url parameter
|
282
|
+
def init_payment_signature_string(invoice_id, amount, description, custom_options={})
|
283
|
+
custom_options_fmt = custom_options.sort.map{|x|"shp#{x[0]}=#{x[1]}"}.join(":")
|
284
|
+
"#{@options[:login]}:#{amount}:#{invoice_id}:#{@options[:password1]}#{unless custom_options_fmt.blank? then ":" + custom_options_fmt else "" end}"
|
285
|
+
end
|
286
|
+
|
287
|
+
# returns http://test.robokassa.ru or https://merchant.roboxchange.com in order to current mode
|
288
|
+
def base_url
|
289
|
+
test_mode? ? 'http://test.robokassa.ru' : 'https://merchant.roboxchange.com'
|
290
|
+
end
|
291
|
+
|
292
|
+
# returns url to redirect user to payment page
|
293
|
+
def init_payment_base_url
|
294
|
+
"#{base_url}/Index.aspx"
|
295
|
+
end
|
296
|
+
|
297
|
+
# returns base url for API access
|
298
|
+
def xml_services_base_url
|
299
|
+
"#{base_url}/WebService/Service.asmx"
|
300
|
+
end
|
301
|
+
|
302
|
+
@@notification_params_map = {
|
303
|
+
'OutSum' => :amount,
|
304
|
+
'InvId' => :invoice_id,
|
305
|
+
'SignatureValue' => :signature,
|
306
|
+
'Culture' => :language
|
307
|
+
}
|
308
|
+
|
309
|
+
@@params_map = {
|
310
|
+
'MrchLogin' => :login,
|
311
|
+
'OutSum' => :amount,
|
312
|
+
'InvId' => :invoice_id,
|
313
|
+
'Desc' => :description,
|
314
|
+
'Email' => :email,
|
315
|
+
'IncCurrLabel' => :currency,
|
316
|
+
'Culture' => :language,
|
317
|
+
'SignatureValue' => :signature
|
318
|
+
}.invert
|
319
|
+
|
320
|
+
@@service_params_map = {
|
321
|
+
'MerchantLogin' => :login,
|
322
|
+
'Language' => :language,
|
323
|
+
'IncCurrLabel' => :currency,
|
324
|
+
'OutSum' => :amount
|
325
|
+
}.invert
|
326
|
+
|
327
|
+
def md5(str) #:nodoc:
|
328
|
+
Digest::MD5.hexdigest(str).downcase
|
329
|
+
end
|
330
|
+
|
331
|
+
def subhash(hash, keys) #:nodoc:
|
332
|
+
Hash[keys.map do |key|
|
333
|
+
[key.to_sym, hash[key.to_sym]]
|
334
|
+
end]
|
335
|
+
end
|
336
|
+
|
337
|
+
# Maps gem parameter names, to robokassa names
|
338
|
+
def self.map_params(params, map)
|
339
|
+
Hash[params.map do|key, value| [(map[key] || map[key.to_sym] || key), value] end]
|
340
|
+
end
|
341
|
+
|
342
|
+
def map_params(params, map) #:nodoc:
|
343
|
+
self.class.map_params params, map
|
344
|
+
end
|
345
|
+
|
346
|
+
def query_string(params) #:nodoc:
|
347
|
+
params.map do |name, value|
|
348
|
+
"#{CGI::escape(name.to_s)}=#{CGI::escape(value.to_s)}"
|
349
|
+
end.join("&")
|
350
|
+
end
|
351
|
+
|
352
|
+
# make request and parse XML from specified url
|
353
|
+
def get_remote_xml(url)
|
354
|
+
# xml_data = Net::HTTP.get_response(URI.parse(url)).body
|
355
|
+
begin
|
356
|
+
xml_data = URI.parse(url).read
|
357
|
+
doc = REXML::Document.new(xml_data)
|
358
|
+
rescue REXML::ParseException => e
|
359
|
+
sleep 1
|
360
|
+
get_remote_xml(url)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
class << self
|
366
|
+
# This method creates new instance of Interface for specified key (for multi-account support)
|
367
|
+
# it calls then Robokassa call ResultURL callback
|
368
|
+
def create_by_notification_key(key)
|
369
|
+
self.new get_options_by_notification_key(key)
|
370
|
+
end
|
371
|
+
|
372
|
+
%w{success fail notify}.map{|m| m + '_implementation'} + ['get_options_by_notification_key'].each do |m|
|
373
|
+
define_method m.to_sym do |*args|
|
374
|
+
raise NoMethodError, "Robokassa::Interface.#{m} should be defined by app developer"
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
data/robokassa.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
version = File.read(File.expand_path("../VERSION",__FILE__)).strip
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "robokassa"
|
7
|
+
s.version = version
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Victor Zagorski aka shaggyone"]
|
10
|
+
s.email = ["victor@zagorski.ru"]
|
11
|
+
s.homepage = "http://github.com/shaggyone/robokassa"
|
12
|
+
s.summary = %q{This gem adds robokassa support to your app.}
|
13
|
+
s.description = %q{
|
14
|
+
Robokassa is payment system, that provides a single simple interface for payment systems popular in Russia.
|
15
|
+
If you have customers in Russia you can use the gem.
|
16
|
+
|
17
|
+
The first thing about this gem, is that it was oribinally designed for spree commerce. So keep it in mind.
|
18
|
+
}
|
19
|
+
|
20
|
+
s.rubyforge_project = "robokassa"
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
|
27
|
+
s.add_dependency "rails", ">= 3.2.0"
|
28
|
+
|
29
|
+
s.add_development_dependency "rake"
|
30
|
+
s.add_development_dependency "thor"
|
31
|
+
s.add_development_dependency "shoulda"
|
32
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
33
|
+
s.add_development_dependency 'combustion', '~> 0.3.1'
|
34
|
+
s.add_development_dependency 'sqlite3'
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'robokassa/interface'
|
3
|
+
describe "Interface should work correct" do
|
4
|
+
before :each do
|
5
|
+
end
|
6
|
+
|
7
|
+
it "Should correctly use test server" do
|
8
|
+
i = Robokassa::Interface.new :test_mode => true
|
9
|
+
i.should be_test_mode
|
10
|
+
i.base_url.should == "http://test.robokassa.ru"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should compute correct signature string" do
|
14
|
+
i = Robokassa::Interface.new :test_mode => true, :login => 'demo', 'password1' => '12345'
|
15
|
+
i.init_payment_signature_string(15, 185.0, "Order #125").should == "demo:185.0:15:12345"
|
16
|
+
i.init_payment_signature_string(15, 185.0, "Order #125", {:a => 15, :c => 30, :b => 20}).should == "demo:185.0:15:12345:shpa=15:shpb=20:shpc=30"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should create correct init payment url" do
|
20
|
+
i = Robokassa::Interface.new :test_mode => true, :login => 'demo', 'password1' => '12345'
|
21
|
+
i = Robokassa::Interface.new :test_mode => true, :login => 'shaggyone239', 'password1' => '12345asdf'
|
22
|
+
i.init_payment_signature_string(15, 185.11, "Order #125").should == "shaggyone239:185.11:15:12345asdf"
|
23
|
+
i.init_payment_signature(15, 185.11, "Order #125").should == "55f2aee20767cde28e7fc49919cec969"
|
24
|
+
i.init_payment_url(15, 185.11, "Order 125", '', 'ru', 'demo@robokassa.ru', {}).should ==
|
25
|
+
"http://test.robokassa.ru/Index.aspx?MrchLogin=shaggyone239&OutSum=185.11&InvId=15&Desc=Order+125&SignatureValue=55f2aee20767cde28e7fc49919cec969&IncCurrLabel=&Email=demo%40robokassa.ru&Culture=ru"
|
26
|
+
i.init_payment_signature(196, 2180.0, "R602412577").should == "adeedf2afbac5eca09b44898da3ef51a"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return correct notification, success and fail urls" do
|
30
|
+
i = Robokassa::Interface.new :test_mode => true, :login => 'demo', 'password1' => '12345', :notification_key => "asfadsf"
|
31
|
+
i.default_url_options = {
|
32
|
+
:host => '127.0.0.1',
|
33
|
+
:port => 3000
|
34
|
+
}
|
35
|
+
i.notification_url.should == 'http://127.0.0.1:3000/robokassa/asfadsf/notify'
|
36
|
+
i.on_success_url.should == 'http://127.0.0.1:3000/robokassa/success'
|
37
|
+
i.on_fail_url.should == 'http://127.0.0.1:3000/robokassa/fail'
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe 'routes' do
|
5
|
+
context "robokassa" do
|
6
|
+
specify { get('/robokassa/some-secure-notification-key/notify').should route_to(
|
7
|
+
controller: 'robokassa',
|
8
|
+
action: 'notify',
|
9
|
+
notification_key: 'some-secure-notification-key'
|
10
|
+
)}
|
11
|
+
|
12
|
+
specify { get('/robokassa/success').should route_to(
|
13
|
+
controller: 'robokassa',
|
14
|
+
action: 'success'
|
15
|
+
)}
|
16
|
+
|
17
|
+
specify { get('/robokassa/fail').should route_to(
|
18
|
+
controller: 'robokassa',
|
19
|
+
action: 'fail'
|
20
|
+
)}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
require 'rails'
|
5
|
+
|
6
|
+
Bundler.require :default, :development
|
7
|
+
|
8
|
+
Combustion.initialize! :active_record, :action_controller
|
9
|
+
|
10
|
+
require 'rspec/rails'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.use_transactional_fixtures = true
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: robokassa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Victor Zagorski aka shaggyone
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: thor
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: shoulda
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.0.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.0.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: combustion
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.3.1
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.3.1
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: sqlite3
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: ! "\n Robokassa is payment system, that provides a single simple interface
|
127
|
+
for payment systems popular in Russia.\n If you have customers in Russia you
|
128
|
+
can use the gem.\n\n The first thing about this gem, is that it was oribinally
|
129
|
+
designed for spree commerce. So keep it in mind.\n "
|
130
|
+
email:
|
131
|
+
- victor@zagorski.ru
|
132
|
+
executables: []
|
133
|
+
extensions: []
|
134
|
+
extra_rdoc_files: []
|
135
|
+
files:
|
136
|
+
- .gitignore
|
137
|
+
- Gemfile
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- VERSION
|
141
|
+
- app/assets/images/robokassa/AlfaBank.gif
|
142
|
+
- app/assets/images/robokassa/HandyBank.gif
|
143
|
+
- app/assets/images/robokassa/Mastercard.gif
|
144
|
+
- app/assets/images/robokassa/MoneyMail.gif
|
145
|
+
- app/assets/images/robokassa/RBKMoney.gif
|
146
|
+
- app/assets/images/robokassa/TeleMoney.gif
|
147
|
+
- app/assets/images/robokassa/TerminalsAbsolutplat.gif
|
148
|
+
- app/assets/images/robokassa/TerminalsElecsnet.gif
|
149
|
+
- app/assets/images/robokassa/TerminalsPinpay.gif
|
150
|
+
- app/assets/images/robokassa/TerminalsQiwi.gif
|
151
|
+
- app/assets/images/robokassa/TerminalsUnikassa.gif
|
152
|
+
- app/assets/images/robokassa/TerminalsmobilElement.gif
|
153
|
+
- app/assets/images/robokassa/VKontakte.gif
|
154
|
+
- app/assets/images/robokassa/VTB24.gif
|
155
|
+
- app/assets/images/robokassa/Visa.gif
|
156
|
+
- app/assets/images/robokassa/WMTrustID.gif
|
157
|
+
- app/assets/images/robokassa/WebmoneyR.gif
|
158
|
+
- app/assets/images/robokassa/YandexMoney.gif
|
159
|
+
- app/assets/images/robokassa/iRobo.gif
|
160
|
+
- app/assets/images/robokassa/logo.gif
|
161
|
+
- app/controllers/robokassa_controller.rb
|
162
|
+
- app/helpers/robokassa_helper.rb
|
163
|
+
- app/views/payment_method/robokassa/_init.html.erb
|
164
|
+
- autotest/discover.rb
|
165
|
+
- config/locales/en.yml
|
166
|
+
- config/locales/ru.yml
|
167
|
+
- config/routes.rb
|
168
|
+
- lib/robokassa.rb
|
169
|
+
- lib/robokassa/controller.rb
|
170
|
+
- lib/robokassa/interface.rb
|
171
|
+
- robokassa.gemspec
|
172
|
+
- spec/lib/interface_spec.rb
|
173
|
+
- spec/routing/routes_spec.rb
|
174
|
+
- spec/spec_helper.rb
|
175
|
+
homepage: http://github.com/shaggyone/robokassa
|
176
|
+
licenses: []
|
177
|
+
post_install_message:
|
178
|
+
rdoc_options: []
|
179
|
+
require_paths:
|
180
|
+
- lib
|
181
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
+
none: false
|
183
|
+
requirements:
|
184
|
+
- - ! '>='
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
|
+
none: false
|
189
|
+
requirements:
|
190
|
+
- - ! '>='
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '0'
|
193
|
+
requirements: []
|
194
|
+
rubyforge_project: robokassa
|
195
|
+
rubygems_version: 1.8.19
|
196
|
+
signing_key:
|
197
|
+
specification_version: 3
|
198
|
+
summary: This gem adds robokassa support to your app.
|
199
|
+
test_files: []
|