xpay 0.0.5

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