xpay 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ module Xpay
2
+
3
+ # The following attribute is required for each operation:
4
+ # amount -> supplied either as String or Integer, must be in Base i.e.: 10.99 becomes 1099
5
+ #
6
+ # The following attributes are taken from the default config and can be overridden here for this request only:
7
+ # auth_type: defaults to the module default. Options are: AUTH, ST3DCARDQUERY, AUTHREVERSAL,REFUND,REFUNDREVERSAL, SETTLEMENT
8
+ # currency: defaults to module default. Override with approved currencies
9
+ # settlement day:
10
+ # callback_url: Specify the callback url for the callback from the 3D secure server. (Will be the bank that issued the card usually)
11
+ # site_reference and site_alias: are usually the same. Override the default setting for this request
12
+ # merchant_name: same as above
13
+ #
14
+ # The following attributes are entirely optional
15
+ # order_reference: Your internal order reference
16
+ # order_info: Additional info about this order/transaction
17
+
18
+ class Operation
19
+
20
+ attr_accessor :auth_type, :currency, :settlement_day, :callback_url, :site_reference, :site_alias, :merchant_name,
21
+ :order_reference, :order_info
22
+
23
+ def initialize(options={})
24
+ if !options.nil? && options.is_a?(Hash)
25
+ options.each do |key, value|
26
+ self.send("#{key}=", value) if self.respond_to? key
27
+ end
28
+ end
29
+ end
30
+
31
+ def add_to_xml(doc)
32
+ req = REXML::XPath.first(doc, "//Request")
33
+ req.attributes["Type"] = self.auth_type if self.auth_type
34
+ REXML::XPath.first(doc, "//SiteReference").text = self.site_reference if self.site_reference
35
+ ops = REXML::XPath.first(doc, "//Operation")
36
+ (ops.elements["Amount"] || ops.add_element("Amount")).text = self.amount if self.amount
37
+ (ops.elements["Currency"] || ops.add_element("Currency")).text = self.currency if self.currency
38
+ (ops.elements["MerchantName"] || ops.add_element("MerchantName")).text = self.merchant_name if self.merchant_name
39
+ (ops.elements["TermUrl"] || ops.add_element("TermUrl")).text = self.callback_url if self.callback_url
40
+ if (self.order_reference || self.order_info)
41
+ req.delete_element("Order")
42
+ order = req.add_element("Order")
43
+ order.add_element("OrderReference").add_text(self.order_reference) if self.order_reference
44
+ order.add_element("OrderInformation").add_text(self.order_info) if self.order_info
45
+ end
46
+ root = doc.root
47
+ (root.elements["Certificate"] || root.add_element("Certificate")).text = self.site_alias if self.site_alias
48
+ end
49
+
50
+ def amount=(new_val)
51
+ @amount = new_val.to_s
52
+ end
53
+
54
+ def amount
55
+ @amount
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,209 @@
1
+ module Xpay
2
+
3
+ # Payment Class handles all payment transaction AUTH, ST3DCARDQUERY and ST3DAUTH, also repeat transactions with ParentTransactionReference
4
+ # instantiated with p = Xpay::Payment.new(options)
5
+ # options is a Hash of the form keys creditcard, customer and operation
6
+ # there are several different options:
7
+ #
8
+ # Option 1: pass as hash options = {:creditcard => {}, :customer => {}, :operation => {}}
9
+ # in this case if the hash key is present and new CreditCard, Customer and Operation instance will be created from each hash and assigned to the class attributes
10
+ #
11
+ # Option 2: pass as Class instances of Xpay::CreditCard, Xpay::Customer and Xpay::Operation
12
+ # simply assigns it to class attributes
13
+ #
14
+ # Option 3: create with emtpy hash and use attribute accessors
15
+ # both as class and hash are possible
16
+ #
17
+ # It is not necessary to use the inbuilt classes Xpay::CreditCard, Xpay::Customer and Xpay::Operation,
18
+ # you can use your own classes (for example if you have an active_record CreditCard class.
19
+ # In this case your class(es) needs to implement a .add_to_xml method (best to copy the code from the gem classes)
20
+ #
21
+ # After initalization call the .make_payment method to process the payment
22
+ # return codes are as follows:
23
+
24
+
25
+ class Payment < Transaction
26
+ attr_reader :creditcard, :customer, :operation
27
+
28
+ def initialize(options={})
29
+ @request_xml = REXML::Document.new(Xpay.root_to_s)
30
+ self.creditcard = options[:creditcard] #.is_a?(Hash) ? Xpay::CreditCard.new(options[:creditcard]) : options[:creditcard]
31
+ self.customer = options[:customer].is_a?(Hash) ? Xpay::Customer.new(options[:customer]) : options[:customer]
32
+ self.operation = options[:operation].is_a?(Hash) ? Xpay::Operation.new(options[:operation]) : options[:operation]
33
+ create_from_xml(options[:xml], options[:pares] || nil) if options[:xml]
34
+ create_request
35
+ end
36
+
37
+ def creditcard=(v)
38
+ @creditcard = v.is_a?(Hash) ? Xpay::CreditCard.new(v) : v
39
+ create_request
40
+ end
41
+
42
+ def customer=(v)
43
+ @customer = v.is_a?(Hash) ? Xpay::Customer.new(v) : v
44
+ create_request
45
+ end
46
+
47
+ def operation=(v)
48
+ @operation = v.is_a?(Hash) ? Xpay::Operation.new(v) : v
49
+ create_request
50
+ end
51
+
52
+ # the make_payment method is where all the action is happening
53
+ # call it after you have initalized the Xpay::Payment class
54
+ # the following returns are possible:
55
+ #
56
+ # -1 a 3D Secure Authorisation is required, query your payment instance e.g. p.three_secure
57
+ # this will return a hash with all the necessary information to process the request further
58
+ # TODO provide further documentation for three_secure response block
59
+ #
60
+ # 0 Error in processing settlement request,
61
+ # query your instance e.g. p.response_block for further information
62
+ #
63
+ # 1 Settlement request approved,
64
+ # query your instance e.g. p.response_block for further information
65
+ #
66
+ # 2 Settlement request declined,
67
+ # query your instance e.g. p.response_block for further information
68
+
69
+ def make_payment
70
+ @response_xml = self.process()
71
+ if request_method=="ST3DCARDQUERY"
72
+ # In case the request was a ST3DCARDQUERY (the default case) the further processing depends on the respones. If it was an AUTH request than all is done and the response_xml gets processed
73
+ @response_xml = self.process() if response_code==0 # try once more if the response code is ZERO in ST3DCARDQUERY (According to securtrading tech support)
74
+ case response_code
75
+ when 1 # one means -> 3D AUTH required
76
+ rewrite_request_block() # Rewrite the request block with information from the response, deleting unused items
77
+
78
+ # If the card is enrolled in the scheme a redirect to a 3D Secure server is necessary, for this we need to store the request_xml in the database to be retrieved after the callback from the 3D secure Server and used to initialize a new payment object
79
+ # otherwise, if the card is not enrolled we just do a 3D AUTH straight away
80
+ if REXML::XPath.first(@response_xml, "//Enrolled").text == "Y"
81
+ rt = -1
82
+ else
83
+ # The Card is not enrolled and we do a 3D Auth request without going through a 3D Secure Server
84
+ # The PaRes element is required but empty as we did not go through a 3D Secure Server
85
+ threedsecure = REXML::XPath.first(@request_xml, "//ThreeDSecure")
86
+ pares = threedsecure.add_element("PaRes")
87
+ pares.text = ""
88
+ @response_xml = self.process()
89
+ rt = REXML::XPath.first(@response_xml, "//Result").text.to_i
90
+ end
91
+ when 2 # TWO -> do a normal AUTH request
92
+ rewrite_request_block("AUTH") # Rewrite the request block as AUTH request with information from the response, deleting unused items
93
+ @response_xml = self.process()
94
+ rt = REXML::XPath.first(@response_xml, "//Result").text.to_i
95
+ else # ALL other cases, payment declined
96
+ rt = REXML::XPath.first(@response_xml, "//Result").text.to_i
97
+ end
98
+ else
99
+ rt = REXML::XPath.first(@response_xml, "//Result").text.to_i
100
+ end
101
+ return rt
102
+ end
103
+
104
+ # The response_block is a hash and can have one of several values:
105
+ # the follwing values are always present after a transaction and can be queried to gain further details of the transaction:
106
+ # * result_code:
107
+ # 0 for failure, check error_code for further details
108
+ # 1 transaction was succesful
109
+ # 2 transaction was denied
110
+ # * security_response_code
111
+ # * security_response_postcode
112
+ # * transaction_reference
113
+ # * transactionverifier
114
+ # * transaction_time
115
+ # * auth_code
116
+ # * settlement_status
117
+ # * error_code
118
+ def response_block
119
+ create_response_block
120
+ end
121
+
122
+ def three_secure
123
+ create_three_secure
124
+ end
125
+
126
+ private
127
+ def create_request
128
+ self.creditcard.add_to_xml(@request_xml) if self.creditcard.respond_to?(:add_to_xml)
129
+ self.customer.add_to_xml(@request_xml) if self.customer.respond_to?(:add_to_xml)
130
+ self.operation.add_to_xml(@request_xml) if self.operation.respond_to?(:add_to_xml)
131
+ end
132
+
133
+ #TODO function to create classes (Customer, CreditCard and Operation) from xml document
134
+ def create_from_xml(xml, pares)
135
+ raise PaResMissing.new "(2500) PaRes argument can not be omitted." if pares.nil?
136
+ @request_xml = REXML::Document.new xml
137
+ REXML::XPath.first(@request_xml, "//ThreeDSecure").add_element("PaRes").text=pares
138
+ end
139
+
140
+ def create_response_block
141
+ @response_xml.is_a?(REXML::Document) ?
142
+ {
143
+ :result_code => (REXML::XPath.first(@response_xml, "//Result").text.to_i rescue nil),
144
+ :security_response_code => (REXML::XPath.first(@response_xml, "//SecurityResponseSecurityCode").text.to_i rescue nil),
145
+ :security_response_postcode => (REXML::XPath.first(@response_xml, "//SecurityResponsePostCode").text.to_i rescue nil),
146
+ :security_response_address => (REXML::XPath.first(@response_xml, "//SecurityResponseAddress").text.to_i rescue nil),
147
+ :transaction_reference => (REXML::XPath.first(@response_xml, "//TransactionReference").text rescue nil),
148
+ :transactionverifier => (REXML::XPath.first(@response_xml, "//TransactionVerifier").text rescue nil),
149
+ :transaction_time => (REXML::XPath.first(@response_xml, "//TransactionCompletedTimestamp").text rescue nil),
150
+ :auth_code => (REXML::XPath.first(@response_xml, "//AuthCode").text rescue nil),
151
+ :settlement_status => (REXML::XPath.first(@response_xml, "//SettleStatus").text.to_i rescue nil),
152
+ :error_code => (REXML::XPath.first(@response_xml, "//Message").text rescue nil)
153
+ } : {}
154
+ end
155
+
156
+ def create_three_secure
157
+ @response_xml.is_a?(REXML::Document) ?
158
+ {
159
+ :md => (REXML::XPath.first(@response_xml, "//MD").text rescue nil),
160
+ :pareq => (REXML::XPath.first(@response_xml, "//PaReq").text rescue nil),
161
+ :termurl => (REXML::XPath.first(@response_xml, "//TermUrl").text rescue nil),
162
+ :acsurl => (REXML::XPath.first(@response_xml, "//AcsUrl").text rescue nil),
163
+ :html => (REXML::XPath.first(@response_xml, "//Html").text rescue nil),
164
+ :request_xml => (@request_xml.to_s rescue nil)
165
+ } : {}
166
+ end
167
+
168
+ # Rewrites the request according to the response coming from SecureTrading according to the required auth_type
169
+ # This only applies if the inital request was a ST3DCARDQUERY
170
+ # It deletes elements which are not needed for the subsequent request and
171
+ # adds the required additional information if an ST3DAUTH is needed
172
+ def rewrite_request_block(auth_type="ST3DAUTH")
173
+
174
+ # set the required AUTH type
175
+ REXML::XPath.first(@request_xml, "//Request").attributes["Type"] = auth_type
176
+
177
+ # delete term url and merchant name
178
+ op = REXML::XPath.first(@request_xml, "//Operation")
179
+ op.delete_element "TermUrl"
180
+ op.delete_element "MerchantName"
181
+
182
+ # delete accept and user agent in customer info
183
+ customer_info = REXML::XPath.first(@request_xml, "//CustomerInfo")
184
+ customer_info.delete_element "//Accept"
185
+ customer_info.delete_element "//UserAgent"
186
+
187
+ # delete credit card details and add TransactionVerifier and TransactionReference from response xml
188
+ # CC details are not needed anymore as verifier and reference are sufficient
189
+ cc_details = REXML::XPath.first(@request_xml, "//CreditCard")
190
+ cc_details.delete_element "//Number"
191
+ cc_details.delete_element "//Type"
192
+ trans_ver = cc_details.add_element("TransactionVerifier")
193
+ trans_ver.text = REXML::XPath.first(@response_xml, "//TransactionVerifier").text
194
+ trans_ref = cc_details.add_element("ParentTransactionReference")
195
+ trans_ref.text = REXML::XPath.first(@response_xml, "//TransactionReference").text
196
+
197
+ # unless it is an AUTH request, add additional required info for a 3DAUTH request
198
+ unless auth_type == "AUTH"
199
+ pm_method = REXML::XPath.first(@request_xml, "//PaymentMethod")
200
+ threedsecure = pm_method.add_element("ThreeDSecure")
201
+ enrolled = threedsecure.add_element("Enrolled")
202
+ enrolled.text = REXML::XPath.first(@response_xml, "//Enrolled").text
203
+ md = threedsecure.add_element("MD")
204
+ md.text = REXML::XPath.first(@response_xml, "//MD").text rescue ""
205
+ end
206
+ true
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,35 @@
1
+ module Xpay
2
+ require 'socket'
3
+
4
+ # The transaction class is the parent class of all Transactions be it Payment, Refund or Paypal etc.
5
+ # it provides underlying methods which all transactions have in common
6
+ # It should not be instantiated by itself
7
+
8
+ class Transaction
9
+
10
+ attr_accessor :request_xml
11
+ attr_reader :three_secure, :response_xml, :response_block
12
+
13
+ def process()
14
+ a = TCPSocket.open("localhost", Xpay.config.port)
15
+ a.write(self.request_xml.to_s + "\n")
16
+ res = a.read()
17
+ a.close
18
+ # create an xml document, use everything from the start of <ResponseBlock to the end, discard header and status etc and return it
19
+ REXML::Document.new res[res.index("<ResponseBlock"), res.length]
20
+ end
21
+
22
+ def request_method
23
+ @request_method ||= REXML::XPath.first(@request_xml, "//Request").attributes["Type"]
24
+ end
25
+
26
+ def response_code
27
+ @response_code ||= REXML::XPath.first(@response_xml, "//Result").text.to_i rescue -1
28
+ end
29
+
30
+ private
31
+ def response_xml=(new_val)
32
+ @response_xml = new_val if new_val.is_a?(REXML::Document)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Xpay
2
+ Version = '0.0.5'
3
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'xpay'
2
+ Xpay.load_config
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :xpay do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,25 @@
1
+ # config/xpay.yml
2
+ base: &base
3
+ port: 5000
4
+ merchant_name: Outlet Residential
5
+
6
+ development:
7
+ <<: *base
8
+ alias: testoutlet12091
9
+ site_reference: testoutlet12091
10
+ version: "3.52"
11
+ port: 6000
12
+ callback_url: "http://127.0.0.1/callback"
13
+ default_query: "AUTH"
14
+ settlement_day: "2"
15
+ default_currency: "USD"
16
+
17
+ test:
18
+ <<: *base
19
+ alias: testoutlet12091
20
+ site_reference: testoutlet12091
21
+
22
+ production:
23
+ <<: *base
24
+ alias: accommodationoutlet2538
25
+ site_reference: accommodationoutlet2538
@@ -0,0 +1,37 @@
1
+ base: &base
2
+ security_code: 123
3
+ valid_until: <%= Date.today.advance(:months => 3).strftime("%m/%y") %>
4
+
5
+ class_test:
6
+ <<: *base
7
+ card_type: Visa
8
+ number: 4111111111111111
9
+ security_code: 123
10
+ valid_from: <%= Date.today.advance(:months => -5).strftime("%m/%y") %>
11
+ issue: "1"
12
+
13
+
14
+ visa_no3d_auth:
15
+ <<: *base
16
+ card_type: Visa
17
+ number: 4111111111111111
18
+
19
+ visa_no3d_decl:
20
+ <<: *base
21
+ card_type: Visa
22
+ number: 4242424242424242
23
+
24
+ visa_3d_auth:
25
+ <<: *base
26
+ card_type: Visa
27
+ number: 4111111111111160
28
+
29
+ master_no3d_auth:
30
+ <<: *base
31
+ card_type: Mastercard
32
+ number: 5111111111111118
33
+
34
+ master_no3d_decl:
35
+ <<: *base
36
+ card_type: Mastercard
37
+ number: 5111111111111142
@@ -0,0 +1 @@
1
+ <?xml version='1.0' encoding='ISO-8859-1'?><RequestBlock Version='3.51'><Request Type='ST3DCARDQUERY'><Operation><SiteReference>site12345</SiteReference><MerchantName>CompanyName</MerchantName><TermUrl>http://localhost/gateway_callback</TermUrl></Operation><CustomerInfo><Postal><Name>JOE BLOGGS<NamePrefix>MR</NamePrefix><FirstName>Joe</FirstName><MiddleName>X</MiddleName><LastName>Bloggs</LastName><NameSuffix>PhD</NameSuffix></Name><Company>NotInventedHere.com</Company><Street>tonowhere crescent</Street><City>beyond the rainbow on stale bread</City><StateProv>East-West Swampshire</StateProv><PostalCode>X01 Z10</PostalCode><CountryCode>GB</CountryCode></Postal><Telecom><Phone>07950 843 363</Phone></Telecom><Online><Email>joe.x.bloggs@notinventedhere.com</Email></Online><Accept>text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</Accept><UserAgent>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.1; .NET4.0C; AskTbGLSV5/5.8.0.12304)</UserAgent></CustomerInfo></Request><Certificate>site12345</Certificate></RequestBlock>
@@ -0,0 +1,22 @@
1
+ class_test:
2
+ title: MR
3
+ fullname: JOE BLOGGS
4
+ firstname: Joe
5
+ lastname: Bloggs
6
+ middlename: X
7
+ namesuffix: PhD
8
+ companyname: NotInventedHere.com
9
+ street: tonowhere crescent
10
+ city: beyond the rainbow on stale bread
11
+ stateprovince: East-West Swampshire
12
+ postcode: X01 Z10
13
+ countrycode: GB
14
+ phone: 07950 843 363
15
+ email: joe.x.bloggs@notinventedhere.com
16
+ http_accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
17
+ user_agent: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.1; .NET4.0C; AskTbGLSV5/5.8.0.12304)"
18
+
19
+ test_1:
20
+ fullname: V Pacher
21
+ http_accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
22
+ user_agent: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.1; .NET4.0C; AskTbGLSV5/5.8.0.12304)"
@@ -0,0 +1 @@
1
+ <?xml version='1.0' encoding='ISO-8859-1'?><RequestBlock Version='3.51'><Request Type='AUTH'><Operation><SiteReference>site56987</SiteReference><MerchantName>TestMerchant</MerchantName><TermUrl>https://localhost/3dcallback</TermUrl><Amount>1000</Amount><Currency>USD</Currency></Operation><Order><OrderReference>TestOrder1245</OrderReference><OrderInformation>TestOrderInfo</OrderInformation></Order></Request><Certificate>site56987</Certificate></RequestBlock>
@@ -0,0 +1,23 @@
1
+ class_test:
2
+ auth_type: AUTH
3
+ currency: USD
4
+ amount: "1000"
5
+ settlement_day: "3"
6
+ callback_url: "https://localhost/3dcallback"
7
+ site_reference: site56987
8
+ site_alias: site56987
9
+ merchant_name: TestMerchant
10
+ order_reference: TestOrder1245
11
+ order_info: TestOrderInfo
12
+ port: 7000
13
+
14
+ test_1:
15
+ auth_type: ST3DCARDQUERY
16
+ currency: GBP
17
+ amount: "1000"
18
+ settlement_day: "1"
19
+ site_reference: testoutlet12092
20
+ site_alias: testoutlet12092
21
+ callback_url: "https://localhost/3dcallback"
22
+ merchant_name: Outlet Residential
23
+ order_reference: TestOrder1245
@@ -0,0 +1 @@
1
+ <?xml version='1.0' encoding='ISO-8859-1'?><RequestBlock Version='3.51'><Request Type='ST3DAUTH'><Operation><SiteReference>site56987</SiteReference><Amount>1000</Amount><Currency>USD</Currency></Operation><PaymentMethod><CreditCard><StartDate>05/10</StartDate><ExpiryDate>01/11</ExpiryDate><SecurityCode>123</SecurityCode><Issue>1</Issue><TransactionVerifier>ArOH5um+qGhwLbuobJ5mdgzTr1qclm1j/qyG+VGDe9QSslGFXtwFA2A6jvM+qgUuZ0lLQPvJ82U1SkLOhm32dPRABlh0EkyrvKamrsDDRcsFZhWQLmS+yE0+keSaBq8C537NQAXIYcCYVvoOVKhDwdW4hZVfOtKFND+QxR2p89rw=</TransactionVerifier><ParentTransactionReference>17-9-1909368</ParentTransactionReference></CreditCard><ThreeDSecure><Enrolled>Y</Enrolled><MD>MTI4ODAwNjYzNi41MjAuMTc2MzczMzc3MDExMjg4MDA2NjM2LjUyMC4xNzYzNzMzNzcwMTEyODgwMDY2MzYuNTIwLjE3NjM3MzM3NzAxMTI4ODAwNjYzNi41MjAuMTc2MzczMzc3MDExMjg4MDA2NjM2LjUyMC4xNzYzNzMzNzcwMTEyODgwMDY2MzYuNTIwLjE3NjM3MzM3NzAxMTI4ODAwNjYzNi41MjAuMTc2MzczMzc3MDExMjg4MDA2NjM2LjUyMC4xNzYzNzMzNzcwMTEyODgwMDY2MzYuNTIwLjE3NjM3MzM3NzAxMTI4ODAwNjYzNi41MjAuMTc2MzczMzc3MDE=</MD></ThreeDSecure></PaymentMethod><CustomerInfo><Postal><Name>JOE BLOGGS<NamePrefix>MR</NamePrefix><FirstName>Joe</FirstName><MiddleName>X</MiddleName><LastName>Bloggs</LastName><NameSuffix>PhD</NameSuffix></Name><Company>NotInventedHere.com</Company><Street>tonowhere crescent</Street><City>beyond the rainbow on stale bread</City><StateProv>East-West Swampshire</StateProv><PostalCode>X01 Z10</PostalCode><CountryCode>GB</CountryCode></Postal><Telecom><Phone>07950 843 363</Phone></Telecom><Online><Email>joe.x.bloggs@notinventedhere.com</Email></Online></CustomerInfo><Order><OrderReference>TestOrder1245</OrderReference><OrderInformation>TestOrderInfo</OrderInformation></Order></Request><Certificate>site56987</Certificate></RequestBlock>
@@ -0,0 +1,9 @@
1
+ <ResponseBlock Live='FALSE' Version='3.51'>
2
+ <Response Type='ST3DCARDQUERY'>
3
+ <OperationResponse>
4
+ <Message>(3100) Invalid ExpiryDate</Message>
5
+ <TransactionReference>17-9-1908322</TransactionReference>
6
+ <Result>0</Result>
7
+ </OperationResponse>
8
+ </Response>
9
+ </ResponseBlock>