payu 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTM2OGIzMTZhY2FlZjM5OGZiZjNlODAwYjYyNjYyZWU3Y2JmOTYzMQ==
5
+ data.tar.gz: !binary |-
6
+ MWQzMGE4YjdhY2E3YjY2NDNkOTkwMzdmNDZhOGU3MDJjZDNlMmU0Zg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTUzYzBmNGQ4NmE2YTg5NTFlNzU1OWYwMjMxN2VkNmQxNjNmZjJjNjQ3YjI2
10
+ NmYyMzBmMjRlMjQ4MTY5NmJjYjA3ZmExNmMwMmE5YWFiZDBiNDBiMzZmODM5
11
+ MmRmZDM3NmJmOGNlMmRlN2FiZTFjMzEyMGEwOTVjNTA2MTkzYWE=
12
+ data.tar.gz: !binary |-
13
+ MGY4ZGVkNWY1MWEwN2EzYzcwNmZjNGM3N2UwN2MwN2MxOWEwODhiZDg2YmNi
14
+ OWFkMzcwZWMyNjA3ZmUxYzRhMTEyYWE0NzI4MmU3YjNkYjRkOGVkMGQ0MjE4
15
+ NTJjMTc1YzgzNzU1YzgyZmRiYjIzMDIyN2VhMDUyMGJiOTFmMDI=
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ .rvmrc
4
+ Gemfile.lock
5
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in payu.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Michał Młoźniak
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,171 @@
1
+ # payu
2
+
3
+ Simple library for accepting payments via PayU.
4
+
5
+ By [Visuality](http://www.visuality.pl).
6
+
7
+ ## Features
8
+
9
+ * No dependencies on ActiveSupport or any similar libraries, so it should work in any Ruby application or framework
10
+ * Simple configuration (multiple points of sale)
11
+ * Automatic signature generation and verification
12
+ * View helpers for generating payment form and verifying signatures
13
+
14
+ ## Installation
15
+
16
+ In your Gemfile:
17
+
18
+ ```ruby
19
+ gem 'payu'
20
+ ````
21
+
22
+ Or, from the command line:
23
+
24
+ ```ruby
25
+ gem install payu
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ This gem implements only core functionality for integrating with PayU gateway. It is designed to work with any ruby framework or plain ruby application. To integrate it in your app you need to do some work, but it is really simple.
31
+
32
+ ### Create Pos
33
+
34
+ Your app will interact with PayU via point of sale (Pos). You can create it on PayU website and get its credentials. With these credentials you can create Pos instance:
35
+
36
+ ```ruby
37
+ pos = Payu::Pos.new :pos_id => '12345', :pos_auth_key => 'abcdefghijk', :key1 => 'xxxxxxxx', :key2 => 'xxxxxxxx'
38
+ ```
39
+
40
+ You can also load Pos configuration from yaml file. For example config/payu.yml:
41
+
42
+ ```yaml
43
+ bank:
44
+ pos_id: 12345
45
+ pos_auth_key: XXX
46
+ key1: XXX
47
+ key2: XXX
48
+ type: default
49
+
50
+ sms:
51
+ pos_id: 56789
52
+ pos_auth_key: XXX
53
+ key1: XXX
54
+ key2: XXX
55
+ type: sms_premium
56
+ ```
57
+
58
+ Then add new initializer config/initializers/payu.rb:
59
+
60
+ ```ruby
61
+ Payu.load_pos_from_yaml(Rails.root.join('config', 'payu.yml'))
62
+ ```
63
+
64
+ Now you can access them by name or pos_id:
65
+
66
+ ```ruby
67
+ Payu['bank']
68
+ Payu[:bank]
69
+ Payu[12345]
70
+ Payu['56789']
71
+ ```
72
+
73
+ ### Create new payment
74
+
75
+ To create new payment:
76
+
77
+ ```ruby
78
+ @transaction = pos.new_transaction(:first_name => 'John', :last_name => 'Doe', :email => 'john.doe@example.org', :client_ip => '1.2.3.4', :amount => 10000)
79
+ ```
80
+
81
+ Now you need to build form with this transaction object:
82
+
83
+ ```
84
+ <%= form_tag(@transaction.new_url) do %>
85
+ <%= payu_hidden_fields(@transaction) %>
86
+ <%= submit_tag 'Pay' %>
87
+ <% end %>
88
+ ```
89
+
90
+ ### Read payment status
91
+
92
+ You can check status of any payment:
93
+
94
+ ```ruby
95
+ response = pos.get(123456789)
96
+
97
+ if response.status == 'OK'
98
+ if response.completed?
99
+ # payment completed
100
+ end
101
+ end
102
+ ```
103
+
104
+ 123456789 is a payment session id. It is automatically generated when you create new transaction.
105
+
106
+ ### Confirm payment
107
+
108
+ By default when somebody sends a payment it is automatically accepted. You can turn it off and confirm every payment:
109
+
110
+ ```ruby
111
+ response = pos.confirm(123456789)
112
+ ```
113
+
114
+ ### Cancel payment
115
+
116
+ You can cancel payment:
117
+
118
+ ```ruby
119
+ response = pos.cancel(123456789)
120
+ ```
121
+
122
+ ### Handle Payu callbacks
123
+
124
+ When payment status changes, Payu will send report to your application. You need controller to handle these reports:
125
+
126
+ ```ruby
127
+
128
+ class PayuController < ApplicationController
129
+ include Payu::Helpers
130
+ skip_before_filter :verify_authenticity_token
131
+
132
+ def ok
133
+ # successful redirect
134
+ end
135
+
136
+ def error
137
+ # failed redirect
138
+ end
139
+
140
+ def report
141
+ payu_verify_params(params)
142
+
143
+ response = Payu['main'].get params[:session_id]
144
+
145
+ if response.status == 'OK'
146
+ order = Order.find(response.trans_order_id)
147
+
148
+ if response.completed? && order.present?
149
+ # mark order as paid
150
+ else
151
+ # payment not completed
152
+ end
153
+ end
154
+
155
+ render :text => 'OK'
156
+ end
157
+ ```
158
+
159
+ And routes:
160
+
161
+ ```ruby
162
+ match '/payu/ok' => 'payu#ok'
163
+ match '/payu/error' => 'payu#error'
164
+ match '/payu/report' => 'payu#report'
165
+ ```
166
+
167
+ Actions ok and error are pages where user is redirected after payment. You need to enter these url on Payu website.
168
+
169
+ ## Copyright
170
+
171
+ Copyright (c) 2013 Michał Młoźniak. See LICENSE for details.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ $:.unshift "#{File.dirname(__FILE__)}/lib"
2
+ require 'payu'
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ require 'payu/version'
6
+ require 'payu/pos'
7
+ require 'payu/transaction'
8
+ require 'payu/response'
9
+ require 'payu/signature'
10
+ require 'payu/errors'
11
+
12
+ module Payu
13
+ ENCODINGS = ['ISO', 'WIN', 'UTF']
14
+
15
+ @@pos_table = {}
16
+
17
+ class SignatureInvalid < StandardError; end
18
+ class PosNotFound < StandardError; end
19
+ class RequestFailed < StandardError; end
20
+ class PosInvalid < StandardError; end
21
+ class TransactionInvalid < StandardError; end
22
+
23
+ class << self
24
+
25
+ # Loads payments.yml file and creates specified Pos objects
26
+ def load_pos_from_yaml(filename)
27
+ if File.exist?(filename)
28
+ config = YAML.load_file(filename)
29
+ config.each do |name, config|
30
+ pos = Pos.new(
31
+ :pos_id => config['pos_id'],
32
+ :pos_auth_key => config['pos_auth_key'],
33
+ :key1 => config['key1'],
34
+ :key2 => config['key2'],
35
+ :variant => config['variant'],
36
+ :add_signature => config['add_signature'],
37
+ :test_payment => config['test_payment']
38
+ )
39
+ @@pos_table[name] = pos
40
+ end
41
+
42
+ true
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def add_pos(name, pos)
49
+ @@pos_table[name.to_s] = pos
50
+ end
51
+
52
+ # Combined accessor, returns Pos object with given pos_id or name
53
+ #
54
+ # @param [String, Integer] name_or_id name or pos_id of Pos
55
+ # @return [Object] the Pos object
56
+ def [](name_or_id)
57
+ get_pos_by_name(name_or_id) || get_pos_by_id(name_or_id) || raise(PosNotFound)
58
+ end
59
+
60
+ # Returns Pos object with given name, the same as in payments.yml file
61
+ #
62
+ # @param [String] name name of Pos
63
+ # @return [Object] the Pos object
64
+ def get_pos_by_name(name)
65
+ @@pos_table[name.to_s]
66
+ end
67
+
68
+ # Returns Pos object with given pos_id, the same as in payments.yml file
69
+ #
70
+ # @param [Integer] id pos_id of Pos
71
+ # @return [Object] the Pos object
72
+ def get_pos_by_id(pos_id)
73
+ pos_id = pos_id.to_i rescue 0
74
+ @@pos_table.each do |k, v|
75
+ return v if v.pos_id == pos_id
76
+ end
77
+ nil
78
+ end
79
+ end
80
+ end
81
+
82
+ if defined?(Rails)
83
+ require "payu/helpers"
84
+
85
+ ActionView::Base.send(:include, Payu::Helpers)
86
+ ActionController::Base.send(:include, Payu::Helpers)
87
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ module Payu
4
+ ERRORS = {
5
+ 100 => 'Missing parameter pos_id',
6
+ 101 => 'Missing parameter session_id',
7
+ 102 => 'Missing parameter ts',
8
+ 103 => 'Missing parameter sig',
9
+ 104 => 'Missing parameter desc',
10
+ 105 => 'Missing parameter client_ip',
11
+ 106 => 'Missing parameter first_name',
12
+ 107 => 'Missing parameter last_name',
13
+ 108 => 'Missing parameter street',
14
+ 109 => 'Missing parameter city',
15
+ 110 => 'Missing parameter post_code',
16
+ 111 => 'Missing parameter amount',
17
+ 112 => 'Invalid bank account number',
18
+ 113 => 'Missing parameter email',
19
+ 114 => 'Missing parameter phone',
20
+ 200 => 'Temporary error',
21
+ 201 => 'Temporary database error',
22
+ 202 => 'POS blocked',
23
+ 203 => 'Invalid pay_type value for provided pos_id',
24
+ 204 => 'Selected payment method (pay_type value) is temporary unavailable for provided pos_id',
25
+ 205 => 'Provided amount is smaller than minimal amount',
26
+ 206 => 'Provided amount is larger than maximal amount',
27
+ 207 => 'Transaction limit exceeded',
28
+ 208 => 'ExpressPayment has not been activated',
29
+ 209 => 'Invalid pos_id or pos_auth_key',
30
+ 500 => 'Transaction does not exist',
31
+ 501 => 'Unauthorized for this transaction',
32
+ 502 => 'Transaction already started',
33
+ 503 => 'Transaction already authorized',
34
+ 504 => 'Transaction already canceled',
35
+ 505 => 'Confirm order already sent',
36
+ 506 => 'Transaction already confirmed',
37
+ 507 => 'Error while returning funds to client',
38
+ 599 => 'Invalid transaction status. Please contact PayU support',
39
+ 999 => 'Critical error'
40
+ }
41
+
42
+ class << self
43
+ def get_error_description(code)
44
+ return ERRORS[code.to_i]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+ require 'pp'
5
+
6
+ module Payu
7
+ class Gateway
8
+ attr_reader :encoding, :key1, :key2, :pos_id
9
+
10
+ def initialize(options = {})
11
+ @encoding = options[:encoding]
12
+ @key1 = options[:key1]
13
+ @key2 = options[:key2]
14
+ @pos_id = options[:pos_id]
15
+ end
16
+
17
+ def get(session_id)
18
+ send_request("/paygw/#{encoding}/Payment/get/txt", session_id)
19
+ end
20
+
21
+ def confirm(session_id)
22
+ send_request("/paygw/#{encoding}/Payment/confirm/txt", session_id)
23
+ end
24
+
25
+ def cancel(session_id)
26
+ send_request("/paygw/#{encoding}/Payment/cancel/txt", session_id)
27
+ end
28
+
29
+ private
30
+ def send_request(url, session_id)
31
+ data = prepare_data(session_id)
32
+ connection = Net::HTTP.new('www.platnosci.pl', 443)
33
+ connection.use_ssl = true
34
+
35
+ http_response = connection.start do |http|
36
+ post = Net::HTTP::Post.new(url)
37
+ post.set_form_data(data)
38
+ http.request(post)
39
+ end
40
+
41
+ if http_response.code == '200'
42
+ response = Response.parse(http_response.body)
43
+ verify!(response) if response.status == 'OK'
44
+
45
+ return response
46
+ else
47
+ raise RequestFailed
48
+ end
49
+ end
50
+
51
+ def prepare_data(session_id)
52
+ ts = (Time.now.to_f * 1000).to_i
53
+ sig = Signature.generate(pos_id, session_id, ts, key1)
54
+
55
+ {
56
+ 'pos_id' => pos_id,
57
+ 'session_id' => session_id,
58
+ 'ts' => ts,
59
+ 'sig' => sig
60
+ }
61
+ end
62
+
63
+ def verify!(response)
64
+ Signature.verify!(response.trans_sig,
65
+ response.trans_pos_id,
66
+ response.trans_session_id,
67
+ response.trans_order_id,
68
+ response.trans_status,
69
+ response.trans_amount,
70
+ response.trans_desc,
71
+ response.trans_ts,
72
+ key2
73
+ )
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ module Payu
4
+ module Helpers
5
+ def payu_hidden_fields(transaction)
6
+ html = ""
7
+
8
+ %w(pos_id pos_auth_key pay_type session_id amount amount_netto desc
9
+ order_id desc2 trsDesc first_name last_name street street_hn
10
+ street_an city post_code country email phone language client_ip
11
+ js payback_login sig ts
12
+ ).each do |field|
13
+ value = transaction.send(field)
14
+ html << hidden_field_tag(field, value) unless value.blank?
15
+ end
16
+
17
+ html.html_safe
18
+ end
19
+
20
+ def payu_verify_params(params)
21
+ pos_id = params['pos_id']
22
+ pos = Payu[pos_id]
23
+
24
+ Signature.verify!(
25
+ params['sig'],
26
+ params['pos_id'],
27
+ params['session_id'],
28
+ params['ts'],
29
+ pos.key2
30
+ )
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ostruct'
4
+ require 'payu/gateway'
5
+
6
+ module Payu
7
+ class Pos
8
+ TYPES = ['default', 'sms']
9
+
10
+ attr_reader :pos_id, :pos_auth_key, :key1, :key2, :type, :encoding, :variant
11
+
12
+ # Creates new Pos instance
13
+ # @param [Hash] options options hash
14
+ # @return [Object] Pos object
15
+ def initialize(options)
16
+ @pos_id = options[:pos_id].to_i
17
+ @pos_auth_key = options[:pos_auth_key]
18
+ @key1 = options[:key1]
19
+ @key2 = options[:key2]
20
+ @variant = options[:variant] || 'default'
21
+ @encoding = options[:encoding] || 'UTF'
22
+ @test_payment = options[:test_payment] || false
23
+ @add_signature = options[:add_signature] || false
24
+
25
+ raise PosInvalid.new('Missing pos_id parameter') if pos_id.nil? || pos_id == 0
26
+ raise PosInvalid.new('Missing pos_auth_key parameter') if pos_auth_key.nil? || pos_auth_key == ''
27
+ raise PosInvalid.new('Missing key1 parameter') if key1.nil? || key1 == ''
28
+ raise PosInvalid.new('Missing key2 parameter') if key2.nil? || key2 == ''
29
+ raise PosInvalid.new("Invalid variant parameter, expected one of these: #{TYPES.join(', ')}") unless TYPES.include?(variant)
30
+ raise PosInvalid.new("Invalid encoding parameter, expected one of these: #{ENCODINGS.join(', ')}") unless ENCODINGS.include?(encoding)
31
+ end
32
+
33
+ # Creates new transaction
34
+ # @param [Hash] options options hash for new transaction
35
+ # @return [Object] Transaction object
36
+ def new_transaction(options = {})
37
+ options = options.dup
38
+
39
+ options.merge!({
40
+ :pos_id => @pos_id,
41
+ :pos_auth_key => @pos_auth_key,
42
+ :key1 => @key1,
43
+ :add_signature => add_signature?,
44
+ :encoding => encoding,
45
+ :variant => variant
46
+ })
47
+
48
+ if !options.has_key?(:pay_type)
49
+ options[:pay_type] = test_payment? ? 't' : nil
50
+ end
51
+
52
+ Transaction.new(options)
53
+ end
54
+
55
+ def get(session_id)
56
+ get_gateway.get(session_id)
57
+ end
58
+
59
+ def confirm(session_id)
60
+ get_gateway.confirm(session_id)
61
+ end
62
+
63
+ def cancel(session_id)
64
+ get_gateway.cancel(session_id)
65
+ end
66
+
67
+ def add_signature?
68
+ @add_signature
69
+ end
70
+
71
+ private
72
+ def get_gateway
73
+ Gateway.new(:encoding => encoding, :key1 => key1, :key2 => key2, :pos_id => pos_id)
74
+ end
75
+
76
+ def test_payment?
77
+ @test_payment && @variant == 'default'
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ module Payu
4
+ class Response < OpenStruct
5
+ PATTERN = /^(\w+):(?:[ ])?(.*)$/
6
+
7
+ def self.parse(body)
8
+ temp = body.gsub("\r", "")
9
+ data = temp.scan(PATTERN)
10
+
11
+ new(data)
12
+ end
13
+
14
+ def completed?
15
+ trans_status.to_i == 99
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'digest'
4
+
5
+ module Payu
6
+ class SignatureInvalid < StandardError
7
+ end
8
+
9
+ class Signature
10
+ def self.generate(*params)
11
+ Digest::MD5.hexdigest(params.join)
12
+ end
13
+
14
+ def self.verify!(expected, *params)
15
+ raise SignatureInvalid if expected != generate(params)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ module Payu
4
+ class Transaction
5
+ attr_accessor :pos_id, :pos_auth_key, :pay_type, :session_id, :amount, :amount_netto, :desc,
6
+ :order_id, :desc2, :trsDesc, :first_name, :last_name, :street, :street_hn,
7
+ :street_an, :city, :post_code, :country, :email, :phone, :language, :client_ip,
8
+ :js, :payback_login, :sig, :ts, :key1, :add_signature, :variant, :encoding
9
+
10
+ def initialize(options = {})
11
+ options[:session_id] ||= generate_timestamp
12
+
13
+ options.each do |name, value|
14
+ send("#{name.to_s}=", value)
15
+ end
16
+
17
+ validate!
18
+
19
+ if options[:add_signature]
20
+ self.ts = generate_timestamp
21
+ self.sig = generate_signature
22
+ end
23
+
24
+ if variant == 'sms'
25
+ self.amount_netto = amount
26
+ self.amount = nil
27
+ end
28
+ end
29
+
30
+ def new_url
31
+ if variant == 'sms'
32
+ return "https://www.platnosci.pl/paygw/#{encoding}/NewSMS"
33
+ else
34
+ return "https://www.platnosci.pl/paygw/#{encoding}/NewPayment"
35
+ end
36
+ end
37
+
38
+ private
39
+ def generate_timestamp
40
+ (Time.now.to_f * 100).to_i
41
+ end
42
+
43
+ def generate_signature
44
+ Signature.generate(
45
+ pos_id,
46
+ pay_type,
47
+ session_id,
48
+ pos_auth_key,
49
+ amount,
50
+ desc,
51
+ desc2,
52
+ trsDesc,
53
+ order_id,
54
+ first_name,
55
+ last_name,
56
+ payback_login,
57
+ street,
58
+ street_hn,
59
+ street_an,
60
+ city,
61
+ post_code,
62
+ country,
63
+ email,
64
+ phone,
65
+ language,
66
+ client_ip,
67
+ ts,
68
+ key1
69
+ )
70
+ end
71
+
72
+ private
73
+ def validate!
74
+ invalid_attributes = []
75
+
76
+ [:pos_id, :pos_auth_key, :session_id, :desc, :first_name, :last_name, :email, :client_ip, :amount].each do |name|
77
+ invalid_attributes << name if attribute_empty?(name)
78
+ end
79
+
80
+ if attribute_empty?(:amount)
81
+ invalid_attributes << :amount
82
+ end
83
+
84
+ if invalid_attributes.any?
85
+ raise TransactionInvalid.new("Attributes required: #{invalid_attributes.join(', ')}")
86
+ end
87
+ end
88
+
89
+ def attribute_empty?(name)
90
+ value = send(name)
91
+ value.nil? || value.respond_to?(:empty?) && value.empty?
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Payu
4
+ VERSION = "0.7.0"
5
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+ require "payu/version"
5
+ require "date"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "payu"
9
+ s.version = Payu::VERSION
10
+ s.date = Date.today
11
+ s.summary = "Simple integration with PayU gateway"
12
+ s.description = "Simple integration with PayU gateway"
13
+ s.author = "Michał Młoźniak"
14
+ s.email = "michal.mlozniak@visuality.pl"
15
+ s.files = `git ls-files`.split("\n")
16
+ s.homepage = "http://github.com/ronin/payu"
17
+
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Payu::Response" do
6
+ @key1 = '3d91f185cacad7c1d830d1472dfaacc5'
7
+ @key2 = 'a747e4b3e49e17459a8a402518d36022'
8
+
9
+ it "should parse payment/get response" do
10
+ body = <<-EOF
11
+ status:OK
12
+ trans_id:7
13
+ trans_pos_id:1
14
+ trans_session_id:417419
15
+ trans_order_id:
16
+ trans_amount:200
17
+ trans_status:5
18
+ trans_pay_type:t
19
+ trans_pay_gw_name:pt
20
+ trans_desc:Wpłata dla test@test.pl
21
+ trans_desc2:
22
+ trans_create:2004-08-2310:39:52
23
+ trans_init:2004-08-3113:42:43
24
+ trans_sent:2004-08-3113:48:13
25
+ trans_recv:
26
+ trans_cancel:
27
+ trans_auth_fraud:0
28
+ trans_ts:1094205761232
29
+ trans_sig:b6d68525f724a6d69fb1260874924759
30
+ EOF
31
+
32
+ response = Payu::Response.parse(body)
33
+
34
+ response.status.should == 'OK'
35
+ response.trans_id.should == '7'
36
+ response.trans_pos_id.should == '1'
37
+ response.trans_session_id.should == '417419'
38
+ response.trans_order_id.should == ''
39
+ response.trans_amount.should == '200'
40
+ response.trans_status.should == '5'
41
+ response.trans_pay_type.should == 't'
42
+ response.trans_pay_gw_name.should == 'pt'
43
+ response.trans_desc.should == 'Wpłata dla test@test.pl'
44
+ response.trans_desc2.should == ''
45
+ response.trans_create.should == '2004-08-2310:39:52'
46
+ response.trans_init.should == '2004-08-3113:42:43'
47
+ response.trans_sent.should == '2004-08-3113:48:13'
48
+ response.trans_recv.should == ''
49
+ response.trans_cancel.should == ''
50
+ response.trans_auth_fraud.should == '0'
51
+ response.trans_ts.should == '1094205761232'
52
+ response.trans_sig.should == 'b6d68525f724a6d69fb1260874924759'
53
+ end
54
+
55
+ it "should parse error response" do
56
+ body = <<-EOF
57
+ status: ERROR
58
+ error_nr: 103
59
+ error_message: Kod błędu: 103
60
+ EOF
61
+
62
+ response = Payu::Response.parse(body)
63
+
64
+ response.status.should == 'ERROR'
65
+ response.error_nr.should == '103'
66
+ response.error_message.should == 'Kod błędu: 103'
67
+ end
68
+
69
+ it "should respond to completed?" do
70
+ response = Payu::Response.new(:trans_status => '99')
71
+ response.should be_completed
72
+ end
73
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Signature' do
6
+ before do
7
+ @expected_hash = 'e80b5017098950fc58aad83c8c14978e' # hash for 'abcdef'
8
+ end
9
+
10
+ it 'should generate MD5 hash for passed value' do
11
+ Payu::Signature.generate('abcdef').should == @expected_hash
12
+ end
13
+
14
+ it 'should concatenate multiple values' do
15
+ Payu::Signature.generate('abc', 'def').should == @expected_hash
16
+ Payu::Signature.generate('ab' ,'cd', 'ef').should == @expected_hash
17
+ Payu::Signature.generate(%w{a b c d e f}).should == @expected_hash
18
+
19
+ Payu::Signature.generate('def', 'abc').should_not == @expected_hash
20
+ end
21
+
22
+ it 'should skip empty strings' do
23
+ Payu::Signature.generate('abc', '', 'def').should == @expected_hash
24
+ end
25
+
26
+ it 'should skip nils' do
27
+ Payu::Signature.generate('abc', nil, 'def').should == @expected_hash
28
+ end
29
+
30
+ it 'should do verify signature' do
31
+ lambda do
32
+ Payu::Signature.verify!(@expected_hash, 'abc', 'def')
33
+ end.should_not raise_exception
34
+ end
35
+
36
+ it 'should raise exception when signature is not valid' do
37
+ lambda do
38
+ Payu::Signature.verify!(@expected_hash, 'def', 'abc')
39
+ end.should raise_exception(Payu::SignatureInvalid)
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ require 'bundler/setup'
2
+ require 'payu'
@@ -0,0 +1,136 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Transaction' do
6
+ before do
7
+ @pos1 = Payu::Pos.new(
8
+ :pos_id => 1,
9
+ :pos_auth_key => 'abcde',
10
+ :key1 => '3d91f185cacad7c1d830d1472dfaacc5',
11
+ :key2 => 'a747e4b3e49e17459a8a402518d36022'
12
+ )
13
+
14
+ @pos2 = Payu::Pos.new(
15
+ :pos_id => 1,
16
+ :pos_auth_key => 'abcde',
17
+ :key1 => '3d91f185cacad7c1d830d1472dfaacc5',
18
+ :key2 => 'a747e4b3e49e17459a8a402518d36022',
19
+ :add_signature => true
20
+ )
21
+
22
+ @sms_pos = Payu::Pos.new(
23
+ :pos_id => 1,
24
+ :pos_auth_key => 'abcde',
25
+ :key1 => '3d91f185cacad7c1d830d1472dfaacc5',
26
+ :key2 => 'a747e4b3e49e17459a8a402518d36022',
27
+ :add_signature => true,
28
+ :variant => 'sms'
29
+ )
30
+ end
31
+
32
+ it "should raise exception when required attributes are empty" do
33
+ lambda do
34
+ @pos1.new_transaction
35
+ end.should raise_exception(Payu::TransactionInvalid)
36
+ end
37
+
38
+ it 'should generate session_id when empty' do
39
+ transaction = @pos1.new_transaction(
40
+ :amount => 100,
41
+ :desc => 'Description',
42
+ :first_name => 'John',
43
+ :last_name => 'Doe',
44
+ :email => 'john.doe@example.org',
45
+ :client_ip => '127.0.0.1'
46
+ )
47
+
48
+ transaction.session_id.should_not be_nil
49
+ transaction.session_id.class.should == Fixnum
50
+ end
51
+
52
+ it 'should not overwrite passed session_id' do
53
+ transaction = @pos1.new_transaction(
54
+ :session_id => 123,
55
+ :amount => 100,
56
+ :desc => 'Description',
57
+ :first_name => 'John',
58
+ :last_name => 'Doe',
59
+ :email => 'john.doe@example.org',
60
+ :client_ip => '127.0.0.1'
61
+ )
62
+
63
+ transaction.session_id.should == 123
64
+ end
65
+
66
+ it 'should generate signature' do
67
+ transaction = @pos2.new_transaction(
68
+ :session_id => '123',
69
+ :amount => 100,
70
+ :desc => 'Description',
71
+ :first_name => 'John',
72
+ :last_name => 'Doe',
73
+ :email => 'john.doe@example.org',
74
+ :client_ip => '127.0.0.1'
75
+ )
76
+
77
+ transaction.ts.should_not be_nil
78
+ transaction.ts.class.should == Fixnum
79
+
80
+ signature_keys = [1, '123', 'abcde', 100, 'Description', 'John', 'Doe', 'john.doe@example.org', '127.0.0.1', transaction.ts, '3d91f185cacad7c1d830d1472dfaacc5']
81
+ expected_signature = Digest::MD5.hexdigest(signature_keys.join)
82
+
83
+ transaction.sig.should == expected_signature
84
+ end
85
+
86
+ it 'should generate signature from all keys' do
87
+ transaction = @pos2.new_transaction(
88
+ :pos_auth_key => 'abcde',
89
+ :pay_type => 't',
90
+ :amount => 15000,
91
+ :desc => 'Testowa',
92
+ :desc2 => 'Szczegółowy opis',
93
+ :trsDesc => 'Dodatkowy opis dla banku',
94
+ :order_id => 69,
95
+ :first_name => 'Jan',
96
+ :last_name => 'Kowalski',
97
+ :payback_login => 'jankowalski',
98
+ :street => 'Warszawska',
99
+ :street_hn => '21',
100
+ :street_an => '18',
101
+ :city => 'Szczecin',
102
+ :post_code => '01-259',
103
+ :country => 'PL',
104
+ :email => 'jan.kowalski@example.org',
105
+ :phone => '505-606-100',
106
+ :language => 'pl',
107
+ :client_ip => '192.168.1.1'
108
+ )
109
+
110
+ signature_keys = [
111
+ 1, 't', transaction.session_id, 'abcde', 15000, 'Testowa',
112
+ 'Szczegółowy opis', 'Dodatkowy opis dla banku', 69, 'Jan', 'Kowalski',
113
+ 'jankowalski', 'Warszawska', '21', '18', 'Szczecin', '01-259', 'PL',
114
+ 'jan.kowalski@example.org', '505-606-100', 'pl', '192.168.1.1',
115
+ transaction.ts, '3d91f185cacad7c1d830d1472dfaacc5'
116
+ ]
117
+ expected_signature = Digest::MD5.hexdigest(signature_keys.join)
118
+
119
+ transaction.sig.should == expected_signature
120
+ end
121
+
122
+ it "should set amount_netto attribute for sms variant" do
123
+ transaction = @sms_pos.new_transaction(
124
+ :session_id => '123',
125
+ :amount => 100,
126
+ :desc => 'Description',
127
+ :first_name => 'John',
128
+ :last_name => 'Doe',
129
+ :email => 'john.doe@example.org',
130
+ :client_ip => '127.0.0.1'
131
+ )
132
+
133
+ transaction.amount_netto.should == 100
134
+ transaction.amount.should be_nil
135
+ end
136
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: payu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Michał Młoźniak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-15 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Simple integration with PayU gateway
14
+ email: michal.mlozniak@visuality.pl
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - .gitignore
20
+ - .rspec
21
+ - Gemfile
22
+ - LICENSE
23
+ - README.md
24
+ - Rakefile
25
+ - init.rb
26
+ - lib/payu.rb
27
+ - lib/payu/errors.rb
28
+ - lib/payu/gateway.rb
29
+ - lib/payu/helpers.rb
30
+ - lib/payu/pos.rb
31
+ - lib/payu/response.rb
32
+ - lib/payu/signature.rb
33
+ - lib/payu/transaction.rb
34
+ - lib/payu/version.rb
35
+ - payu.gemspec
36
+ - spec/response_spec.rb
37
+ - spec/signature_spec.rb
38
+ - spec/spec_helper.rb
39
+ - spec/transaction_spec.rb
40
+ homepage: http://github.com/ronin/payu
41
+ licenses: []
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.0.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Simple integration with PayU gateway
63
+ test_files:
64
+ - spec/response_spec.rb
65
+ - spec/signature_spec.rb
66
+ - spec/spec_helper.rb
67
+ - spec/transaction_spec.rb