ipizza 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +18 -0
  5. data/README.markdown +101 -0
  6. data/Rakefile +25 -0
  7. data/VERSION +1 -0
  8. data/autotest/discover.rb +1 -0
  9. data/init.rb +1 -0
  10. data/ipizza.gemspec +103 -0
  11. data/lib/ipizza.rb +13 -0
  12. data/lib/ipizza/authentication_request.rb +4 -0
  13. data/lib/ipizza/authentication_response.rb +24 -0
  14. data/lib/ipizza/config.rb +54 -0
  15. data/lib/ipizza/payment.rb +15 -0
  16. data/lib/ipizza/payment_request.rb +4 -0
  17. data/lib/ipizza/payment_response.rb +23 -0
  18. data/lib/ipizza/provider/nordea.rb +77 -0
  19. data/lib/ipizza/provider/nordea/authentication_request.rb +29 -0
  20. data/lib/ipizza/provider/nordea/authentication_response.rb +38 -0
  21. data/lib/ipizza/provider/nordea/payment_request.rb +33 -0
  22. data/lib/ipizza/provider/nordea/payment_response.rb +43 -0
  23. data/lib/ipizza/provider/sampo.rb +49 -0
  24. data/lib/ipizza/provider/seb.rb +81 -0
  25. data/lib/ipizza/provider/swedbank.rb +69 -0
  26. data/lib/ipizza/request.rb +17 -0
  27. data/lib/ipizza/response.rb +24 -0
  28. data/lib/ipizza/util.rb +74 -0
  29. data/spec/certificates/bank.crt +21 -0
  30. data/spec/certificates/bank.csr +17 -0
  31. data/spec/certificates/bank.key +27 -0
  32. data/spec/certificates/bank.pub +1 -0
  33. data/spec/certificates/dealer.key +30 -0
  34. data/spec/certificates/dealer.pub +1 -0
  35. data/spec/certificates/nordea_test_priv +1 -0
  36. data/spec/certificates/seb_test_priv.pem +16 -0
  37. data/spec/certificates/seb_test_pub.crt +20 -0
  38. data/spec/certificates/seb_test_pub.pem +20 -0
  39. data/spec/config/config.yml +38 -0
  40. data/spec/config/plain_config.yml +13 -0
  41. data/spec/ipizza/authentication_response_spec.rb +23 -0
  42. data/spec/ipizza/config_spec.rb +58 -0
  43. data/spec/ipizza/provider/nordea/authentication_response_spec.rb +41 -0
  44. data/spec/ipizza/provider/nordea/payment_request_spec.rb +4 -0
  45. data/spec/ipizza/provider/nordea/payment_response_spec.rb +4 -0
  46. data/spec/ipizza/provider/nordea_spec.rb +29 -0
  47. data/spec/ipizza/provider/sampo_spec.rb +4 -0
  48. data/spec/ipizza/provider/seb_spec.rb +46 -0
  49. data/spec/ipizza/provider/swedbank_spec.rb +46 -0
  50. data/spec/ipizza/util_spec.rb +21 -0
  51. data/spec/spec_helper.rb +9 -0
  52. data/spec/support/pizza.rb +8 -0
  53. metadata +129 -0
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development, :test do
4
+ gem 'rspec', '2.2.0'
5
+ end
@@ -0,0 +1,18 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ rspec (2.2.0)
6
+ rspec-core (~> 2.2)
7
+ rspec-expectations (~> 2.2)
8
+ rspec-mocks (~> 2.2)
9
+ rspec-core (2.2.1)
10
+ rspec-expectations (2.2.0)
11
+ diff-lcs (~> 1.1.2)
12
+ rspec-mocks (2.2.0)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ rspec (= 2.2.0)
@@ -0,0 +1,101 @@
1
+ Implements iPizza protocol to communicate with Estonian Banks.
2
+
3
+ Usage
4
+ =====
5
+
6
+ Add gem dependency in your `Gemfile` and install the gem:
7
+
8
+ gem 'ipizza', :git => 'git://github.com/priithaamer/ipizza.git'
9
+
10
+ Configuration
11
+ =============
12
+
13
+ Configuration can be made in two different ways, using `Ipizza::Config.configure` block or loading configuration properties from YAML file.
14
+
15
+ Loading from YAML file:
16
+
17
+ Ipizza::Config.load_from_file('config.yml')
18
+
19
+ Configuration values should be set in YAML file in **provider.attribute_value** format. See example YAML file below in "Configuration parameters" section.
20
+
21
+ At any time, configuration can be modified with `Ipizza::Config.configure` block:
22
+
23
+ Ipizza::Config.configure do |c|
24
+ c.swedbank_service_url = 'http://foo.bar/swedbank'
25
+ end
26
+
27
+ Configuration parameters
28
+ ------------------------
29
+
30
+ swedbank:
31
+ service_url: http://foo.bar/swedbank
32
+ return_url: http://mycompany.com/store
33
+ cancel_url: http://mycompany.com/cancel
34
+
35
+ # Your private key file path. Can be specified relatively
36
+ # to YAML file
37
+ file_key: ./certificates/my_private.key
38
+
39
+ # If your private key is protected with password,
40
+ # provide it here
41
+ key_secret: private_key_password
42
+
43
+ # Path to bank's public key file. Can be specified
44
+ # relatively to YAML file
45
+ file_cert: ./certificates/bank_public.crt
46
+ snd_id: dealer
47
+ encoding: UTF-8
48
+
49
+ Payment requests
50
+ ----------------
51
+
52
+ ### Building request
53
+
54
+ payment = Ipizza::Payment.new(
55
+ :stamp => 1, :amount => '123.34', :refnum => 1,
56
+ :message => 'Payment message', :currency => 'EUR'
57
+ )
58
+ request = Ipizza::Provider::Swedbank.new.payment_request(@payment)
59
+
60
+ Authentication requests
61
+ -----------------------
62
+
63
+ ### Building request
64
+
65
+ request = Ipizza::Provider::Swedbank.new.authentication_request
66
+
67
+ ### Validating response
68
+
69
+ response = Ipizza::Provider::Swedbank.new.authentication_response({'VK_PARAM_1' => 'VALUE 1', ...})
70
+ response.valid?
71
+
72
+ Gateway specifications
73
+ ======================
74
+
75
+ This library currently works with four Estonian Banks. Here are their respective interface specifications:
76
+
77
+ * [Swedbank](https://www.swedbank.ee/static/pdf/business/d2d/paymentcollection/info_banklink_techspec_est.pdf)
78
+ * [SEB](http://www.seb.ee/index/130212050201)
79
+ * [Sampo](http://www.sampopank.ee/et/14732.html)
80
+ * [Nordea](http://www.nordea.ee/Teenused+ärikliendile/E-lahendused/787802.html)
81
+
82
+ Helpful links
83
+ =============
84
+
85
+ * [Repository](http://github.com/priithaamer/ipizza)
86
+ * [Issue tracker](http://github.com/priithaamer/ipizza/issues)
87
+
88
+ Todo
89
+ ====
90
+
91
+ * Raise reasonable exception during configuration when certificates or keys cannot be loaded
92
+ * Write ipizza-rails module:
93
+ * Proper Rails initialization
94
+ * Write Rails controller and model generator
95
+ * Rails helper to generate iPizza request forms
96
+
97
+ Authors
98
+ =======
99
+
100
+ * Priit Haamer
101
+ * Tarmo Talu (Thanks for the 7-3-1 algorithm)
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gemspec|
10
+ gemspec.name = 'ipizza'
11
+
12
+ gemspec.summary = 'Implements iPizza protocol to communicate with Estonian Banks'
13
+ gemspec.description = <<-DESC
14
+ Simplifies generating payment requests and parsing responses from banks when using iPizza protocol.
15
+ DESC
16
+
17
+ gemspec.email = 'priit@fraktal.ee'
18
+ gemspec.homepage = 'http://github.com/priithaamer/ipizza'
19
+ gemspec.authors = ['Priit Haamer']
20
+ end
21
+ rescue LoadError
22
+ puts 'Jeweler not available. Install it with: gem install jeweler'
23
+ end
24
+
25
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'ipizza'
@@ -0,0 +1,103 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ipizza}
8
+ s.version = "0.4.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Priit Haamer"]
12
+ s.date = %q{2010-12-04}
13
+ s.description = %q{ Simplifies generating payment requests and parsing responses from banks when using iPizza protocol.
14
+ }
15
+ s.email = %q{priit@fraktal.ee}
16
+ s.extra_rdoc_files = [
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "README.markdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "autotest/discover.rb",
28
+ "init.rb",
29
+ "ipizza.gemspec",
30
+ "lib/ipizza.rb",
31
+ "lib/ipizza/authentication_request.rb",
32
+ "lib/ipizza/authentication_response.rb",
33
+ "lib/ipizza/config.rb",
34
+ "lib/ipizza/payment.rb",
35
+ "lib/ipizza/payment_request.rb",
36
+ "lib/ipizza/payment_response.rb",
37
+ "lib/ipizza/provider/nordea.rb",
38
+ "lib/ipizza/provider/nordea/authentication_request.rb",
39
+ "lib/ipizza/provider/nordea/authentication_response.rb",
40
+ "lib/ipizza/provider/nordea/payment_request.rb",
41
+ "lib/ipizza/provider/nordea/payment_response.rb",
42
+ "lib/ipizza/provider/sampo.rb",
43
+ "lib/ipizza/provider/seb.rb",
44
+ "lib/ipizza/provider/swedbank.rb",
45
+ "lib/ipizza/request.rb",
46
+ "lib/ipizza/response.rb",
47
+ "lib/ipizza/util.rb",
48
+ "spec/certificates/bank.crt",
49
+ "spec/certificates/bank.csr",
50
+ "spec/certificates/bank.key",
51
+ "spec/certificates/bank.pub",
52
+ "spec/certificates/dealer.key",
53
+ "spec/certificates/dealer.pub",
54
+ "spec/certificates/nordea_test_priv",
55
+ "spec/certificates/seb_test_priv.pem",
56
+ "spec/certificates/seb_test_pub.crt",
57
+ "spec/certificates/seb_test_pub.pem",
58
+ "spec/config/config.yml",
59
+ "spec/config/plain_config.yml",
60
+ "spec/ipizza/authentication_response_spec.rb",
61
+ "spec/ipizza/config_spec.rb",
62
+ "spec/ipizza/provider/nordea/authentication_response_spec.rb",
63
+ "spec/ipizza/provider/nordea/payment_request_spec.rb",
64
+ "spec/ipizza/provider/nordea/payment_response_spec.rb",
65
+ "spec/ipizza/provider/nordea_spec.rb",
66
+ "spec/ipizza/provider/sampo_spec.rb",
67
+ "spec/ipizza/provider/seb_spec.rb",
68
+ "spec/ipizza/provider/swedbank_spec.rb",
69
+ "spec/ipizza/util_spec.rb",
70
+ "spec/spec_helper.rb",
71
+ "spec/support/pizza.rb"
72
+ ]
73
+ s.homepage = %q{http://github.com/priithaamer/ipizza}
74
+ s.rdoc_options = ["--charset=UTF-8"]
75
+ s.require_paths = ["lib"]
76
+ s.rubygems_version = %q{1.3.7}
77
+ s.summary = %q{Implements iPizza protocol to communicate with Estonian Banks}
78
+ s.test_files = [
79
+ "spec/ipizza/authentication_response_spec.rb",
80
+ "spec/ipizza/config_spec.rb",
81
+ "spec/ipizza/provider/nordea/authentication_response_spec.rb",
82
+ "spec/ipizza/provider/nordea/payment_request_spec.rb",
83
+ "spec/ipizza/provider/nordea/payment_response_spec.rb",
84
+ "spec/ipizza/provider/nordea_spec.rb",
85
+ "spec/ipizza/provider/sampo_spec.rb",
86
+ "spec/ipizza/provider/seb_spec.rb",
87
+ "spec/ipizza/provider/swedbank_spec.rb",
88
+ "spec/ipizza/util_spec.rb",
89
+ "spec/spec_helper.rb",
90
+ "spec/support/pizza.rb"
91
+ ]
92
+
93
+ if s.respond_to? :specification_version then
94
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
95
+ s.specification_version = 3
96
+
97
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
98
+ else
99
+ end
100
+ else
101
+ end
102
+ end
103
+
@@ -0,0 +1,13 @@
1
+ require 'ipizza/config'
2
+ require 'ipizza/util'
3
+ require 'ipizza/payment'
4
+ require 'ipizza/request'
5
+ require 'ipizza/response'
6
+ require 'ipizza/payment_request'
7
+ require 'ipizza/payment_response'
8
+ require 'ipizza/authentication_request'
9
+ require 'ipizza/authentication_response'
10
+ require 'ipizza/provider/swedbank'
11
+ require 'ipizza/provider/seb'
12
+ require 'ipizza/provider/sampo'
13
+ require 'ipizza/provider/nordea'
@@ -0,0 +1,4 @@
1
+ module Ipizza
2
+ class AuthenticationRequest < Ipizza::Request
3
+ end
4
+ end
@@ -0,0 +1,24 @@
1
+ module Ipizza
2
+ class AuthenticationResponse < Ipizza::Response
3
+
4
+ def success?
5
+ return ['3002'].include?(@params['VK_SERVICE'])
6
+ end
7
+
8
+ def valid?
9
+ return @valid
10
+ end
11
+
12
+ def authentication_info
13
+ @params['VK_INFO']
14
+ end
15
+
16
+ def info_social_security_id
17
+ /ISIK:([^;.]+)/i.match(@params['VK_INFO'])[1] if @params['VK_INFO']
18
+ end
19
+
20
+ def info_name
21
+ /NIMI:([^;.]+)/.match(@params['VK_INFO'])[1] if @params['VK_INFO']
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'yaml'
2
+
3
+ module Ipizza
4
+ class Config
5
+ class << self
6
+
7
+ def load_from_file(yaml_path)
8
+ config = YAML::load_file(yaml_path)
9
+
10
+ config.each do |bank, params|
11
+ params.each do |param, value|
12
+ value = normalize_file_location(yaml_path, value) if /^file_(cert|key)/ =~ param
13
+
14
+ begin
15
+ self.send(:"#{bank}_#{param}=", value)
16
+ rescue NoMethodError; end
17
+ end
18
+ end
19
+ end
20
+
21
+ def configure
22
+ yield self
23
+ end
24
+
25
+ def method_missing(m, *args)
26
+ if /^(swedbank|seb|sampo|nordea)_(.*)=$/ =~ m.to_s
27
+ clz = Ipizza::Provider.const_get($1.capitalize)
28
+ if clz.respond_to?(:"#{$2}=")
29
+ return clz.send(:"#{$2}=", *args)
30
+ end
31
+ end
32
+
33
+ super
34
+ end
35
+
36
+ private
37
+
38
+ def normalize_file_location(yaml_path, file_path)
39
+ if File.exists?(file_path)
40
+ file_path
41
+ else
42
+ file_path = File.expand_path(File.join(File.dirname(yaml_path), file_path))
43
+ end
44
+
45
+ if File.exists?(file_path)
46
+ file_path
47
+ else
48
+ raise "Could not load certificate from file '#{file_path}'"
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ module Ipizza
2
+ class Payment
3
+
4
+ attr_accessor :stamp, :amount, :currency, :refnum, :receiver_account, :receiver_name, :sender_account, :sender_name, :message, :transaction_id
5
+
6
+ def initialize(attribs = {})
7
+ attribs.each do |key, value|
8
+ if self.respond_to?("#{key.to_s}=".to_sym)
9
+ self.send("#{key.to_s}=".to_sym, value)
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module Ipizza
2
+ class PaymentRequest < Ipizza::Request
3
+ end
4
+ end
@@ -0,0 +1,23 @@
1
+ class Ipizza::PaymentResponse < Ipizza::Response
2
+
3
+ def success?
4
+ return ['1101'].include?(@params['VK_SERVICE'])
5
+ end
6
+
7
+ def valid?
8
+ return @valid
9
+ end
10
+
11
+ def automatic_message?
12
+ @params['VK_AUTO'] and @params['VK_AUTO'] == 'Y'
13
+ end
14
+
15
+ def payment_info
16
+ @payment_info ||= Ipizza::Payment.new(
17
+ :stamp => @params['VK_STAMP'], :amount => @params['VK_AMOUNT'], :currency => @params['VK_CURR'],
18
+ :refnum => @params['VK_REF'], :message => @params['VK_MSG'], :transaction_id => @params['VK_T_NO'],
19
+ :receiver_account => @params['VK_REC_ACC'], :receiver_name => @params['VK_REC_NAME'],
20
+ :sender_account => @params['VK_SND_ACC'], :sender_name => @params['VK_SND_NAME']
21
+ )
22
+ end
23
+ end
@@ -0,0 +1,77 @@
1
+ module Ipizza::Provider
2
+
3
+ # TODO: configure whether use sha-1 or md5 for signing and verification
4
+ class Nordea
5
+
6
+ require 'ipizza/provider/nordea/payment_request'
7
+ require 'ipizza/provider/nordea/payment_response'
8
+ require 'ipizza/provider/nordea/authentication_request'
9
+ require 'ipizza/provider/nordea/authentication_response'
10
+
11
+ class << self
12
+ attr_accessor :payments_service_url, :payments_return_url, :payments_reject_url, :payments_cancel_url
13
+ attr_accessor :payments_rcv_id, :payments_language
14
+ attr_accessor :auth_service_url, :auth_return_url, :auth_reject_url, :auth_cancel_url, :auth_language
15
+ attr_accessor :auth_rcv_id
16
+ attr_accessor :file_key, :rcv_account, :rcv_name, :confirm, :keyvers
17
+ end
18
+
19
+ def payment_request(payment, service = 1002)
20
+ req = Ipizza::Provider::Nordea::PaymentRequest.new
21
+ req.service_url = self.class.payments_service_url
22
+ req.params = {
23
+ 'VERSION' => '0003',
24
+ 'STAMP' => payment.stamp,
25
+ 'RCV_ID' => self.class.payments_rcv_id,
26
+ # 'RCV_ACCOUNT' => self.rcv_account,
27
+ # 'RCV_NAME' => self.rcv_name,
28
+ 'LANGUAGE' => self.class.payments_language,
29
+ 'AMOUNT' => sprintf('%.2f', payment.amount),
30
+ 'REF' => Ipizza::Util.sign_731(payment.refnum),
31
+ 'DATE' => 'EXPRESS',
32
+ 'MSG' => payment.message,
33
+ 'CONFIRM' => self.class.confirm,
34
+ 'CUR' => payment.currency,
35
+ 'KEYVERS' => self.class.keyvers,
36
+ 'REJECT' => self.class.payments_reject_url,
37
+ 'RETURN' => self.class.payments_return_url,
38
+ 'CANCEL' => self.class.payments_cancel_url
39
+ }
40
+
41
+ req.sign(self.class.file_key)
42
+ req
43
+ end
44
+
45
+ def payment_response(params)
46
+ response = Ipizza::Provider::Nordea::PaymentResponse.new(params)
47
+ response.verify(self.class.key)
48
+ return response
49
+ end
50
+
51
+ def authentication_request
52
+ req = Ipizza::Provider::Nordea::AuthenticationRequest.new
53
+ req.service_url = self.class.auth_service_url
54
+ req.params = {
55
+ 'ACTION_ID' => '701',
56
+ 'VERS' => '0002',
57
+ 'RCVID' => self.class.auth_rcv_id,
58
+ 'LANGCODE' => self.class.auth_language,
59
+ 'STAMP' => Time.now.strftime('%Y%m%d%H%M%S'),
60
+ 'IDTYPE' => '02',
61
+ 'KEYVERS' => self.class.keyvers,
62
+ 'RETLINK' => self.class.auth_return_url,
63
+ 'CANLINK' => self.class.auth_cancel_url,
64
+ 'REJLINK' => self.class.auth_reject_url,
65
+ 'ALG' => '01'
66
+ }
67
+ req.sign(self.class.file_key)
68
+ req
69
+ end
70
+
71
+ def authentication_response(params)
72
+ response = Ipizza::Provider::Nordea::AuthenticationResponse.new(params)
73
+ response.verify(self.class.file_key)
74
+ return response
75
+ end
76
+ end
77
+ end