activemerchant 1.66.0 → 1.67.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: 5c4bbd0ee52bf39713dad270b4e4b923d24d193c
4
- data.tar.gz: 304ee11c40f4c5c68d108ccbab8a6eceaddb0315
3
+ metadata.gz: 8d1e9a2bf41a0e836aba8e3d1296464f62d7765e
4
+ data.tar.gz: 6cd30371be55f49a0beef1eb5a3a8796cf226db0
5
5
  SHA512:
6
- metadata.gz: 69f9b2b2082147a44ec23c987b1f6ffaf139b44a2773e84c4c30e70b18812e02677c37714f36de005db44ebe66237b1617ddce74f58fae8731544d990accf216
7
- data.tar.gz: 394bc190204e8e3cb01b1b1b5f5a27c6af430672c51767a760b2f816d0c07e29ef5cb0d9bb3346a616a0ba3083d954d43034d7d8333b721a5ce44bb06ef5e940
6
+ metadata.gz: 4c0426a9d705e5b7c29031ac35ccdb1a2bf062efe8693760b485f9ece001e6149f64bbfa11dcc8cc23310ac5a13586b66683762cb0db98bd300d3eb5c8cd27d7
7
+ data.tar.gz: 54b90b7a5e2235c16e5ea1e91a161437b50081b2b19ccd7938f35f8398f65eafffee05eca2317c19f4cda4007986b59f9daea99db36e184b2a357a4aed437ebf
data/CHANGELOG CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.67.0 (June 8, 2017)
6
+ * Acapture: Pass 3D Secure fields [davidsantoso] #2451
7
+ * Authorize.net: Pass Level 2 Data Fields [curiousepic] #2444
8
+ * Credorax: Add 3D Secure authentication fields [davidsantoso] #2446
9
+ * Ebanx: Add gateway support [davidsantoso] #2447
10
+ * Ebanx: Reduce supported countries to Brazil and Mexico [davidsantoso]
11
+ * FirstData Payeezy: Set default ECI value for auth/purchase transactions [jasonwebster] #2448
12
+ * JetPay V2: Add new gateway [shasum] #2442
13
+ * JetPay V2: Add optional tax data to capture calls [shasum] #2445
14
+ * NMI: Add Network Tokenization support [shasum] #2431
15
+ * Orbital: Pass soft descriptors from options hash [curiousepic]
16
+ * Orbital: Update test and production urls [jcowhigjr] #2436
17
+ * Payeezy: Add client_email field for telecheck [davidsantoso] #2455
18
+ * Payeezy: Add customer_id_type and customer_id_number fields [davidsantoso] #2454
19
+ * Quickpay V10: Fix store and token use for recurring payments [wsmoak] #2180
20
+
5
21
  == Version 1.66.0 (May 4, 2017)
6
22
  * Support Rails 5.1 [jhawthorn] #2407
7
23
  * ProPay: Add Canada as supported country [davidsantoso]
@@ -10,6 +26,18 @@
10
26
  * WePay: Add scrub method [shasum] #2406
11
27
  * iVeri: Add gateway support [curiousepic] #2400
12
28
  * iVeri: Support 3DSecure data fields [davidsantoso] #2412
29
+ * Opp: Fix transaction success criteria and clean up options [shasum] #2414
30
+ * Elavon: Support custom fields [curiousepic] #2416
31
+ * WePay: Support risk headers [shasum] #2419
32
+ * WePay: Add Canada as supported country [shasum] #2419
33
+ * Fat Zebra: Fix xid 3D Secure field [curiousepic]
34
+ * SafeCharge: Mark support for European countries [curiousepic]
35
+ * Checkout V2: Pass customer ip option [curiousepic]
36
+ * Realex: Map AVS and CVV response codes [davidsantoso] #2424
37
+ * Opp: Send disable3DSecure custom parameter if present [davidsantoso] #2432
38
+ * SafeCharge: Map standard Active Merchant order_id field [davidsantoso] #2434
39
+ * Payeezy: Default check number to 001 if not present [davidsantoso] #2439
40
+ * Opp: Fix incorrect customParameter key to disable 3DS [davidsantoso]
13
41
 
14
42
  == Version 1.65.0 (April 26, 2017)
15
43
  * Adyen: Add Adyen v18 gateway [adyenpayments] #2272
@@ -166,7 +166,7 @@ module ActiveMerchant
166
166
  xml.amount(amount(amount))
167
167
 
168
168
  add_payment_source(xml, payment)
169
- add_invoice(xml, options)
169
+ add_invoice(xml, 'refundTransaction', options)
170
170
  add_customer_data(xml, payment, options)
171
171
  add_settings(xml, payment, options)
172
172
  add_user_fields(xml, amount, options)
@@ -244,7 +244,12 @@ module ActiveMerchant
244
244
  xml.transactionType(transaction_type)
245
245
  xml.amount(amount(amount))
246
246
  add_payment_source(xml, payment)
247
- add_invoice(xml, options)
247
+ add_invoice(xml, transaction_type, options)
248
+ add_tax_fields(xml, options)
249
+ add_duty_fields(xml, options)
250
+ add_shipping_fields(xml, options)
251
+ add_tax_exempt_status(xml, options)
252
+ add_po_number(xml, options)
248
253
  add_customer_data(xml, payment, options)
249
254
  add_market_type_device_type(xml, payment, options)
250
255
  add_settings(xml, payment, options)
@@ -257,8 +262,12 @@ module ActiveMerchant
257
262
  xml.transaction do
258
263
  xml.send(transaction_type) do
259
264
  xml.amount(amount(amount))
265
+ add_tax_fields(xml, options)
266
+ add_shipping_fields(xml, options)
267
+ add_duty_fields(xml, options)
260
268
  add_payment_source(xml, payment)
261
- add_invoice(xml, options)
269
+ add_invoice(xml, transaction_type, options)
270
+ add_tax_exempt_status(xml, options)
262
271
  end
263
272
  end
264
273
  end
@@ -269,6 +278,9 @@ module ActiveMerchant
269
278
  xml.transaction do
270
279
  xml.profileTransPriorAuthCapture do
271
280
  xml.amount(amount(amount))
281
+ add_tax_fields(xml, options)
282
+ add_shipping_fields(xml, options)
283
+ add_duty_fields(xml, options)
272
284
  xml.transId(transaction_id_from(authorization))
273
285
  end
274
286
  end
@@ -281,8 +293,13 @@ module ActiveMerchant
281
293
  xml.transactionRequest do
282
294
  xml.transactionType('priorAuthCaptureTransaction')
283
295
  xml.amount(amount(amount))
296
+ add_tax_fields(xml, options)
297
+ add_duty_fields(xml, options)
298
+ add_shipping_fields(xml, options)
299
+ add_tax_exempt_status(xml, options)
300
+ add_po_number(xml, options)
284
301
  xml.refTransId(transaction_id_from(authorization))
285
- add_invoice(xml, options)
302
+ add_invoice(xml, "capture", options)
286
303
  add_user_fields(xml, amount, options)
287
304
  end
288
305
  end
@@ -296,8 +313,11 @@ module ActiveMerchant
296
313
  xml.transaction do
297
314
  xml.profileTransRefund do
298
315
  xml.amount(amount(amount))
316
+ add_tax_fields(xml, options)
317
+ add_shipping_fields(xml, options)
318
+ add_duty_fields(xml, options)
299
319
  xml.creditCardNumberMasked(card_number)
300
- add_invoice(xml, options)
320
+ add_invoice(xml, "profileTransRefund", options)
301
321
  xml.transId(transaction_id)
302
322
  end
303
323
  end
@@ -319,7 +339,12 @@ module ActiveMerchant
319
339
  end
320
340
  xml.refTransId(transaction_id)
321
341
 
322
- add_invoice(xml, options)
342
+ add_invoice(xml, 'refundTransaction', options)
343
+ add_tax_fields(xml, options)
344
+ add_duty_fields(xml, options)
345
+ add_shipping_fields(xml, options)
346
+ add_tax_exempt_status(xml, options)
347
+ add_po_number(xml, options)
323
348
  add_customer_data(xml, nil, options)
324
349
  add_user_fields(xml, amount, options)
325
350
  end
@@ -570,10 +595,11 @@ module ActiveMerchant
570
595
  xml.refId(truncate(options[:order_id], 20))
571
596
  end
572
597
 
573
- def add_invoice(xml, options)
598
+ def add_invoice(xml, transaction_type, options)
574
599
  xml.order do
575
600
  xml.invoiceNumber(truncate(options[:order_id], 20))
576
601
  xml.description(truncate(options[:description], 255))
602
+ xml.purchaseOrderNumber(options[:po_number]) if options[:po_number] && transaction_type.start_with?("profileTrans")
577
603
  end
578
604
 
579
605
  # Authorize.net API requires lineItems to be placed directly after order tag
@@ -590,6 +616,47 @@ module ActiveMerchant
590
616
  end
591
617
  end
592
618
 
619
+ def add_tax_fields(xml, options)
620
+ tax = options[:tax]
621
+ if tax.is_a?(Hash)
622
+ xml.tax do
623
+ xml.amount(amount(tax[:amount].to_i))
624
+ xml.name(tax[:name])
625
+ xml.description(tax[:description])
626
+ end
627
+ end
628
+ end
629
+
630
+ def add_duty_fields(xml, options)
631
+ duty = options[:duty]
632
+ if duty.is_a?(Hash)
633
+ xml.duty do
634
+ xml.amount(amount(duty[:amount].to_i))
635
+ xml.name(duty[:name])
636
+ xml.description(duty[:description])
637
+ end
638
+ end
639
+ end
640
+
641
+ def add_shipping_fields(xml, options)
642
+ shipping = options[:shipping]
643
+ if shipping.is_a?(Hash)
644
+ xml.shipping do
645
+ xml.amount(amount(shipping[:amount].to_i))
646
+ xml.name(shipping[:name])
647
+ xml.description(shipping[:description])
648
+ end
649
+ end
650
+ end
651
+
652
+ def add_tax_exempt_status(xml, options)
653
+ xml.taxExempt(options[:tax_exempt]) if options[:tax_exempt]
654
+ end
655
+
656
+ def add_po_number(xml, options)
657
+ xml.poNumber(options[:po_number]) if options[:po_number]
658
+ end
659
+
593
660
  def create_customer_payment_profile(credit_card, options)
594
661
  commit(:cim_store_update) do |xml|
595
662
  xml.customerProfileId options[:customer_profile_id]
@@ -94,6 +94,7 @@ module ActiveMerchant #:nodoc:
94
94
 
95
95
  def add_customer_data(post, options)
96
96
  post[:email] = options[:email] || "unspecified@example.com"
97
+ post[:customerIp] = options[:ip] if options[:ip]
97
98
  address = options[:billing_address]
98
99
  if(address && post[:card])
99
100
  post[:card][:billingDetails] = {}
@@ -108,6 +108,7 @@ module ActiveMerchant #:nodoc:
108
108
  add_payment_method(post, payment_method)
109
109
  add_customer_data(post, options)
110
110
  add_email(post, options)
111
+ add_3d_secure(post, options)
111
112
  add_echo(post, options)
112
113
 
113
114
  commit(:purchase, post)
@@ -119,6 +120,7 @@ module ActiveMerchant #:nodoc:
119
120
  add_payment_method(post, payment_method)
120
121
  add_customer_data(post, options)
121
122
  add_email(post, options)
123
+ add_3d_secure(post, options)
122
124
  add_echo(post, options)
123
125
 
124
126
  commit(:authorize, post)
@@ -230,6 +232,11 @@ module ActiveMerchant #:nodoc:
230
232
  post[:c3] = options[:email] || 'unspecified@example.com'
231
233
  end
232
234
 
235
+ def add_3d_secure(post, options)
236
+ return unless options[:eci] && options[:xid]
237
+ post[:i8] = "#{options[:eci]}:#{(options[:cavv] || "none")}:#{options[:xid]}"
238
+ end
239
+
233
240
  def add_echo(post, options)
234
241
  # The d2 parameter is used during the certification process
235
242
  # See remote tests for full certification test suite
@@ -240,7 +247,7 @@ module ActiveMerchant #:nodoc:
240
247
  purchase: '1',
241
248
  authorize: '2',
242
249
  capture: '3',
243
- authorize_void:'4',
250
+ authorize_void: '4',
244
251
  refund: '5',
245
252
  credit: '6',
246
253
  purchase_void: '7',
@@ -691,7 +691,8 @@ module ActiveMerchant #:nodoc:
691
691
  end
692
692
 
693
693
  success = response[:decision] == "ACCEPT"
694
- message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
694
+ message = response[:message]
695
+
695
696
  authorization = success ? [ options[:order_id], response[:requestID], response[:requestToken], action, amount, options[:currency]].compact.join(";") : nil
696
697
 
697
698
  Response.new(success, message, response,
@@ -709,9 +710,10 @@ module ActiveMerchant #:nodoc:
709
710
  xml = REXML::Document.new(xml)
710
711
  if root = REXML::XPath.first(xml, "//c:replyMessage")
711
712
  root.elements.to_a.each do |node|
712
- case node.name
713
+ case node.expanded_name
713
714
  when 'c:reasonCode'
714
- reply[:message] = reply(node.text)
715
+ reply[:reasonCode] = node.text
716
+ reply[:message] = reason_message(node.text)
715
717
  else
716
718
  parse_element(reply, node)
717
719
  end
@@ -728,14 +730,19 @@ module ActiveMerchant #:nodoc:
728
730
  node.elements.each{|e| parse_element(reply, e) }
729
731
  else
730
732
  if node.parent.name =~ /item/
731
- parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
732
- reply[(parent + '_' + node.name).to_sym] = node.text
733
- else
734
- reply[node.name.to_sym] = node.text
733
+ parent = node.parent.name
734
+ parent += '_' + node.parent.attributes["id"] if node.parent.attributes["id"]
735
+ parent += '_'
735
736
  end
737
+ reply["#{parent}#{node.name}".to_sym] ||= node.text
736
738
  end
737
739
  return reply
738
740
  end
741
+
742
+ def reason_message(reason_code)
743
+ return if reason_code.blank?
744
+ @@response_codes[:"r#{reason_code}"]
745
+ end
739
746
  end
740
747
  end
741
748
  end
@@ -0,0 +1,234 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class EbanxGateway < Gateway
4
+ self.test_url = 'https://sandbox.ebanx.com/ws/'
5
+ self.live_url = 'https://api.ebanx.com/ws/'
6
+
7
+ self.supported_countries = ['BR', 'MX']
8
+ self.default_currency = 'USD'
9
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club]
10
+
11
+ self.homepage_url = 'http://www.ebanx.com/'
12
+ self.display_name = 'Ebanx'
13
+
14
+ CARD_BRAND = {
15
+ visa: "visa",
16
+ master: "master_card",
17
+ american_express: "amex",
18
+ discover: "discover",
19
+ diners_club: "diners"
20
+ }
21
+
22
+ URL_MAP = {
23
+ purchase: "direct",
24
+ authorize: "direct",
25
+ capture: "capture",
26
+ refund: "refund",
27
+ void: "cancel"
28
+ }
29
+
30
+ HTTP_METHOD = {
31
+ purchase: :post,
32
+ authorize: :post,
33
+ capture: :get,
34
+ refund: :post,
35
+ void: :get
36
+ }
37
+
38
+ def initialize(options={})
39
+ requires!(options, :integration_key)
40
+ super
41
+ end
42
+
43
+ def purchase(money, payment, options={})
44
+ post = { payment: {} }
45
+ add_integration_key(post)
46
+ add_operation(post)
47
+ add_invoice(post, money, options)
48
+ add_customer_data(post, payment, options)
49
+ add_payment(post, payment)
50
+ add_address(post, options)
51
+
52
+ commit(:purchase, post)
53
+ end
54
+
55
+ def authorize(money, payment, options={})
56
+ post = { payment: {} }
57
+ add_integration_key(post)
58
+ add_operation(post)
59
+ add_invoice(post, money, options)
60
+ add_customer_data(post, payment, options)
61
+ add_payment(post, payment)
62
+ add_address(post, options)
63
+ post[:payment][:creditcard][:auto_capture] = false
64
+
65
+ commit(:authorize, post)
66
+ end
67
+
68
+ def capture(money, authorization, options={})
69
+ post = {}
70
+ add_integration_key(post)
71
+ post[:hash] = authorization
72
+ post[:amount] = amount(money)
73
+
74
+ commit(:capture, post)
75
+ end
76
+
77
+ def refund(money, authorization, options={})
78
+ post = {}
79
+ add_integration_key(post)
80
+ add_operation(post)
81
+ add_authorization(post, authorization)
82
+ post[:amount] = amount(money)
83
+ post[:description] = options[:description]
84
+
85
+ commit(:refund, post)
86
+ end
87
+
88
+ def void(authorization, options={})
89
+ post = {}
90
+ add_integration_key(post)
91
+ add_authorization(post, authorization)
92
+
93
+ commit(:void, post)
94
+ end
95
+
96
+ def verify(credit_card, options={})
97
+ MultiResponse.run(:use_first_response) do |r|
98
+ r.process { authorize(100, credit_card, options) }
99
+ r.process(:ignore_result) { void(r.authorization, options) }
100
+ end
101
+ end
102
+
103
+ def supports_scrubbing?
104
+ true
105
+ end
106
+
107
+ def scrub(transcript)
108
+ transcript.
109
+ gsub(/(integration_key\\?":\\?")(\d*)/, '\1[FILTERED]').
110
+ gsub(/(card_number\\?":\\?")(\d*)/, '\1[FILTERED]').
111
+ gsub(/(card_cvv\\?":\\?")(\d*)/, '\1[FILTERED]')
112
+ end
113
+
114
+ private
115
+
116
+ def add_integration_key(post)
117
+ post[:integration_key] = @options[:integration_key].to_s
118
+ end
119
+
120
+ def add_operation(post)
121
+ post[:operation] = "request"
122
+ end
123
+
124
+ def add_authorization(post, authorization)
125
+ post[:hash] = authorization
126
+ end
127
+
128
+ def add_customer_data(post, payment, options)
129
+ post[:payment][:name] = payment.name
130
+ post[:payment][:email] = options[:email] || "unspecified@example.com"
131
+ post[:payment][:document] = options[:document]
132
+ end
133
+
134
+ def add_address(post, options)
135
+ if address = options[:billing_address] || options[:address]
136
+ post[:payment][:address] = address[:address1].split[1..-1].join(" ") if address[:address1]
137
+ post[:payment][:street_number] = address[:address1].split.first if address[:address1]
138
+ post[:payment][:city] = address[:city]
139
+ post[:payment][:state] = address[:state]
140
+ post[:payment][:zipcode] = address[:zip]
141
+ post[:payment][:country] = address[:country]
142
+ post[:payment][:phone_number] = address[:phone]
143
+ end
144
+ end
145
+
146
+ def add_invoice(post, money, options)
147
+ post[:payment][:amount_total] = amount(money)
148
+ post[:payment][:currency_code] = (options[:currency] || currency(money))
149
+ post[:payment][:merchant_payment_code] = options[:order_id]
150
+ end
151
+
152
+ def add_payment(post, payment)
153
+ post[:payment][:payment_type_code] = CARD_BRAND[payment.brand.to_sym]
154
+ post[:payment][:creditcard] = {
155
+ card_number: payment.number,
156
+ card_name: payment.name,
157
+ card_due_date: "#{payment.month}/#{payment.year}",
158
+ card_cvv: payment.verification_value
159
+ }
160
+ end
161
+
162
+ def parse(body)
163
+ JSON.parse(body)
164
+ end
165
+
166
+ def commit(action, parameters)
167
+ url = url_for((test? ? test_url : live_url), action, parameters)
168
+ response = parse(ssl_request(HTTP_METHOD[action], url, post_data(action, parameters), {}))
169
+
170
+ success = success_from(action, response)
171
+
172
+ Response.new(
173
+ success,
174
+ message_from(response),
175
+ response,
176
+ authorization: authorization_from(response),
177
+ test: test?,
178
+ error_code: error_code_from(response, success)
179
+ )
180
+ end
181
+
182
+ def success_from(action, response)
183
+ if [:purchase, :capture, :refund].include?(action)
184
+ response.try(:[], "payment").try(:[], "status") == "CO"
185
+ elsif action == :authorize
186
+ response.try(:[], "payment").try(:[], "status") == "PE"
187
+ elsif action == :void
188
+ response.try(:[], "payment").try(:[], "status") == "CA"
189
+ else
190
+ false
191
+ end
192
+ end
193
+
194
+ def message_from(response)
195
+ return response["status_message"] if response["status"] == "ERROR"
196
+ response.try(:[], "payment").try(:[], "transaction_status").try(:[], "description")
197
+ end
198
+
199
+ def authorization_from(response)
200
+ response.try(:[], "payment").try(:[], "hash")
201
+ end
202
+
203
+ def post_data(action, parameters = {})
204
+ return nil if requires_http_get(action)
205
+ return convert_to_url_form_encoded(parameters) if action == :refund
206
+ "request_body=#{parameters.to_json}"
207
+ end
208
+
209
+ def url_for(hostname, action, parameters)
210
+ return hostname + URL_MAP[action] + "?#{convert_to_url_form_encoded(parameters)}" if requires_http_get(action)
211
+ hostname + URL_MAP[action]
212
+ end
213
+
214
+ def requires_http_get(action)
215
+ return true if [:capture, :void].include?(action)
216
+ false
217
+ end
218
+
219
+ def convert_to_url_form_encoded(parameters)
220
+ parameters.map do |key, value|
221
+ next if value != false && value.blank?
222
+ "#{key}=#{value}"
223
+ end.compact.join("&")
224
+ end
225
+
226
+ def error_code_from(response, success)
227
+ unless success
228
+ return response["status_code"] if response["status"] == "ERROR"
229
+ response.try(:[], "payment").try(:[], "transaction_status").try(:[], "code")
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end