glebtv-robokassa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 007382a6dd8c570fd47e7486f79131ea44154bf3
4
+ data.tar.gz: e3774534c02e48cd7bed965a61a4a3c1db0735c0
5
+ SHA512:
6
+ metadata.gz: 6a5631f25c6356f46f02fc62e113d840095624f8244e99574410e4c8fc75055abaf19b338e14d478e58ef747bd936216fb1dc0446b8d016ab4777f659609fe85
7
+ data.tar.gz: f0a79f325bed46fa329091773eb92ba07d02402c92da45e4260014c05893d5ba16272bc04e66756c7a48b9b55ca08882be464f8e3bcf73de6435df9cb6ee9be2
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in glebtv-robokassa.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 glebtv
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Glebtv::Robokassa
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'glebtv-robokassa'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install glebtv-robokassa
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ class RobokassaController < ApplicationController
2
+ include Robokassa::Controller
3
+ end
@@ -0,0 +1,3 @@
1
+ en:
2
+ robokassa:
3
+ proceed_to_payment: "Proceed to payment"
@@ -0,0 +1,3 @@
1
+ ru:
2
+ robokassa:
3
+ proceed_to_payment: "Перейти к оплате"
@@ -0,0 +1,5 @@
1
+ Robokassa::Engine.routes.draw do
2
+ get "notify/:token" => 'robokassa#notify', as: :robokassa_notification
3
+ get "success" => 'robokassa#success', as: :robokassa_on_success
4
+ get "fail" => 'robokassa#fail', as: :robokassa_on_fail
5
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'robokassa/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "glebtv-robokassa"
8
+ spec.version = Robokassa::VERSION
9
+ spec.authors = ["glebtv"]
10
+ spec.email = ["glebtv@gmail.com"]
11
+ spec.description = %q{Fork of Robokassa gem with only basic payment support}
12
+ spec.summary = %q{This gem adds robokassa support to your app.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rails", ">= 3.2.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ end
@@ -0,0 +1 @@
1
+ require 'robokassa'
@@ -0,0 +1,22 @@
1
+ require 'cgi'
2
+ require 'digest/md5'
3
+
4
+ module Robokassa
5
+ class InvalidSignature < ArgumentError; end
6
+ class InvalidToken < ArgumentError; end
7
+
8
+ mattr_accessor :interface
9
+
10
+ class Engine < Rails::Engine #:nodoc:
11
+ config.autoload_paths += %W(#{config.root}/lib)
12
+
13
+ def self.activate
14
+ end
15
+
16
+ config.to_prepare &method(:activate).to_proc
17
+ end
18
+ end
19
+
20
+ require 'robokassa/controller'
21
+ require 'robokassa/interface'
22
+ require 'robokassa/engine'
@@ -0,0 +1,23 @@
1
+ module Robokassa::Controller
2
+ extend ActiveSupport::Concern
3
+ included do
4
+ skip_before_action :verify_authenticity_token
5
+ end
6
+
7
+ def notify
8
+ if params[:token] != Robokassa.interface.token
9
+ raise Robokassa::InvalidToken.new
10
+ end
11
+ render :text => Robokassa.interface.notify(params, self)
12
+ end
13
+
14
+ def success
15
+ retval = Robokassa.interface.success(params, self)
16
+ redirect_to retval if retval.is_a? String
17
+ end
18
+
19
+ def fail
20
+ retval = Robokassa.interface.fail(params, self)
21
+ redirect_to retval if retval.is_a? String
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ module Robokassa
2
+ class Engine < Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,183 @@
1
+
2
+
3
+ module Robokassa
4
+ class Interface
5
+
6
+ @@notification_params_map = {
7
+ 'OutSum' => :amount,
8
+ 'InvId' => :invoice_id,
9
+ 'SignatureValue' => :signature,
10
+ 'Culture' => :language
11
+ }
12
+
13
+ @@params_map = {
14
+ 'MrchLogin' => :login,
15
+ 'OutSum' => :amount,
16
+ 'InvId' => :invoice_id,
17
+ 'Desc' => :description,
18
+ 'Email' => :email,
19
+ 'IncCurrLabel' => :currency,
20
+ 'Culture' => :language,
21
+ 'SignatureValue' => :signature
22
+ }.invert
23
+
24
+ @@service_params_map = {
25
+ 'MerchantLogin' => :login,
26
+ 'Language' => :language,
27
+ 'IncCurrLabel' => :currency,
28
+ 'OutSum' => :amount
29
+ }.invert
30
+
31
+ def initialize(options)
32
+ @options = options
33
+ end
34
+
35
+ def token
36
+ @options[:token]
37
+ end
38
+
39
+ # Indicate if calling api in test mode
40
+ # === Returns
41
+ # true or false
42
+ def test_mode?
43
+ @options[:test_mode]
44
+ end
45
+
46
+ # build signature string
47
+ def notify_response_signature_string(parsed_params)
48
+ custom_options_fmt = parsed_params[:custom_options].sort.map{|x|"shp#{x[0]}=#{x[1]}"}.join(":")
49
+ "#{parsed_params[:amount]}:#{parsed_params[:invoice_id]}:#{@options[:password2]}#{custom_options_fmt.blank? ? "" : ":" + custom_options_fmt}"
50
+ end
51
+
52
+ # calculates signature to check params from Robokassa
53
+ def notify_response_signature(parsed_params)
54
+ md5 notify_response_signature_string(parsed_params)
55
+ end
56
+
57
+ def notify_validate_signature(params)
58
+ parsed_params = map_params(params, @@notification_params_map)
59
+ parsed_params[:custom_options] = Hash[params.select{ |k,v| k.starts_with?('shp') }.sort.map{|k, v| [k[3, k.size], v]}]
60
+ if notify_response_signature(parsed_params) != parsed_params[:signature].downcase
61
+ raise Robokassa::InvalidSignature.new
62
+ end
63
+ end
64
+
65
+ # build signature string
66
+ def success_response_signature_string(parsed_params)
67
+ custom_options_fmt = parsed_params[:custom_options].sort.map{|x|"shp#{x[0]}=#{x[1]}"}.join(":")
68
+ "#{parsed_params[:amount]}:#{parsed_params[:invoice_id]}:#{@options[:password1]}#{custom_options_fmt.blank? ? "" : ":" + custom_options_fmt}"
69
+ end
70
+
71
+ # calculates signature to check params from Robokassa
72
+ def success_response_signature(parsed_params)
73
+ md5 success_response_signature_string(parsed_params)
74
+ end
75
+
76
+ def success_validate_signature(params)
77
+ parsed_params = map_params(params, @@notification_params_map)
78
+ parsed_params[:custom_options] = Hash[params.select{ |k,v| k.starts_with?('shp') }.sort.map{|k, v| [k[3, k.size], v]}]
79
+ if success_response_signature(parsed_params) != parsed_params[:signature].downcase
80
+ raise Robokassa::InvalidSignature.new
81
+ end
82
+ end
83
+
84
+
85
+ # This method verificates request params recived from robocassa server
86
+ def notify(params, controller)
87
+ begin
88
+ notify_validate_signature(params)
89
+ parsed_params = map_params(params, @@notification_params_map)
90
+ notify_implementation(
91
+ parsed_params[:invoice_id],
92
+ parsed_params[:amount],
93
+ parsed_params[:custom_options],
94
+ controller)
95
+ "OK#{parsed_params[:invoice_id]}"
96
+ rescue Robokassa::InvalidSignature
97
+ "signature_error"
98
+ end
99
+ end
100
+
101
+ # Handler for success api callback
102
+ # this method calls from RobokassaController
103
+ # It requires Robokassa::Interface.success_implementation to be inmplemented by user
104
+ def success(params, controller)
105
+ success_validate_signature(params)
106
+ parsed_params = map_params(params, @@notification_params_map)
107
+ success_implementation(
108
+ parsed_params[:invoice_id],
109
+ parsed_params[:amount],
110
+ parsed_params[:language],
111
+ parsed_params[:custom_options],
112
+ controller)
113
+ end
114
+
115
+ # Fail callback requiest handler
116
+ # It requires Robokassa::Interface.fail_implementation to be inmplemented by user
117
+ def fail(params, controller)
118
+ parsed_params = map_params(params, @@notification_params_map)
119
+ fail_implementation(
120
+ parsed_params[:invoice_id],
121
+ parsed_params[:amount],
122
+ parsed_params[:language],
123
+ parsed_params[:custom_options],
124
+ controller)
125
+ end
126
+
127
+
128
+ # Generates url for payment page
129
+ #
130
+ # === Example
131
+ # <%= link_to "Pay with Robokassa", interface.init_payment_url(order.id, order.amount, "Order #{order.id}", '', 'ru', order.user.email) %>
132
+ #
133
+ def init_payment_url(invoice_id, amount, description, currency='', language='ru', email='', custom_options={})
134
+ url_options = init_payment_options(invoice_id, amount, description, custom_options, currency, language, email)
135
+ "#{init_payment_base_url}?" + url_options.map do |k, v| "#{CGI::escape(k.to_s)}=#{CGI::escape(v.to_s)}" end.join('&')
136
+ end
137
+
138
+ # Maps gem parameter names, to robokassa names
139
+ def map_params(params, map)
140
+ Hash[params.map do|key, value| [(map[key] || map[key.to_sym] || key), value] end]
141
+ end
142
+
143
+ # make hash of options for init_payment_url
144
+ def init_payment_options(invoice_id, amount, description, custom_options = {}, currency='', language='ru', email='')
145
+ options = {
146
+ :login => @options[:login],
147
+ :amount => amount.to_s,
148
+ :invoice_id => invoice_id,
149
+ :description => description[0, 100],
150
+ :signature => init_payment_signature(invoice_id, amount, description, custom_options),
151
+ :currency => currency,
152
+ :email => email,
153
+ :language => language
154
+ }.merge(Hash[custom_options.sort.map{|x| ["shp#{x[0]}", x[1]]}])
155
+ map_params(options, @@params_map)
156
+ end
157
+
158
+ # calculates md5 from result of :init_payment_signature_string
159
+ def init_payment_signature(invoice_id, amount, description, custom_options={})
160
+ md5 init_payment_signature_string(invoice_id, amount, description, custom_options)
161
+ end
162
+
163
+ # generates signature string to calculate 'SignatureValue' url parameter
164
+ def init_payment_signature_string(invoice_id, amount, description, custom_options={})
165
+ custom_options_fmt = custom_options.sort.map{|x|"shp#{x[0]}=#{x[1]}"}.join(":")
166
+ "#{@options[:login]}:#{amount}:#{invoice_id}:#{@options[:password1]}#{custom_options_fmt.blank? ? "" : ":" + custom_options_fmt}"
167
+ end
168
+
169
+ # returns http://test.robokassa.ru or https://merchant.roboxchange.com in order to current mode
170
+ def base_url
171
+ test_mode? ? 'http://test.robokassa.ru' : 'https://merchant.roboxchange.com'
172
+ end
173
+
174
+ # returns url to redirect user to payment page
175
+ def init_payment_base_url
176
+ "#{base_url}/Index.aspx"
177
+ end
178
+
179
+ def md5(str) #:nodoc:
180
+ Digest::MD5.hexdigest(str).downcase
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,3 @@
1
+ module Robokassa
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,55 @@
1
+ #encoding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/../spec_helper'
4
+ require 'robokassa/interface'
5
+ describe "Interface should work correct" do
6
+ before :each do
7
+ end
8
+
9
+ it "Should correctly use test server" do
10
+ i = Robokassa::Interface.new :test_mode => true
11
+ i.should be_test_mode
12
+ i.base_url.should == "http://test.robokassa.ru"
13
+ end
14
+
15
+ it "should compute correct signature string" do
16
+ i = Robokassa::Interface.new :test_mode => true, :login => 'demo', :password1 => '12345'
17
+ i.init_payment_signature_string(15, 185.0, "Order #125").should == "demo:185.0:15:12345"
18
+ 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"
19
+ end
20
+
21
+ it "should create correct init payment url" do
22
+ i = Robokassa::Interface.new :test_mode => true, :login => 'demo', :password1 => '12345'
23
+ i = Robokassa::Interface.new :test_mode => true, :login => 'shaggyone239', :password1 => '12345asdf'
24
+ i.init_payment_signature_string(15, 185.11, "Order #125").should == "shaggyone239:185.11:15:12345asdf"
25
+ i.init_payment_signature(15, 185.11, "Order #125").should == "55f2aee20767cde28e7fc49919cec969"
26
+ i.init_payment_url(15, 185.11, "Order 125", '', 'ru', 'demo@robokassa.ru', {}).should ==
27
+ "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"
28
+ i.init_payment_signature(196, 2180.0, "R602412577").should == "adeedf2afbac5eca09b44898da3ef51a"
29
+ end
30
+
31
+ it "should check success result" do
32
+ i = Robokassa::Interface.new :test_mode => true, :login => 'demo', :password1 => '12345'
33
+
34
+ data = [ 15, 185.55, "Order #125", { :a => 15, :b => 30, :c => 20 } ]
35
+
36
+ signature_string = i.init_payment_signature_string( *data )
37
+
38
+ expect( signature_string ).to eq "demo:185.55:15:12345:shpa=15:shpb=30:shpc=20"
39
+
40
+ expect( i.init_payment_signature( *data ) ).to eq '3de046ed8d103b39b2bea2728b2eab27'
41
+
42
+ params = {
43
+ "OutSum" => "185.55",
44
+ "InvId" => "15",
45
+ "SignatureValue" => '75d4910b8b751dda4362d89333b247ca',
46
+ "Culture" => 'ru',
47
+ "shpa" => "15",
48
+ "shpb" => "30",
49
+ "shpc" => "20",
50
+ }
51
+
52
+ expect { i.success_validate_signature( params ) }.not_to raise_error
53
+ end
54
+ end
55
+
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe 'routes' do
5
+ context "robokassa" do
6
+ specify { get('/robokassa/notify/some-secure-notification-key').should route_to(
7
+ controller: 'robokassa',
8
+ action: 'notify',
9
+ token: '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
+
@@ -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,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: glebtv-robokassa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - glebtv
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Fork of Robokassa gem with only basic payment support
56
+ email:
57
+ - glebtv@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - app/controllers/robokassa_controller.rb
68
+ - config/locales/en.yml
69
+ - config/locales/ru.yml
70
+ - config/routes.rb
71
+ - glebtv-robokassa.gemspec
72
+ - lib/glebtv-robokassa.rb
73
+ - lib/robokassa.rb
74
+ - lib/robokassa/controller.rb
75
+ - lib/robokassa/engine.rb
76
+ - lib/robokassa/interface.rb
77
+ - lib/robokassa/version.rb
78
+ - spec/lib/interface_spec.rb
79
+ - spec/routing/routes_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: ''
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.1.11
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: This gem adds robokassa support to your app.
105
+ test_files:
106
+ - spec/lib/interface_spec.rb
107
+ - spec/routing/routes_spec.rb
108
+ - spec/spec_helper.rb