activemerchant 1.67.0 → 1.68.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d1e9a2bf41a0e836aba8e3d1296464f62d7765e
4
- data.tar.gz: 6cd30371be55f49a0beef1eb5a3a8796cf226db0
3
+ metadata.gz: 3ed51178560acbebf43bedcb1e5ce966b279c04a
4
+ data.tar.gz: fb02cd941337c8fb5a2e2ae2626a25d967e86750
5
5
  SHA512:
6
- metadata.gz: 4c0426a9d705e5b7c29031ac35ccdb1a2bf062efe8693760b485f9ece001e6149f64bbfa11dcc8cc23310ac5a13586b66683762cb0db98bd300d3eb5c8cd27d7
7
- data.tar.gz: 54b90b7a5e2235c16e5ea1e91a161437b50081b2b19ccd7938f35f8398f65eafffee05eca2317c19f4cda4007986b59f9daea99db36e184b2a357a4aed437ebf
6
+ metadata.gz: d8fe0383adc62eb2a6af23bbe0ab0155d9121994fc7b9b0fe05f84ad7e521e3fe62db4814fb1efc02c8889e88fb561bba0807dc8133b3e39ad04d167d5b28c15
7
+ data.tar.gz: 2159f015c245a4ebd0dc2ba57808884f08b2923271c951c8ffd4d0b581139d1426c0cadaa6f66bb6a26de4d63a12c2dcc26cd0adafc6ac51229c04668a05b640
data/CHANGELOG CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.68.0 (June 27, 2017)
6
+ * Authorize.Net: Return failed response if forced refund settlement fails [bizla] #2476
7
+ * Authorize.net: Concatenate address1 and address2 [dtykocki] #2479
8
+ * Braintree Blue: Braintree Blue: Add ECI indicator to Android Pay transactions [davidsantoso] #2474
9
+ * Credorax: Support 0- and 3-exponent currencies [curiousepic]
10
+ * Cybersource: update supported card types [bdewater] #2477
11
+ * FirstData: Add a default network tokenization strategy for FirstData E4 [krystosterone] #2473
12
+ * FirstPay: FirstPay: Update hostname and force TLSv1 minimum [davidsantoso] #2478
13
+ * JetPay V2: Support store transactions and token based payments [shasum] #2475
14
+ * Moneris: Add 3DS fields for decrypted Apple and Android Pay data [davidsantoso] #2457
15
+ * Openpay: Send customer name and email in authorize and purchase [dtykocki] #2468
16
+ * Payflow: Moved to name value pair (NVP) with payflow [jusleg] #2462
17
+ * Payflow: Set PAYPAL_NVP header as optional [davidsantoso] #2480
18
+ * QuickPay V10: Return last response for purchase and authorize [curiousepic] #2461
19
+ * SafeCharge: Map billing address fields [davidsantoso] #2464
20
+ * SafeCharge: Track currency from original transaction [davidsantoso] #2470
21
+ * Support three-decimal currencies [curiousepic] #2466
22
+ * Trexle: Add gateway support [hossamhossny] #2351
23
+
5
24
  == Version 1.67.0 (June 8, 2017)
6
25
  * Acapture: Pass 3D Secure fields [davidsantoso] #2451
7
26
  * Authorize.net: Pass Level 2 Data Fields [curiousepic] #2444
@@ -17,16 +36,6 @@
17
36
  * Payeezy: Add client_email field for telecheck [davidsantoso] #2455
18
37
  * Payeezy: Add customer_id_type and customer_id_number fields [davidsantoso] #2454
19
38
  * Quickpay V10: Fix store and token use for recurring payments [wsmoak] #2180
20
-
21
- == Version 1.66.0 (May 4, 2017)
22
- * Support Rails 5.1 [jhawthorn] #2407
23
- * ProPay: Add Canada as supported country [davidsantoso]
24
- * ProPay: Add gateway support [davidsantoso] #2405
25
- * SafeCharge: Support credit transactions [shasum] #2404
26
- * WePay: Add scrub method [shasum] #2406
27
- * iVeri: Add gateway support [curiousepic] #2400
28
- * iVeri: Support 3DSecure data fields [davidsantoso] #2412
29
- * Opp: Fix transaction success criteria and clean up options [shasum] #2414
30
39
  * Elavon: Support custom fields [curiousepic] #2416
31
40
  * WePay: Support risk headers [shasum] #2419
32
41
  * WePay: Add Canada as supported country [shasum] #2419
@@ -39,6 +48,16 @@
39
48
  * Payeezy: Default check number to 001 if not present [davidsantoso] #2439
40
49
  * Opp: Fix incorrect customParameter key to disable 3DS [davidsantoso]
41
50
 
51
+ == Version 1.66.0 (May 4, 2017)
52
+ * Support Rails 5.1 [jhawthorn] #2407
53
+ * ProPay: Add Canada as supported country [davidsantoso]
54
+ * ProPay: Add gateway support [davidsantoso] #2405
55
+ * SafeCharge: Support credit transactions [shasum] #2404
56
+ * WePay: Add scrub method [shasum] #2406
57
+ * iVeri: Add gateway support [curiousepic] #2400
58
+ * iVeri: Support 3DSecure data fields [davidsantoso] #2412
59
+ * Opp: Fix transaction success criteria and clean up options [shasum] #2414
60
+
42
61
  == Version 1.65.0 (April 26, 2017)
43
62
  * Adyen: Add Adyen v18 gateway [adyenpayments] #2272
44
63
  * Authorize.Net: Force refund of unsettled payments via void [bizla] #2399
@@ -125,8 +125,9 @@ module ActiveMerchant #:nodoc:
125
125
  class_attribute :supported_cardtypes
126
126
  self.supported_cardtypes = []
127
127
 
128
- class_attribute :currencies_without_fractions
128
+ class_attribute :currencies_without_fractions, :currencies_with_three_decimal_places
129
129
  self.currencies_without_fractions = %w(BIF BYR CLP CVE DJF GNF HUF ISK JPY KMF KRW PYG RWF UGX VND VUV XAF XOF XPF)
130
+ self.currencies_with_three_decimal_places = %w()
130
131
 
131
132
  class_attribute :homepage_url
132
133
  class_attribute :display_name
@@ -263,15 +264,26 @@ module ActiveMerchant #:nodoc:
263
264
  self.currencies_without_fractions.include?(currency.to_s)
264
265
  end
265
266
 
267
+ def three_decimal_currency?(currency)
268
+ self.currencies_with_three_decimal_places.include?(currency.to_s)
269
+ end
270
+
266
271
  def localized_amount(money, currency)
267
272
  amount = amount(money)
268
273
 
269
- return amount unless non_fractional_currency?(currency)
270
-
271
- if self.money_format == :cents
272
- sprintf("%.0f", amount.to_f / 100)
273
- else
274
- amount.split('.').first
274
+ return amount unless non_fractional_currency?(currency) || three_decimal_currency?(currency)
275
+ if non_fractional_currency?(currency)
276
+ if self.money_format == :cents
277
+ sprintf("%.0f", amount.to_f / 100)
278
+ else
279
+ amount.split('.').first
280
+ end
281
+ elsif three_decimal_currency?(currency)
282
+ if self.money_format == :cents
283
+ (amount.to_i * 10).to_s
284
+ else
285
+ sprintf("%.3f", amount.to_f)
286
+ end
275
287
  end
276
288
  end
277
289
 
@@ -143,6 +143,8 @@ module ActiveMerchant
143
143
 
144
144
  if response.params["response_reason_code"] == INELIGIBLE_FOR_ISSUING_CREDIT_ERROR
145
145
  void(authorization, options)
146
+ else
147
+ response
146
148
  end
147
149
  end
148
150
 
@@ -553,11 +555,12 @@ module ActiveMerchant
553
555
 
554
556
  xml.billTo do
555
557
  first_name, last_name = names_from(payment_source, address, options)
558
+ full_address = "#{address[:address1]} #{address[:address2]}".strip
559
+
556
560
  xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
557
561
  xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
558
-
559
562
  xml.company(truncate(address[:company], 50)) unless empty?(address[:company])
560
- xml.address(truncate(address[:address1], 60))
563
+ xml.address(truncate(full_address, 60))
561
564
  xml.city(truncate(address[:city], 40))
562
565
  xml.state(empty?(address[:state]) ? 'n/a' : truncate(address[:state], 40))
563
566
  xml.zip(truncate((address[:zip] || options[:zip]), 20))
@@ -577,12 +580,12 @@ module ActiveMerchant
577
580
  else
578
581
  [address[:first_name], address[:last_name]]
579
582
  end
583
+ full_address = "#{address[:address1]} #{address[:address2]}".strip
580
584
 
581
585
  xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
582
586
  xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
583
-
584
587
  xml.company(truncate(address[:company], 50)) unless empty?(address[:company])
585
- xml.address(truncate(address[:address1], 60))
588
+ xml.address(truncate(full_address, 60))
586
589
  xml.city(truncate(address[:city], 40))
587
590
  xml.state(truncate(address[:state], 40))
588
591
  xml.zip(truncate(address[:zip], 20))
@@ -592,7 +592,8 @@ module ActiveMerchant #:nodoc:
592
592
  :expiration_year => credit_card_or_vault_id.year.to_s,
593
593
  :google_transaction_id => credit_card_or_vault_id.transaction_id,
594
594
  :source_card_type => credit_card_or_vault_id.brand,
595
- :source_card_last_four => credit_card_or_vault_id.last_digits
595
+ :source_card_last_four => credit_card_or_vault_id.last_digits,
596
+ :eci_indicator => credit_card_or_vault_id.eci
596
597
  }
597
598
  end
598
599
  else
@@ -5,7 +5,7 @@ module ActiveMerchant #:nodoc:
5
5
  module Billing #:nodoc:
6
6
  class CheckoutGateway < Gateway
7
7
  self.default_currency = 'USD'
8
- self.money_format = :decimals
8
+ self.money_format = :cents
9
9
 
10
10
  self.supported_countries = ['AD', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA']
11
11
  self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
@@ -17,6 +17,9 @@ module ActiveMerchant #:nodoc:
17
17
 
18
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)
19
19
  self.default_currency = "EUR"
20
+ self.currencies_without_fractions = %w(CLP JPY KRW PYG VND)
21
+ self.currencies_with_three_decimal_places = %w(BHD JOD KWD OMR RSD TND)
22
+
20
23
  self.money_format = :cents
21
24
  self.supported_cardtypes = [:visa, :master, :maestro]
22
25
 
@@ -187,9 +190,11 @@ module ActiveMerchant #:nodoc:
187
190
  private
188
191
 
189
192
  def add_invoice(post, money, options)
190
- post[:a4] = amount(money)
193
+ currency = options[:currency] || currency(money)
194
+
195
+ post[:a4] = localized_amount(money, currency)
191
196
  post[:a1] = generate_unique_id
192
- post[:a5] = options[:currency] || currency(money)
197
+ post[:a5] = currency
193
198
  post[:h9] = options[:order_id]
194
199
  end
195
200
 
@@ -26,7 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
 
27
27
  XSD_VERSION = "1.121"
28
28
 
29
- self.supported_cardtypes = [:visa, :master, :american_express, :discover]
29
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :switch, :dankort, :maestro]
30
30
  self.supported_countries = %w(US BR CA CN DK FI FR DE JP MX NO SE GB SG LB)
31
31
 
32
32
  self.default_currency = 'USD'
@@ -39,7 +39,12 @@ module ActiveMerchant #:nodoc:
39
39
  :visa => '001',
40
40
  :master => '002',
41
41
  :american_express => '003',
42
- :discover => '004'
42
+ :discover => '004',
43
+ :diners_club => '005',
44
+ :jcb => '007',
45
+ :switch => '024',
46
+ :dankort => '034',
47
+ :maestro => '042'
43
48
  }
44
49
 
45
50
  @@response_codes = {
@@ -3,7 +3,7 @@ require 'nokogiri'
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  class FirstPayGateway < Gateway
6
- self.live_url = 'https://secure.1stpaygateway.net/secure/gateway/xmlgateway.aspx'
6
+ self.live_url = 'https://secure.goemerchant.com/secure/gateway/xmlgateway.aspx'
7
7
 
8
8
  self.supported_countries = ['US']
9
9
  self.default_currency = 'USD'
@@ -12,6 +12,7 @@ module ActiveMerchant #:nodoc:
12
12
 
13
13
  self.homepage_url = 'http://1stpaygateway.net/'
14
14
  self.display_name = '1stPayGateway.Net'
15
+ self.ssl_version = :TLSv1
15
16
 
16
17
  def initialize(options={})
17
18
  requires!(options, :transaction_center_id, :gateway_id)
@@ -262,16 +262,13 @@ module ActiveMerchant #:nodoc:
262
262
 
263
263
  def add_network_tokenization_credit_card(xml, credit_card)
264
264
  case card_brand(credit_card).to_sym
265
- when :visa
266
- xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
267
- xml.tag!("CAVV", credit_card.payment_cryptogram)
268
- when :mastercard
269
- xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
270
- xml.tag!("CAVV", credit_card.payment_cryptogram)
271
265
  when :american_express
272
266
  cryptogram = Base64.decode64(credit_card.payment_cryptogram)
273
267
  xml.tag!("XID", Base64.encode64(cryptogram[20...40]))
274
268
  xml.tag!("CAVV", Base64.encode64(cryptogram[0...20]))
269
+ else
270
+ xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
271
+ xml.tag!("CAVV", credit_card.payment_cryptogram)
275
272
  end
276
273
  end
277
274
 
@@ -153,41 +153,41 @@ module ActiveMerchant #:nodoc:
153
153
  super
154
154
  end
155
155
 
156
- def purchase(money, credit_card, options = {})
157
- commit(money, build_sale_request(money, credit_card, options))
156
+ def purchase(money, payment, options = {})
157
+ commit(money, build_sale_request(money, payment, options))
158
158
  end
159
159
 
160
- def authorize(money, credit_card, options = {})
161
- commit(money, build_authonly_request(money, credit_card, options))
160
+ def authorize(money, payment, options = {})
161
+ commit(money, build_authonly_request(money, payment, options))
162
162
  end
163
163
 
164
164
  def capture(money, reference, options = {})
165
- split_authorization = reference.split(";")
166
- transaction_id = split_authorization[0]
167
- token = split_authorization[3]
168
- commit(money, build_capture_request(transaction_id, money, options), token)
165
+ transaction_id, _, _, token = reference.split(";")
166
+ commit(money, build_capture_request(money, transaction_id, options), token)
169
167
  end
170
168
 
171
169
  def void(reference, options = {})
172
- transaction_id, approval, amount, token = reference.split(";")
173
- commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options), token)
170
+ transaction_id, _, amount, token = reference.split(";")
171
+ commit(amount.to_i, build_void_request(amount.to_i, transaction_id, options), token)
174
172
  end
175
173
 
176
- def credit(money, credit_card, options = {})
177
- commit(money, build_credit_request(money, nil, credit_card, nil, options))
174
+ def credit(money, payment, options = {})
175
+ commit(money, build_credit_request(money, nil, payment, options))
178
176
  end
179
177
 
180
178
  def refund(money, reference, options = {})
181
- split_authorization = reference.split(";")
182
- transaction_id = split_authorization[0]
183
- token = split_authorization[3]
184
- commit(money, build_credit_request(money, transaction_id, nil, token, options))
179
+ transaction_id, _, _, token = reference.split(";")
180
+ commit(money, build_credit_request(money, transaction_id, token, options), token)
185
181
  end
186
182
 
187
- def verify(credit_card, options={})
183
+ def verify(credit_card, options = {})
188
184
  authorize(0, credit_card, options)
189
185
  end
190
186
 
187
+ def store(credit_card, options = {})
188
+ commit(nil, build_store_request(credit_card, options))
189
+ end
190
+
191
191
  def supports_scrubbing
192
192
  true
193
193
  end
@@ -224,9 +224,9 @@ module ActiveMerchant #:nodoc:
224
224
  end
225
225
  end
226
226
 
227
- def build_sale_request(money, credit_card, options)
227
+ def build_sale_request(money, payment, options)
228
228
  build_xml_request('SALE', options) do |xml|
229
- add_credit_card(xml, credit_card)
229
+ add_payment(xml, payment)
230
230
  add_addresses(xml, options)
231
231
  add_customer_data(xml, options)
232
232
  add_invoice_data(xml, options)
@@ -237,9 +237,9 @@ module ActiveMerchant #:nodoc:
237
237
  end
238
238
  end
239
239
 
240
- def build_authonly_request(money, credit_card, options)
240
+ def build_authonly_request(money, payment, options)
241
241
  build_xml_request('AUTHONLY', options) do |xml|
242
- add_credit_card(xml, credit_card)
242
+ add_payment(xml, payment)
243
243
  add_addresses(xml, options)
244
244
  add_customer_data(xml, options)
245
245
  add_invoice_data(xml, options)
@@ -250,9 +250,10 @@ module ActiveMerchant #:nodoc:
250
250
  end
251
251
  end
252
252
 
253
- def build_capture_request(transaction_id, money, options)
253
+ def build_capture_request(money, transaction_id, options)
254
254
  build_xml_request('CAPT', options, transaction_id) do |xml|
255
255
  add_invoice_data(xml, options)
256
+ add_purchase_order(xml, options)
256
257
  add_user_defined_fields(xml, options)
257
258
  xml.tag! 'TotalAmount', amount(money)
258
259
 
@@ -260,25 +261,31 @@ module ActiveMerchant #:nodoc:
260
261
  end
261
262
  end
262
263
 
263
- def build_void_request(money, transaction_id, approval, token, options)
264
+ def build_void_request(money, transaction_id, options)
264
265
  build_xml_request('VOID', options, transaction_id) do |xml|
265
- xml.tag! 'Approval', approval
266
266
  xml.tag! 'TotalAmount', amount(money)
267
- xml.tag! 'Token', token if token
268
-
269
267
  xml.target!
270
268
  end
271
269
  end
272
270
 
273
- def build_credit_request(money, transaction_id, card, token, options)
271
+ def build_credit_request(money, transaction_id, payment, options)
274
272
  build_xml_request('CREDIT', options, transaction_id) do |xml|
275
- add_credit_card(xml, card) if card
273
+ add_payment(xml, payment)
276
274
  add_invoice_data(xml, options)
277
275
  add_addresses(xml, options)
278
276
  add_customer_data(xml, options)
279
277
  add_user_defined_fields(xml, options)
280
278
  xml.tag! 'TotalAmount', amount(money)
281
- xml.tag! 'Token', token if token
279
+
280
+ xml.target!
281
+ end
282
+ end
283
+
284
+ def build_store_request(credit_card, options)
285
+ build_xml_request('TOKENIZE', options) do |xml|
286
+ add_payment(xml, credit_card)
287
+ add_addresses(xml, options)
288
+ add_customer_data(xml, options)
282
289
 
283
290
  xml.target!
284
291
  end
@@ -344,6 +351,18 @@ module ActiveMerchant #:nodoc:
344
351
  response[:action_code]
345
352
  end
346
353
 
354
+ def add_payment(xml, payment)
355
+ return unless payment
356
+
357
+ if payment.is_a? String
358
+ token = payment
359
+ _, _, _, token = payment.split(";") if payment.include? ";"
360
+ xml.tag! 'Token', token if token
361
+ else
362
+ add_credit_card(xml, payment)
363
+ end
364
+ end
365
+
347
366
  def add_credit_card(xml, credit_card)
348
367
  xml.tag! 'CardNum', credit_card.number, "CardPresent" => false, "Tokenize" => true
349
368
  xml.tag! 'CardExpMonth', format_exp(credit_card.month)
@@ -391,7 +410,15 @@ module ActiveMerchant #:nodoc:
391
410
  def add_invoice_data(xml, options)
392
411
  xml.tag! 'OrderNumber', options[:order_id] if options[:order_id]
393
412
  if tax_amount = options[:tax_amount]
394
- xml.tag! 'TaxAmount', tax_amount, {'ExemptInd' => options[:tax_exemption] || "false"}
413
+ xml.tag! 'TaxAmount', tax_amount, {'ExemptInd' => options[:tax_exempt] || "false"}
414
+ end
415
+ end
416
+
417
+ def add_purchase_order(xml, options)
418
+ if purchase_order = options[:purchase_order]
419
+ xml.tag! 'Billing' do
420
+ xml.tag! 'CustomerPO', purchase_order
421
+ end
395
422
  end
396
423
  end
397
424
 
@@ -181,21 +181,23 @@ module ActiveMerchant #:nodoc:
181
181
  sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month)
182
182
  end
183
183
 
184
- def add_payment_source(post, source, options)
185
- if source.is_a?(String)
186
- post[:data_key] = source
184
+ def add_payment_source(post, payment_method, options)
185
+ if payment_method.is_a?(String)
186
+ post[:data_key] = payment_method
187
187
  post[:cust_id] = options[:customer]
188
188
  else
189
- if source.respond_to?(:track_data) && source.track_data.present?
189
+ if payment_method.respond_to?(:track_data) && payment_method.track_data.present?
190
190
  post[:pos_code] = '00'
191
- post[:track2] = source.track_data
191
+ post[:track2] = payment_method.track_data
192
192
  else
193
- post[:pan] = source.number
194
- post[:expdate] = expdate(source)
195
- post[:cvd_value] = source.verification_value if source.verification_value?
196
- post[:cavv] = source.payment_cryptogram if source.is_a?(NetworkTokenizationCreditCard)
193
+ post[:pan] = payment_method.number
194
+ post[:expdate] = expdate(payment_method)
195
+ post[:cvd_value] = payment_method.verification_value if payment_method.verification_value?
196
+ post[:cavv] = payment_method.payment_cryptogram if payment_method.is_a?(NetworkTokenizationCreditCard)
197
+ post[:wallet_indicator] = wallet_indicator(payment_method.source.to_s) if payment_method.is_a?(NetworkTokenizationCreditCard)
198
+ post[:crypt_type] = (payment_method.eci || 7) if payment_method.is_a?(NetworkTokenizationCreditCard)
197
199
  end
198
- post[:cust_id] = options[:customer] || source.name
200
+ post[:cust_id] = options[:customer] || payment_method.name
199
201
  end
200
202
  end
201
203
 
@@ -310,6 +312,12 @@ module ActiveMerchant #:nodoc:
310
312
  element
311
313
  end
312
314
 
315
+ def wallet_indicator(token_source)
316
+ return 'APP' if token_source == 'apple_pay'
317
+ return 'ANP' if token_source == 'android_pay'
318
+ nil
319
+ end
320
+
313
321
  def message_from(message)
314
322
  return 'Unspecified error' if message.blank?
315
323
  message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
@@ -324,8 +332,8 @@ module ActiveMerchant #:nodoc:
324
332
  "indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
325
333
  "completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
326
334
  "purchasecorrection" => [:order_id, :txn_number, :crypt_type],
327
- "cavv_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
328
- "cavv_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
335
+ "cavv_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv, :crypt_type, :wallet_indicator],
336
+ "cavv_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv, :crypt_type, :wallet_indicator],
329
337
  "transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
330
338
  "Batchcloseall" => [],
331
339
  "opentotals" => [:ecr_number],
@@ -130,10 +130,22 @@ module ActiveMerchant #:nodoc:
130
130
  holder_name: creditcard.name
131
131
  }
132
132
  add_address(card, options)
133
+ add_customer_data(post, creditcard, options)
133
134
  post[:card] = card
134
135
  end
135
136
  end
136
137
 
138
+ def add_customer_data(post, creditcard, options)
139
+ if options[:email]
140
+ customer = {
141
+ name: creditcard.name || options[:name],
142
+ email: options[:email]
143
+ }
144
+ post[:customer] = customer
145
+ end
146
+ post
147
+ end
148
+
137
149
  def add_address(card, options)
138
150
  return unless card.kind_of?(Hash)
139
151
  if address = (options[:billing_address] || options[:address])
@@ -180,8 +180,8 @@ module ActiveMerchant #:nodoc:
180
180
  end
181
181
  end
182
182
 
183
- def build_headers(content_length)
184
- {
183
+ def build_headers(content_length, options = {})
184
+ headers = {
185
185
  "Content-Type" => "text/xml",
186
186
  "Content-Length" => content_length.to_s,
187
187
  "X-VPS-Client-Timeout" => timeout.to_s,
@@ -189,11 +189,14 @@ module ActiveMerchant #:nodoc:
189
189
  "X-VPS-VIT-Runtime-Version" => RUBY_VERSION,
190
190
  "X-VPS-Request-ID" => SecureRandom.hex(16)
191
191
  }
192
+
193
+ headers.merge!("PAYPAL-NVP" => options[:paypal_nvp]) if options[:paypal_nvp]
194
+ headers
192
195
  end
193
196
 
194
- def commit(request_body, options = {})
197
+ def commit(request_body, options = {})
195
198
  request = build_request(request_body, options)
196
- headers = build_headers(request.size)
199
+ headers = build_headers(request.size, options)
197
200
 
198
201
  response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers))
199
202
 
@@ -15,7 +15,7 @@ module ActiveMerchant
15
15
  end
16
16
 
17
17
  def purchase(money, credit_card_or_reference, options = {})
18
- MultiResponse.run(true) do |r|
18
+ MultiResponse.run do |r|
19
19
  if credit_card_or_reference.is_a?(String)
20
20
  r.process { create_token(credit_card_or_reference, options) }
21
21
  credit_card_or_reference = r.authorization
@@ -34,7 +34,7 @@ module ActiveMerchant
34
34
  end
35
35
 
36
36
  def authorize(money, credit_card_or_reference, options = {})
37
- MultiResponse.run(true) do |r|
37
+ MultiResponse.run do |r|
38
38
  if credit_card_or_reference.is_a?(String)
39
39
  r.process { create_token(credit_card_or_reference, options) }
40
40
  credit_card_or_reference = r.authorization
@@ -132,7 +132,6 @@ module ActiveMerchant
132
132
 
133
133
  def create_token(identification, options)
134
134
  post = {}
135
- # post[:id] = options[:id]
136
135
  commit(synchronized_path("/cards/#{identification}/tokens"), post)
137
136
  end
138
137
 
@@ -196,7 +195,7 @@ module ActiveMerchant
196
195
  post[:shipping_address] = map_address(options[:shipping_address])
197
196
  end
198
197
 
199
- [:metadata, :brading_id, :variables].each do |field|
198
+ [:metadata, :branding_id, :variables].each do |field|
200
199
  post[field] = options[field] if options[field]
201
200
  end
202
201
  end
@@ -24,6 +24,7 @@ module ActiveMerchant #:nodoc:
24
24
  post = {}
25
25
  add_transaction_data("Sale", post, money, options)
26
26
  add_payment(post, payment)
27
+ add_customer_details(post, payment, options)
27
28
 
28
29
  commit(post)
29
30
  end
@@ -32,14 +33,15 @@ module ActiveMerchant #:nodoc:
32
33
  post = {}
33
34
  add_transaction_data("Auth", post, money, options)
34
35
  add_payment(post, payment)
36
+ add_customer_details(post, payment, options)
35
37
 
36
38
  commit(post)
37
39
  end
38
40
 
39
41
  def capture(money, authorization, options={})
40
42
  post = {}
41
- add_transaction_data("Settle", post, money, options)
42
- auth, transaction_id, token, exp_month, exp_year, _ = authorization.split("|")
43
+ auth, transaction_id, token, exp_month, exp_year, _, original_currency = authorization.split("|")
44
+ add_transaction_data("Settle", post, money, (options.merge!({currency: original_currency})))
43
45
  post[:sg_AuthCode] = auth
44
46
  post[:sg_TransactionID] = transaction_id
45
47
  post[:sg_CCToken] = token
@@ -51,8 +53,8 @@ module ActiveMerchant #:nodoc:
51
53
 
52
54
  def refund(money, authorization, options={})
53
55
  post = {}
54
- add_transaction_data("Credit", post, money, options)
55
- auth, transaction_id, token, exp_month, exp_year, _ = authorization.split("|")
56
+ auth, transaction_id, token, exp_month, exp_year, _, original_currency = authorization.split("|")
57
+ add_transaction_data("Credit", post, money, (options.merge!({currency: original_currency})))
56
58
  post[:sg_CreditType] = 2
57
59
  post[:sg_AuthCode] = auth
58
60
  post[:sg_TransactionID] = transaction_id
@@ -74,8 +76,8 @@ module ActiveMerchant #:nodoc:
74
76
 
75
77
  def void(authorization, options={})
76
78
  post = {}
77
- auth, transaction_id, token, exp_month, exp_year, original_amount = authorization.split("|")
78
- add_transaction_data("Void", post, (original_amount.to_f * 100), options)
79
+ auth, transaction_id, token, exp_month, exp_year, original_amount, original_currency = authorization.split("|")
80
+ add_transaction_data("Void", post, (original_amount.to_f * 100), (options.merge!({currency: original_currency})))
79
81
  post[:sg_CreditType] = 2
80
82
  post[:sg_AuthCode] = auth
81
83
  post[:sg_TransactionID] = transaction_id
@@ -125,6 +127,21 @@ module ActiveMerchant #:nodoc:
125
127
  post[:sg_CVV2] = payment.verification_value
126
128
  end
127
129
 
130
+ def add_customer_details(post, payment, options)
131
+ if address = options[:billing_address] || options[:address]
132
+ post[:sg_FirstName] = payment.first_name
133
+ post[:sg_LastName] = payment.last_name
134
+ post[:sg_Address] = address[:address1] if address[:address1]
135
+ post[:sg_City] = address[:city] if address[:city]
136
+ post[:sg_State] = address[:state] if address[:state]
137
+ post[:sg_Zip] = address[:zip] if address[:zip]
138
+ post[:sg_Country] = address[:country] if address[:country]
139
+ post[:sg_Phone] = address[:phone] if address[:phone]
140
+ end
141
+
142
+ post[:sg_Email] = options[:email]
143
+ end
144
+
128
145
  def parse(xml)
129
146
  response = {}
130
147
 
@@ -177,7 +194,8 @@ module ActiveMerchant #:nodoc:
177
194
  response[:token],
178
195
  parameters[:sg_ExpMonth],
179
196
  parameters[:sg_ExpYear],
180
- parameters[:sg_Amount]
197
+ parameters[:sg_Amount],
198
+ parameters[:sg_Currency]
181
199
  ].join("|")
182
200
  end
183
201
 
@@ -0,0 +1,217 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class TrexleGateway < Gateway
4
+ self.test_url = 'https://core.trexle.com/api/v1'
5
+ self.live_url = 'https://core.trexle.com/api/v1'
6
+
7
+ self.default_currency = 'USD'
8
+ self.money_format = :cents
9
+ self.supported_countries = %w(AD AE AT AU BD BE BG BN CA CH CY CZ DE DK EE EG ES FI FR GB
10
+ GI GR HK HU ID IE IL IM IN IS IT JO KW LB LI LK LT LU LV MC
11
+ MT MU MV MX MY NL NO NZ OM PH PL PT QA RO SA SE SG SI SK SM
12
+ TR TT UM US VA VN ZA)
13
+ self.supported_cardtypes = [:visa, :master, :american_express]
14
+ self.homepage_url = 'https://trexle.com'
15
+ self.display_name = 'Trexle'
16
+
17
+ def initialize(options = {})
18
+ requires!(options, :api_key)
19
+ super
20
+ end
21
+
22
+ # Create a charge using a credit card, card token or customer token
23
+ #
24
+ # To charge a credit card: purchase([money], [creditcard hash], ...)
25
+ # To charge a customer: purchase([money], [token], ...)
26
+ def purchase(money, creditcard, options = {})
27
+ post = {}
28
+
29
+ add_amount(post, money, options)
30
+ add_customer_data(post, options)
31
+ add_invoice(post, options)
32
+ add_creditcard(post, creditcard)
33
+ add_address(post, creditcard, options)
34
+ commit(:post, 'charges', post, options)
35
+ end
36
+
37
+ # Create a customer and associated credit card. The token that is returned
38
+ # can be used instead of a credit card parameter in the purchase method
39
+ def store(creditcard, options = {})
40
+ post = {}
41
+
42
+ add_creditcard(post, creditcard)
43
+ add_customer_data(post, options)
44
+ add_address(post, creditcard, options)
45
+ commit(:post, 'customers', post, options)
46
+ end
47
+
48
+ # Refund a transaction
49
+ def refund(money, token, options = {})
50
+ commit(:post, "charges/#{CGI.escape(token)}/refunds", { amount: amount(money) }, options)
51
+ end
52
+
53
+ # Authorize an amount on a credit card. Once authorized, you can later
54
+ # capture this charge using the charge token that is returned.
55
+ def authorize(money, creditcard, options = {})
56
+ post = {}
57
+
58
+ add_amount(post, money, options)
59
+ add_customer_data(post, options)
60
+ add_invoice(post, options)
61
+ add_creditcard(post, creditcard)
62
+ add_address(post, creditcard, options)
63
+ post[:capture] = false
64
+ commit(:post, 'charges', post, options)
65
+ end
66
+
67
+ # Captures a previously authorized charge. Capturing only part of the original
68
+ # authorization is currently not supported.
69
+ def capture(money, token, options = {})
70
+ commit(:put, "charges/#{CGI.escape(token)}/capture", { amount: amount(money) }, options)
71
+ end
72
+
73
+ # Updates the credit card for the customer.
74
+ def update(token, creditcard, options = {})
75
+ post = {}
76
+
77
+ add_creditcard(post, creditcard)
78
+ add_customer_data(post, options)
79
+ add_address(post, creditcard, options)
80
+ commit(:put, "customers/#{CGI.escape(token)}", post, options)
81
+ end
82
+
83
+ def supports_scrubbing
84
+ true
85
+ end
86
+
87
+ def scrub(transcript)
88
+ transcript.
89
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
90
+ gsub(/(number\\?":\\?")(\d*)/, '\1[FILTERED]').
91
+ gsub(/(cvc\\?":\\?")(\d*)/, '\1[FILTERED]')
92
+ end
93
+ private
94
+
95
+ def add_amount(post, money, options)
96
+ post[:amount] = amount(money)
97
+ post[:currency] = (options[:currency] || currency(money))
98
+ post[:currency] = post[:currency].upcase if post[:currency]
99
+ end
100
+
101
+ def add_customer_data(post, options)
102
+ post[:email] = options[:email] if options[:email]
103
+ post[:ip_address] = options[:ip] if options[:ip]
104
+ end
105
+
106
+ def add_address(post, creditcard, options)
107
+ return if creditcard.kind_of?(String)
108
+ address = (options[:billing_address] || options[:address])
109
+ return unless address
110
+
111
+ post[:card] ||= {}
112
+ post[:card].merge!(
113
+ address_line1: address[:address1],
114
+ address_line2: address[:address_line2],
115
+ address_city: address[:city],
116
+ address_postcode: address[:zip],
117
+ address_state: address[:state],
118
+ address_country: address[:country]
119
+ )
120
+ end
121
+
122
+ def add_invoice(post, options)
123
+ post[:description] = options[:description] || "Active Merchant Purchase"
124
+ end
125
+
126
+ def add_creditcard(post, creditcard)
127
+ if creditcard.respond_to?(:number)
128
+ post[:card] ||= {}
129
+
130
+ post[:card].merge!(
131
+ number: creditcard.number,
132
+ expiry_month: creditcard.month,
133
+ expiry_year: creditcard.year,
134
+ cvc: creditcard.verification_value,
135
+ name: creditcard.name
136
+ )
137
+ elsif creditcard.kind_of?(String)
138
+ if creditcard =~ /^token_/
139
+ post[:card_token] = creditcard
140
+ else
141
+ post[:customer_token] = creditcard
142
+ end
143
+ end
144
+ end
145
+
146
+ def headers(params = {})
147
+ result = {
148
+ "Content-Type" => "application/json",
149
+ "Authorization" => "Basic #{Base64.strict_encode64(options[:api_key] + ':').strip}"
150
+ }
151
+
152
+ result['X-Partner-Key'] = params[:partner_key] if params[:partner_key]
153
+ result['X-Safe-Card'] = params[:safe_card] if params[:safe_card]
154
+ result
155
+ end
156
+
157
+ def commit(method, action, params, options)
158
+ url = "#{test? ? test_url : live_url}/#{action}"
159
+ raw_response = ssl_request(method, url, post_data(params), headers(options))
160
+ parsed_response = parse(raw_response)
161
+ success_response(parsed_response)
162
+ rescue ResponseError => e
163
+ error_response(parse(e.response.body))
164
+ rescue JSON::ParserError
165
+ unparsable_response(raw_response)
166
+ end
167
+
168
+ def success_response(body)
169
+ return invalid_response unless body['response']
170
+
171
+ response = body['response']
172
+ Response.new(
173
+ true,
174
+ response['status_message'],
175
+ body,
176
+ authorization: token(response),
177
+ test: test?
178
+ )
179
+ end
180
+
181
+ def error_response(body)
182
+ return invalid_response unless body['error']
183
+ Response.new(
184
+ false,
185
+ body['error'],
186
+ body,
187
+ authorization: nil,
188
+ test: test?
189
+ )
190
+ end
191
+
192
+ def unparsable_response(raw_response)
193
+ message = "Invalid JSON response received from Trexle. Please contact support@trexle.com if you continue to receive this message."
194
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
195
+ return Response.new(false, message)
196
+ end
197
+
198
+ def invalid_response
199
+ message = "Invalid response."
200
+ return Response.new(false, message)
201
+ end
202
+
203
+ def token(response)
204
+ response['token']
205
+ end
206
+
207
+ def parse(body)
208
+ return {} if body.blank?
209
+ JSON.parse(body)
210
+ end
211
+
212
+ def post_data(parameters = {})
213
+ parameters.to_json
214
+ end
215
+ end
216
+ end
217
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.67.0"
2
+ VERSION = "1.68.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.67.0
4
+ version: 1.68.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-08 00:00:00.000000000 Z
11
+ date: 2017-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -358,6 +358,7 @@ files:
358
358
  - lib/active_merchant/billing/gateways/transact_pro.rb
359
359
  - lib/active_merchant/billing/gateways/transax.rb
360
360
  - lib/active_merchant/billing/gateways/transnational.rb
361
+ - lib/active_merchant/billing/gateways/trexle.rb
361
362
  - lib/active_merchant/billing/gateways/trust_commerce.rb
362
363
  - lib/active_merchant/billing/gateways/usa_epay.rb
363
364
  - lib/active_merchant/billing/gateways/usa_epay_advanced.rb