debitech 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ Gemfile.lock
6
+ pkg
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.3-p0@debitech --create
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Joakim Kolsjö
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Debitech
2
+
3
+ This is a DebiTech payment library extracted from production code.
4
+
5
+ You can use this to do subscription payments without needing to handle any
6
+ credit card information yourself.
7
+
8
+ Todo:
9
+
10
+ * Examples and documentation
11
+ * Gotchas about the debitech API
12
+ * Page set templates (the pages that are shown at DIBS to input credit card info)
13
+ * Rake tasks to update page sets
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'debitech'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install debitech
28
+
29
+ ## Usage
30
+
31
+ TODO: Write usage instructions here
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc "Run all specs"
6
+ RSpec::Core::RakeTask.new('spec') do |t|
7
+ t.pattern = 'spec/**/*.rb'
8
+ end
9
+
10
+ task :default => :spec
11
+
data/debitech.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/debitech/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Joakim Kolsjö"]
6
+ gem.email = ["joakim@barsoom.se"]
7
+ gem.description = %q{Library for doing payments using DebiTech (DIBS)}
8
+ gem.summary = %q{Library for doing payments using DebiTech (DIBS)}
9
+ gem.homepage = "https://github.com/barsoom/debitech"
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.test_files = `git ls-files -- {spec}/*`.split("\n")
12
+ gem.name = "debitech"
13
+ gem.require_paths = ["lib"]
14
+ gem.version = Debitech::VERSION
15
+ gem.add_dependency "debitech_soap"
16
+ gem.add_development_dependency "rake"
17
+ gem.add_development_dependency 'rspec'
18
+ gem.add_development_dependency "guard"
19
+ gem.add_development_dependency "guard-rspec"
20
+ end
@@ -0,0 +1,9 @@
1
+ require 'digest/sha1'
2
+
3
+ module Debitech
4
+ class Mac
5
+ def self.build(data)
6
+ Digest::SHA1.hexdigest(data.map { |s| s.to_s + "&" }.join).upcase
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ require 'debitech_soap'
2
+ require 'debitech/mac'
3
+
4
+ module Debitech
5
+ class ServerApi
6
+ class ChargeResult
7
+ attr_reader :response
8
+
9
+ PENDING_RESULT_CODE = 403
10
+
11
+ def initialize(response)
12
+ @response = response
13
+ end
14
+
15
+ def success?
16
+ @response.result_code.to_s[0,1] == "1"
17
+ end
18
+
19
+ def pending?
20
+ @response.result_code == PENDING_RESULT_CODE
21
+ end
22
+ end
23
+
24
+ class ValidUniqueReferenceRequired < StandardError; end
25
+
26
+ # Don't know about the upper bound, but we need it to be atleast 5 characters to be
27
+ # able to search for it in DIBS Manager.
28
+ LEAST_NUMBER_OF_CHARACTERS_IN_UNIQUE_ID = 5
29
+
30
+ def initialize(config = {})
31
+ @config = config
32
+ @soap_api = DebitechSoap::API.new(config[:soap_opts])
33
+ end
34
+
35
+ def charge(opts = {})
36
+ if opts[:amount] && (opts[:amount] - opts[:amount].to_i) > 0
37
+ raise "The amount (#{opts[:amount]}) contains fractions (for example 10.44 instead of 10), amount should specified in cents."
38
+ end
39
+
40
+ if opts[:unique_reference].to_s.size < LEAST_NUMBER_OF_CHARACTERS_IN_UNIQUE_ID
41
+ raise ValidUniqueReferenceRequired
42
+ end
43
+
44
+ data = "001:payment:1:#{opts[:amount].to_i}:"
45
+ mac = Mac.build [ data, opts[:currency], opts[:unique_reference], @config[:secret_key] ]
46
+ extra = "&method=#{@config[:method]}&currency=#{opts[:currency]}&MAC=#{mac}&referenceNo=#{opts[:unique_reference]}"
47
+ response = @soap_api.subscribe_and_settle(:verifyID => opts[:verify_id], :ip => opts[:ip], :data => data, :extra => extra)
48
+ ChargeResult.new(response)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module Debitech
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,56 @@
1
+ require 'digest/sha1'
2
+ require 'debitech/mac'
3
+
4
+ module Debitech
5
+ class WebApi
6
+ APPROVED_REPLY = "A"
7
+
8
+ def initialize(opts = {})
9
+ @opts = opts
10
+ end
11
+
12
+ def form_fields
13
+ base_fields.merge(:MAC => request_mac)
14
+ end
15
+
16
+ def form_action
17
+ "https://secure.incab.se/verify/bin/#{@opts[:merchant]}/index"
18
+ end
19
+
20
+ def valid_response?(mac, sum, reply, verify_id)
21
+ response_mac(sum, reply, verify_id) == mac.upcase.split("=").last
22
+ end
23
+
24
+ def approved_reply?(reply)
25
+ reply == APPROVED_REPLY
26
+ end
27
+
28
+ private
29
+
30
+ def base_fields
31
+ {
32
+ :currency => "SEK",
33
+ :method => 'cc.test',
34
+ :amount => '100',
35
+ :authOnly => 'true',
36
+ :pageSet => 'creditcard',
37
+ :data => '001:auth:1:100:',
38
+ :uses3dsecure => 'false',
39
+ :billingFirstName => "First name",
40
+ :billingLastName => "Last name",
41
+ :billingAddress => "Address",
42
+ :billingCity => "City",
43
+ :billingCountry => "Country",
44
+ :eMail => "email@example.com"
45
+ }.merge!(@opts[:fields] || {})
46
+ end
47
+
48
+ def request_mac
49
+ Mac.build [ base_fields[:data], base_fields[:currency], base_fields[:method], @opts[:secret_key] ]
50
+ end
51
+
52
+ def response_mac(sum, reply, verify_id)
53
+ Mac.build [ sum, base_fields[:currency], reply, verify_id, @opts[:secret_key] ]
54
+ end
55
+ end
56
+ end
data/lib/debitech.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "debitech/version"
2
+ require "debitech/server_api"
3
+ require "debitech/web_api"
@@ -0,0 +1,104 @@
1
+ require 'debitech'
2
+
3
+ describe Debitech::ServerApi, "charge" do
4
+
5
+ it "should perform a subscribe_and_settle call" do
6
+ settings = {
7
+ :secret_key => "112756FC8C60C5603C58DA6E0A4844ACFDB60525",
8
+ :method => "cc.cekab",
9
+ :soap_opts => { :merchant => "store", :username => "api_user", :password => "api_password" } }
10
+
11
+ DebitechSoap::API.should_receive(:new).with({ :merchant => "store", :username => "api_user", :password => "api_password" }).
12
+ and_return(soap_api = mock)
13
+ soap_api.should_receive(:subscribe_and_settle).with(:verifyID => 1234567,
14
+ :data => "001:payment:1:10000:",
15
+ :ip => "127.0.0.1",
16
+ :extra => "&method=cc.cekab&currency=SEK&MAC=1931EE498A77F6B12B2C2D2EC8599719EF9CE419&referenceNo=some_unique_ref")
17
+
18
+ server_api = Debitech::ServerApi.new(settings)
19
+ server_api.charge(:verify_id => 1234567, :amount => 10000, :currency => "SEK", :unique_reference => "some_unique_ref", :ip => "127.0.0.1")
20
+ end
21
+
22
+ it "should convert the amount to a integer to avoid 500 errors" do
23
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
24
+ soap_api.should_receive(:subscribe_and_settle).with(:verifyID => 1234567,
25
+ :data => "001:payment:1:2235:",
26
+ :ip => "127.0.0.1",
27
+ :extra => "&method=&currency=SEK&MAC=78B1144270B1A74A55539FAEB81BB49EC39B90DF&referenceNo=some_unique_ref")
28
+
29
+ server_api = Debitech::ServerApi.new({})
30
+ server_api.charge(:verify_id => 1234567, :amount => 2235.0, :currency => "SEK", :unique_reference => "some_unique_ref", :ip => "127.0.0.1")
31
+ end
32
+
33
+ it "should raise an error if the amount has a fraction" do
34
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
35
+ server_api = Debitech::ServerApi.new({})
36
+ lambda {
37
+ server_api.charge(:verify_id => 1234567, :amount => 2235.55, :currency => "SEK", :unique_reference => "some_unique_ref", :ip => "127.0.0.1")
38
+ }.should raise_error("The amount (2235.55) contains fractions (for example 10.44 instead of 10), amount should specified in cents.")
39
+ end
40
+
41
+ it "should raise an error if the unique_reference is nil" do
42
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
43
+ server_api = Debitech::ServerApi.new({})
44
+ lambda {
45
+ server_api.charge(:verify_id => 1234567, :amount => 2235, :currency => "SEK", :unique_reference => nil, :ip => "127.0.0.1")
46
+ }.should raise_error(Debitech::ServerApi::ValidUniqueReferenceRequired)
47
+ end
48
+
49
+ it "should raise an error if the unique_reference is less than 4 characters" do
50
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
51
+ server_api = Debitech::ServerApi.new({})
52
+ lambda {
53
+ server_api.charge(:verify_id => 1234567, :amount => 2235, :currency => "SEK",
54
+ :unique_reference => "1234", :ip => "127.0.0.1")
55
+ }.should raise_error(Debitech::ServerApi::ValidUniqueReferenceRequired)
56
+ end
57
+
58
+ it "should be valid with a 5 character unique_reference" do
59
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
60
+ soap_api.stub!(:subscribe_and_settle)
61
+ server_api = Debitech::ServerApi.new({})
62
+ server_api.charge(:verify_id => 1234567, :amount => 2235, :currency => "SEK",
63
+ :unique_reference => "12345", :ip => "127.0.0.1")
64
+ end
65
+
66
+ [ 100, 101, 150, 199 ].each do |result_code|
67
+ it "should return success for result_code #{result_code}" do
68
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
69
+ soap_api.stub!(:subscribe_and_settle).and_return(response = mock(:result_code => result_code))
70
+
71
+ server_api = Debitech::ServerApi.new({})
72
+ result = server_api.charge({ :unique_reference => "some_unique_ref" })
73
+
74
+ result.should be_success
75
+ result.should_not be_pending
76
+ result.response.result_code.should == result_code
77
+ end
78
+ end
79
+
80
+ [ 200, 250, 300, 400 ].each do |result_code|
81
+ it "should not be successful for result_code #{result_code}" do
82
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
83
+ soap_api.stub!(:subscribe_and_settle).and_return(response = mock(:result_code => result_code))
84
+
85
+ server_api = Debitech::ServerApi.new({})
86
+ result = server_api.charge({ :unique_reference => "some_unique_ref" })
87
+
88
+ result.should_not be_success
89
+ result.should_not be_pending
90
+ result.response.result_code.should == result_code
91
+ end
92
+ end
93
+
94
+ it "should return pending and not be successful for 403" do
95
+ DebitechSoap::API.stub!(:new).and_return(soap_api = mock)
96
+ soap_api.stub!(:subscribe_and_settle).and_return(response = mock(:result_code => 403))
97
+ server_api = Debitech::ServerApi.new({})
98
+ result = server_api.charge({ :unique_reference => "some_unique_ref" })
99
+
100
+ result.should_not be_success
101
+ result.should be_pending
102
+ end
103
+
104
+ end
@@ -0,0 +1,65 @@
1
+ require 'ostruct'
2
+ require 'debitech'
3
+
4
+ describe Debitech::WebApi, "form_fields" do
5
+ it "should include static values" do
6
+ fields = Debitech::WebApi.new({ :secret_key => 'secretkey' }).form_fields
7
+ fields[:currency].should == 'SEK'
8
+ fields[:method].should == 'cc.test'
9
+ end
10
+
11
+ it "should be possible to override the values" do
12
+ fields = Debitech::WebApi.new({ :secret_key => 'secretkey', :fields => { :method => "cc.cekab" } }).form_fields
13
+ fields[:method].should == 'cc.cekab'
14
+ end
15
+
16
+ it "should calculate mac" do
17
+ Debitech::WebApi.new({ :secret_key => "secretkey1" }).form_fields[:MAC].should == "DF253765337968C5ED7E6EA530CD692942416ABE"
18
+ Debitech::WebApi.new({ :secret_key => "secretkey2" }).form_fields[:MAC].should == "BEB3C370E37837734642111D44CA7E304A0F45F2"
19
+ end
20
+ end
21
+
22
+ describe Debitech::WebApi, "form_action" do
23
+ it "should return the url based on shop" do
24
+ Debitech::WebApi.new({ :merchant => "shop" }).form_action.should == "https://secure.incab.se/verify/bin/shop/index"
25
+ end
26
+ end
27
+
28
+ describe Debitech::WebApi, "approved_response?" do
29
+ context "given reply is 'A' for approved" do
30
+ it "should be true" do
31
+ api = Debitech::WebApi.new
32
+ api.approved_reply?("A").should be_true
33
+ end
34
+ end
35
+
36
+ context "given reply is 'D' for denied" do
37
+ it "should be false" do
38
+ api = Debitech::WebApi.new
39
+ api.approved_reply?("D").should be_false
40
+ end
41
+ end
42
+ end
43
+
44
+ describe Debitech::WebApi, "valid_response?" do
45
+ it "should validate that the response hashes down to the mac value" do
46
+ api = Debitech::WebApi.new({ :secret_key => "secretkey" })
47
+ api.valid_response?("MAC=667026AD7692F9AFDA362919EA72D8E6A250A849", "1,00", "A", "1234567").should be_true
48
+ end
49
+
50
+ it "should not be true if any of the values are wrong" do
51
+ api = Debitech::WebApi.new({ :secret_key => "secretkey" })
52
+ api.valid_response?("MAC=667026AD7692F9AFDA362919EA72D8E6A250A849", "1,00", "A", "1234568").should be_false
53
+ end
54
+
55
+ it "should not be true if the secretkey is different" do
56
+ api = Debitech::WebApi.new({ :secret_key => "secretkey2" })
57
+ api.valid_response?("MAC=667026AD7692F9AFDA362919EA72D8E6A250A849", "1,00", "A", "1234567").should be_false
58
+ end
59
+
60
+ it "should handle if verify_id is an int" do
61
+ api = Debitech::WebApi.new({ :secret_key => "secretkey" })
62
+ api.valid_response?("MAC=667026AD7692F9AFDA362919EA72D8E6A250A849", "1,00", "A", 1234567).should be_true
63
+ end
64
+
65
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: debitech
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joakim Kolsjö
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: debitech_soap
16
+ requirement: &2152307340 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2152307340
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &2152306700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152306700
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2152306260 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2152306260
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard
49
+ requirement: &2152305800 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2152305800
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &2152304760 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2152304760
69
+ description: Library for doing payments using DebiTech (DIBS)
70
+ email:
71
+ - joakim@barsoom.se
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rvmrc
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - debitech.gemspec
83
+ - lib/debitech.rb
84
+ - lib/debitech/mac.rb
85
+ - lib/debitech/server_api.rb
86
+ - lib/debitech/version.rb
87
+ - lib/debitech/web_api.rb
88
+ - spec/debitech/server_api_spec.rb
89
+ - spec/debitech/web_api_spec.rb
90
+ homepage: https://github.com/barsoom/debitech
91
+ licenses: []
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.5
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Library for doing payments using DebiTech (DIBS)
114
+ test_files: []