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