activemerchant 1.93.0 → 1.98.0

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +111 -0
  3. data/README.md +3 -0
  4. data/lib/active_merchant/billing/avs_result.rb +4 -5
  5. data/lib/active_merchant/billing/credit_card.rb +6 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +67 -4
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +106 -22
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +2 -0
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -2
  13. data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +56 -9
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +3 -1
  16. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +98 -61
  18. data/lib/active_merchant/billing/gateways/credorax.rb +29 -3
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +30 -13
  20. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  21. data/lib/active_merchant/billing/gateways/decidir.rb +233 -0
  22. data/lib/active_merchant/billing/gateways/elavon.rb +9 -0
  23. data/lib/active_merchant/billing/gateways/epay.rb +13 -2
  24. data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
  25. data/lib/active_merchant/billing/gateways/fat_zebra.rb +6 -0
  26. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
  27. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  28. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/migs.rb +8 -0
  31. data/lib/active_merchant/billing/gateways/monei.rb +31 -0
  32. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -2
  33. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  34. data/lib/active_merchant/billing/gateways/nmi.rb +39 -1
  35. data/lib/active_merchant/billing/gateways/opp.rb +20 -1
  36. data/lib/active_merchant/billing/gateways/orbital.rb +60 -10
  37. data/lib/active_merchant/billing/gateways/payflow.rb +40 -2
  38. data/lib/active_merchant/billing/gateways/paymill.rb +5 -0
  39. data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
  40. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -2
  41. data/lib/active_merchant/billing/gateways/qvalent.rb +43 -1
  42. data/lib/active_merchant/billing/gateways/realex.rb +32 -9
  43. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  44. data/lib/active_merchant/billing/gateways/stripe.rb +54 -9
  45. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +267 -0
  46. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
  47. data/lib/active_merchant/billing/gateways/trust_commerce.rb +45 -6
  48. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
  49. data/lib/active_merchant/billing/gateways/worldpay.rb +171 -39
  50. data/lib/active_merchant/country.rb +1 -0
  51. data/lib/active_merchant/version.rb +1 -1
  52. metadata +19 -4
@@ -6,6 +6,10 @@ module ActiveMerchant #:nodoc:
6
6
  self.display_name = 'Credorax Gateway'
7
7
  self.homepage_url = 'https://www.credorax.com/'
8
8
 
9
+ # NOTE: the IP address you run the remote tests from will need to be
10
+ # whitelisted by Credorax; contact support@credorax.com as necessary to
11
+ # request your IP address be added to the whitelist for your test
12
+ # account.
9
13
  self.test_url = 'https://intconsole.credorax.com/intenv/service/gateway'
10
14
 
11
15
  # The live URL is assigned on a per merchant basis once certification has passed
@@ -15,7 +19,7 @@ module ActiveMerchant #:nodoc:
15
19
  # ActiveMerchant::Billing::CredoraxGateway.live_url = "https://assigned-subdomain.credorax.net/crax_gate/service/gateway"
16
20
  self.live_url = 'https://assigned-subdomain.credorax.net/crax_gate/service/gateway'
17
21
 
18
- self.supported_countries = %w(DE GB FR IT ES PL NL BE GR CZ PT SE HU RS AT CH BG DK FI SK NO IE HR BA AL LT MK SI LV EE ME LU MT IS AD MC LI SM)
22
+ self.supported_countries = %w(AD AT BE BG HR CY CZ DK EE FR DE GI GR GG HU IS IE IM IT JE LV LI LT LU MT MC NO PL PT RO SM SK ES SE CH GB)
19
23
  self.default_currency = 'EUR'
20
24
  self.currencies_without_fractions = %w(CLP JPY KRW PYG VND)
21
25
  self.currencies_with_three_decimal_places = %w(BHD JOD KWD OMR RSD TND)
@@ -264,8 +268,30 @@ module ActiveMerchant #:nodoc:
264
268
  end
265
269
 
266
270
  def add_3d_secure(post, options)
267
- return unless options[:eci] && options[:xid]
268
- post[:i8] = "#{options[:eci]}:#{(options[:cavv] || "none")}:#{options[:xid]}"
271
+ if options[:eci] && options[:xid]
272
+ add_3d_secure_1_data(post, options)
273
+ elsif options[:three_d_secure]
274
+ add_normalized_3d_secure_2_data(post, options)
275
+ end
276
+ end
277
+
278
+ def add_3d_secure_1_data(post, options)
279
+ post[:i8] = build_i8(options[:eci], options[:cavv], options[:xid])
280
+ end
281
+
282
+ def add_normalized_3d_secure_2_data(post, options)
283
+ three_d_secure_options = options[:three_d_secure]
284
+
285
+ post[:i8] = build_i8(
286
+ three_d_secure_options[:eci],
287
+ three_d_secure_options[:cavv]
288
+ )
289
+ post[:'3ds_version'] = three_d_secure_options[:version]
290
+ post[:'3ds_dstrxid'] = three_d_secure_options[:ds_transaction_id]
291
+ end
292
+
293
+ def build_i8(eci, cavv=nil, xid=nil)
294
+ "#{eci}:#{cavv || 'none'}:#{xid || 'none'}"
269
295
  end
270
296
 
271
297
  def add_echo(post, options)
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  # Initial setup instructions can be found in
4
- # http://cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
4
+ # http://apps.cybersource.com/library/documentation/dev_guides/SOAP_Toolkits/SOAP_toolkits.pdf
5
5
  #
6
6
  # Important Notes
7
7
  # * For checks you can purchase and store.
@@ -24,10 +24,12 @@ module ActiveMerchant #:nodoc:
24
24
  self.test_url = 'https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor'
25
25
  self.live_url = 'https://ics2wsa.ic3.com/commerce/1.x/transactionProcessor'
26
26
 
27
- XSD_VERSION = '1.153'
27
+ # Schema files can be found here: https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/
28
+ TEST_XSD_VERSION = '1.156'
29
+ PRODUCTION_XSD_VERSION = '1.155'
28
30
 
29
- self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :dankort, :maestro]
30
- self.supported_countries = %w(US BR CA CN DK FI FR DE IN JP MX NO SE GB SG LB)
31
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :dankort, :maestro, :elo]
32
+ self.supported_countries = %w(US BR CA CN DK FI FR DE IN JP MX NO SE GB SG LB PK)
31
33
 
32
34
  self.default_currency = 'USD'
33
35
  self.currencies_without_fractions = %w(JPY)
@@ -43,7 +45,8 @@ module ActiveMerchant #:nodoc:
43
45
  :diners_club => '005',
44
46
  :jcb => '007',
45
47
  :dankort => '034',
46
- :maestro => '042'
48
+ :maestro => '042',
49
+ :elo => '054'
47
50
  }
48
51
 
49
52
  @@response_codes = {
@@ -142,9 +145,10 @@ module ActiveMerchant #:nodoc:
142
145
  end
143
146
  end
144
147
 
145
- # Adds credit to a subscription (stand alone credit).
146
- def credit(money, reference, options = {})
147
- commit(build_credit_request(money, reference, options), :credit, money, options)
148
+ # Adds credit to a card or subscription (stand alone credit).
149
+ def credit(money, creditcard_or_reference, options = {})
150
+ setup_address_hash(options)
151
+ commit(build_credit_request(money, creditcard_or_reference, options), :credit, money, options)
148
152
  end
149
153
 
150
154
  # Stores a customer subscription/profile with type "on-demand".
@@ -261,6 +265,8 @@ module ActiveMerchant #:nodoc:
261
265
  add_payment_network_token(xml) if network_tokenization?(creditcard_or_reference)
262
266
  add_business_rules_data(xml, creditcard_or_reference, options)
263
267
  add_stored_credential_options(xml, options)
268
+ add_issuer_additional_data(xml, options)
269
+
264
270
  xml.target!
265
271
  end
266
272
 
@@ -299,6 +305,8 @@ module ActiveMerchant #:nodoc:
299
305
  add_payment_network_token(xml) if network_tokenization?(payment_method_or_reference)
300
306
  add_business_rules_data(xml, payment_method_or_reference, options) unless options[:pinless_debit_card]
301
307
  end
308
+ add_issuer_additional_data(xml, options)
309
+
302
310
  xml.target!
303
311
  end
304
312
 
@@ -327,11 +335,10 @@ module ActiveMerchant #:nodoc:
327
335
  xml.target!
328
336
  end
329
337
 
330
- def build_credit_request(money, reference, options)
338
+ def build_credit_request(money, creditcard_or_reference, options)
331
339
  xml = Builder::XmlMarkup.new :indent => 2
332
340
 
333
- add_purchase_data(xml, money, true, options)
334
- add_subscription(xml, options, reference)
341
+ add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
335
342
  add_credit_service(xml)
336
343
 
337
344
  xml.target!
@@ -484,6 +491,14 @@ module ActiveMerchant #:nodoc:
484
491
  end
485
492
  end
486
493
 
494
+ def add_issuer_additional_data(xml, options)
495
+ return unless options[:issuer_additional_data]
496
+
497
+ xml.tag! 'issuer' do
498
+ xml.tag! 'additionalData', options[:issuer_additional_data]
499
+ end
500
+ end
501
+
487
502
  def add_mdd_fields(xml, options)
488
503
  return unless options.keys.any? { |key| key.to_s.start_with?('mdd_field') }
489
504
 
@@ -549,7 +564,7 @@ module ActiveMerchant #:nodoc:
549
564
  xml.tag!('commerceIndicator', 'vbv')
550
565
  xml.tag!('xid', payment_method.payment_cryptogram)
551
566
  end
552
- when :mastercard
567
+ when :master
553
568
  xml.tag! 'ucaf' do
554
569
  xml.tag!('authenticationData', payment_method.payment_cryptogram)
555
570
  xml.tag!('collectionIndicator', '2')
@@ -712,6 +727,8 @@ module ActiveMerchant #:nodoc:
712
727
 
713
728
  # Where we actually build the full SOAP request using builder
714
729
  def build_request(body, options)
730
+ xsd_version = test? ? TEST_XSD_VERSION : PRODUCTION_XSD_VERSION
731
+
715
732
  xml = Builder::XmlMarkup.new :indent => 2
716
733
  xml.instruct!
717
734
  xml.tag! 's:Envelope', {'xmlns:s' => 'http://schemas.xmlsoap.org/soap/envelope/'} do
@@ -724,7 +741,7 @@ module ActiveMerchant #:nodoc:
724
741
  end
725
742
  end
726
743
  xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
727
- xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{XSD_VERSION}"} do
744
+ xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{xsd_version}"} do
728
745
  add_merchant_data(xml, options)
729
746
  xml << body
730
747
  end
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
 
7
7
  self.supported_countries = ['AR', 'BR', 'CL', 'CO', 'MX', 'PE', 'UY', 'TR']
8
8
  self.default_currency = 'USD'
9
- self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
9
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro, :naranja, :cabal]
10
10
 
11
11
  self.homepage_url = 'https://dlocal.com/'
12
12
  self.display_name = 'dLocal'
@@ -0,0 +1,233 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class DecidirGateway < Gateway
4
+ self.test_url = 'https://developers.decidir.com/api/v2'
5
+ self.live_url = 'https://live.decidir.com/api/v2'
6
+
7
+ self.supported_countries = ['AR']
8
+ self.money_format = :cents
9
+ self.default_currency = 'ARS'
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :naranja, :cabal]
11
+
12
+ self.homepage_url = 'http://www.decidir.com'
13
+ self.display_name = 'Decidir'
14
+
15
+ STANDARD_ERROR_CODE_MAPPING = {
16
+ 1 => STANDARD_ERROR_CODE[:call_issuer],
17
+ 2 => STANDARD_ERROR_CODE[:call_issuer],
18
+ 3 => STANDARD_ERROR_CODE[:config_error],
19
+ 4 => STANDARD_ERROR_CODE[:pickup_card],
20
+ 5 => STANDARD_ERROR_CODE[:card_declined],
21
+ 7 => STANDARD_ERROR_CODE[:pickup_card],
22
+ 12 => STANDARD_ERROR_CODE[:processing_error],
23
+ 14 => STANDARD_ERROR_CODE[:invalid_number],
24
+ 28 => STANDARD_ERROR_CODE[:processing_error],
25
+ 38 => STANDARD_ERROR_CODE[:incorrect_pin],
26
+ 39 => STANDARD_ERROR_CODE[:invalid_number],
27
+ 43 => STANDARD_ERROR_CODE[:pickup_card],
28
+ 45 => STANDARD_ERROR_CODE[:card_declined],
29
+ 46 => STANDARD_ERROR_CODE[:invalid_number],
30
+ 47 => STANDARD_ERROR_CODE[:card_declined],
31
+ 48 => STANDARD_ERROR_CODE[:card_declined],
32
+ 49 => STANDARD_ERROR_CODE[:invalid_expiry_date],
33
+ 51 => STANDARD_ERROR_CODE[:card_declined],
34
+ 53 => STANDARD_ERROR_CODE[:card_declined],
35
+ 54 => STANDARD_ERROR_CODE[:expired_card],
36
+ 55 => STANDARD_ERROR_CODE[:incorrect_pin],
37
+ 56 => STANDARD_ERROR_CODE[:card_declined],
38
+ 57 => STANDARD_ERROR_CODE[:card_declined],
39
+ 76 => STANDARD_ERROR_CODE[:call_issuer],
40
+ 96 => STANDARD_ERROR_CODE[:processing_error],
41
+ 97 => STANDARD_ERROR_CODE[:processing_error],
42
+ }
43
+
44
+ def initialize(options={})
45
+ requires!(options, :api_key)
46
+ super
47
+ @options[:preauth_mode] ||= false
48
+ end
49
+
50
+ def purchase(money, payment, options={})
51
+ raise ArgumentError, 'Purchase is not supported on Decidir gateways configured with the preauth_mode option' if @options[:preauth_mode]
52
+
53
+ post = {}
54
+ add_auth_purchase_params(post, money, payment, options)
55
+ commit(:post, 'payments', post)
56
+ end
57
+
58
+ def authorize(money, payment, options={})
59
+ raise ArgumentError, 'Authorize is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
60
+
61
+ post = {}
62
+ add_auth_purchase_params(post, money, payment, options)
63
+ commit(:post, 'payments', post)
64
+ end
65
+
66
+ def capture(money, authorization, options={})
67
+ raise ArgumentError, 'Capture is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
68
+
69
+ post = {}
70
+ add_amount(post, money, options)
71
+ commit(:put, "payments/#{authorization}", post)
72
+ end
73
+
74
+ def refund(money, authorization, options={})
75
+ post = {}
76
+ add_amount(post, money, options)
77
+ commit(:post, "payments/#{authorization}/refunds", post)
78
+ end
79
+
80
+ def void(authorization, options={})
81
+ post = {}
82
+ commit(:post, "payments/#{authorization}/refunds", post)
83
+ end
84
+
85
+ def verify(credit_card, options={})
86
+ raise ArgumentError, 'Verify is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
87
+
88
+ MultiResponse.run(:use_first_response) do |r|
89
+ r.process { authorize(100, credit_card, options) }
90
+ r.process(:ignore_result) { void(r.authorization, options) }
91
+ end
92
+ end
93
+
94
+ def supports_scrubbing?
95
+ true
96
+ end
97
+
98
+ def scrub(transcript)
99
+ transcript.
100
+ gsub(%r((apikey: )\w+)i, '\1[FILTERED]').
101
+ gsub(%r((\"card_number\\\":\\\")\d+), '\1[FILTERED]').
102
+ gsub(%r((\"security_code\\\":\\\")\d+), '\1[FILTERED]').
103
+ gsub(%r((\"emv_issuer_data\\\":\\\")\d+), '\1[FILTERED]')
104
+ end
105
+
106
+ private
107
+
108
+ def add_auth_purchase_params(post, money, credit_card, options)
109
+ post[:payment_method_id] = options[:payment_method_id] ? options[:payment_method_id].to_i : 1
110
+ post[:site_transaction_id] = options[:order_id]
111
+ post[:bin] = credit_card.number[0..5]
112
+ post[:payment_type] = options[:payment_type] || 'single'
113
+ post[:installments] = options[:installments] ? options[:installments].to_i : 1
114
+ post[:description] = options[:description] if options[:description]
115
+ post[:email] = options[:email] if options[:email]
116
+ post[:sub_payments] = []
117
+
118
+ add_invoice(post, money, options)
119
+ add_payment(post, credit_card, options)
120
+ end
121
+
122
+ def add_invoice(post, money, options)
123
+ add_amount(post, money, options)
124
+ post[:currency] = (options[:currency] || currency(money))
125
+ end
126
+
127
+ def add_amount(post, money, options)
128
+ currency = (options[:currency] || currency(money))
129
+ post[:amount] = localized_amount(money, currency).to_i
130
+ end
131
+
132
+ def add_payment(post, credit_card, options)
133
+ card_data = {}
134
+ card_data[:card_number] = credit_card.number
135
+ card_data[:card_expiration_month] = format(credit_card.month, :two_digits)
136
+ card_data[:card_expiration_year] = format(credit_card.year, :two_digits)
137
+ card_data[:security_code] = credit_card.verification_value if credit_card.verification_value?
138
+ card_data[:card_holder_name] = credit_card.name if credit_card.name
139
+
140
+ # additional data used for Visa transactions
141
+ card_data[:card_holder_door_number] = options[:card_holder_door_number].to_i if options[:card_holder_door_number]
142
+ card_data[:card_holder_birthday] = options[:card_holder_birthday] if options[:card_holder_birthday]
143
+
144
+ card_data[:card_holder_identification] = {}
145
+ card_data[:card_holder_identification][:type] = options[:card_holder_identification_type] if options[:card_holder_identification_type]
146
+ card_data[:card_holder_identification][:number] = options[:card_holder_identification_number] if options[:card_holder_identification_number]
147
+
148
+ post[:card_data] = card_data
149
+ end
150
+
151
+ def headers(options = {})
152
+ {
153
+ 'apikey' => @options[:api_key],
154
+ 'Content-type' => 'application/json',
155
+ 'Cache-Control' => 'no-cache'
156
+ }
157
+ end
158
+
159
+ def commit(method, endpoint, parameters, options={})
160
+ url = "#{(test? ? test_url : live_url)}/#{endpoint}"
161
+
162
+ begin
163
+ raw_response = ssl_request(method, url, post_data(parameters), headers(options))
164
+ response = parse(raw_response)
165
+ rescue ResponseError => e
166
+ raw_response = e.response.body
167
+ response = parse(raw_response)
168
+ end
169
+
170
+ success = success_from(response)
171
+ Response.new(
172
+ success,
173
+ message_from(success, response),
174
+ response,
175
+ authorization: authorization_from(response),
176
+ test: test?,
177
+ error_code: success ? nil : error_code_from(response)
178
+ )
179
+ end
180
+
181
+ def post_data(parameters = {})
182
+ parameters.to_json
183
+ end
184
+
185
+ def parse(body)
186
+ JSON.parse(body)
187
+ rescue JSON::ParserError
188
+ {
189
+ 'message' => "A non-JSON response was received from Decidir where one was expected. The raw response was:\n\n#{body}"
190
+ }
191
+ end
192
+
193
+ def message_from(success, response)
194
+ return response['status'] if success
195
+ return response['message'] if response['message']
196
+
197
+ message = nil
198
+
199
+ if error = response.dig('status_details', 'error')
200
+ message = error.dig('reason', 'description')
201
+ elsif response['error_type']
202
+ if response['validation_errors']
203
+ message = response['validation_errors'].map { |errors| "#{errors['code']}: #{errors['param']}" }.join(', ')
204
+ end
205
+ message ||= response['error_type']
206
+ end
207
+
208
+ message
209
+ end
210
+
211
+ def success_from(response)
212
+ response['status'] == 'approved' || response['status'] == 'pre_approved'
213
+ end
214
+
215
+ def authorization_from(response)
216
+ response['id']
217
+ end
218
+
219
+ def error_code_from(response)
220
+ error_code = nil
221
+ if error = response.dig('status_details', 'error')
222
+ code = error.dig('reason', 'id')
223
+ error_code = STANDARD_ERROR_CODE_MAPPING[code]
224
+ error_code ||= error['type']
225
+ elsif response['error_type']
226
+ error_code = response['error_type'] if response['validation_errors']
227
+ end
228
+
229
+ error_code || STANDARD_ERROR_CODE[:processing_error]
230
+ end
231
+ end
232
+ end
233
+ end
@@ -42,6 +42,7 @@ module ActiveMerchant #:nodoc:
42
42
  else
43
43
  add_creditcard(form, payment_method)
44
44
  end
45
+ add_currency(form, money, options)
45
46
  add_address(form, options)
46
47
  add_customer_data(form, options)
47
48
  add_test_mode(form, options)
@@ -54,6 +55,7 @@ module ActiveMerchant #:nodoc:
54
55
  add_salestax(form, options)
55
56
  add_invoice(form, options)
56
57
  add_creditcard(form, creditcard)
58
+ add_currency(form, money, options)
57
59
  add_address(form, options)
58
60
  add_customer_data(form, options)
59
61
  add_test_mode(form, options)
@@ -69,6 +71,7 @@ module ActiveMerchant #:nodoc:
69
71
  add_approval_code(form, authorization)
70
72
  add_invoice(form, options)
71
73
  add_creditcard(form, options[:credit_card])
74
+ add_currency(form, money, options)
72
75
  add_customer_data(form, options)
73
76
  add_test_mode(form, options)
74
77
  else
@@ -102,6 +105,7 @@ module ActiveMerchant #:nodoc:
102
105
  form = {}
103
106
  add_invoice(form, options)
104
107
  add_creditcard(form, creditcard)
108
+ add_currency(form, money, options)
105
109
  add_address(form, options)
106
110
  add_customer_data(form, options)
107
111
  add_test_mode(form, options)
@@ -178,6 +182,11 @@ module ActiveMerchant #:nodoc:
178
182
  form[:last_name] = truncate(creditcard.last_name, 30)
179
183
  end
180
184
 
185
+ def add_currency(form, money, options)
186
+ currency = options[:currency] || currency(money)
187
+ form[:transaction_currency] = currency if currency && (@options[:multi_currency] || options[:multi_currency])
188
+ end
189
+
181
190
  def add_token(form, token)
182
191
  form[:token] = token
183
192
  end