datacash 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +48 -0
  6. data/Rakefile +9 -0
  7. data/datacash.gemspec +34 -0
  8. data/lib/datacash.rb +51 -0
  9. data/lib/datacash/client.rb +61 -0
  10. data/lib/datacash/exceptions.rb +3 -0
  11. data/lib/datacash/request/amount.rb +14 -0
  12. data/lib/datacash/request/authentication.rb +7 -0
  13. data/lib/datacash/request/base.rb +37 -0
  14. data/lib/datacash/request/browser.rb +11 -0
  15. data/lib/datacash/request/card.rb +9 -0
  16. data/lib/datacash/request/card_transaction.rb +11 -0
  17. data/lib/datacash/request/cv2avs.rb +7 -0
  18. data/lib/datacash/request/historic_transaction.rb +8 -0
  19. data/lib/datacash/request/hps_transaction.rb +8 -0
  20. data/lib/datacash/request/request.rb +19 -0
  21. data/lib/datacash/request/thirdman.rb +23 -0
  22. data/lib/datacash/request/three_d_secure.rb +12 -0
  23. data/lib/datacash/request/transaction.rb +12 -0
  24. data/lib/datacash/request/transaction_details.rb +11 -0
  25. data/lib/datacash/response/auth_attempts.rb +7 -0
  26. data/lib/datacash/response/base.rb +33 -0
  27. data/lib/datacash/response/card.rb +7 -0
  28. data/lib/datacash/response/hps_transaction.rb +12 -0
  29. data/lib/datacash/response/query_transaction_result.rb +9 -0
  30. data/lib/datacash/response/response.rb +11 -0
  31. data/lib/datacash/session.rb +175 -0
  32. data/lib/datacash/version.rb +3 -0
  33. data/spec/datacash/client_spec.rb +124 -0
  34. data/spec/datacash/request/amount_spec.rb +27 -0
  35. data/spec/datacash/request/authentication_spec.rb +12 -0
  36. data/spec/datacash/request/browser_spec.rb +18 -0
  37. data/spec/datacash/request/card_spec.rb +24 -0
  38. data/spec/datacash/request/card_transaction_spec.rb +28 -0
  39. data/spec/datacash/request/cv2avs_spec.rb +12 -0
  40. data/spec/datacash/request/historic_transaction_spec.rb +16 -0
  41. data/spec/datacash/request/hps_transaction_spec.rb +16 -0
  42. data/spec/datacash/request/request_spec.rb +58 -0
  43. data/spec/datacash/request/thirdman_spec.rb +12 -0
  44. data/spec/datacash/request/three_d_secure_spec.rb +17 -0
  45. data/spec/datacash/request/transaction_details_spec.rb +36 -0
  46. data/spec/datacash/request/transaction_spec.rb +24 -0
  47. data/spec/datacash/response/response_spec.rb +48 -0
  48. data/spec/datacash/response_spec.rb +48 -0
  49. data/spec/spec_helper.rb +20 -0
  50. metadata +265 -0
@@ -0,0 +1,12 @@
1
+ module Datacash
2
+ module Request
3
+ class ThreeDSecure < Base
4
+ root "ThreeDSecure"
5
+
6
+ coerce_key :browser, Browser
7
+
8
+ key :verify, default: 'yes'
9
+ key :purchase_desc, default: 'goods'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Datacash
2
+ module Request
3
+ class Transaction < Base
4
+ root "Transaction"
5
+
6
+ coerce_key :transaction_details, TransactionDetails
7
+ coerce_key :card_transaction, CardTransaction
8
+ coerce_key :hps_transaction, HpsTransaction
9
+ coerce_key :historic_transaction, HistoricTransaction
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Datacash
2
+ module Request
3
+ class TransactionDetails < Base
4
+ root "TxnDetails"
5
+
6
+ coerce_key :amount, Amount
7
+ coerce_key :three_d_secure, ThreeDSecure
8
+ coerce_key :thirdman, Thirdman
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Datacash
2
+ module Response
3
+ class AuthAttempts < Base
4
+ key :attempt, from: 'Attempt'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module Datacash
2
+ module Response
3
+ class Base < Hash
4
+ include Hashie::Extensions::MergeInitializer
5
+ include Hashie::Extensions::MethodReader
6
+ include Hashie::Extensions::Coercion
7
+
8
+ def initialize(attributes = {})
9
+ self.class.keys.each do |key, options|
10
+ next unless options.has_key?(:from)
11
+
12
+ from = options[:from].to_sym
13
+ if attributes.has_key?(from)
14
+ attributes[key] = attributes.delete(from)
15
+ end
16
+ end
17
+ super(attributes)
18
+ end
19
+
20
+ def self.keys
21
+ @keys ||= {}
22
+ end
23
+
24
+ def self.key(key, options={})
25
+ keys[key.to_sym] = options
26
+ end
27
+
28
+ def success?
29
+ self[:status].to_i == 1
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module Datacash
2
+ module Response
3
+ class Card < Base
4
+ key :cv2avs, from: 'Cv2Avs'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Datacash
2
+ module Response
3
+ class HpsTransaction < Base
4
+ key :auth_attempts, from: 'AuthAttempts'
5
+ coerce_key :auth_attempts, AuthAttempts
6
+
7
+ def redirect_url
8
+ "#{self[:hps_url]}?HPS_SessionID=#{self[:session_id]}"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Datacash
2
+ module Response
3
+ class QueryTransactionResult < Base
4
+ key :card, from: 'Card'
5
+
6
+ coerce_key :card, Card
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Datacash
2
+ module Response
3
+ class Response < Base
4
+ key :query_transaction_result, from: 'QueryTxnResult'
5
+ key :hps_transaction, from: 'HpsTxn'
6
+
7
+ coerce_key :query_transaction_result, QueryTransactionResult
8
+ coerce_key :hps_transaction, HpsTransaction
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,175 @@
1
+ module Datacash
2
+ class SessionRequest
3
+ #def initialize(options = {})
4
+ #@client = options.fetch(:client)
5
+ #@datacash_page_set_id = options.fetch(:datacash_page_set_id, 0)
6
+ #@datacash_3d_secure_enabled = options.fetch(:datacash_3d_secure_enabled, true)
7
+ #rescue KeyError => e
8
+ #raise ArgumentError, "Missing option - #{e}"
9
+ #end
10
+
11
+ #private
12
+ #attr_reader :client
13
+
14
+ #belongs_to :order
15
+
16
+ #before_validation :generate_our_reference, :on => :create
17
+ #before_validation :generate_amount, :on => :create
18
+ #before_validation :execute_session_request, :on => :create
19
+ #serialize :response
20
+
21
+ #TIME_FORMAT = '%Y%m%d %H:%M:%S'
22
+
23
+ #attr_accessor :payment_url
24
+
25
+ #def update_status!
26
+ #response = DataCash.query(datacash_reference)
27
+
28
+ ## if the server returns 200 ok and datacash returns a status of 1
29
+ ## payment has been successfully taken and there will be auth attempts
30
+ #if response.successful? && response.status == 1
31
+
32
+ #attempts = []
33
+ #attempts << response.data['HpsTxn']['AuthAttempts']['Attempt']
34
+ #attempts.flatten!
35
+
36
+ #for attempt in attempts
37
+ #if order.order_payments.where('datacash_reference = ?', attempt['datacash_reference']).count == 0
38
+
39
+ #if attempt['reason'] == 'ACCEPTED'
40
+ #order.order_payments.create(
41
+ #:amount => amount,
42
+ #:datacash_reference => attempt['datacash_reference']
43
+ #)
44
+ #end
45
+ #end
46
+ #end
47
+
48
+ #self.response = response.data
49
+ #self.successful = true
50
+ #end
51
+ #self.updated_at = Time.now
52
+ #save
53
+ #end
54
+
55
+
56
+
57
+ #private
58
+ #def generate_our_reference
59
+ #self.our_reference = "#{self.order.order_number}-#{Time.now.to_i.to_s}".first(30)
60
+ #end
61
+
62
+ #def generate_amount
63
+ #self.amount = order.total_inc_vat
64
+ #end
65
+
66
+ #def execute_session_request
67
+ #response = session_request
68
+
69
+ ## if the server returns 200 ok and datacash returns a status of 1
70
+ ## the session has been successfully created
71
+ #if response.successful? && response.status == 1
72
+ #self.datacash_reference = response.datacash_reference
73
+ #self.payment_url = "#{response.data['HpsTxn']['hps_url']}?HPS_SessionID=#{response.data['HpsTxn']['session_id']}"
74
+ #else
75
+ #logger.error "datacash did not return a valid session response"
76
+ #errors.add(:datacash_reference, "datacash did not return a valid session response")
77
+ #end
78
+ #end
79
+
80
+ ## Requests a session from DataCash
81
+ #def session_request
82
+ #DataCash.request do |xml|
83
+
84
+ #xml.tag! :TxnDetails do
85
+
86
+ #xml.tag! :merchantreference, our_reference # reference can not be more than 30 characters
87
+ #xml.tag! :amount, amount.to_s, :currency => 'GBP'
88
+
89
+ #if APP_CONFIG[:datacash_3d_secure_enabled] == 'true'
90
+
91
+ #xml.tag! :ThreeDSecure do
92
+ #xml.tag! :verify, 'yes'
93
+ #xml.tag! :merchant_url, "http://#{APP_CONFIG[:domain]}"
94
+ #xml.tag! :purchase_desc, 'goods'
95
+ #xml.tag! :purchase_datetime, Time.now.strftime(TIME_FORMAT)
96
+
97
+ #xml.tag! :Browser do
98
+ #xml.tag! :device_category, 0
99
+ #xml.tag! :accept_headers, '*/*'
100
+ #xml.tag! :user_agent, 'Mozilla/5.0'
101
+ #end
102
+ #end
103
+ #end
104
+
105
+ #xml.tag! :The3rdMan do
106
+ #xml.tag! :CustomerInformation do
107
+ #xml.tag! :order_number, order.order_number
108
+ #xml.tag! :customer_reference, order.customer.id
109
+ #xml.tag! :title, order.invoice_address.title
110
+ #xml.tag! :forename, order.invoice_address.first_name
111
+ #xml.tag! :surname, order.invoice_address.last_name
112
+ #xml.tag! :delivery_title, order.delivery_address.title
113
+ #xml.tag! :delivery_forename, order.delivery_address.first_name
114
+ #xml.tag! :delivery_surname, order.delivery_address.last_name
115
+ #xml.tag! :telephone, order.invoice_address.phone.gsub(/[\D]/, '')
116
+ #xml.tag! :email, order.email
117
+ #xml.tag! :ip_address, order.ip_address
118
+ #end
119
+ #xml.tag! :DeliveryAddress do
120
+ #xml.tag! :street_address_1, order.delivery_address.address1
121
+ #xml.tag! :street_address_2, order.delivery_address.address2
122
+ #xml.tag! :city, order.delivery_address.town
123
+ #xml.tag! :county, order.delivery_address.county
124
+ #xml.tag! :postcode, order.delivery_address.postcode
125
+ #end
126
+ #xml.tag! :BillingAddress do
127
+ #xml.tag! :street_address_1, order.invoice_address.address1
128
+ #xml.tag! :street_address_2, order.invoice_address.address2
129
+ #xml.tag! :city, order.invoice_address.town
130
+ #xml.tag! :county, order.invoice_address.county
131
+ #xml.tag! :postcode, order.invoice_address.postcode
132
+ #end
133
+
134
+ #unless order.discount
135
+ #xml.tag! :OrderInformation do
136
+ #xml.tag! :Products, :count => order.order_lines.count do
137
+ #for order_line in order.order_lines
138
+ #xml.tag! :Product do
139
+ #xml.tag! :code, order_line.reference
140
+ #xml.tag! :quantity, order_line.quantity
141
+ #xml.tag! :price, order_line.unit_price
142
+ #xml.tag! :prod_description, order_line.name
143
+ #if order_line.product
144
+ #xml.tag! :prod_id, order_line.product.id
145
+ #xml.tag! :prod_category, order_line.product.category.name if order_line.product.category
146
+ #xml.tag! :prod_type, order_line.product.product_type.name if order_line.product.product_type
147
+ #end
148
+ #end
149
+ #end
150
+ #end
151
+ #end
152
+ #end
153
+ #end
154
+ #end
155
+
156
+ #xml.tag! :HpsTxn do
157
+ #xml.tag! :method, 'setup_full'
158
+ #xml.tag! :page_set_id, APP_CONFIG[:datacash_page_set_id]
159
+ #xml.tag! :return_url, "http#{'s' unless Rails.env == 'development'}://#{APP_CONFIG[:domain]}/order/payment"
160
+ #xml.tag! :expiry_url, "http#{'s' unless Rails.env == 'development'}://#{APP_CONFIG[:domain]}/order/payment/new"
161
+ #end
162
+
163
+ #xml.tag! :CardTxn do
164
+ #xml.tag! :method, 'auth'
165
+ #xml.tag! :Card do
166
+ #xml.tag! :Cv2Avs do
167
+ #xml.tag! :street_address1, order.invoice_address.address1.first(100) # bank only checks numeric data
168
+ #xml.tag! :postcode, order.invoice_address.postcode.first(10)
169
+ #end
170
+ #end
171
+ #end
172
+ #end
173
+ #end
174
+ end
175
+ end
@@ -0,0 +1,3 @@
1
+ module Datacash
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Datacash::Client, "#post" do
4
+
5
+ subject do
6
+ described_class.new(
7
+ client: "TEST",
8
+ password: "PASSWORD123"
9
+ )
10
+ end
11
+
12
+ describe "#post" do
13
+ context "when not authenticated" do
14
+
15
+ let(:request) do
16
+ '<?xml version="1.0" encoding="UTF-8"?>'\
17
+ '<Request>'\
18
+ '<Authentication>'\
19
+ '<client>TEST</client><password>PASSWORD123</password>'\
20
+ '</Authentication>'\
21
+ '<Transaction></Transaction>'\
22
+ '</Request>'
23
+ end
24
+
25
+ let(:response) do
26
+ '<?xml version="1.0" encoding="UTF-8"?>'\
27
+ '<Response>'\
28
+ '<reason>Invalid CLIENT/PASS</reason><status>10</status>'\
29
+ '</Response>'
30
+ end
31
+
32
+ before do
33
+ stub_request(:post, "https://accreditation.datacash.com/Transaction/cnp_a").
34
+ with(body: request).
35
+ to_return(:status => 200, :body => response)
36
+ end
37
+
38
+ it "should raise AuthenticationError" do
39
+ expect { subject.post(Datacash::Request::Request.new)}.to raise_error(Datacash::AuthenticationError)
40
+ end
41
+ end
42
+
43
+ context "response containing an array of elements" do
44
+ let(:response) do
45
+ '<?xml version="1.0" encoding="UTF-8"?>'\
46
+ '<Response>'\
47
+ '<HpsTxn>'\
48
+ '<AuthAttempts>'\
49
+ '<Attempt>'\
50
+ '<datacash_reference>1</datacash_reference>'\
51
+ '<dc_response>1</dc_response>'\
52
+ '<reason>ACCEPTED</reason>'\
53
+ '</Attempt>'\
54
+ '<Attempt>'\
55
+ '<datacash_reference>2</datacash_reference>'\
56
+ '<dc_response>1</dc_response>'\
57
+ '<reason>ACCEPTED</reason>'\
58
+ '</Attempt>'\
59
+ '</AuthAttempts>'\
60
+ '</HpsTxn>'\
61
+ '<reason></reason>'\
62
+ '</Response>'
63
+ end
64
+
65
+ before do
66
+ stub_request(:post, "https://accreditation.datacash.com/Transaction/cnp_a").
67
+ to_return(:status => 200, :body => response)
68
+ end
69
+
70
+ it "should return all the elements" do
71
+ response = subject.post(Datacash::Request::Request.new)
72
+ response.hps_transaction.auth_attempts do |attempt|
73
+ attempt.should be_a(Datacash::Response::Attempt)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "#query" do
80
+
81
+ let(:request) do
82
+ '<?xml version="1.0" encoding="UTF-8"?>'\
83
+ '<Request>'\
84
+ '<Authentication>'\
85
+ '<client>TEST</client><password>PASSWORD123</password>'\
86
+ '</Authentication>'\
87
+ '<Transaction>'\
88
+ '<HistoricTxn>'\
89
+ '<method>query</method>'\
90
+ '<reference>12345</reference>'\
91
+ '</HistoricTxn>'\
92
+ '</Transaction>'\
93
+ '</Request>'
94
+ end
95
+
96
+ let(:response) do
97
+ '<?xml version="1.0" encoding="UTF-8"?>'\
98
+ '<Response>'\
99
+ '<HpsTxn>'\
100
+ '<AuthAttempts>'\
101
+ '<Attempt>'\
102
+ '<datacash_reference>1</datacash_reference>'\
103
+ '<dc_response>1</dc_response>'\
104
+ '<reason>ACCEPTED</reason>'\
105
+ '</Attempt>'\
106
+ '<Attempt>'\
107
+ '<datacash_reference>2</datacash_reference>'\
108
+ '<dc_response>1</dc_response>'\
109
+ '<reason>ACCEPTED</reason>'\
110
+ '</Attempt>'\
111
+ '</AuthAttempts>'\
112
+ '</HpsTxn>'\
113
+ '<reason></reason>'\
114
+ '</Response>'
115
+ end
116
+
117
+ it "should send the correct request" do
118
+ stub_request(:post, "https://accreditation.datacash.com/Transaction/cnp_a").
119
+ with(body: request).
120
+ to_return(:status => 200, :body => response)
121
+ subject.query("12345")
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Datacash::Request::Amount do
4
+
5
+ describe "#to_xml" do
6
+ subject do
7
+ MultiXml.parse(
8
+ described_class.new(
9
+ amount: 12.00,
10
+ currency: 'PLN'
11
+ ).to_xml
12
+ )
13
+ end
14
+
15
+ it "should have a root element of 'amount'" do
16
+ subject.should have_key('amount')
17
+ end
18
+
19
+ it "should have an amount" do
20
+ subject['amount']['__content__'].to_f.should eq(12.00)
21
+ end
22
+
23
+ it "should have a currency attribute" do
24
+ subject['amount']['currency'].should eq('PLN')
25
+ end
26
+ end
27
+ end