activemerchant 1.58.0 → 1.59.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +54 -0
  3. data/README.md +3 -3
  4. data/lib/active_merchant/billing/check.rb +3 -0
  5. data/lib/active_merchant/billing/credit_card.rb +7 -2
  6. data/lib/active_merchant/billing/credit_card_methods.rb +5 -1
  7. data/lib/active_merchant/billing/gateway.rb +5 -3
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +3 -3
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +34 -5
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +348 -0
  12. data/lib/active_merchant/billing/gateways/braintree_blue.rb +6 -3
  13. data/lib/active_merchant/billing/gateways/card_stream.rb +33 -15
  14. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -0
  15. data/lib/active_merchant/billing/gateways/cyber_source.rb +7 -3
  16. data/lib/active_merchant/billing/gateways/global_collect.rb +293 -0
  17. data/lib/active_merchant/billing/gateways/jetpay.rb +11 -8
  18. data/lib/active_merchant/billing/gateways/latitude19.rb +416 -0
  19. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +13 -0
  20. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +10 -7
  21. data/lib/active_merchant/billing/gateways/mercury.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/metrics_global.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/moneris.rb +8 -1
  24. data/lib/active_merchant/billing/gateways/nmi.rb +25 -9
  25. data/lib/active_merchant/billing/gateways/openpay.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/orbital.rb +5 -3
  27. data/lib/active_merchant/billing/gateways/paymill.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -6
  29. data/lib/active_merchant/billing/gateways/payu_in.rb +3 -2
  30. data/lib/active_merchant/billing/gateways/s5.rb +8 -5
  31. data/lib/active_merchant/billing/gateways/sage.rb +1 -7
  32. data/lib/active_merchant/billing/gateways/sage_pay.rb +0 -4
  33. data/lib/active_merchant/billing/gateways/secure_net.rb +0 -5
  34. data/lib/active_merchant/billing/gateways/secure_pay.rb +1 -1
  35. data/lib/active_merchant/billing/gateways/securion_pay.rb +46 -17
  36. data/lib/active_merchant/billing/gateways/stripe.rb +5 -8
  37. data/lib/active_merchant/billing/gateways/tns.rb +1 -1
  38. data/lib/active_merchant/billing/gateways/trans_first.rb +1 -2
  39. data/lib/active_merchant/billing/gateways/vanco.rb +1 -1
  40. data/lib/active_merchant/billing/gateways/visanet_peru.rb +218 -0
  41. data/lib/active_merchant/billing/gateways/world_net.rb +344 -0
  42. data/lib/active_merchant/billing/gateways/worldpay.rb +8 -11
  43. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +4 -0
  44. data/lib/active_merchant/country.rb +0 -2
  45. data/lib/active_merchant/version.rb +1 -1
  46. metadata +7 -2
@@ -572,12 +572,15 @@ module ActiveMerchant #:nodoc:
572
572
  end
573
573
  parameters[:billing] = map_address(options[:billing_address]) if options[:billing_address]
574
574
  parameters[:shipping] = map_address(options[:shipping_address]) if options[:shipping_address]
575
- parameters[:channel] = application_id if application_id.present? && application_id != "ActiveMerchant"
576
575
 
577
- if options[:descriptor_name] || options[:descriptor_phone]
576
+ channel = @options[:channel] || application_id
577
+ parameters[:channel] = channel if channel
578
+
579
+ if options[:descriptor_name] || options[:descriptor_phone] || options[:descriptor_url]
578
580
  parameters[:descriptor] = {
579
581
  name: options[:descriptor_name],
580
- phone: options[:descriptor_phone]
582
+ phone: options[:descriptor_phone],
583
+ url: options[:descriptor_url]
581
584
  }
582
585
  end
583
586
 
@@ -75,21 +75,21 @@ module ActiveMerchant #:nodoc:
75
75
  super
76
76
  end
77
77
 
78
- def authorize(money, creditcard, options = {})
78
+ def authorize(money, credit_card_or_reference, options = {})
79
79
  post = {}
80
80
  add_pair(post, :captureDelay, -1)
81
81
  add_amount(post, money, options)
82
- add_invoice(post, creditcard, money, options)
83
- add_creditcard(post, creditcard)
82
+ add_invoice(post, credit_card_or_reference, money, options)
83
+ add_credit_card_or_reference(post, credit_card_or_reference)
84
84
  add_customer_data(post, options)
85
85
  commit('SALE', post)
86
86
  end
87
87
 
88
- def purchase(money, creditcard, options = {})
88
+ def purchase(money, credit_card_or_reference, options = {})
89
89
  post = {}
90
90
  add_amount(post, money, options)
91
- add_invoice(post, creditcard, money, options)
92
- add_creditcard(post, creditcard)
91
+ add_invoice(post, credit_card_or_reference, money, options)
92
+ add_credit_card_or_reference(post, credit_card_or_reference)
93
93
  add_customer_data(post, options)
94
94
  commit('SALE', post)
95
95
  end
@@ -112,7 +112,7 @@ module ActiveMerchant #:nodoc:
112
112
  def void(authorization, options = {})
113
113
  post = {}
114
114
  add_pair(post, :xref, authorization)
115
- commit('REFUND', post)
115
+ commit('CANCEL', post)
116
116
  end
117
117
 
118
118
  def verify(creditcard, options={})
@@ -149,20 +149,34 @@ module ActiveMerchant #:nodoc:
149
149
  end
150
150
  end
151
151
 
152
- def add_invoice(post, credit_card, money, options)
152
+ def add_invoice(post, credit_card_or_reference, money, options)
153
153
  add_pair(post, :transactionUnique, options[:order_id], :required => true)
154
154
  add_pair(post, :orderRef, options[:description] || options[:order_id], :required => true)
155
- if ['american_express', 'diners_club'].include?(card_brand(credit_card).to_s)
156
- add_pair(post, :item1Quantity, 1)
157
- add_pair(post, :item1Description, (options[:description] || options[:order_id]).slice(0, 15))
158
- add_pair(post, :item1GrossValue, amount(money))
155
+ if credit_card_or_reference.respond_to?(:number)
156
+ if ['american_express', 'diners_club'].include?(card_brand(credit_card_or_reference).to_s)
157
+ add_pair(post, :item1Quantity, 1)
158
+ add_pair(post, :item1Description, (options[:description] || options[:order_id]).slice(0, 15))
159
+ add_pair(post, :item1GrossValue, amount(money))
160
+ end
159
161
  end
160
162
 
161
163
  add_pair(post, :type, options[:type] || '1')
162
164
  add_threeds_required(post, options)
163
165
  end
164
166
 
165
- def add_creditcard(post, credit_card)
167
+ def add_credit_card_or_reference(post, credit_card_or_reference)
168
+ if credit_card_or_reference.respond_to?(:number)
169
+ add_credit_card(post, credit_card_or_reference)
170
+ else
171
+ add_reference(post, credit_card_or_reference.to_s)
172
+ end
173
+ end
174
+
175
+ def add_reference(post, reference)
176
+ add_pair(post, :xref, reference, :required => true)
177
+ end
178
+
179
+ def add_credit_card(post, credit_card)
166
180
  add_pair(post, :customerName, credit_card.name, :required => true)
167
181
  add_pair(post, :cardNumber, credit_card.number, :required => true)
168
182
 
@@ -183,8 +197,12 @@ module ActiveMerchant #:nodoc:
183
197
  add_pair(post, :threeDSRequired, (options[:threeds_required] || @threeds_required) ? 'Y' : 'N')
184
198
  end
185
199
 
200
+ def normalize_line_endings(str)
201
+ str.gsub(/%0D%0A|%0A%0D|%0D/, "%0A")
202
+ end
203
+
186
204
  def add_hmac(post)
187
- result = post.sort.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
205
+ result = post.sort.collect { |key, value| "#{key}=#{normalize_line_endings(CGI.escape(value.to_s))}" }.join("&")
188
206
  result = Digest::SHA512.hexdigest("#{result}#{@options[:shared_secret]}")
189
207
 
190
208
  add_pair(post, :signature, result)
@@ -202,7 +220,7 @@ module ActiveMerchant #:nodoc:
202
220
  end
203
221
 
204
222
  def commit(action, parameters)
205
- parameters.update(:countryCode => self.supported_countries[0]) unless action == 'CAPTURE'
223
+ parameters.update(:countryCode => self.supported_countries[0]) unless ['CAPTURE', 'CANCEL'].include?(action)
206
224
  parameters.update(
207
225
  :merchantID => @options[:login],
208
226
  :action => action
@@ -10,6 +10,7 @@ module ActiveMerchant #:nodoc:
10
10
  self.homepage_url = "http://www.higherone.com/"
11
11
  self.display_name = "Cashnet"
12
12
  self.money_format = :dollars
13
+ self.max_retries = 0
13
14
 
14
15
  # Creates a new CashnetGateway
15
16
  #
@@ -30,8 +30,8 @@ module ActiveMerchant #:nodoc:
30
30
  # * The order of the XML elements does matter, make sure to follow the order in
31
31
  # the documentation exactly.
32
32
  class CyberSourceGateway < Gateway
33
- self.test_url = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor'
34
- self.live_url = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor'
33
+ self.test_url = 'https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor'
34
+ self.live_url = 'https://ics2wsa.ic3.com/commerce/1.x/transactionProcessor'
35
35
 
36
36
  XSD_VERSION = "1.121"
37
37
 
@@ -683,7 +683,11 @@ module ActiveMerchant #:nodoc:
683
683
  # Contact CyberSource, make the SOAP request, and parse the reply into a
684
684
  # Response object
685
685
  def commit(request, options)
686
- response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(request, options)))
686
+ begin
687
+ response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(request, options)))
688
+ rescue ResponseError => e
689
+ response = parse(e.response.body)
690
+ end
687
691
 
688
692
  success = response[:decision] == "ACCEPT"
689
693
  message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
@@ -0,0 +1,293 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class GlobalCollectGateway < Gateway
4
+ self.display_name = "GlobalCollect"
5
+ self.homepage_url = "http://www.globalcollect.com/"
6
+
7
+ self.test_url = "https://api-sandbox.globalcollect.com/"
8
+ self.live_url = "https://api.globalcollect.com/"
9
+
10
+ self.supported_countries = %w(AD AE AT AU BD BE BG BN CA CH CY CZ DE DK
11
+ EE EG ES FI FR GB GI GR HK HU ID IE IL IM IN IS IT JO KW LB LI LK LT LU
12
+ LV MC MT MU MV MX MY NL NO NZ OM PH PL PT QA RO SA SE SG SI SK SM TR TT
13
+ UM US VA VN ZA)
14
+ self.default_currency = "USD"
15
+ self.money_format = :cents
16
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
17
+
18
+ def initialize(options={})
19
+ requires!(options, :merchant_id, :api_key_id, :secret_api_key)
20
+ super
21
+ end
22
+
23
+ def purchase(money, payment, options={})
24
+ MultiResponse.run do |r|
25
+ r.process { authorize(money, payment, options) }
26
+ r.process { capture(money, r.authorization, options) }
27
+ end
28
+ end
29
+
30
+ def authorize(money, payment, options={})
31
+ post = nestable_hash
32
+ add_order(post, money, options)
33
+ add_payment(post, payment)
34
+ add_customer_data(post, options, payment)
35
+ add_address(post, payment, options)
36
+
37
+ commit(:authorize, post)
38
+ end
39
+
40
+ def capture(money, authorization, options={})
41
+ post = nestable_hash
42
+ add_order(post, money, options)
43
+ add_customer_data(post, options)
44
+ commit(:capture, post, authorization)
45
+ end
46
+
47
+ def refund(money, authorization, options={})
48
+ post = nestable_hash
49
+ add_amount(post, money, options={})
50
+ add_refund_customer_data(post, options)
51
+ commit(:refund, post, authorization)
52
+ end
53
+
54
+ def void(authorization, options={})
55
+ post = nestable_hash
56
+ commit(:void, post, authorization)
57
+ end
58
+
59
+ def verify(payment, options={})
60
+ MultiResponse.run(:use_first_response) do |r|
61
+ r.process { authorize(100, payment, options) }
62
+ r.process { void(r.authorization, options) }
63
+ end
64
+ end
65
+
66
+ def supports_scrubbing?
67
+ true
68
+ end
69
+
70
+ def scrub(transcript)
71
+ transcript.
72
+ gsub(%r((Authorization: )[^\\]*)i, '\1[FILTERED]').
73
+ gsub(%r(("cardNumber\\":\\")\d+), '\1[FILTERED]').
74
+ gsub(%r(("cvv\\":\\")\d+), '\1[FILTERED]')
75
+ end
76
+
77
+ private
78
+
79
+ BRAND_MAP = {
80
+ "visa" => "1",
81
+ "american_express" => "2",
82
+ "master" => "3",
83
+ "discover" => "128"
84
+ }
85
+
86
+ def add_order(post, money, options)
87
+ post["order"]["amountOfMoney"] = {
88
+ "amount" => amount(money),
89
+ "currencyCode" => options[:currency] || currency(money)
90
+ }
91
+ post["order"]["references"] = {
92
+ "merchantReference" => options[:order_id],
93
+ "descriptor" => options[:description] # Max 256 chars
94
+ }
95
+ post["order"]["references"]["invoiceData"] = {
96
+ "invoiceNumber" => options[:invoice]
97
+ }
98
+ end
99
+
100
+ def add_amount(post, money, options)
101
+ post["amountOfMoney"] = {
102
+ "amount" => amount(money),
103
+ "currencyCode" => options[:currency] || currency(money)
104
+ }
105
+ end
106
+
107
+ def add_payment(post, payment)
108
+ year = format(payment.year, :two_digits)
109
+ month = format(payment.month, :two_digits)
110
+ expirydate = "#{month}#{year}"
111
+
112
+ post["cardPaymentMethodSpecificInput"] = {
113
+ "paymentProductId" => BRAND_MAP[payment.brand],
114
+ "skipAuthentication" => "true", # refers to 3DSecure
115
+ "skipFraudService" => "true"
116
+ }
117
+ post["cardPaymentMethodSpecificInput"]["card"] = {
118
+ "cvv" => payment.verification_value,
119
+ "cardNumber" => payment.number,
120
+ "expiryDate" => expirydate,
121
+ "cardholderName" => payment.name
122
+ }
123
+ end
124
+
125
+ def add_customer_data(post, options, payment = nil)
126
+ post["order"]["customer"] = {
127
+ "merchantCustomerId" => options[:customer]
128
+ }
129
+ if payment
130
+ post["order"]["customer"]["personalInformation"] = {
131
+ "name" => {
132
+ "firstName" => payment.first_name,
133
+ "surname" => payment.last_name
134
+ }
135
+ }
136
+ end
137
+ post["order"]["companyInformation"] = {
138
+ "name" => options[:company]
139
+ }
140
+ post["order"]["contactDetails"] = {
141
+ "emailAddress" => options[:email]
142
+ }
143
+ if address = options[:billing_address] || options[:address]
144
+ post["order"]["contactDetails"] = {
145
+ "phoneNumber" => address[:phone]
146
+ }
147
+ end
148
+ end
149
+
150
+ def add_refund_customer_data(post, options)
151
+ if address = options[:billing_address] || options[:address]
152
+ post["customer"]["address"] = {
153
+ "countryCode" => address[:country]
154
+ }
155
+ post["customer"]["contactDetails"] = {
156
+ "emailAddress" => options[:email],
157
+ "phoneNumber" => address[:phone]
158
+ }
159
+ end
160
+ end
161
+
162
+ def add_address(post, creditcard, options)
163
+ billing_address = options[:billing_address] || options[:address]
164
+ shipping_address = options[:shipping_address]
165
+ if billing_address = options[:billing_address] || options[:address]
166
+ post["order"]["customer"]["billingAddress"] = {
167
+ "street" => billing_address[:address1],
168
+ "additionalInfo" => billing_address[:address2],
169
+ "zip" => billing_address[:zip],
170
+ "city" => billing_address[:city],
171
+ "state" => billing_address[:state],
172
+ "countryCode" => billing_address[:country]
173
+ }
174
+ end
175
+ if shipping_address
176
+ post["order"]["customer"]["shippingAddress"] = {
177
+ "street" => shipping_address[:address1],
178
+ "additionalInfo" => shipping_address[:address2],
179
+ "zip" => shipping_address[:zip],
180
+ "city" => shipping_address[:city],
181
+ "state" => shipping_address[:state],
182
+ "countryCode" => shipping_address[:country]
183
+ }
184
+ post["order"]["customer"]["shippingAddress"]["name"] = {
185
+ "firstName" => shipping_address[:firstname],
186
+ "surname" => shipping_address[:lastname]
187
+ }
188
+ end
189
+ end
190
+
191
+ def parse(body)
192
+ JSON.parse(body)
193
+ end
194
+
195
+ def url(action, authorization)
196
+ (test? ? test_url : live_url) + uri(action, authorization)
197
+ end
198
+
199
+ def uri(action, authorization)
200
+ uri = "/v1/#{@options[:merchant_id]}/"
201
+ case action
202
+ when :authorize
203
+ uri + "payments"
204
+ when :capture
205
+ uri + "payments/#{authorization}/approve"
206
+ when :refund
207
+ uri + "payments/#{authorization}/refund"
208
+ when :void
209
+ uri + "payments/#{authorization}/cancel"
210
+ end
211
+ end
212
+
213
+ def commit(action, post, authorization = nil)
214
+ begin
215
+ response = parse(ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization)))
216
+ rescue ResponseError => e
217
+ if e.response.code.to_i >= 400
218
+ response = parse(e.response.body)
219
+ end
220
+ end
221
+
222
+ succeeded = success_from(response)
223
+ Response.new(
224
+ succeeded,
225
+ message_from(succeeded, response),
226
+ response,
227
+ authorization: authorization_from(succeeded, response),
228
+ error_code: error_code_from(succeeded, response),
229
+ test: test?
230
+ )
231
+
232
+ end
233
+
234
+ def headers(action, post, authorization = nil)
235
+ {
236
+ "Content-type" => content_type,
237
+ "Authorization" => auth_digest(action, post, authorization),
238
+ "Date" => date
239
+ }
240
+ end
241
+
242
+ def auth_digest(action, post, authorization = nil)
243
+ data = <<-EOS
244
+ POST
245
+ #{content_type}
246
+ #{date}
247
+ #{uri(action, authorization)}
248
+ EOS
249
+ digest = OpenSSL::Digest.new('sha256')
250
+ key = @options[:secret_api_key]
251
+ "GCS v1HMAC:#{@options[:api_key_id]}:#{Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, data))}"
252
+ end
253
+
254
+ def date
255
+ @date ||= Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z") # Must be same in digest and HTTP header
256
+ end
257
+
258
+ def content_type
259
+ "application/json"
260
+ end
261
+
262
+ def success_from(response)
263
+ !response["errorId"]
264
+ end
265
+
266
+ def message_from(succeeded, response)
267
+ if succeeded
268
+ "Succeeded"
269
+ else
270
+ response["errors"][0]["message"] || "Unable to read error message"
271
+ end
272
+ end
273
+
274
+ def authorization_from(succeeded, response)
275
+ if succeeded
276
+ response["id"] || response["payment"]["id"] || response["paymentResult"]["payment"]["id"]
277
+ else
278
+ response["errorId"]
279
+ end
280
+ end
281
+
282
+ def error_code_from(succeeded, response)
283
+ unless succeeded
284
+ response["errors"][0]["code"] || "Unable to read error code"
285
+ end
286
+ end
287
+
288
+ def nestable_hash
289
+ Hash.new {|h,k| h[k] = Hash.new(&h.default_proc) }
290
+ end
291
+ end
292
+ end
293
+ end
@@ -20,7 +20,7 @@ module ActiveMerchant #:nodoc:
20
20
  self.money_format = :cents
21
21
 
22
22
  ACTION_CODE_MESSAGES = {
23
- "000" => "Approved.",
23
+ "000" => "Approved.",
24
24
  "001" => "Refer to card issuer.",
25
25
  "002" => "Refer to card issuer, special condition.",
26
26
  "003" => "Invalid merchant or service provider.",
@@ -167,7 +167,7 @@ module ActiveMerchant #:nodoc:
167
167
 
168
168
  def void(reference, options = {})
169
169
  transaction_id, approval, amount, token = reference.split(";")
170
- commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token))
170
+ commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options))
171
171
  end
172
172
 
173
173
  def credit(money, transaction_id_or_card, options = {})
@@ -200,13 +200,16 @@ module ActiveMerchant #:nodoc:
200
200
 
201
201
  private
202
202
 
203
- def build_xml_request(transaction_type, transaction_id = nil, &block)
203
+ def build_xml_request(transaction_type, options = {}, transaction_id = nil, &block)
204
204
  xml = Builder::XmlMarkup.new
205
205
  xml.tag! 'JetPay' do
206
206
  # The basic values needed for any request
207
207
  xml.tag! 'TerminalID', @options[:login]
208
208
  xml.tag! 'TransactionType', transaction_type
209
209
  xml.tag! 'TransactionID', transaction_id.nil? ? generate_unique_id.slice(0, 18) : transaction_id
210
+ if options && options[:origin]
211
+ xml.tag! 'Origin', options[:origin]
212
+ end
210
213
 
211
214
  if block_given?
212
215
  yield xml
@@ -217,7 +220,7 @@ module ActiveMerchant #:nodoc:
217
220
  end
218
221
 
219
222
  def build_sale_request(money, credit_card, options)
220
- build_xml_request('SALE') do |xml|
223
+ build_xml_request('SALE', options) do |xml|
221
224
  add_credit_card(xml, credit_card)
222
225
  add_addresses(xml, options)
223
226
  add_customer_data(xml, options)
@@ -243,14 +246,14 @@ module ActiveMerchant #:nodoc:
243
246
  end
244
247
 
245
248
  def build_capture_request(transaction_id, money, options)
246
- build_xml_request('CAPT', transaction_id) do |xml|
249
+ build_xml_request('CAPT', options, transaction_id) do |xml|
247
250
  xml.tag! 'TotalAmount', amount(money)
248
251
  add_user_defined_fields(xml, options)
249
252
  end
250
253
  end
251
254
 
252
- def build_void_request(money, transaction_id, approval, token)
253
- build_xml_request('VOID', transaction_id) do |xml|
255
+ def build_void_request(money, transaction_id, approval, token, options)
256
+ build_xml_request('VOID', options, transaction_id) do |xml|
254
257
  xml.tag! 'Approval', approval
255
258
  xml.tag! 'TotalAmount', amount(money)
256
259
  xml.tag! 'Token', token if token
@@ -260,7 +263,7 @@ module ActiveMerchant #:nodoc:
260
263
 
261
264
  # `transaction_id` may be nil for unlinked credit transactions.
262
265
  def build_credit_request(transaction_type, money, transaction_id, card, token, options)
263
- build_xml_request(transaction_type, transaction_id) do |xml|
266
+ build_xml_request(transaction_type, options, transaction_id) do |xml|
264
267
  add_credit_card(xml, card) if card
265
268
  add_invoice_data(xml, options)
266
269
  add_addresses(xml, options)