activemerchant 1.67.0 → 1.68.0

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