ipizza 0.4.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.
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