paysera 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +93 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +114 -0
- data/Rakefile +6 -0
- data/lib/paysera.rb +17 -0
- data/lib/paysera/attributes.rb +155 -0
- data/lib/paysera/error.rb +9 -0
- data/lib/paysera/request.rb +76 -0
- data/lib/paysera/response.rb +81 -0
- data/lib/paysera/version.rb +3 -0
- data/paysera.gemspec +24 -0
- data/spec/request_spec.rb +60 -0
- data/spec/response_spec.rb +92 -0
- data/spec/spec_helper.rb +2 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4db3c9e0b922a48f34110f61da51681ae8820d79
|
4
|
+
data.tar.gz: d5ec01d783c2d30bb1ba344d235b5349bbe9eeb6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ab25e3b2ef8afdeeb1255cd37f893d3eb60d2cc4937b8a54cf2fe9377993b7179191b24c0651f02c2bcfe9c4acdfb2f7db53f642b96d5afaa74f792e084dd2a2
|
7
|
+
data.tar.gz: b94565c982d25657300bb7454e7cf4e18dce59ef0498965dc9eafe4069720c5437d6f52311a89716604aff20eb38935dd91a336cc4de04b924ebf6e1fcd4890e
|
data/.gitignore
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Created by https://www.gitignore.io
|
2
|
+
|
3
|
+
### RubyMine ###
|
4
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
|
5
|
+
|
6
|
+
*.iml
|
7
|
+
|
8
|
+
## Directory-based project format:
|
9
|
+
.idea/
|
10
|
+
# if you remove the above rule, at least ignore the following:
|
11
|
+
|
12
|
+
# User-specific stuff:
|
13
|
+
# .idea/workspace.xml
|
14
|
+
# .idea/tasks.xml
|
15
|
+
# .idea/dictionaries
|
16
|
+
|
17
|
+
# Sensitive or high-churn files:
|
18
|
+
# .idea/dataSources.ids
|
19
|
+
# .idea/dataSources.xml
|
20
|
+
# .idea/sqlDataSources.xml
|
21
|
+
# .idea/dynamic.xml
|
22
|
+
# .idea/uiDesigner.xml
|
23
|
+
|
24
|
+
# Gradle:
|
25
|
+
# .idea/gradle.xml
|
26
|
+
# .idea/libraries
|
27
|
+
|
28
|
+
# Mongo Explorer plugin:
|
29
|
+
# .idea/mongoSettings.xml
|
30
|
+
|
31
|
+
## File-based project format:
|
32
|
+
*.ipr
|
33
|
+
*.iws
|
34
|
+
|
35
|
+
## Plugin-specific files:
|
36
|
+
|
37
|
+
# IntelliJ
|
38
|
+
out/
|
39
|
+
|
40
|
+
# mpeltonen/sbt-idea plugin
|
41
|
+
.idea_modules/
|
42
|
+
|
43
|
+
# JIRA plugin
|
44
|
+
atlassian-ide-plugin.xml
|
45
|
+
|
46
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
47
|
+
com_crashlytics_export_strings.xml
|
48
|
+
crashlytics.properties
|
49
|
+
crashlytics-build.properties
|
50
|
+
|
51
|
+
|
52
|
+
### Ruby ###
|
53
|
+
*.gem
|
54
|
+
*.rbc
|
55
|
+
/.config
|
56
|
+
/coverage/
|
57
|
+
/InstalledFiles
|
58
|
+
/pkg/
|
59
|
+
/spec/reports/
|
60
|
+
/test/tmp/
|
61
|
+
/test/version_tmp/
|
62
|
+
/tmp/
|
63
|
+
|
64
|
+
## Specific to RubyMotion:
|
65
|
+
.dat*
|
66
|
+
.repl_history
|
67
|
+
build/
|
68
|
+
|
69
|
+
## Documentation cache and generated files:
|
70
|
+
/.yardoc/
|
71
|
+
/_yardoc/
|
72
|
+
/doc/
|
73
|
+
/rdoc/
|
74
|
+
|
75
|
+
## Environment normalisation:
|
76
|
+
/.bundle/
|
77
|
+
/lib/bundler/man/
|
78
|
+
|
79
|
+
# for a library or gem, you might want to ignore these files since the code is
|
80
|
+
# intended to run in multiple environments; otherwise, check them in:
|
81
|
+
Gemfile.lock
|
82
|
+
.ruby-version
|
83
|
+
.ruby-gemset
|
84
|
+
|
85
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
86
|
+
.rvmrc
|
87
|
+
|
88
|
+
|
89
|
+
### Linux ###
|
90
|
+
*~
|
91
|
+
|
92
|
+
# KDE directory preferences
|
93
|
+
.directory
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Tomas
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# Paysera
|
2
|
+
|
3
|
+
*"Paysera is an advanced, effective, proven and safe electronic money system with an unlimited electronic money licence No. 1 issued by the Bank of Lithuania. It offers a free settlement account which allows for a quick, safe and low-cost (often – free of charge) payment for goods and services.
|
4
|
+
Paysera account is a true electronic wallet, which can not be lost; you will always find the amount of money that you have deposited in your account. In most cases, this type of account is better than bank account, because it is subject to higher security requirements, administrators of Paysera system can not lend or invest money held on the owner’s account.
|
5
|
+
Paysera.com services are constantly expanded and improved by the top-level specialists in accordance with the latest payment innovations."* — [Paysera](https://www.paysera.com/index.html)
|
6
|
+
|
7
|
+
This gem provides easy access to Paysera payment API.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'paysera'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install paysera
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
You can set default config like so:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
Paysera.config do |config|
|
31
|
+
config.projectid = 56571
|
32
|
+
config.sign_password = '36947d6dcbccc03ad591deab138dbb0c'
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
If you are using *Ruby on Rails* add it to `config/initializers/paysera.rb`. However it do not necessarily has to be rails,
|
37
|
+
you can add this into any Ruby app.
|
38
|
+
|
39
|
+
### Request
|
40
|
+
|
41
|
+
To make a request you only need to execute this:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
# Minimum requirements for request_params
|
45
|
+
request_params_example = {
|
46
|
+
orderid: 1,
|
47
|
+
accepturl: 'http://0.0.0.0:3000?accept',
|
48
|
+
cancelurl: 'http://0.0.0.0:3000?cancel',
|
49
|
+
callbackurl: 'http://0.0.0.0:3000?callback'
|
50
|
+
}
|
51
|
+
Paysera::Request.build_request(request_params_example, [sign_password])
|
52
|
+
```
|
53
|
+
|
54
|
+
It will generate payment link to paysera - `https://mokejimai.lt/pay/?data=...&sign=...`.
|
55
|
+
So you can do this if you are using *Rails*:
|
56
|
+
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
redirect_to Paysera::Request.build_request(...)
|
60
|
+
```
|
61
|
+
|
62
|
+
You can use all parameters from: https://developers.paysera.com/en/payments/current#request-parameters
|
63
|
+
|
64
|
+
If required parameter not found or it is invalid it will raise `Paysera::Error::Request` error with specific error message.
|
65
|
+
|
66
|
+
If you specify `projectid` or `sign_password` it will overwrite initializer.
|
67
|
+
|
68
|
+
### Response
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# params should include valid data, ss1 and ss2
|
72
|
+
response = Paysera::Response.new(params, [projectid], [sign_password])
|
73
|
+
```
|
74
|
+
|
75
|
+
If `ss1` or `ss2` will fail to validate it will raise `Paysera::Error::Response` error with specific error message.
|
76
|
+
|
77
|
+
To check if response is sms/mikro
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
if response.sms?
|
81
|
+
...
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
To check if response is bank/makro:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
if response.bank?
|
89
|
+
...
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
To get response data:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
response.get_data
|
97
|
+
```
|
98
|
+
|
99
|
+
It will return Hash of data: [SMS specification](https://developers.paysera.com/en/sms-keywords/current#detailed-specification) and [Bank specification(see "Encoded parameters")](https://developers.paysera.com/en/payments/1.6#integration-via-specification)
|
100
|
+
|
101
|
+
|
102
|
+
If you specify `projectid` or `sign_password` it will overwrite initializer.
|
103
|
+
|
104
|
+
## Contributing
|
105
|
+
|
106
|
+
1. Fork it ( https://github.com/TomasAchmedovas/paysera/fork )
|
107
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
108
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
109
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
110
|
+
5. Create a new Pull Request
|
111
|
+
|
112
|
+
## License
|
113
|
+
|
114
|
+
MIT License: see [LICENSE](https://github.com/TomasAchmedovas/paysera/blob/master/LICENSE) file
|
data/Rakefile
ADDED
data/lib/paysera.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'paysera/version'
|
2
|
+
require 'paysera/attributes'
|
3
|
+
require 'paysera/error'
|
4
|
+
require 'paysera/request'
|
5
|
+
require 'paysera/response'
|
6
|
+
|
7
|
+
module Paysera
|
8
|
+
API_VERSION = '1.6'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :projectid, :sign_password
|
12
|
+
|
13
|
+
def config
|
14
|
+
yield self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
class Paysera::Attributes
|
2
|
+
SMS = {
|
3
|
+
:to => {},
|
4
|
+
:sms => {},
|
5
|
+
:from => {},
|
6
|
+
:operator => {},
|
7
|
+
:amount => {
|
8
|
+
:maxlen => 11,
|
9
|
+
:required => false,
|
10
|
+
:regex => /^\d+$/
|
11
|
+
},
|
12
|
+
:currency => {
|
13
|
+
:maxlen => 3,
|
14
|
+
:required => false,
|
15
|
+
:regex => /^[a-z]{3}$/i
|
16
|
+
},
|
17
|
+
:country => {
|
18
|
+
:maxlen => 2,
|
19
|
+
:required => false,
|
20
|
+
:regex => /^[a-z]{2}$/i
|
21
|
+
},
|
22
|
+
:id => {},
|
23
|
+
:test => {
|
24
|
+
:maxlen => 1,
|
25
|
+
:required => false,
|
26
|
+
:regex => /^[01]$/
|
27
|
+
},
|
28
|
+
:key => {},
|
29
|
+
:projectid => {
|
30
|
+
:maxlen => 11,
|
31
|
+
:required => true,
|
32
|
+
:regex => /^\d+$/
|
33
|
+
},
|
34
|
+
:version => {
|
35
|
+
:maxlen => 9,
|
36
|
+
:required => true,
|
37
|
+
:regex => /^\d+\.\d+$/
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
REQUEST = {
|
42
|
+
:projectid => {
|
43
|
+
:maxlen => 11,
|
44
|
+
:required => true,
|
45
|
+
:regex => /^\d+$/
|
46
|
+
},
|
47
|
+
:orderid => {
|
48
|
+
:maxlen => 40,
|
49
|
+
:required => true,
|
50
|
+
:regex => /^\d+$/
|
51
|
+
},
|
52
|
+
:accepturl => {
|
53
|
+
:maxlen => 255,
|
54
|
+
:required => true,
|
55
|
+
},
|
56
|
+
:cancelurl => {
|
57
|
+
:maxlen => 255,
|
58
|
+
:required => true,
|
59
|
+
},
|
60
|
+
:callbackurl => {
|
61
|
+
:maxlen => 255,
|
62
|
+
:required => true,
|
63
|
+
},
|
64
|
+
:version => {
|
65
|
+
:maxlen => 9,
|
66
|
+
:required => true,
|
67
|
+
:regex => /^\d+\.\d+$/
|
68
|
+
},
|
69
|
+
:lang => {
|
70
|
+
:maxlen => 3,
|
71
|
+
:required => false,
|
72
|
+
:regex => /^[a-z]{3}$/i
|
73
|
+
},
|
74
|
+
:amount => {
|
75
|
+
:maxlen => 11,
|
76
|
+
:required => false,
|
77
|
+
:regex => /^\d+$/
|
78
|
+
},
|
79
|
+
:currency => {
|
80
|
+
:maxlen => 3,
|
81
|
+
:required => false,
|
82
|
+
:regex => /^[a-z]{3}$/i
|
83
|
+
},
|
84
|
+
:payment => {
|
85
|
+
:maxlen => 20,
|
86
|
+
:required => false
|
87
|
+
},
|
88
|
+
:country => {
|
89
|
+
:maxlen => 2,
|
90
|
+
:required => false,
|
91
|
+
:regex => /^[a-z]{2}$/i
|
92
|
+
},
|
93
|
+
:paytext => {
|
94
|
+
:maxlen => 255,
|
95
|
+
:required => false,
|
96
|
+
},
|
97
|
+
:p_firstname => {
|
98
|
+
:maxlen => 255,
|
99
|
+
:required => false,
|
100
|
+
},
|
101
|
+
:p_lastname => {
|
102
|
+
:maxlen => 255,
|
103
|
+
:required => false,
|
104
|
+
},
|
105
|
+
:p_email => {
|
106
|
+
:maxlen => 255,
|
107
|
+
:required => false,
|
108
|
+
},
|
109
|
+
:p_street => {
|
110
|
+
:maxlen => 255,
|
111
|
+
:required => false,
|
112
|
+
},
|
113
|
+
:p_city => {
|
114
|
+
:maxlen => 255,
|
115
|
+
:required => false,
|
116
|
+
},
|
117
|
+
:p_state => {
|
118
|
+
:maxlen => 20,
|
119
|
+
:required => false,
|
120
|
+
},
|
121
|
+
:p_zip => {
|
122
|
+
:maxlen => 20,
|
123
|
+
:required => false,
|
124
|
+
},
|
125
|
+
:p_countrycode => {
|
126
|
+
:maxlen => 2,
|
127
|
+
:required => false,
|
128
|
+
:regex => /^[a-z]{2}$/i
|
129
|
+
},
|
130
|
+
:only_payments => {
|
131
|
+
:required => false,
|
132
|
+
},
|
133
|
+
:disallow_payments => {
|
134
|
+
:required => false,
|
135
|
+
},
|
136
|
+
:test => {
|
137
|
+
:maxlen => 1,
|
138
|
+
:required => false,
|
139
|
+
:regex => /^[01]$/
|
140
|
+
},
|
141
|
+
:time_limit => {
|
142
|
+
:maxlen => 19,
|
143
|
+
:required => false,
|
144
|
+
},
|
145
|
+
:personcode => {
|
146
|
+
:maxlen => 255,
|
147
|
+
:required => false,
|
148
|
+
},
|
149
|
+
:developerid => {
|
150
|
+
:maxlen => 11,
|
151
|
+
:required => false,
|
152
|
+
:regex => /^\d+$/
|
153
|
+
}
|
154
|
+
}
|
155
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'cgi'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module Paysera
|
6
|
+
class Request
|
7
|
+
def self.build_request(paysera_params, sign_password=nil)
|
8
|
+
# Ensure that all key will be symbols
|
9
|
+
paysera_params = Hash[paysera_params.map { |k, v| [k.to_sym, v] }]
|
10
|
+
|
11
|
+
# Set default values
|
12
|
+
paysera_params[:version] = Paysera::API_VERSION
|
13
|
+
paysera_params[:projectid] ||= Paysera.projectid
|
14
|
+
sign_password ||= Paysera.sign_password
|
15
|
+
|
16
|
+
raise send_error("'sign_password' is required but missing") if sign_password.nil?
|
17
|
+
|
18
|
+
valid_request = validate_request(paysera_params)
|
19
|
+
encoded_query = encode_string make_query(valid_request)
|
20
|
+
signed_request = sign_request(encoded_query, sign_password)
|
21
|
+
|
22
|
+
query = make_query({
|
23
|
+
:data => encoded_query,
|
24
|
+
:sign => signed_request
|
25
|
+
})
|
26
|
+
|
27
|
+
|
28
|
+
"https://www.mokejimai.lt/pay/?#{query}"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.make_query(data)
|
34
|
+
data.collect do |key, value|
|
35
|
+
"#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
36
|
+
end.compact.sort! * '&'
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.sign_request(query, password)
|
40
|
+
# Encode string + password with md5
|
41
|
+
Digest::MD5.hexdigest(query + password)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.encode_string(string)
|
45
|
+
# 1) Encode with base64
|
46
|
+
# 2) Replace / with _ and + with -
|
47
|
+
Base64.encode64(string).gsub("\n", '').gsub('/', '_').gsub('+', '-')
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.validate_request(req)
|
51
|
+
request = {}
|
52
|
+
|
53
|
+
Paysera::Attributes::REQUEST.each do |k, v|
|
54
|
+
raise send_error("'#{k}' is required but missing") if v[:required] and req[k].nil?
|
55
|
+
|
56
|
+
req_value = req[k].to_s
|
57
|
+
regex = v[:regex].to_s
|
58
|
+
maxlen = v[:maxlen]
|
59
|
+
|
60
|
+
unless req[k].nil?
|
61
|
+
raise send_error("'#{k}' value '#{req[k]}' is too long, #{v[:maxlen]} characters allowed.") if maxlen and req_value.length > maxlen
|
62
|
+
raise send_error("'#{k}' value '#{req[k]}' invalid.") if '' != regex and !req_value.match(regex)
|
63
|
+
|
64
|
+
# Add only existing params
|
65
|
+
request[k] = req[k]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
request
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.send_error(msg)
|
73
|
+
Paysera::Error::Request.new msg
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'cgi'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
module Paysera
|
8
|
+
class Response
|
9
|
+
PAYSERA_PUBLIC_KEY = 'https://www.webtopay.com/download/public.key'
|
10
|
+
|
11
|
+
def initialize(query, projectid: nil, sign_password: nil)
|
12
|
+
raise send_error("'data' parameter was not found") if query[:data].nil?
|
13
|
+
raise send_error("'ss1' parameter was not found") if query[:ss1].nil?
|
14
|
+
raise send_error("'ss2' parameter was not found") if query[:ss2].nil?
|
15
|
+
|
16
|
+
projectid ||= Paysera.projectid
|
17
|
+
raise send_error("'projectid' parameter was not found") if projectid.nil?
|
18
|
+
|
19
|
+
sign_password ||= Paysera.sign_password
|
20
|
+
raise send_error("'sign_password' parameter was not found") if sign_password.nil?
|
21
|
+
|
22
|
+
raise send_error("Unable to verify 'ss1'") unless valid_ss1? query[:data], query[:ss1], sign_password
|
23
|
+
raise send_error("Unable to verify 'ss2'") unless valid_ss2? query[:data], query[:ss2]
|
24
|
+
|
25
|
+
@data = convert_to_hash safely_decode_string(query[:data])
|
26
|
+
|
27
|
+
raise send_error("'projectid' mismatch") if @data[:projectid].to_i != projectid
|
28
|
+
end
|
29
|
+
|
30
|
+
def sms?
|
31
|
+
# Basically if response data have sms param then it is sms payment
|
32
|
+
@data.key? :sms
|
33
|
+
end
|
34
|
+
|
35
|
+
def bank?
|
36
|
+
# Same here, if response have paytext param, then it is bank payment
|
37
|
+
@data.key? :paytext
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_data
|
41
|
+
@data
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def convert_to_hash(query)
|
47
|
+
Hash[query.split('&').collect do |s|
|
48
|
+
a = s.split('=')
|
49
|
+
[unescape_string(a[0]).to_sym, unescape_string(a[1])]
|
50
|
+
end]
|
51
|
+
end
|
52
|
+
|
53
|
+
def valid_ss1?(data, ss1, sign_password)
|
54
|
+
Digest::MD5.hexdigest(CGI.unescape(data) + sign_password) == ss1
|
55
|
+
end
|
56
|
+
|
57
|
+
def valid_ss2?(data, ss2)
|
58
|
+
public_key = get_public_key
|
59
|
+
ss2 = unescape_string safely_decode_string(unescape_string(ss2))
|
60
|
+
data = unescape_string data
|
61
|
+
|
62
|
+
public_key.verify(OpenSSL::Digest::SHA1.new, ss2, data)
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_public_key
|
66
|
+
OpenSSL::X509::Certificate.new(open(PAYSERA_PUBLIC_KEY).read).public_key
|
67
|
+
end
|
68
|
+
|
69
|
+
def safely_decode_string(string)
|
70
|
+
Base64.decode64 string.gsub('-', '+').gsub('_', '/').gsub("\n", '')
|
71
|
+
end
|
72
|
+
|
73
|
+
def unescape_string(string)
|
74
|
+
CGI.unescape string.to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_error(msg)
|
78
|
+
Paysera::Error::Response.new msg
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/paysera.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'paysera/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'paysera'
|
8
|
+
spec.version = Paysera::VERSION
|
9
|
+
spec.authors = ['Tomas Achmedovas']
|
10
|
+
spec.email = ['achmedovas.tomas@gmail.com']
|
11
|
+
spec.summary = %q{Paysera.com payment API}
|
12
|
+
spec.description = %q{Paysera.com payment API}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'cgi'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
describe Paysera::Request do
|
6
|
+
let(:valid_request) do
|
7
|
+
{
|
8
|
+
projectid: 56571,
|
9
|
+
orderid: 1,
|
10
|
+
accepturl: '0.0.0.0:3000/get_request?accept',
|
11
|
+
cancelurl: '0.0.0.0:3000/get_request?cancel',
|
12
|
+
callbackurl: '0.0.0.0:3000/get_request?callback',
|
13
|
+
|
14
|
+
}
|
15
|
+
end
|
16
|
+
let(:sign_password) { '36947d6dcbccc03ad591deab138dbb0c' }
|
17
|
+
|
18
|
+
subject { Paysera::Request.build_request(valid_request, sign_password) }
|
19
|
+
|
20
|
+
describe '#build_request' do
|
21
|
+
context 'when no projectid given' do
|
22
|
+
let (:invalid_request) { valid_request.delete(:projectid); valid_request }
|
23
|
+
it { expect { Paysera::Request.build_request(invalid_request, sign_password) }.to raise_error /projectid.+missing/ }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when no orderid given' do
|
27
|
+
let (:invalid_request) { valid_request.delete(:orderid); valid_request }
|
28
|
+
it { expect { Paysera::Request.build_request(invalid_request, sign_password) }.to raise_error /orderid.+missing/ }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when no accepturl given' do
|
32
|
+
let (:invalid_request) { valid_request.delete(:accepturl); valid_request }
|
33
|
+
it { expect { Paysera::Request.build_request(invalid_request, sign_password) }.to raise_error /accepturl.+missing/ }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when no cancelurl given' do
|
37
|
+
let (:invalid_request) { valid_request.delete(:cancelurl); valid_request }
|
38
|
+
it { expect { Paysera::Request.build_request(invalid_request, sign_password) }.to raise_error /cancelurl.+missing/ }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when no callbackurl given' do
|
42
|
+
let (:invalid_request) { valid_request.delete(:callbackurl); valid_request }
|
43
|
+
it { expect { Paysera::Request.build_request(invalid_request, sign_password) }.to raise_error /callbackurl.+missing/ }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when no sign_password given' do
|
47
|
+
it { expect { Paysera::Request.build_request(valid_request, nil) }.to raise_error /sign_password.+missing/ }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when valid' do
|
51
|
+
it { expect(subject).to match(/^https:\/\/(www\.)*(mokejimai|paysera)\.(lt|com)\/pay\/*\?(data|sign)=.+&(data|sign)=.+$/i) }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when generated url params are valid' do
|
55
|
+
let(:request) { Hash[subject.split('?')[1].split('&').map { |a| k=a.split('='); [k[0].to_sym, CGI.unescape(k[1]).gsub('+', '-').gsub('/', '_')] }] }
|
56
|
+
|
57
|
+
it { expect(Digest::MD5.hexdigest(request[:data] + sign_password)).to eq(request[:sign]) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Paysera::Response do
|
4
|
+
|
5
|
+
# TODO: add sms response
|
6
|
+
let(:response_data) do
|
7
|
+
{
|
8
|
+
:data => 'b3JkZXJpZD0xMjM0JnByb2plY3RpZD01NjU3MSZ0ZXN0PTEmdmVyc2lvbj0xLjYmdHlwZT1URVNUJmxhbmc9JnBheW1lbnQ9aGFuemEmY3VycmVuY3k9TFRMJmFtb3VudD0xMDAwMDAmcGF5dGV4dD1PcmRlcitObyUzQSsxMjM0K2F0K2h0dHAlM0ElMkYlMkZwYXlzZXJhLndwci5sdCtwcm9qZWN0LislMjhTZWxsZXIlM0ErVG9tYXMrQWNobWVkb3ZhcyUyOSZwX2VtYWlsPXRyeWtpejExJTQwZ21haWwuY29tJmNvdW50cnk9TFQmbV9wYXlfcmVzdG9yZWQ9MHg0Jl9jbGllbnRfbGFuZ3VhZ2U9ZW5nJnJlY2VpdmVyaWQ9MTg2NTA2JnN0YXR1cz0xJnBheWFtb3VudD0xMDAwMDAmcGF5Y3VycmVuY3k9TFRMJnJlcXVlc3RpZD02OTk0MzY5NyZuYW1lPSZzdXJlbmFtZT0%3D',
|
9
|
+
:ss1 => '0a58f2b1fe7972e616a01150427f7912',
|
10
|
+
:ss2 => '02pTV2VtdxJlMIdrKakCHpIxgiG--1iUwZkxikwAvqPEiiEj_ekmMYsnYDu3B8RlrmDBWrsSIRLIaPwOGLbInQc9vduK3lsjm9PQ9RImNXEHcc5Pyk-6cQ93U833bQ73r5NxvwfxE8s3KhBUxwHDLTSPCMnF7yEhskLx63I0SW8%3D'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
let(:projectid) { 56571 }
|
14
|
+
let(:sign_password) { '36947d6dcbccc03ad591deab138dbb0c' }
|
15
|
+
|
16
|
+
subject(:bank_response) { Paysera::Response.new response_data, projectid: projectid, sign_password: sign_password }
|
17
|
+
subject(:sms_response) { Paysera::Response.new response_data, projectid: projectid, sign_password: sign_password }
|
18
|
+
|
19
|
+
describe '#initialize' do
|
20
|
+
def new_response(data: nil, ss1: nil, ss2: nil, p_id: nil, sign: nil)
|
21
|
+
Paysera::Response.new({ data: data, ss1: ss1, ss2: ss2 }, projectid: p_id, sign_password: sign)
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when no data param' do
|
25
|
+
it { expect { new_response(ss1: 't', ss2: 't', p_id: 1, sign: 't') }.to raise_exception /data.+not\sfound/i }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when no ss1 param' do
|
29
|
+
it { expect { new_response(data: 't', ss2: 't', p_id: 1, sign: 't') }.to raise_exception /ss1.+not\sfound/i }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when no ss2 param' do
|
33
|
+
it { expect { new_response(data: 't', ss1: 't', p_id: 1, sign: 't') }.to raise_exception /ss2.+not\sfound/i }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when no projectid param' do
|
37
|
+
it { expect { new_response(data: 't', ss1: 't', ss2: 't', sign: 't') }.to raise_exception /projectid.+not\sfound/i }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when no sign_password param' do
|
41
|
+
it { expect { new_response(data: 't', ss1: 't', ss2: 't', p_id: 1) }.to raise_exception /sign_password.+not\sfound/i }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when invalid ss1' do
|
45
|
+
it { expect { new_response(data: response_data[:data],
|
46
|
+
ss1: 't',
|
47
|
+
ss2: response_data[:ss2],
|
48
|
+
p_id: projectid,
|
49
|
+
sign: sign_password) }.to raise_exception /verify.+ss1/i }
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when invalid ss2' do
|
53
|
+
it { expect { new_response(data: response_data[:data],
|
54
|
+
ss1: response_data[:ss1],
|
55
|
+
ss2: 't',
|
56
|
+
p_id: projectid,
|
57
|
+
sign: sign_password) }.to raise_exception /verify.+ss2/i }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when projectid mismatch' do
|
61
|
+
it { expect { new_response(data: response_data[:data],
|
62
|
+
ss1: response_data[:ss1],
|
63
|
+
ss2: response_data[:ss2],
|
64
|
+
p_id: 1,
|
65
|
+
sign: sign_password) }.to raise_exception /projectid.+mismatch/i }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#bank?' do
|
70
|
+
context 'when sms response' do
|
71
|
+
it { expect(sms_response.bank?).to be false }
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when bank response' do
|
75
|
+
it { expect(bank_response.bank?).to be true }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#sms?' do
|
80
|
+
context 'when bank response' do
|
81
|
+
it { expect(bank_response.sms?).to be false }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when bank response' do
|
85
|
+
it { expect(sms_response.sms?).to be true }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#get_data' do
|
90
|
+
it { expect(bank_response.get_data).to be_kind_of(Hash) }
|
91
|
+
end
|
92
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paysera
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomas Achmedovas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
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: Paysera.com payment API
|
56
|
+
email:
|
57
|
+
- achmedovas.tomas@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/paysera.rb
|
68
|
+
- lib/paysera/attributes.rb
|
69
|
+
- lib/paysera/error.rb
|
70
|
+
- lib/paysera/request.rb
|
71
|
+
- lib/paysera/response.rb
|
72
|
+
- lib/paysera/version.rb
|
73
|
+
- paysera.gemspec
|
74
|
+
- spec/request_spec.rb
|
75
|
+
- spec/response_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
homepage: ''
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.4.5
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Paysera.com payment API
|
101
|
+
test_files:
|
102
|
+
- spec/request_spec.rb
|
103
|
+
- spec/response_spec.rb
|
104
|
+
- spec/spec_helper.rb
|