seb_elink 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4754ac20b5f8fb96fa6f5da401ad7a743eb9f0bc
4
+ data.tar.gz: 35fd08333c236cc5e4b3fca833cf178880b81f4c
5
+ SHA512:
6
+ metadata.gz: e91e10a2ecfe7bc6a1dc525ca931f2111af3c38356c8866a8c82e58cfc3a5fdc0df4de4c99552b77a62fb0b540b746d2c31c558c41913027110714147f571185
7
+ data.tar.gz: 2bae5422c88af83cd9b16c07af01efa1501044b482b4167b41c896f024d16d7c49fb6b3d15eb0753e4f496b60856436df5b2b731acfd7ac46186cd2fad1cc7b5
@@ -0,0 +1,24 @@
1
+ version: 2
2
+ jobs:
3
+ build:
4
+ working_directory: ~/seb_elink
5
+ docker:
6
+ # ruby:2-node
7
+ - image: circleci/ruby@sha256:382aa3816e4a1447f56dfa5c70751a727eb276bb0d1617f1989e3ca28bb251f5
8
+
9
+ steps:
10
+ - checkout
11
+
12
+ - run: gem install bundler -v 1.16.0 --no-doc
13
+
14
+ - restore_cache:
15
+ key: seb_elink-v1-{{ checksum "Gemfile.lock" }}
16
+
17
+ - run: bundle install --path vendor/bundle
18
+
19
+ - save_cache:
20
+ key: seb_elink-v1-{{ checksum "Gemfile.lock" }}
21
+ paths:
22
+ - vendor/bundle
23
+
24
+ - run: bundle exec rspec
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at cto@creative.gs. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in seb_elink.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ seb_elink (0.9.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.2)
10
+ coveralls (0.7.2)
11
+ multi_json (~> 1.3)
12
+ rest-client (= 1.6.7)
13
+ simplecov (>= 0.7)
14
+ term-ansicolor (= 1.2.2)
15
+ thor (= 0.18.1)
16
+ diff-lcs (1.3)
17
+ docile (1.1.5)
18
+ json (2.1.0)
19
+ method_source (0.9.0)
20
+ mime-types (3.1)
21
+ mime-types-data (~> 3.2015)
22
+ mime-types-data (3.2016.0521)
23
+ multi_json (1.12.2)
24
+ pry (0.11.3)
25
+ coderay (~> 1.1.0)
26
+ method_source (~> 0.9.0)
27
+ rake (10.5.0)
28
+ rest-client (1.6.7)
29
+ mime-types (>= 1.16)
30
+ rspec (3.7.0)
31
+ rspec-core (~> 3.7.0)
32
+ rspec-expectations (~> 3.7.0)
33
+ rspec-mocks (~> 3.7.0)
34
+ rspec-core (3.7.0)
35
+ rspec-support (~> 3.7.0)
36
+ rspec-expectations (3.7.0)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.7.0)
39
+ rspec-mocks (3.7.0)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.7.0)
42
+ rspec-support (3.7.0)
43
+ simplecov (0.15.1)
44
+ docile (~> 1.1.0)
45
+ json (>= 1.8, < 3)
46
+ simplecov-html (~> 0.10.0)
47
+ simplecov-html (0.10.2)
48
+ term-ansicolor (1.2.2)
49
+ tins (~> 0.8)
50
+ thor (0.18.1)
51
+ tins (0.13.2)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ bundler (~> 1.16)
58
+ coveralls
59
+ pry (~> 0.11.3)
60
+ rake (~> 10.0)
61
+ rspec (~> 3.7)
62
+ seb_elink!
63
+ simplecov (~> 0.15.1)
64
+
65
+ BUNDLED WITH
66
+ 1.16.0
data/LICENSE.txt ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-clause "New" or "Revised" License
2
+
3
+ Copyright (c) 2017 Creative.gs . All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification,
6
+ are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice,
9
+ this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation and/or
13
+ other materials provided with the distribution.
14
+
15
+ 3. Neither the name of Creative.gs nor the names of its contributors
16
+ may be used to endorse or promote products derived from this software without
17
+ specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY Creative.gs AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,136 @@
1
+ [![Version](https://badge.fury.io/rb/seb_elink.svg)](https://badge.fury.io/rb/seb_elink)
2
+ [![Build](https://circleci.com/gh/CreativeGS/seb_elink/tree/master.svg?style=shield)](https://circleci.com/gh/CreativeGS/seb_elink/tree/master)
3
+ [![Coverage](https://coveralls.io/repos/github/CreativeGS/seb_elink/badge.svg?branch=master)](https://coveralls.io/github/CreativeGS/seb_elink?branch=master)
4
+
5
+ # SebElink
6
+ Lightweight Ruby wrapper for communicating with SEB.lv i-bank payment API.
7
+ Solves the cryptographic requirements for you.
8
+
9
+ ## Installation
10
+ Bundle or manually install the latest version of the gem:
11
+
12
+ ```ruby
13
+ gem 'seb_elink'
14
+ ```
15
+
16
+ ## Usage
17
+ Please note that for consistency in this gem all hash keys are constant-case __symbols__.
18
+
19
+ The gem has three elements represented as Ruby classes:
20
+
21
+ __1. SebElink::Gateway__
22
+ Think of this as the communication adapter. Initialize it with a base64-encoded private key string (the human-readable .pem format). You can store the instance in a constant to reduce processing overhead.
23
+
24
+ You can pass an optional options hash as the second argument that will specify default values for communications processed by that gateway instance. Useful for setting company-related data just once.
25
+
26
+ ```rb
27
+ SebElink::Gateway.new(
28
+ <privkey string>,
29
+ {
30
+ IB_SND_ID: "TESTCOMPANY",
31
+ IB_NAME: "Test Inc."
32
+ }
33
+ )
34
+ ```
35
+
36
+ Additionally, you can rewrite values used by the gem pertaining to SEB.lv i-bank, such as their public key, API uri etc. Here's a complete list:
37
+
38
+ ```rb
39
+ {
40
+ IB_VERSION: "001", # which API version to use
41
+ IBANK_CERT: "-----BEGIN CERTIFICATE-----..." # public key/cer of SEB.lv, changes rarely
42
+ IBANK_API_URI: "https://ibanka.seb.lv/ipc/epakindex.jsp" # where to POST users
43
+ }
44
+ ```
45
+
46
+ Instances of `SebElink::Gateway` have one method for public use:
47
+
48
+ ```rb
49
+ gateway.ibank_api_uri #=> uri for POSTing intial message to.
50
+ ```
51
+
52
+ __2. SebElink::Message__
53
+ Instances represent requests to i-bank, generally for payment.
54
+
55
+ Initialize these with `SebElink::Message.new(gateway_instance, message_code, data_hash)`
56
+
57
+ ```rb
58
+ SEB_LV_GATEWAY = SebElink::Gateway.new(<privkey string>)
59
+ message_instance = SebElink::Message.new(SEB_LV_GATEWAY, "0002", {IB_SND_ID: ...})
60
+ ```
61
+
62
+ Please consult `message_specs.rb` for the full list of data_hash keys.
63
+
64
+ Instances of `SebElink::Message` have two methods:
65
+
66
+ ```rb
67
+ message_instance.to_h
68
+ #=> hash of all fields you need to POST to i-bank API uri.
69
+
70
+ message_instance.digital_signature
71
+ #=> outputs the value of :IB_CRC key, the base64-encoded digital signature of the message
72
+ ```
73
+
74
+ __3. SebElink::Response__
75
+ Instances represent responses from SEB.lv i-bank server.
76
+ Well-formedness is not validated since if digital signature is OK, one would think that the bank adheres to it's own spec.
77
+
78
+ Initialize these with `SebElink::Response.new(gateway_instance, response_body)`
79
+
80
+ Please note that the method name `#response` is reserved in Rails, use something else for response variable names!
81
+
82
+ ```rb
83
+ SEB_LV_GATEWAY = SebElink::Gateway.new(<privkey string>)
84
+
85
+ # in a Rails controller context you can obtain the response_body with:
86
+ response_body =
87
+ if request.get?
88
+ request.query_string
89
+ else
90
+ request.raw_post
91
+ end #=> "IB_SND_ID=TEST..."
92
+
93
+ response_instance = SebElink::Response.new(SEB_LV_GATEWAY, response_body)
94
+
95
+ # Please note that the :IB_CRC signature values will often end with "==\n" which, when uri-escaped will be "%3D%3D%0A", pass the response just like that into the initializer
96
+ ```
97
+
98
+ Instances of `SebElink::Response` have two methods:
99
+
100
+ ```rb
101
+ response_instance.valid?
102
+ #=> true, if the digital signature is OK.
103
+ # DO NOT process responses that are invalid, someone has tampered with the values!
104
+
105
+ response_instance.to_h
106
+ #=> {IB_SND_ID: "TEST", ...}
107
+ # Will raise if called on an invalid response_instance
108
+ # to override this default safety setting, call with to_h(:insecure)
109
+ ```
110
+
111
+ Tests have been written in a documenting manner, so, please,
112
+ have a look at the contents of `spec/` directory to get a feel of what the gem can do.
113
+
114
+ Version 001 (current) uses the deprecated SHA-1 hashing algorithm. Let SEB.lv know that v2 that uses SHA-256 is needed.
115
+
116
+ ## Contributing
117
+ Bug reports and pull requests are welcome on GitHub at https://github.com/CreativeGS/seb_elink. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
118
+
119
+ The project uses TDD approach to software development, follow these steps to set up:
120
+ 1. fork and clone the repo on github
121
+ 2. Install appropriate Ruby and Bundler
122
+ 3. `bundle`
123
+ 4. See if all specs are green with `rspec`
124
+ 5. TDD new features
125
+ 6. Make a Pull Request in github
126
+
127
+ ## Releasing a new version
128
+ TODO
129
+
130
+ ## License
131
+
132
+ The gem is available as open source under the terms of the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
133
+
134
+ ## Code of Conduct
135
+
136
+ Everyone interacting in the SebElink project’s codebases and issue trackers is expected to follow the [code of conduct](https://github.com/CreativeGS/seb_elink/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "seb_elink"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/seb_elink.rb ADDED
@@ -0,0 +1,38 @@
1
+ require "base64"
2
+ require "openssl"
3
+
4
+ require "seb_elink/message_specs"
5
+ require "seb_elink/communications"
6
+ require "seb_elink/gateway"
7
+ require "seb_elink/message"
8
+ require "seb_elink/response"
9
+ require "seb_elink/version"
10
+
11
+ module SebElink
12
+ extend self
13
+
14
+ def root
15
+ @@root ||= Pathname.new(Gem::Specification.find_by_name("seb_elink").gem_dir)
16
+ end
17
+
18
+ DEFAULTS = {
19
+ IB_VERSION: "001",
20
+ IBANK_API_URI: "https://ibanka.seb.lv/ipc/epakindex.jsp",
21
+ IBANK_CERT: <<-HEREDOC
22
+ -----BEGIN CERTIFICATE-----
23
+ MIIB9TCCAV4CCQC7RlKZ7y4JPTANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQGEwJM
24
+ VjENMAsGA1UEBwwEUmlnYTERMA8GA1UECgwIU0VCIGJhbmsxDjAMBgNVBAMMBVNF
25
+ QlVCMB4XDTE2MDcxODA4NTQxM1oXDTIxMDYyMjA4NTQxM1owPzELMAkGA1UEBhMC
26
+ TFYxDTALBgNVBAcMBFJpZ2ExETAPBgNVBAoMCFNFQiBiYW5rMQ4wDAYDVQQDDAVT
27
+ RUJVQjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3gfAFe64YIu21x2UOTby
28
+ stFPMo7TFRWd7oW1L1YQWLHQuNVOh1kjrQWehECyK8cyX1hdHXPoAY3B2Virgj8U
29
+ g70ZfO6QQx9zifhlN0gbxRdPjq5jM7Ni5RMWsIayErAhk8IjbPSINLe5l/CpVAhp
30
+ yGJWRW8CYH9c/HLsUeg0sKUCAwEAATANBgkqhkiG9w0BAQUFAAOBgQDY9hRVOZkK
31
+ 957h2Ij8iwV7fIR3Nw8l+248D09xuknBrDOSYMXEDvEPKAW+CS0sQ64MOFybAFRx
32
+ YICfpu2DQkcmMM5APo79YzwMCjElRn5BsNyX7oDe4SZHbdUVqF4/mrFI3FU1KdN2
33
+ MJizE92BjvgQlJocLJePUi6jbO5YrmICkw==
34
+ -----END CERTIFICATE-----
35
+ HEREDOC
36
+
37
+ }
38
+ end
@@ -0,0 +1,19 @@
1
+ module SebElink::Communications
2
+ private
3
+
4
+ def validate_message_code
5
+ message_codes = send(:class)::SUPPORTED_MESSAGES
6
+
7
+ raise ArgumentError.new(
8
+ "'#{message_code}' is not a supported message code. Supported ones are: #{message_codes}"
9
+ ) unless message_codes.include?(message_code)
10
+ end
11
+
12
+ def validate_version
13
+ versions = send(:class)::SUPPORTED_VERSIONS
14
+
15
+ raise ArgumentError.new(
16
+ "'#{version}' is not a supported version. Supported ones are: #{versions}"
17
+ ) unless versions.include?(version)
18
+ end
19
+ end
@@ -0,0 +1,106 @@
1
+ class SebElink::Gateway
2
+ include SebElink::MessageSpecs
3
+
4
+ attr_reader :privkey, :defaults
5
+
6
+ def initialize(privkey, defaults={})
7
+ @privkey = privkey
8
+ @defaults = SebElink::DEFAULTS.merge(defaults)
9
+ end
10
+
11
+ def ibank_api_uri
12
+ @ibank_api_uri ||= defaults[:IBANK_API_URI]
13
+ end
14
+
15
+ # options: {
16
+ # message_code: "000x",
17
+ # version: "00x"
18
+ # skip_validation: false, # true for from-SEB messages like 0003 and 0004
19
+ # data: {
20
+ # IB_SND_ID: "TESTACC",
21
+ # ...
22
+ # }
23
+ # }
24
+ def produce_footprint(options)
25
+ data_hash = options[:data]
26
+ spec_set = spec_for(version: options[:version], message_code: options[:message_code])
27
+
28
+ footprint_string = spec_set.map do |field, spec|
29
+ next unless spec[:in_signature]
30
+
31
+ unless options[:skip_validation]
32
+ # 1. validate each field's length in .bytesize and format
33
+ raise_nil_error(field) if data_hash[field] == nil
34
+ raise_length_error(field) if data_hash[field].to_s.bytesize > spec[:max_length]
35
+ raise_format_error(field) if !data_hash[field].to_s[spec[:format]]
36
+ end
37
+
38
+ # 2. build the 'len(p1)||p1..' string
39
+ "#{data_hash[field].to_s.bytesize.to_s.rjust(3, "0")}#{data_hash[field]}"
40
+ end.join("")
41
+ end
42
+
43
+ # options: {
44
+ # version: "00x",
45
+ # message_code: "000x"
46
+ # }
47
+ def spec_for(options)
48
+ send(:class).const_get("V#{options[:version]}_MESSAGE#{options[:message_code]}_SPEC")
49
+ end
50
+
51
+ # options: {
52
+ # version: "00x",
53
+ # message_footprint: "001a.."
54
+ # }
55
+ def sign(options)
56
+ Base64.encode64(
57
+ privkey_rsa.sign(
58
+ send("v#{options[:version]}_digest"), #=> digest algorythm, SHA1
59
+ options[:message_footprint]
60
+ )
61
+ )
62
+ end
63
+
64
+ # options: {
65
+ # version: "00x",
66
+ # message:,
67
+ # base64_signature:
68
+ # # OR
69
+ # signature:
70
+ # }
71
+ def verify(options)
72
+ received_binary_signature = options[:signature] ||
73
+ Base64.decode64(options[:base64_signature])
74
+
75
+ ibank_pubkey_rsa.verify(
76
+ send("v#{options[:version]}_digest"),
77
+ received_binary_signature,
78
+ options[:message]
79
+ )
80
+ end
81
+
82
+ private
83
+ def v001_digest
84
+ OpenSSL::Digest::SHA1.new
85
+ end
86
+
87
+ def privkey_rsa
88
+ @privkey_rsa ||= OpenSSL::PKey::RSA.new(privkey)
89
+ end
90
+
91
+ def ibank_pubkey_rsa
92
+ @ibank_pubkey_rsa ||= OpenSSL::X509::Certificate.new(defaults[:IBANK_CERT]).public_key
93
+ end
94
+
95
+ def raise_length_error(field)
96
+ raise ArgumentError.new("#{field} value is too long")
97
+ end
98
+
99
+ def raise_format_error(field)
100
+ raise ArgumentError.new("#{field} value format does not match the spec")
101
+ end
102
+
103
+ def raise_nil_error(field)
104
+ raise ArgumentError.new("#{field} key is absent")
105
+ end
106
+ end
@@ -0,0 +1,59 @@
1
+ class SebElink::Message
2
+ include SebElink::Communications
3
+
4
+ SUPPORTED_VERSIONS = ["001"].freeze
5
+ SUPPORTED_MESSAGES = ["0002"].freeze
6
+
7
+ attr_reader :gateway_instance, :message_code, :data_hash
8
+
9
+ def initialize(gateway_instance, message_code, data_hash)
10
+ @gateway_instance = gateway_instance
11
+
12
+ @message_code = message_code
13
+
14
+ validate_message_code
15
+
16
+ @data_hash = gateway_instance.defaults.
17
+ merge(IB_SERVICE: @message_code).merge(data_hash)
18
+
19
+ validate_version
20
+ end
21
+
22
+ def digital_signature
23
+ @digital_signature ||= to_h[:IB_CRC]
24
+ end
25
+
26
+ def to_h
27
+ @to_h ||= send("message_#{message_code}")
28
+ end
29
+
30
+ private
31
+
32
+ def message_0002
33
+ footprint_string = gateway_instance.produce_footprint({
34
+ message_code: message_code,
35
+ version: version,
36
+ skip_validation: false,
37
+ data: data_hash
38
+ })
39
+
40
+ # 3. Hash-sign the footprint string => obtain "digital signature" of the message
41
+ digital_signature = gateway_instance.sign({
42
+ version: version,
43
+ message_footprint: footprint_string
44
+ })
45
+
46
+ # 4. Append IB_CRC key with the "digital signature" and return the hash
47
+ gateway_instance.spec_for(version: version, message_code: message_code).
48
+ keys.each_with_object({}) do |k, hash|
49
+ hash[k] = data_hash[k] if data_hash.has_key?(k)
50
+ end.merge(IB_CRC: digital_signature)
51
+ end
52
+
53
+ def version
54
+ return @version if defined?(@version)
55
+
56
+ @version = data_hash[:IB_VERSION]
57
+ end
58
+
59
+ end
@@ -0,0 +1,96 @@
1
+ module SebElink::MessageSpecs
2
+ # Storing SEB-defined specs for messages here
3
+
4
+ # AKA P.MU.1
5
+ # Sequence, Parameter title, Max length, Example of value, Description
6
+ # 1, IB_SND_ID, 10, SOMENAME, AAA Request sender (E-system)
7
+ # 2, IB_SERVICE, 4, 0002, Code of the Internet bank request type. Constant 0002
8
+ # 3, IB_VERSION, 3, 001, ID of used digital signature algorithm. Constant 001.
9
+ # 4, IB_AMOUNT, 17, 1234.56, Payment amount
10
+ # 5, IB_CURR, 3, EUR, Payment currency (EUR)
11
+ # 6, IB_NAME, 30, Company Beneficiary’s name, (in this case: Company)
12
+ # 7, IB_PAYMENT_ID, 20, UB0000000000015, Payment order reference number
13
+ # 8, IB_PAYMENT_DESC, 100, Your invoice No. 1234 is paid, Payment order description
14
+ # 9, IB_CRC, 500, abs51ajksa..., Request digital signature
15
+ # 10, IB_FEEDBACK, 150, URL to which the Bank will send the message of acceptance
16
+ # of the payment order for processing, execution, cancellation.
17
+ # 11, IB_LANG, 3, LAT, Preferable language (LAT, ENG, RUS)
18
+ V001_MESSAGE0002_SPEC = {
19
+ IB_SND_ID: {no: 1, in_signature: true, max_length: 10, format: %r'\A.{1,}\z'},
20
+ IB_SERVICE: {no: 2, in_signature: true, max_length: 4, format: %r'\A0002\z'},
21
+ IB_VERSION: {no: 3, in_signature: true, max_length: 3, format: %r'\A001\z'},
22
+ IB_AMOUNT: {no: 4, in_signature: true, max_length: 17, format: %r'\A\d+([.,]\d{,2})?\z'},
23
+ IB_CURR: {no: 5, in_signature: true, max_length: 3, format: %r'\A[A-Z]{3}\z'},
24
+ IB_NAME: {no: 6, in_signature: true, max_length: 30, format: %r'\A.{1,}\z'},
25
+ IB_PAYMENT_ID: {no: 7, in_signature: true, max_length: 20, format: %r'\A[0-9a-zA-Z]{1,20}\z'},
26
+ IB_PAYMENT_DESC: {no: 8, in_signature: true, max_length: 100, format: %r'\A.{1,}\z'},
27
+ # IB_CRC: {no: 9, in_signature: false, max_length: 500, format: %r'\A.*\z'},
28
+ IB_FEEDBACK: {no: 10, in_signature: false, max_length: 150, format: %r'\A.*\z'},
29
+ IB_LANG: {no: 11, in_signature: false, max_length: 3, format: %r'\A(?:LAT)|(?:ENG)|(?:RUS)\z'},
30
+ }.freeze
31
+
32
+ # # 4.2 Message 0003 - Payment order acceptance for processing (P.MU.3 and P.MU.4 parameters):
33
+ # Sequence Parameter title Max length Example of value Description
34
+ # 1. IB_SND_ID 10 B1 Request sender (Banks ID)
35
+ # 2. IB_SERVICE 4 0003 Code of the Internet bank request type
36
+ # 3. IB_VERSION 3 001 ID of used digital signature algorithm
37
+ # 4. IB_PAYMENT_ID 20 UB0000000000015 Payment order reference number
38
+ # 5. IB_AMOUNT 17 1234.56 Payment amount
39
+ # 6. IB_CURR 3 EUR Payment currency (EUR)
40
+ # 7. IB_REC_ID 10 AAA Beneficiary’s identifier (in this case: AAA)
41
+ # 8. IB_REC_ACC 21 Beneficiary’s account (IBAN).
42
+ # 9. IB_REC_NAME 30 Company Beneficiary’s name (in this case: Company)
43
+ # 10. IB_PAYER_ACC 21 Payer’s account (IBAN)
44
+ # 11. IB_PAYER_NAME 110 Jānis Ozols Payer’s name
45
+ # 12. IB_PAYMENT_DESC 100 Your invoice No.1234 is paid Payment order description
46
+ # 13. IB_PAYMENT_DATE 10 12.12.2005 Payment confirmation date (DD.MM.YYYY)
47
+ # 14. IB_PAYMENT_TIME 8 21:12:34 Payment confirmation time (HH:MM:SS)
48
+ # 15. IB_CRC 500 Request digital signature
49
+ # 16. IB_LANG 3 LAT Language (possible values: LAT, ENG, RUS)
50
+ # 17. IB_FROM_SERVER 1 Y / N In case of P.MU.2: Y, P.MU.3: N
51
+ V001_MESSAGE0003_SPEC = {
52
+ IB_SND_ID: {no: 1, in_signature: true},
53
+ IB_SERVICE: {no: 2, in_signature: true},
54
+ IB_VERSION: {no: 3, in_signature: true},
55
+ IB_PAYMENT_ID: {no: 4, in_signature: true},
56
+ IB_AMOUNT: {no: 5, in_signature: true},
57
+ IB_CURR: {no: 6, in_signature: true},
58
+ IB_REC_ID: {no: 7, in_signature: true},
59
+ IB_REC_ACC: {no: 8, in_signature: true},
60
+ IB_REC_NAME: {no: 9, in_signature: true},
61
+ IB_PAYER_ACC: {no: 10, in_signature: true},
62
+ IB_PAYER_NAME: {no: 11, in_signature: true},
63
+ IB_PAYMENT_DESC: {no: 12, in_signature: true},
64
+ IB_PAYMENT_DATE: {no: 13, in_signature: true},
65
+ IB_PAYMENT_TIME: {no: 14, in_signature: true},
66
+ IB_PAYMENT_TIME: {no: 15, in_signature: true}
67
+ }.freeze
68
+
69
+ # # 4.3 Message 0004 - Payment order execution or cancellation (P.MU.2 and P.MU.5 parameters):
70
+ # Sequence, Parameter title, Max length, Example of value, Description
71
+ # 1. IB_SND_ID 10 B1 Request sender (Banks ID)
72
+ # 2. IB_SERVICE 4 0004 Code of the Internet bank request type
73
+ # 3. IB_VERSION 3 001 ID of used digital signature algorithm
74
+ # 4. IB_REC_ID 10 AAA Beneficiary’s identifier (in this case: “AAA”)
75
+ # 5. IB_PAYMENT_ID 20 UB0000000000015 Payment order reference number
76
+ # 6. IB_PAYMENT_DESC 100 Your invoice No. 1234 is paid Payment order description
77
+ # 7. IB_FROM_SERVER 1 Y/ N In case of P.MU.4: “Y”, P.MU.5: “N”
78
+ # 8. IB_STATUS 12 ACCOMPLISHED Payment order status
79
+ # 9. IB_CRC 300 Message digital signature
80
+ # 10. IB_LANG 3 LAT Language (possible values: “LAT”, “ENG”, “RUS”)
81
+ V001_MESSAGE0004_SPEC = {
82
+ IB_SND_ID: {no: 1, in_signature: true},
83
+ IB_SERVICE: {no: 2, in_signature: true},
84
+ IB_VERSION: {no: 3, in_signature: true},
85
+ IB_REC_ID: {no: 4, in_signature: true},
86
+ IB_PAYMENT_ID: {no: 5, in_signature: true},
87
+ IB_PAYMENT_DESC: {no: 6, in_signature: true},
88
+ # -- IB_FROM_SERVER {no: 7, in_signature: true},
89
+ IB_STATUS: {no: 7, in_signature: true},
90
+ }.freeze
91
+
92
+
93
+
94
+
95
+
96
+ end
@@ -0,0 +1,67 @@
1
+ class SebElink::Response
2
+ include SebElink::Communications
3
+
4
+ SUPPORTED_VERSIONS = ["001"].freeze
5
+ SUPPORTED_MESSAGES = ["0003", "0004"].freeze
6
+
7
+ attr_reader :gateway_instance, :body
8
+
9
+ def initialize(gateway_instance, body)
10
+ @gateway_instance = gateway_instance
11
+ @body = body
12
+ end
13
+
14
+ def valid?
15
+ return @valid if defined?(@valid)
16
+
17
+ validate_message_code
18
+ validate_version
19
+
20
+ footprint = gateway_instance.produce_footprint({
21
+ message_code: message_code,
22
+ version: version,
23
+ skip_validation: true,
24
+ data: body_hash
25
+ })
26
+
27
+ @valid = gateway_instance.verify({
28
+ version: version,
29
+ message_footprint: footprint,
30
+ message_signature: body_hash[:IB_CRC]
31
+ })
32
+ end
33
+
34
+ def to_h(mode=:secure)
35
+ raise SebElink::InvalidResponseError.new(
36
+ "The response with body '#{body}' is invalid"
37
+ ) if mode != :insecure && !valid?
38
+
39
+ @to_h ||= body_hash
40
+ end
41
+
42
+ private
43
+ def body_hash
44
+ @body_hash ||= body.split("&").each_with_object({}) do |q_pair, hash|
45
+ pair = q_pair # CGI.unescape(q_pair)
46
+ split_index = CGI.unescape(pair).index("=")
47
+ key, value = pair[0..(split_index - 1)], pair[split_index.next..-1]
48
+ hash[key.to_sym] = value.to_s
49
+ end
50
+ end
51
+
52
+ def message_code
53
+ return @message_code if defined?(@message_code)
54
+
55
+ @message_code = body_hash[:IB_SERVICE]
56
+ end
57
+
58
+ def version
59
+ return @version if defined?(@version)
60
+
61
+ @version = body_hash[:IB_VERSION]
62
+ end
63
+
64
+ end
65
+
66
+ class SebElink::InvalidResponseError < RuntimeError
67
+ end
@@ -0,0 +1,3 @@
1
+ module SebElink
2
+ VERSION = "0.9.0"
3
+ end
data/seb_elink.gemspec ADDED
@@ -0,0 +1,31 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "seb_elink/version"
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "seb_elink"
8
+ gem.required_ruby_version = '>= 2'
9
+ gem.date = "2017-12-14"
10
+ gem.version = SebElink::VERSION
11
+ gem.authors = ["Epigene"]
12
+ gem.email = ["cto@creative.gs", "augusts.bautra@gmail.com"]
13
+
14
+ gem.summary = "Ruby wrapper for communicating with SEB.lv i-bank payment API."
15
+ gem.homepage = "https://github.com/CreativeGS/seb_elink"
16
+ gem.license = "BSD-3-Clause"
17
+
18
+ gem.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ gem.bindir = "exe"
22
+ gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ gem.require_paths = ["lib"]
24
+
25
+ gem.add_development_dependency "bundler", "~> 1.16"
26
+ gem.add_development_dependency "rake", "~> 10.0"
27
+ gem.add_development_dependency "rspec", "~> 3.7"
28
+ gem.add_development_dependency "pry", "~> 0.11.3"
29
+ gem.add_development_dependency "simplecov", "~> 0.15.1"
30
+ gem.add_development_dependency "coveralls"
31
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: seb_elink
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Epigene
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-14 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.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
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: '3.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.11.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.11.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.15.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.15.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: coveralls
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - cto@creative.gs
100
+ - augusts.bautra@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - .circleci/config.yml
106
+ - .gitignore
107
+ - .rspec
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/setup
116
+ - lib/seb_elink.rb
117
+ - lib/seb_elink/communications.rb
118
+ - lib/seb_elink/gateway.rb
119
+ - lib/seb_elink/message.rb
120
+ - lib/seb_elink/message_specs.rb
121
+ - lib/seb_elink/response.rb
122
+ - lib/seb_elink/version.rb
123
+ - seb_elink.gemspec
124
+ homepage: https://github.com/CreativeGS/seb_elink
125
+ licenses:
126
+ - BSD-3-Clause
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '2'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.4.8
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Ruby wrapper for communicating with SEB.lv i-bank payment API.
148
+ test_files: []