activemerchant 1.93.0 → 1.98.0

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