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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +9 -0
- data/datacash.gemspec +34 -0
- data/lib/datacash.rb +51 -0
- data/lib/datacash/client.rb +61 -0
- data/lib/datacash/exceptions.rb +3 -0
- data/lib/datacash/request/amount.rb +14 -0
- data/lib/datacash/request/authentication.rb +7 -0
- data/lib/datacash/request/base.rb +37 -0
- data/lib/datacash/request/browser.rb +11 -0
- data/lib/datacash/request/card.rb +9 -0
- data/lib/datacash/request/card_transaction.rb +11 -0
- data/lib/datacash/request/cv2avs.rb +7 -0
- data/lib/datacash/request/historic_transaction.rb +8 -0
- data/lib/datacash/request/hps_transaction.rb +8 -0
- data/lib/datacash/request/request.rb +19 -0
- data/lib/datacash/request/thirdman.rb +23 -0
- data/lib/datacash/request/three_d_secure.rb +12 -0
- data/lib/datacash/request/transaction.rb +12 -0
- data/lib/datacash/request/transaction_details.rb +11 -0
- data/lib/datacash/response/auth_attempts.rb +7 -0
- data/lib/datacash/response/base.rb +33 -0
- data/lib/datacash/response/card.rb +7 -0
- data/lib/datacash/response/hps_transaction.rb +12 -0
- data/lib/datacash/response/query_transaction_result.rb +9 -0
- data/lib/datacash/response/response.rb +11 -0
- data/lib/datacash/session.rb +175 -0
- data/lib/datacash/version.rb +3 -0
- data/spec/datacash/client_spec.rb +124 -0
- data/spec/datacash/request/amount_spec.rb +27 -0
- data/spec/datacash/request/authentication_spec.rb +12 -0
- data/spec/datacash/request/browser_spec.rb +18 -0
- data/spec/datacash/request/card_spec.rb +24 -0
- data/spec/datacash/request/card_transaction_spec.rb +28 -0
- data/spec/datacash/request/cv2avs_spec.rb +12 -0
- data/spec/datacash/request/historic_transaction_spec.rb +16 -0
- data/spec/datacash/request/hps_transaction_spec.rb +16 -0
- data/spec/datacash/request/request_spec.rb +58 -0
- data/spec/datacash/request/thirdman_spec.rb +12 -0
- data/spec/datacash/request/three_d_secure_spec.rb +17 -0
- data/spec/datacash/request/transaction_details_spec.rb +36 -0
- data/spec/datacash/request/transaction_spec.rb +24 -0
- data/spec/datacash/response/response_spec.rb +48 -0
- data/spec/datacash/response_spec.rb +48 -0
- data/spec/spec_helper.rb +20 -0
- metadata +265 -0
@@ -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,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,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,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
|