webtopay 1.2.1 → 1.6.0
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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/README.md +145 -0
- data/lib/webtopay.rb +5 -3
- data/lib/webtopay/exception.rb +11 -1
- data/lib/webtopay/payment.rb +45 -0
- data/lib/webtopay/response.rb +198 -0
- data/lib/webtopay/version.rb +1 -1
- data/lib/webtopay_controller.rb +14 -19
- data/lib/webtopay_helper.rb +19 -15
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/payments_controller.rb +11 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/webtopay.rb +4 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +61 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/lib/webtopay/payment_spec.rb +31 -0
- data/spec/lib/webtopay/response_spec.rb +57 -0
- data/spec/lib/webtopay_controller_spec.rb +19 -0
- data/spec/spec_helper.rb +59 -0
- data/webtopay.gemspec +10 -3
- metadata +195 -47
- data/README +0 -71
- data/lib/webtopay/api.rb +0 -407
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff4f52a85253c3642d03c9f73fbdea3b6078a47c
|
4
|
+
data.tar.gz: 9bfca8246d27fc7d85e7924520acf2f476594b06
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dbfac9082c0800b94d4052ab6e9c21a691c2ef2308d1634253722ec7e28cc3c1df3c8a52a93406b372b9bc6c7b59970606a1cf4bbc7f71f1641966162200a0e8
|
7
|
+
data.tar.gz: 12ad2c8eaad9d42bd1e1fe325b7f5f28761e1c812392c232e08bd8a296ff912528c7ee6a95b0b7860cd1d92190be8e82ed790c0dc32a53558ed835b4397cae31
|
data/.gitignore
CHANGED
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# WebToPay
|
2
|
+
|
3
|
+
It is a gem which could be very useful on working with https://www.webtopay.com/ (https://www.mokejimai.lt/) billing system.
|
4
|
+
The main function of this gem is protection against forgeries that could be done by sending required parameters to the application and getting services without paying for free.
|
5
|
+
This gem could be integrated with both billing types - MACRO (VISA, MasterCard, banks etc.) and MICRO (SMS, phone calls etc.).
|
6
|
+
|
7
|
+
*It works with OpenSSL so be sure that you have installed all necessary libs or modules on your system.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add to your gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "webtopay", github: 'bloomrain'
|
15
|
+
```
|
16
|
+
|
17
|
+
## Configuration
|
18
|
+
|
19
|
+
Create initializer
|
20
|
+
config/initializers/webtopay.rb
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
WebToPay.configure do |config|
|
24
|
+
config.project_id = 00000
|
25
|
+
config.sign_password = 'your sign password'
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
## Basic usage
|
30
|
+
|
31
|
+
Add this to your controller:
|
32
|
+
```ruby
|
33
|
+
webtopay :confirm_order, ...
|
34
|
+
|
35
|
+
def confirm_order
|
36
|
+
# do some ordering stuff here...
|
37
|
+
render text: "ok" # response with value "ok" is necessary
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Add this in your view:
|
42
|
+
Or if need only order confirmation button (no action in controller needed:
|
43
|
+
```erb
|
44
|
+
<%= webtopay_confirm_button("Buy", {amount: 2000, p_name: "Jonas", callback_url: confirm_order_url, ... })
|
45
|
+
```
|
46
|
+
|
47
|
+
|
48
|
+
## Advanced usage
|
49
|
+
|
50
|
+
If you want more control you can use this:
|
51
|
+
```ruby
|
52
|
+
before_filter :webtopay, only:[:controller_method1, :controller_method2] ...
|
53
|
+
```
|
54
|
+
instead of:
|
55
|
+
```ruby
|
56
|
+
webtopay :controller_method1, :controller_method2 ...
|
57
|
+
```
|
58
|
+
|
59
|
+
### Response validation
|
60
|
+
|
61
|
+
By default only projectid and sign fields are validated. It is highly recommended to check that order specific params
|
62
|
+
(like paid money amount) also matches params in response.
|
63
|
+
To specify which fields you want to validate add this method in controller:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
def webtopay_expected_params(webtopay_params) # webtopay_params is a hash returned from mokejimai.lt
|
67
|
+
order = Order.find(webtopay_params[:orderid])
|
68
|
+
{ # hash keys should be the same as in mokejimai.lt specification
|
69
|
+
p_email: order.user_email,
|
70
|
+
amount: order.price_in_cents,
|
71
|
+
# ... and any other fields that we save in database
|
72
|
+
}
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### On invalid response
|
77
|
+
|
78
|
+
If response is invalid then exception will be raised. To change this behavior add this to controller:
|
79
|
+
```ruby
|
80
|
+
def webtopay_failed_validation_response(api_response) # api_response is instance of WebToPay::Response
|
81
|
+
render json: api_response.errors # default behavior: raise api_response.errors.first
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
### Payment form
|
86
|
+
|
87
|
+
You can generate payment form like this
|
88
|
+
|
89
|
+
```erb
|
90
|
+
<%= form_for WebToPay::Payment.new, url: order_products_path do |f| %>
|
91
|
+
<%= f.text_field :p_name %>
|
92
|
+
<%= f.text_field :p_surname %>
|
93
|
+
<%= f.text_field :p_email %>
|
94
|
+
... any other fields you like (for complete field list please read mokejimai.lt api specification) ...
|
95
|
+
<%= f.submit "test paying" %>
|
96
|
+
<% end %>
|
97
|
+
```
|
98
|
+
|
99
|
+
Then in your controller
|
100
|
+
```ruby
|
101
|
+
class ProductsController < ApplicationController
|
102
|
+
def order
|
103
|
+
# do some order stuff here ...
|
104
|
+
@payment = WebToPay::Payment(params[:web_to_pay_payment])
|
105
|
+
redirect_to @payment.url
|
106
|
+
end
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
Or if need only order confirmation button (no action in controller needed:
|
111
|
+
```erb
|
112
|
+
<%= webtopay_confirm_button("Buy", {amount: 2000, p_name: "Jonas"})
|
113
|
+
```
|
114
|
+
|
115
|
+
## Examples
|
116
|
+
|
117
|
+
These code slices will protect your controller actions, which work with webtopay.com billing, against forgeries.
|
118
|
+
|
119
|
+
### Usage for MICRO and MACRO billing on controller.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
webtopay :activate_user, :confirm_cart # You can add here as many actions as you want
|
123
|
+
|
124
|
+
def activate_user
|
125
|
+
# write here code which do some stuff
|
126
|
+
render :text => "Your user has been successfully activated. Thank you!" # it sends SMS answer
|
127
|
+
end
|
128
|
+
|
129
|
+
def confirm_cart
|
130
|
+
# write here code which do some stuff
|
131
|
+
render :text => "ok" # it sends successful answer to webtopay.com crawler
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
|
136
|
+
TODO
|
137
|
+
===========
|
138
|
+
|
139
|
+
1. Write more clear documentation with real examples
|
140
|
+
2. Write unit tests for each billing method (requires some testing data from https://www.webtopay.com/)
|
141
|
+
3. Validate Api request by ss2 param
|
142
|
+
4. Check if mikro payments works well
|
143
|
+
|
144
|
+
===========
|
145
|
+
Copyright (c) 2009 Kristijonas Urbaitis, released under the MIT license
|
data/lib/webtopay.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'webtopay/exception'
|
2
2
|
require 'webtopay/configuration'
|
3
|
-
require 'webtopay/
|
3
|
+
require 'webtopay/payment'
|
4
|
+
require 'webtopay/response'
|
4
5
|
require 'webtopay_controller'
|
5
6
|
require 'webtopay_helper'
|
6
7
|
|
7
8
|
module WebToPay
|
9
|
+
API_VERSION = '1.6'
|
10
|
+
|
8
11
|
class << self
|
9
12
|
attr_accessor :config
|
10
13
|
|
@@ -16,5 +19,4 @@ module WebToPay
|
|
16
19
|
end
|
17
20
|
|
18
21
|
ActionController::Base.send(:include, WebToPayController)
|
19
|
-
ActionView::Base.send(:include, WebToPayHelper)
|
20
|
-
|
22
|
+
ActionView::Base.send(:include, WebToPayHelper)
|
data/lib/webtopay/exception.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module WebToPay
|
2
|
-
class Exception < ::
|
2
|
+
class Exception < ::StandardError
|
3
3
|
# Missing field.
|
4
4
|
E_MISSING = 1
|
5
5
|
|
@@ -22,5 +22,15 @@ module WebToPay
|
|
22
22
|
E_SMS_ANSWER = 7
|
23
23
|
|
24
24
|
attr_accessor :code, :field_name
|
25
|
+
|
26
|
+
def as_json(options = {})
|
27
|
+
{
|
28
|
+
error: {
|
29
|
+
message: message,
|
30
|
+
field_name: field_name,
|
31
|
+
code: code
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class WebToPay::Payment
|
2
|
+
ATTRIBUTES = [:projectid, :orderid, :lang, :amount, :currency, :accepturl, :cancelurl, :callbackurl,
|
3
|
+
:country, :paytext, :p_email, :p_name, :p_surname, :payment, :test, :version]
|
4
|
+
attr_accessor *ATTRIBUTES
|
5
|
+
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
include ActiveModel::AttributeMethods
|
8
|
+
|
9
|
+
def initialize(params = {}, user_params = {})
|
10
|
+
self.version = WebToPay::API_VERSION
|
11
|
+
self.projectid = user_params[:projectid] || WebToPay.config.project_id
|
12
|
+
@sign_password = user_params[:sign_password] || WebToPay.config.sign_password
|
13
|
+
|
14
|
+
params.each_pair do |field, value|
|
15
|
+
self.public_send("#{field}=", value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def query
|
20
|
+
@query ||= begin
|
21
|
+
query = []
|
22
|
+
ATTRIBUTES.each do |field|
|
23
|
+
value = self.public_send(field)
|
24
|
+
next if value.blank?
|
25
|
+
query << "#{field}=#{ CGI::escape value.to_s}"
|
26
|
+
end
|
27
|
+
query.join('&')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def data # encoded query
|
32
|
+
@data ||= Base64.encode64(query).gsub("\n", '').gsub('/', '+').gsub('_', '-')
|
33
|
+
end
|
34
|
+
|
35
|
+
def url
|
36
|
+
"https://www.mokejimai.lt/pay?data=#{CGI::escape data}&sign=#{CGI::escape sign}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def sign
|
40
|
+
Digest::MD5.hexdigest(data + @sign_password)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_key
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
class WebToPay::Response
|
2
|
+
PREFIX = "wp_"
|
3
|
+
SPECS_BY_TYPE = {
|
4
|
+
# Array structure:
|
5
|
+
# * name – request item name.
|
6
|
+
# * maxlen – max allowed value for item.
|
7
|
+
# * required – is this item is required in response.
|
8
|
+
# * mustcheck – this item must be checked by user.
|
9
|
+
# * isresponse – if false, item must not be included in response array.
|
10
|
+
# * regexp – regexp to test item value.
|
11
|
+
makro: [
|
12
|
+
[ :projectid, 11, true, true, true, /^\d+$/ ],
|
13
|
+
[ :orderid, 40, false, false, true, '' ],
|
14
|
+
[ :lang, 3, false, false, true, /^[a-z]{3}$/i ],
|
15
|
+
[ :amount, 11, false, false, true, /^\d+$/ ],
|
16
|
+
[ :currency, 3, false, false, true, /^[a-z]{3}$/i ],
|
17
|
+
[ :payment, 20, false, false, true, '' ],
|
18
|
+
[ :country, 2, false, false, true, /^[a-z]{2}$/i ],
|
19
|
+
[ :paytext, 0, false, false, true, '' ],
|
20
|
+
[ :ss2, 0, true, false, true, '' ],
|
21
|
+
[ :ss1, 0, false, false, true, '' ],
|
22
|
+
[ :name, 255, false, false, true, '' ],
|
23
|
+
[ :surename, 255, false, false, true, '' ],
|
24
|
+
[ :status, 255, false, false, true, '' ],
|
25
|
+
[ :error, 20, false, false, true, '' ],
|
26
|
+
[ :test, 1, false, false, true, /^[01]$/ ],
|
27
|
+
[ :p_email, 0, false, false, true, '' ],
|
28
|
+
[ :payamount, 0, false, false, true, '' ],
|
29
|
+
[ :paycurrency, 0, false, false, true, '' ],
|
30
|
+
[ :version, 9, true, false, true, /^\d+\.\d+$/ ],
|
31
|
+
[ :sign_password, 255, false, true, false, '' ]
|
32
|
+
],
|
33
|
+
|
34
|
+
# Specification array for mikro response.
|
35
|
+
#
|
36
|
+
# Array structure:
|
37
|
+
# * name – request item name.
|
38
|
+
# * maxlen – max allowed value for item.
|
39
|
+
# * required – is this item is required in data.
|
40
|
+
# * mustcheck – this item must be checked by user.
|
41
|
+
# * isresponse – if false, item must not be included in response array.
|
42
|
+
# * regexp – regexp to test item value.
|
43
|
+
mikro: [
|
44
|
+
[ :to, 0, true, false, true, '' ],
|
45
|
+
[ :sms, 0, true, false, true, '' ],
|
46
|
+
[ :from, 0, true, false, true, '' ],
|
47
|
+
[ :operator, 0, true, false, true, '' ],
|
48
|
+
[ :amount, 0, true, false, true, '' ],
|
49
|
+
[ :currency, 0, true, false, true, '' ],
|
50
|
+
[ :country, 0, true, false, true, '' ],
|
51
|
+
[ :id, 0, true, false, true, '' ],
|
52
|
+
[ :_ss2, 0, true, false, true, '' ],
|
53
|
+
[ :_ss1, 0, true, false, true, '' ],
|
54
|
+
[ :test, 0, true, false, true, '' ],
|
55
|
+
[ :key, 0, true, false, true, '' ],
|
56
|
+
#[ :version, 9, true, false, true, /^\d+\.\d+$/ ]
|
57
|
+
]
|
58
|
+
}
|
59
|
+
|
60
|
+
attr_reader :query, :user_data, :errors, :project_id
|
61
|
+
attr_accessor :data, :ss1, :ss2
|
62
|
+
|
63
|
+
def initialize(params, user_params = {})
|
64
|
+
params.each_pair do |field, value|
|
65
|
+
self.public_send("#{field}=", value)
|
66
|
+
end
|
67
|
+
@sign_password = user_params[:sign_password] || WebToPay.config.sign_password
|
68
|
+
@project_id = user_params[:projectid] || WebToPay.config.project_id
|
69
|
+
@errors = []
|
70
|
+
end
|
71
|
+
|
72
|
+
def query
|
73
|
+
@query ||= begin
|
74
|
+
data = self.data || ''
|
75
|
+
Base64.decode64( data.gsub('-', '+').gsub('_', '/') )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def query_params
|
80
|
+
@query_params ||= begin
|
81
|
+
params = CGI::parse(query)
|
82
|
+
params.each_pair do |key, value|
|
83
|
+
params[key] = value.first
|
84
|
+
end
|
85
|
+
params.merge(ss1: ss1, ss2: ss2).with_indifferent_access
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid?(params = {})
|
90
|
+
@errors = []
|
91
|
+
params = {
|
92
|
+
projectid: @project_id
|
93
|
+
}.merge(params)
|
94
|
+
|
95
|
+
valid = custom_params_valid?(params)
|
96
|
+
valid &&= ss1_valid?
|
97
|
+
valid &&= all_required_fields_included?
|
98
|
+
valid &&= no_blacklisted_fields_included?
|
99
|
+
valid &&= payment_status_valid?
|
100
|
+
valid
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate!(params = {})
|
104
|
+
raise errors.first unless valid?(params)
|
105
|
+
end
|
106
|
+
|
107
|
+
def type
|
108
|
+
@type ||= if query_params[:to] && query_params[:from] && query_params[:sms] && query_params[:projectid].nil?
|
109
|
+
:mikro
|
110
|
+
else
|
111
|
+
:makro
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def specs
|
116
|
+
SPECS_BY_TYPE[type]
|
117
|
+
end
|
118
|
+
|
119
|
+
def mikro?
|
120
|
+
type == :mikro
|
121
|
+
end
|
122
|
+
|
123
|
+
def makro?
|
124
|
+
type == :makro
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
def custom_params_valid?(params)
|
129
|
+
valid = true
|
130
|
+
params.each_pair do |key, expected_value|
|
131
|
+
query_value = query_params[key].presence
|
132
|
+
if query_value.is_a?(String)
|
133
|
+
if expected_value.is_a?(Integer)
|
134
|
+
query_value = query_value.to_i
|
135
|
+
elsif expected_value.is_a?(Float)
|
136
|
+
query_value = query_value.to_f
|
137
|
+
end
|
138
|
+
end
|
139
|
+
if query_value != expected_value
|
140
|
+
@errors << WebToPay::Exception.new("\"#{key}\" is invalid. Expected \"#{expected_value}\", but was \"#{query_value}\"")
|
141
|
+
valid = false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
valid
|
145
|
+
end
|
146
|
+
|
147
|
+
def required_response_fields
|
148
|
+
specs.select{|s| s[2]}.map(&:first).map(&:to_sym)
|
149
|
+
end
|
150
|
+
|
151
|
+
def blacklisted_response_fields
|
152
|
+
specs.reject{|s| s[4]}.map(&:first).map(&:to_sym)
|
153
|
+
end
|
154
|
+
|
155
|
+
def all_required_fields_included?
|
156
|
+
fields = query_params.keys.map(&:to_sym)
|
157
|
+
if (required_response_fields & fields).size != required_response_fields.size
|
158
|
+
@errors << WebToPay::Exception.new("\"#{(required_response_fields - fields).join('" , "')}\" parameter(s) not found in response query")
|
159
|
+
return false
|
160
|
+
end
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
def no_blacklisted_fields_included?
|
165
|
+
fields = query_params.keys.map(&:to_sym)
|
166
|
+
if (blacklisted_response_fields & fields).size != 0
|
167
|
+
@errors << WebToPay::Exception.new("\"#{(fields & blacklisted_response_fields).join('" , "')}\" blacklisted parameter(s) found in response query")
|
168
|
+
return false
|
169
|
+
end
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
def ss1_valid?
|
174
|
+
if self.ss1 != expected_ss1
|
175
|
+
@errors << WebToPay::Exception.new("ss1 param is invalid. Expected \"#{expected_ss1}\", but was \"#{self.ss1}\"")
|
176
|
+
return false
|
177
|
+
end
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def payment_status_valid?
|
182
|
+
if makro? && query_params[:status].to_i != 1
|
183
|
+
e = WebToPay::Exception.new("Returned transaction status is #{query_params[:status]}, successful status should be 1.")
|
184
|
+
e.code = WebToPay::Exception::E_INVALID
|
185
|
+
@errors << e
|
186
|
+
return false
|
187
|
+
end
|
188
|
+
return true
|
189
|
+
end
|
190
|
+
|
191
|
+
def expected_ss1
|
192
|
+
Digest::MD5.hexdigest(expected_data + @sign_password)
|
193
|
+
end
|
194
|
+
|
195
|
+
def expected_data
|
196
|
+
Base64.encode64(query).gsub("\n", '').gsub('/', '+').gsub('_', '-')
|
197
|
+
end
|
198
|
+
end
|