xpay 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *~
7
+ *.gem
8
+ tmp
9
+ log
10
+ .yardoc
11
+ doc
12
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in efcm.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xpay (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.2)
10
+ log_buddy (0.5.0)
11
+ mocha (0.9.8)
12
+ rake
13
+ rake (0.8.7)
14
+ rspec (2.0.0)
15
+ rspec-core (= 2.0.0)
16
+ rspec-expectations (= 2.0.0)
17
+ rspec-mocks (= 2.0.0)
18
+ rspec-core (2.0.0)
19
+ rspec-expectations (2.0.0)
20
+ diff-lcs (>= 1.1.2)
21
+ rspec-mocks (2.0.0)
22
+ rspec-core (= 2.0.0)
23
+ rspec-expectations (= 2.0.0)
24
+ shoulda (2.11.3)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ bundler (>= 1.0.0)
31
+ log_buddy
32
+ mocha
33
+ rspec (>= 2.0.0)
34
+ shoulda
35
+ xpay!
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Volker Pacher
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,109 @@
1
+ = Xpay
2
+
3
+ An abstaction layer on top Xpay from SecureTrading.com
4
+ http://www.securetrading.com/support/xpay.html for futher details, in depth specifications and installation guides of the xpay java applet
5
+
6
+ == Install
7
+ * install the xpay java applet and configure it according to the installation guidelines from SecureTrading
8
+ (http://www.securetrading.com/download/DOC_COM_ST-XPAY4-USER-GUIDE.pdf)
9
+ * start the applet then install the gem:
10
+
11
+ $ gem install xpay
12
+
13
+ * configure by either creating a yaml file at config/xpay.yml or call Xpay.set_config with a hash
14
+ * the following options can be set:
15
+ - merchant_name
16
+ - version
17
+ - alias
18
+ - site_reference
19
+ - port
20
+ - callback_url
21
+
22
+ == Usage
23
+ === An example of a 3D-Secure Transaction
24
+
25
+ -first: create the class instance for CreditCard, Customer and Operatioon
26
+
27
+ CreditCard:
28
+ cc = Xpay::CreditCard.new(:card_type => "Visa",
29
+ :number => "4111111111111160",
30
+ :security_code => "123",
31
+ :valid_until => "11/11")
32
+
33
+ Customer:
34
+ cus = Xpay::Customer.new(:fullname => "Fred Bloggs")
35
+
36
+ Operation:
37
+ ops = Xpay::Operation.new(:currency => "GBP", :amount => 1999)
38
+
39
+ Create payment instance
40
+ p = Xpay::Payment.new({:creditcard => cc, :customer => cus, :operation => ops)
41
+
42
+ Make payment
43
+ rt = p.make_payment
44
+
45
+ -second: depending on the return value rt:
46
+
47
+ rt==0 An error occured during processing
48
+ Check p.response_block[:error_code] for further details
49
+
50
+ rt==1 Settlement request approve, check p.response_block for all the details
51
+ rt==2 Settlement request declined, check p.response_block for all the details
52
+
53
+ rt==-1 A redirection to an 3D-Secure server is necessary.
54
+ All necessary information is contained in p.three_secure
55
+ It has the following keys:
56
+ :md a unique reference generated according to the 3D Secure specification
57
+ (currently up to 1024 bytes in base64 format).
58
+ it is passed as parameter in the callback from the 3D-Secure server
59
+ and you will need to store it to find the transaction on callback
60
+
61
+ :pareq The pareq contains purchase transaction details upon which ACS authentication decisions are based.
62
+ If you are making your own customised redirect page instead of using the <Html> provided then this
63
+ field MUST be included in that html, as a hidden field. The length of the PaReq is not explicitly
64
+ defined by the 3-D Secure specification. We recommend allowing at least 4096 bytes
65
+ for this field in any variables/storage systems.
66
+
67
+ :termurl Your own callback url. If you are making your own customised redirect page
68
+ instead of using the <Html> provided then this field MUST be included in that html as a hidden field.
69
+
70
+ :acsurl This is the url of the ACS it should be used as the location of the redirect if you use your own form/redirect
71
+
72
+ :html This value contains the complete html code if you don't want to use your own form.
73
+ This can be achieved by sending your normal server headers to the client immediately followed by the contents of the <Html> tag.
74
+
75
+ :request_xml This is a complete xml document as string that you need to pass to a new payment instance after the callback.
76
+ You need to store this field for the callback.
77
+
78
+ -third: after the callback from the 3D Secure Server:
79
+
80
+ make sure that protect_from_forgery is switched off for this controller method as it is not transmitted by the callback
81
+ you will have to paramaters in the params hash
82
+ params[:MD] and params[:PaRes]
83
+
84
+ Use params[:md] to find your stored transaction:
85
+
86
+ create a new class instance with the request_xml stored in the transaction and params[:PaRes] from the callback
87
+
88
+ p = Xpay::Payment.new({:xml => request_xml, :pares => params[:PaRes])
89
+
90
+ make the payment
91
+
92
+ rt = p.make_payment
93
+
94
+ The possible values of rt are as above.
95
+
96
+ === more examples to follow
97
+
98
+
99
+ == Copyright
100
+
101
+ See LICENSE for details.
102
+
103
+ == Note on Patches/Pull Requests
104
+
105
+ * Fork the project.
106
+ * Make your feature addition or bug fix.
107
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
108
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
109
+ * Send me a pull request. Bonus points for topic branches.
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'bundler'
5
+
6
+ require File.expand_path('../lib/xpay/version', __FILE__)
7
+
8
+
9
+ namespace :test do
10
+ Rake::TestTask.new(:all) do |test|
11
+ test.libs << 'lib' << 'test'
12
+ test.ruby_opts << '-rubygems'
13
+ test.pattern = 'test/**/*_test.rb'
14
+ test.verbose = true
15
+ end
16
+ Rake::TestTask.new(:unit) do |test|
17
+ test.libs << 'lib' << 'test'
18
+ test.ruby_opts << '-rubygems'
19
+ test.pattern = 'test/xpay/**/*_test.rb'
20
+ test.verbose = true
21
+ end
22
+ Rake::TestTask.new(:functional) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.ruby_opts << '-rubygems'
25
+ test.pattern = 'test/functional/*_test.rb'
26
+ test.verbose = true
27
+ end
28
+ end
29
+ task :default => :test
30
+
31
+ desc 'Builds the gem'
32
+ task :build do
33
+ sh "gem build xpay.gemspec"
34
+ end
35
+
36
+ desc 'Builds and installs the gem'
37
+ task :install => :build do
38
+ sh "gem install ./pkg/xpay-#{Xpay::Version}"
39
+ end
40
+
41
+ desc 'Tags version, pushes to remote, and pushes gem'
42
+ task :release => :build do
43
+ sh "git tag v#{Xpay::Version}"
44
+ sh "git push origin master"
45
+ sh "git push origin v#{Xpay::Version}"
46
+ sh "gem push ./pkg/xpay-#{Xpay::Version}.gem"
47
+ end
48
+ desc "Open an irb session preloaded with this library"
49
+ task :console do
50
+ sh "irb -rubygems -I lib -r xpay.rb"
51
+ end
52
+ Bundler::GemHelper.install_tasks
data/UPGRADES ADDED
@@ -0,0 +1,9 @@
1
+ 0.0.1 => 0.0.2
2
+ * established basic structure of the gem, wrote most test and added first functional tests, added fixtures
3
+
4
+ 0.0.2 => 0.0.3
5
+ * finished all methods necessary to make a succesful transaction
6
+ * wrote basic documentation for usage
7
+
8
+ 0.0.3 => 0.0.4
9
+ * bug fixed that prevents loading of MerchantName from config and create root xml
data/lib/xpay.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'rexml/document'
2
+ require 'rexml/xmldecl'
3
+ require 'ostruct'
4
+ require 'erb'
5
+ require 'xpay/transaction'
6
+
7
+ module Xpay
8
+ # These are the default settings. You can change them by placing YAML file into config/xpay.yml with settings for each environment.
9
+ # Alternatively you can change the settings by calling the config setter for each attribute for example:
10
+ # Xpay.config.alias = "your_new_alias"
11
+ # Another option is to call Xpay.set_config with a hash containing the attributes you want to change
12
+ #
13
+ # merchant_name: CompanyName
14
+ # version: 3.51 'this is the only supported version at the moment and has to be 3.51, as String'
15
+ # alias: site12345
16
+ # site_reference: site12345
17
+ # port: 5000 'this needs to be an Integer'
18
+ # default_query: ST3DCARDQUERY 'defaults to 3D Card query if not otherwise specified'
19
+ # settlement_day: 1 'this needs to be a String'
20
+ # default_currency: GBP
21
+
22
+ autoload :Payment, 'xpay/payment'
23
+ autoload :CreditCard, 'xpay/core/creditcard'
24
+ autoload :Customer, 'xpay/core/customer'
25
+ autoload :Operation, 'xpay/core/operation'
26
+
27
+ @xpay_config = OpenStruct.new({
28
+ "merchant_name" => "CompanyName",
29
+ "version" => "3.51",
30
+ "alias" => "site12345",
31
+ "site_reference" => "site12345",
32
+ "port" => 5000,
33
+ "callback_url" => "http://localhost/gateway_callback",
34
+ "default_query" => "ST3DCARDQUERY",
35
+ "settlement_day" => "1",
36
+ "default_currency" => "GBP"
37
+ })
38
+ class XpayError < StandardError
39
+ attr_reader :data
40
+
41
+ def initialize(data)
42
+ @data = data
43
+ super
44
+ end
45
+ end
46
+
47
+ class PaResMissing < XpayError; end
48
+ class General < XpayError; end
49
+
50
+ class << self
51
+ attr_accessor :app_root, :environment
52
+
53
+ def load_config(app_root = Dir.pwd)
54
+ self.app_root = (RAILS_ROOT if defined?(RAILS_ROOT)) || app_root
55
+ self.environment = (RAILS_ENV if defined?(RAILS_ENV)) || "development"
56
+ parse_config
57
+ return true
58
+ end
59
+
60
+
61
+ def root_xml
62
+ @request_xml ||= create_root_xml
63
+ end
64
+
65
+ def root_to_s
66
+ self.root_xml.to_s
67
+ end
68
+
69
+
70
+ def config
71
+ @xpay_config
72
+ end
73
+
74
+ def set_config(conf)
75
+ conf.each do |key, value|
76
+ @xpay_config.send("#{key}=", value) if @xpay_config.respond_to? key
77
+ end
78
+ return true
79
+ end
80
+
81
+ private
82
+ def parse_config
83
+ path = "#{app_root}/config/xpay.yml"
84
+ return unless File.exists?(path)
85
+ conf = YAML::load(ERB.new(IO.read(path)).result)[environment]
86
+ self.set_config(conf)
87
+ end
88
+
89
+ def create_root_xml
90
+ r = REXML::Document.new
91
+ r << REXML::XMLDecl.new("1.0", "iso-8859-1")
92
+ rb = r.add_element "RequestBlock", {"Version" => config.version}
93
+ request = rb.add_element "Request", {"Type" => config.default_query}
94
+ operation = request.add_element "Operation"
95
+ site_ref = operation.add_element "SiteReference"
96
+ site_ref.text = config.site_reference
97
+ if config.default_query == "ST3DCARDQUERY"
98
+ mn = operation.add_element "MerchantName"
99
+ mn.text = config.merchant_name
100
+ tu = operation.add_element "TermUrl"
101
+ tu.text = config.callback_url
102
+ end
103
+ cer = rb.add_element "Certificate"
104
+ cer.text = config.alias
105
+ return r
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,49 @@
1
+ module Xpay
2
+ class CreditCard
3
+ attr_accessor :card_type, :valid_until, :valid_from, :issue, :parent_transaction_ref, :transaction_verifier
4
+ attr_writer :security_code
5
+ def initialize(options={})
6
+ if !options.nil? && options.is_a?(Hash)
7
+ options.each do |key, value|
8
+ self.send("#{key.to_s}=", value) if self.respond_to? key.to_s
9
+ end
10
+ end
11
+ end
12
+
13
+ def number
14
+ @number.sub(/^([0-9]+)([0-9]{4})$/) { 'x' * $1.length + $2 }
15
+ end
16
+
17
+ def number=(new_val)
18
+ @number = new_val.to_s.gsub(/[^0-9]/, "")
19
+ end
20
+
21
+ def security_code=(new_val)
22
+ @security_code = new_val.to_s
23
+ end
24
+
25
+ def add_to_xml(doc)
26
+ op = REXML::XPath.first(doc, "//Request")
27
+ op.delete_element "PaymentMethod"
28
+ pa = op.add_element "PaymentMethod"
29
+ cc = pa.add_element("CreditCard")
30
+ cc.add_element("Type").add_text(self.card_type) if self.card_type
31
+ cc.add_element("Number").add_text(self.number_internal) if self.number_internal
32
+ cc.add_element("StartDate").add_text(self.valid_from) if self.valid_from
33
+ cc.add_element("ExpiryDate").add_text(self.valid_until) if self.valid_until
34
+ cc.add_element("ParentTransactionReference").add_text(self.parent_transaction_ref) if self.parent_transaction_ref
35
+ cc.add_element("TransactionVerifier").add_text(self.transaction_verifier) if self.transaction_verifier
36
+ cc.add_element("SecurityCode").add_text(self.security_code) if self.security_code
37
+ cc.add_element("Issue").add_text(self.issue) if self.issue
38
+ end
39
+
40
+ protected
41
+ def number_internal
42
+ @number
43
+ end
44
+
45
+ def security_code
46
+ @security_code
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ module Xpay
2
+
3
+ # The customer is not required for a transaction except for 3D secure transactions in which case
4
+ # http_accept and user_agent are required for ST3DCARDQUERY
5
+ #
6
+ # All other fields are optional and also depend on your security policy with SecureTrading
7
+ #
8
+ # A further note:
9
+ # fullname and firstname + lastname are different and end up in different places in the final xml. You can supply fullname as it appears on the card.
10
+
11
+ class Customer
12
+
13
+ attr_accessor :title, :fullname, :firstname, :lastname, :middlename, :namesuffix, :companyname,
14
+ :street, :city, :stateprovince, :postcode, :countrycode,
15
+ :phone, :email,
16
+ :http_accept, :user_agent
17
+
18
+
19
+ def initialize(options={})
20
+ options.each { |key, value| self.send("#{key}=", value) if self.respond_to? key } if (!options.nil? && options.is_a?(Hash))
21
+ end
22
+
23
+ def add_to_xml(doc)
24
+ op = REXML::XPath.first(doc, "//Request")
25
+ op.delete_element "CustomerInfo"
26
+ ci = op.add_element "CustomerInfo"
27
+ postal = ci.add_element("Postal")
28
+ name = postal.add_element("Name")
29
+ name.text = self.fullname if self.fullname
30
+ name.add_element("NamePrefix").add_text(self.title) if self.title
31
+ name.add_element("FirstName").add_text(self.firstname) if self.firstname
32
+ name.add_element("MiddleName").add_text(self.middlename) if self.middlename
33
+ name.add_element("LastName").add_text(self.lastname) if self.lastname
34
+ name.add_element("NameSuffix").add_text(self.namesuffix) if self.namesuffix
35
+ postal.add_element("Company").add_text(self.companyname) if self.companyname
36
+ postal.add_element("Street").add_text(self.street) if self.street
37
+ postal.add_element("City").add_text(self.city) if self.city
38
+ postal.add_element("StateProv").add_text(self.stateprovince) if self.stateprovince
39
+ postal.add_element("PostalCode").add_text(self.postcode) if self.postcode
40
+ postal.add_element("CountryCode").add_text(self.countrycode) if self.countrycode
41
+ ci.add_element("Telecom").add_element("Phone").add_text(self.phone) if self.phone
42
+ ci.add_element("Online").add_element("Email").add_text(self.email) if self.email
43
+ ci.add_element("Accept").add_text(self.http_accept) if self.http_accept
44
+ ci.add_element("UserAgent").add_text(self.user_agent) if self.user_agent
45
+ end
46
+ end
47
+ end