datacash 0.0.1

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.
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