debitech 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []