activemerchant 1.46.0 → 1.47.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG +17 -0
- data/README.md +1 -1
- data/lib/active_merchant/billing.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +40 -5
- data/lib/active_merchant/billing/gateways.rb +2 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +37 -11
- data/lib/active_merchant/billing/gateways/checkout.rb +1 -3
- data/lib/active_merchant/billing/gateways/cyber_source.rb +62 -18
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +14 -2
- data/lib/active_merchant/billing/gateways/flo2cash.rb +215 -0
- data/lib/active_merchant/billing/gateways/flo2cash_simple.rb +20 -0
- data/lib/active_merchant/billing/gateways/litle.rb +5 -3
- data/lib/active_merchant/billing/gateways/migs.rb +8 -2
- data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +16 -26
- data/lib/active_merchant/billing/gateways/payflow.rb +4 -0
- data/lib/active_merchant/billing/gateways/paymill.rb +2 -2
- data/lib/active_merchant/billing/gateways/pin.rb +3 -2
- data/lib/active_merchant/billing/gateways/quickpay.rb +10 -5
- data/lib/active_merchant/billing/network_tokenization_credit_card.rb +16 -0
- data/lib/active_merchant/errors.rb +6 -0
- data/lib/active_merchant/network_connection_retries.rb +3 -3
- data/lib/active_merchant/version.rb +1 -1
- metadata +5 -17
- metadata.gz.sig +0 -0
- data/lib/active_merchant/billing/gateways/vindicia.rb +0 -385
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbec33f0dbb45aa949b164bf0bc376b5aeee2f51
|
4
|
+
data.tar.gz: 6bb77b71ff18e0cdaf18ff62b2fb28e1cace2588
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e34c4f81edd376e109de5d0e2ef50463763c7ea2be6884d401417044336982cb048235c029771ba8b7e99c7a985b91e214d63ee248a25d0d9e6038999fe1fe1
|
7
|
+
data.tar.gz: a9a128c91e69846177d0e3d9f9b51c1295c741423d9780db449a284902f443255f0f54660e2bfc23d89bad67d26d7956a56e19d17a28ed0a5410bfc8fa9e51a5
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
= ActiveMerchant CHANGELOG
|
2
2
|
|
3
|
+
== Version 1.47.0 (February 25, 2015)
|
4
|
+
|
5
|
+
* Authorize.Net: Properly send name in shipping address line, when shipping address is provided [girasquid]
|
6
|
+
* Payflow: Add verify support [ntalbott]
|
7
|
+
* Capture ConnectionError#triggering_exception [ntalbott]
|
8
|
+
* Flo2Cash: Map Reference->:order_id (not :invoice) [ntalbott]
|
9
|
+
* Flo2Cash: Fix card brand handling [ntalbott]
|
10
|
+
* Flo2Cash: Improve error handling & simplify "Simple" gateway [ntalbott]
|
11
|
+
* Remove Vindicia gateway [mutemule]
|
12
|
+
* Firstdata E4: Support other mastercard string [jcbantuelle]
|
13
|
+
* Checkout: Disallow altering endpoint via options [markabe]
|
14
|
+
|
3
15
|
== Version 1.46.0 (January 20, 2015)
|
4
16
|
|
5
17
|
* CHANGE: drop `offsite_payments` and `active_utils` as dependencies. [wvanbergen]
|
@@ -24,6 +36,11 @@
|
|
24
36
|
* Pin: Handle JSON parsing exception in response [duff]
|
25
37
|
* Improve test suite to test against multiple ActiveSupport versions [wvanbergen]
|
26
38
|
* Misc. code cleanup [wvanbergen]
|
39
|
+
* Add Flo2Cash gateway [markabe]
|
40
|
+
* Litle: Allow order_source override [duff]
|
41
|
+
* Quickpay: Add ability to finalize a capture [askehansen]
|
42
|
+
* AuthorizeNet: Prevent test mode using live gateway [duff]
|
43
|
+
* Add Flo2Cash Simple gateway [markabe]
|
27
44
|
|
28
45
|
== Version 1.45.0 (December 1, 2014)
|
29
46
|
|
data/README.md
CHANGED
@@ -116,6 +116,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
|
|
116
116
|
* [Fat Zebra](https://www.fatzebra.com.au/) - AU
|
117
117
|
* [Federated Canada](http://www.federatedcanada.com/) - CA
|
118
118
|
* [Finansbank WebPOS](https://www.fbwebpos.com/) - US, TR
|
119
|
+
* [Flo2Cash](http://www.flo2cash.co.nz/) - NZ
|
119
120
|
* [1stPayGateway.Net](http://1stpaygateway.net/) - US
|
120
121
|
* [FirstData Global Gateway e4](http://www.firstdata.com) - CA, US
|
121
122
|
* [FirstGiving](http://www.firstgiving.com/) - US
|
@@ -202,7 +203,6 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
|
|
202
203
|
* [USA ePay](http://www.usaepay.com/) - US
|
203
204
|
* [Verifi](http://www.verifi.com/) - US
|
204
205
|
* [ViaKLIX](http://viaklix.com) - US
|
205
|
-
* [Vindicia](http://www.vindicia.com/) - US, CA, GB, AU, MX, BR, DE, KR, CN, HK
|
206
206
|
* [WebPay](https://webpay.jp/) - JP
|
207
207
|
* [WePay](https://www.wepay.com/) - US
|
208
208
|
* [Wirecard](http://www.wirecard.com) - AD, CY, GI, IM, MT, RO, CH, AT, DK, GR, IT, MC, SM, TR, BE, EE, HU, LV, NL, SK, GB, BG, FI, IS, LI, NO, SI, VA, FR, IL, LT, PL, ES, CZ, DE, IE, LU, PT, SE
|
@@ -5,6 +5,7 @@ require 'active_merchant/billing/cvv_result'
|
|
5
5
|
require 'active_merchant/billing/credit_card_methods'
|
6
6
|
require 'active_merchant/billing/credit_card_formatting'
|
7
7
|
require 'active_merchant/billing/credit_card'
|
8
|
+
require 'active_merchant/billing/network_tokenization_credit_card'
|
8
9
|
require 'active_merchant/billing/base'
|
9
10
|
require 'active_merchant/billing/check'
|
10
11
|
require 'active_merchant/billing/payment_token'
|
@@ -143,16 +143,51 @@ module ActiveMerchant #:nodoc:
|
|
143
143
|
%w[1 2 3 success failure error].include?(number.to_s)
|
144
144
|
end
|
145
145
|
|
146
|
+
ODD_LUHN_VALUE = {
|
147
|
+
48 => 0,
|
148
|
+
49 => 1,
|
149
|
+
50 => 2,
|
150
|
+
51 => 3,
|
151
|
+
52 => 4,
|
152
|
+
53 => 5,
|
153
|
+
54 => 6,
|
154
|
+
55 => 7,
|
155
|
+
56 => 8,
|
156
|
+
57 => 9,
|
157
|
+
nil => 0
|
158
|
+
}.freeze
|
159
|
+
|
160
|
+
EVEN_LUHN_VALUE = {
|
161
|
+
48 => 0, # 0 * 2
|
162
|
+
49 => 2, # 1 * 2
|
163
|
+
50 => 4, # 2 * 2
|
164
|
+
51 => 6, # 3 * 2
|
165
|
+
52 => 8, # 4 * 2
|
166
|
+
53 => 1, # 5 * 2 - 9
|
167
|
+
54 => 3, # 6 * 2 - 9
|
168
|
+
55 => 5, # etc ...
|
169
|
+
56 => 7,
|
170
|
+
57 => 9,
|
171
|
+
}.freeze
|
172
|
+
|
146
173
|
# Checks the validity of a card number by use of the Luhn Algorithm.
|
147
174
|
# Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
|
148
|
-
|
175
|
+
# This implementation is from the luhn_checksum gem, https://github.com/zendesk/luhn_checksum.
|
176
|
+
def valid_checksum?(numbers) #:nodoc:
|
149
177
|
sum = 0
|
150
|
-
|
151
|
-
|
152
|
-
|
178
|
+
|
179
|
+
odd = true
|
180
|
+
numbers.reverse.bytes.each do |number|
|
181
|
+
if odd
|
182
|
+
odd = false
|
183
|
+
sum += ODD_LUHN_VALUE[number]
|
184
|
+
else
|
185
|
+
odd = true
|
186
|
+
sum += EVEN_LUHN_VALUE[number]
|
187
|
+
end
|
153
188
|
end
|
154
189
|
|
155
|
-
|
190
|
+
sum % 10 == 0
|
156
191
|
end
|
157
192
|
end
|
158
193
|
end
|
@@ -123,6 +123,16 @@ module ActiveMerchant #:nodoc:
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
def supports_scrubbing?
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
def scrub(transcript)
|
131
|
+
transcript.
|
132
|
+
gsub(%r((<cardNumber>).+(</cardNumber>)), '\1[FILTERED]\2').
|
133
|
+
gsub(%r((<cardCode>).+(</cardCode>)), '\1[FILTERED]\2')
|
134
|
+
end
|
135
|
+
|
126
136
|
private
|
127
137
|
|
128
138
|
def add_payment_source(xml, source)
|
@@ -267,7 +277,11 @@ module ActiveMerchant #:nodoc:
|
|
267
277
|
|
268
278
|
unless shipping_address.blank?
|
269
279
|
xml.shipTo do
|
270
|
-
first_name, last_name =
|
280
|
+
(first_name, last_name) = if shipping_address[:name]
|
281
|
+
shipping_address[:name].split
|
282
|
+
else
|
283
|
+
[shipping_address[:first_name], shipping_address[:last_name]]
|
284
|
+
end
|
271
285
|
xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
|
272
286
|
xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
|
273
287
|
|
@@ -314,16 +328,20 @@ module ActiveMerchant #:nodoc:
|
|
314
328
|
|
315
329
|
avs_result = AVSResult.new(code: response[:avs_result_code])
|
316
330
|
cvv_result = CVVResult.new(response[:card_code])
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
331
|
+
if using_live_gateway_in_test_mode?(response)
|
332
|
+
Response.new(false, "Using a live Authorize.net account in Test Mode is not permitted.")
|
333
|
+
else
|
334
|
+
Response.new(
|
335
|
+
success_from(response),
|
336
|
+
message_from(response, avs_result, cvv_result),
|
337
|
+
response,
|
338
|
+
authorization: authorization_from(response),
|
339
|
+
test: test?,
|
340
|
+
avs_result: avs_result,
|
341
|
+
cvv_result: cvv_result,
|
342
|
+
fraud_review: fraud_review?(response)
|
343
|
+
)
|
344
|
+
end
|
327
345
|
end
|
328
346
|
|
329
347
|
def post_data
|
@@ -386,6 +404,10 @@ module ActiveMerchant #:nodoc:
|
|
386
404
|
(empty?(element.content) ? nil : element.content[-4..-1])
|
387
405
|
end
|
388
406
|
|
407
|
+
response[:test_request] = if(element = doc.at_xpath("//testRequest"))
|
408
|
+
(empty?(element.content) ? nil : element.content)
|
409
|
+
end
|
410
|
+
|
389
411
|
response
|
390
412
|
end
|
391
413
|
|
@@ -425,6 +447,10 @@ module ActiveMerchant #:nodoc:
|
|
425
447
|
return nil unless value
|
426
448
|
value.to_s[0, max_size]
|
427
449
|
end
|
450
|
+
|
451
|
+
def using_live_gateway_in_test_mode?(response)
|
452
|
+
!test? && response[:test_request] == "1"
|
453
|
+
end
|
428
454
|
end
|
429
455
|
end
|
430
456
|
end
|
@@ -26,8 +26,6 @@ module ActiveMerchant #:nodoc:
|
|
26
26
|
}
|
27
27
|
|
28
28
|
def initialize(options = {})
|
29
|
-
@url = (options[:gateway_url] || self.live_url)
|
30
|
-
|
31
29
|
requires!(options, :merchant_id, :password)
|
32
30
|
super
|
33
31
|
end
|
@@ -166,7 +164,7 @@ module ActiveMerchant #:nodoc:
|
|
166
164
|
end
|
167
165
|
|
168
166
|
def commit(action, amount=nil, options={}, &builder)
|
169
|
-
response = parse_xml(ssl_post(
|
167
|
+
response = parse_xml(ssl_post(live_url, build_xml(action, &builder)))
|
170
168
|
Response.new(
|
171
169
|
(response[:responsecode] == "0"),
|
172
170
|
(response[:result] || response[:error_text] || "Unknown Response"),
|
@@ -27,11 +27,13 @@ module ActiveMerchant #:nodoc:
|
|
27
27
|
# * To process pinless debit cards through the pinless debit card
|
28
28
|
# network, your Cybersource merchant account must accept pinless
|
29
29
|
# debit card payments.
|
30
|
+
# * The order of the XML elements does matter, make sure to follow the order in
|
31
|
+
# the documentation exactly.
|
30
32
|
class CyberSourceGateway < Gateway
|
31
33
|
self.test_url = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor'
|
32
34
|
self.live_url = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor'
|
33
35
|
|
34
|
-
XSD_VERSION = "1.
|
36
|
+
XSD_VERSION = "1.109"
|
35
37
|
|
36
38
|
# visa, master, american_express, discover
|
37
39
|
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
@@ -241,8 +243,8 @@ module ActiveMerchant #:nodoc:
|
|
241
243
|
def build_auth_request(money, creditcard_or_reference, options)
|
242
244
|
xml = Builder::XmlMarkup.new :indent => 2
|
243
245
|
add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
|
244
|
-
add_auth_service(xml)
|
245
|
-
add_business_rules_data(xml, options)
|
246
|
+
add_auth_service(xml, creditcard_or_reference, options)
|
247
|
+
add_business_rules_data(xml, creditcard_or_reference, options)
|
246
248
|
xml.target!
|
247
249
|
end
|
248
250
|
|
@@ -253,7 +255,7 @@ module ActiveMerchant #:nodoc:
|
|
253
255
|
add_line_item_data(xml, options)
|
254
256
|
add_purchase_data(xml, 0, false, options)
|
255
257
|
add_tax_service(xml)
|
256
|
-
add_business_rules_data(xml, options)
|
258
|
+
add_business_rules_data(xml, creditcard, options)
|
257
259
|
xml.target!
|
258
260
|
end
|
259
261
|
|
@@ -264,7 +266,7 @@ module ActiveMerchant #:nodoc:
|
|
264
266
|
xml = Builder::XmlMarkup.new :indent => 2
|
265
267
|
add_purchase_data(xml, money, true, options)
|
266
268
|
add_capture_service(xml, request_id, request_token)
|
267
|
-
add_business_rules_data(xml, options)
|
269
|
+
add_business_rules_data(xml, authorization, options)
|
268
270
|
xml.target!
|
269
271
|
end
|
270
272
|
|
@@ -274,8 +276,8 @@ module ActiveMerchant #:nodoc:
|
|
274
276
|
if !payment_method_or_reference.is_a?(String) && card_brand(payment_method_or_reference) == 'check'
|
275
277
|
add_check_service(xml)
|
276
278
|
else
|
277
|
-
add_purchase_service(xml, options)
|
278
|
-
add_business_rules_data(xml, options) unless options[:pinless_debit_card]
|
279
|
+
add_purchase_service(xml, payment_method_or_reference, options)
|
280
|
+
add_business_rules_data(xml, payment_method_or_reference, options) unless options[:pinless_debit_card]
|
279
281
|
end
|
280
282
|
xml.target!
|
281
283
|
end
|
@@ -340,11 +342,11 @@ module ActiveMerchant #:nodoc:
|
|
340
342
|
if card_brand(payment_method) == 'check'
|
341
343
|
add_check_service(xml, options)
|
342
344
|
else
|
343
|
-
add_purchase_service(xml, options)
|
345
|
+
add_purchase_service(xml, payment_method, options)
|
344
346
|
end
|
345
347
|
end
|
346
348
|
add_subscription_create_service(xml, options)
|
347
|
-
add_business_rules_data(xml, options)
|
349
|
+
add_business_rules_data(xml, payment_method, options)
|
348
350
|
xml.target!
|
349
351
|
end
|
350
352
|
|
@@ -356,7 +358,7 @@ module ActiveMerchant #:nodoc:
|
|
356
358
|
add_creditcard_payment_method(xml) if creditcard
|
357
359
|
add_subscription(xml, options, reference)
|
358
360
|
add_subscription_update_service(xml, options)
|
359
|
-
add_business_rules_data(xml, options)
|
361
|
+
add_business_rules_data(xml, creditcard, options)
|
360
362
|
xml.target!
|
361
363
|
end
|
362
364
|
|
@@ -381,12 +383,14 @@ module ActiveMerchant #:nodoc:
|
|
381
383
|
xml.target!
|
382
384
|
end
|
383
385
|
|
384
|
-
def add_business_rules_data(xml, options)
|
386
|
+
def add_business_rules_data(xml, payment_method, options)
|
385
387
|
prioritized_options = [options, @options]
|
386
388
|
|
387
|
-
|
388
|
-
xml.tag!
|
389
|
-
|
389
|
+
unless network_tokenization?(payment_method)
|
390
|
+
xml.tag! 'businessRules' do
|
391
|
+
xml.tag!('ignoreAVSResult', 'true') if extract_option(prioritized_options, :ignore_avs)
|
392
|
+
xml.tag!('ignoreCVResult', 'true') if extract_option(prioritized_options, :ignore_cvv)
|
393
|
+
end
|
390
394
|
end
|
391
395
|
end
|
392
396
|
|
@@ -468,8 +472,48 @@ module ActiveMerchant #:nodoc:
|
|
468
472
|
end
|
469
473
|
end
|
470
474
|
|
471
|
-
def add_auth_service(xml)
|
472
|
-
|
475
|
+
def add_auth_service(xml, payment_method, options)
|
476
|
+
if network_tokenization?(payment_method)
|
477
|
+
add_network_tokenization(xml, payment_method, options)
|
478
|
+
else
|
479
|
+
xml.tag! 'ccAuthService', {'run' => 'true'}
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def network_tokenization?(payment_method)
|
484
|
+
payment_method.is_a?(NetworkTokenizationCreditCard)
|
485
|
+
end
|
486
|
+
|
487
|
+
def add_network_tokenization(xml, payment_method, options)
|
488
|
+
return unless network_tokenization?(payment_method)
|
489
|
+
|
490
|
+
case card_brand(payment_method).to_sym
|
491
|
+
when :visa
|
492
|
+
xml.tag! 'ccAuthService', {'run' => 'true'} do
|
493
|
+
xml.tag!("cavv", payment_method.payment_cryptogram)
|
494
|
+
xml.tag!("commerceIndicator", "vbv")
|
495
|
+
xml.tag!("xid", payment_method.payment_cryptogram)
|
496
|
+
end
|
497
|
+
when :mastercard
|
498
|
+
xml.tag! 'ucaf' do
|
499
|
+
xml.tag!("authenticationData", payment_method.payment_cryptogram)
|
500
|
+
xml.tag!("collectionIndicator", "2")
|
501
|
+
end
|
502
|
+
xml.tag! 'ccAuthService', {'run' => 'true'} do
|
503
|
+
xml.tag!("commerceIndicator", "spa")
|
504
|
+
end
|
505
|
+
when :american_express
|
506
|
+
cryptogram = Base64.decode64(payment_method.payment_cryptogram)
|
507
|
+
xml.tag! 'ccAuthService', {'run' => 'true'} do
|
508
|
+
xml.tag!("cavv", Base64.encode64(cryptogram[0...20]))
|
509
|
+
xml.tag!("commerceIndicator", "aesk")
|
510
|
+
xml.tag!("xid", Base64.encode64(cryptogram[20...40]))
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
xml.tag! 'paymentNetworkToken' do
|
515
|
+
xml.tag!('transactionType', "1")
|
516
|
+
end
|
473
517
|
end
|
474
518
|
|
475
519
|
def add_capture_service(xml, request_id, request_token)
|
@@ -479,11 +523,11 @@ module ActiveMerchant #:nodoc:
|
|
479
523
|
end
|
480
524
|
end
|
481
525
|
|
482
|
-
def add_purchase_service(xml, options)
|
526
|
+
def add_purchase_service(xml, payment_method, options)
|
483
527
|
if options[:pinless_debit_card]
|
484
528
|
xml.tag! 'pinlessDebitService', {'run' => 'true'}
|
485
529
|
else
|
486
|
-
xml
|
530
|
+
add_auth_service(xml, payment_method, options)
|
487
531
|
xml.tag! 'ccCaptureService', {'run' => 'true'}
|
488
532
|
end
|
489
533
|
end
|
@@ -32,6 +32,8 @@ module ActiveMerchant #:nodoc:
|
|
32
32
|
:discover => "Discover"
|
33
33
|
}
|
34
34
|
|
35
|
+
E4_BRANDS = BRANDS.merge({:mastercard => "Mastercard"})
|
36
|
+
|
35
37
|
self.supported_cardtypes = BRANDS.keys
|
36
38
|
self.supported_countries = ["CA", "US"]
|
37
39
|
self.default_currency = "USD"
|
@@ -106,6 +108,16 @@ module ActiveMerchant #:nodoc:
|
|
106
108
|
commit(:store, build_store_request(credit_card, options), credit_card)
|
107
109
|
end
|
108
110
|
|
111
|
+
def supports_scrubbing?
|
112
|
+
true
|
113
|
+
end
|
114
|
+
|
115
|
+
def scrub(transcript)
|
116
|
+
transcript.
|
117
|
+
gsub(%r((<Card_Number>).+(</Card_Number>)), '\1[FILTERED]\2').
|
118
|
+
gsub(%r((<VerificationStr2>).+(</VerificationStr2>)), '\1[FILTERED]\2')
|
119
|
+
end
|
120
|
+
|
109
121
|
private
|
110
122
|
|
111
123
|
def build_request(action, body)
|
@@ -250,7 +262,7 @@ module ActiveMerchant #:nodoc:
|
|
250
262
|
end
|
251
263
|
|
252
264
|
def card_type(credit_card_brand)
|
253
|
-
|
265
|
+
E4_BRANDS[credit_card_brand.to_sym] if credit_card_brand
|
254
266
|
end
|
255
267
|
|
256
268
|
def commit(action, request, credit_card = nil)
|
@@ -263,7 +275,7 @@ module ActiveMerchant #:nodoc:
|
|
263
275
|
|
264
276
|
Response.new(successful?(response), message_from(response), response,
|
265
277
|
:test => test?,
|
266
|
-
:authorization => response_authorization(action, response, credit_card),
|
278
|
+
:authorization => successful?(response) ? response_authorization(action, response, credit_card) : '',
|
267
279
|
:avs_result => {:code => response[:avs]},
|
268
280
|
:cvv_result => response[:cvv2]
|
269
281
|
)
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class Flo2cashGateway < Gateway
|
4
|
+
self.display_name = 'Flo2Cash'
|
5
|
+
self.homepage_url = 'http://www.flo2cash.co.nz/'
|
6
|
+
|
7
|
+
self.test_url = 'https://demo.flo2cash.co.nz/ws/paymentws.asmx'
|
8
|
+
self.live_url = 'https://secure.flo2cash.co.nz/ws/paymentws.asmx'
|
9
|
+
|
10
|
+
self.supported_countries = ['NZ']
|
11
|
+
self.default_currency = 'NZD'
|
12
|
+
self.money_format = :dollars
|
13
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
|
14
|
+
|
15
|
+
BRAND_MAP = {
|
16
|
+
"visa" => "VISA",
|
17
|
+
"master" => "MC",
|
18
|
+
"american_express" => "AMEX",
|
19
|
+
"diners_club" => "DINERS"
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize(options={})
|
23
|
+
requires!(options, :username, :password, :account_id)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def purchase(amount, payment_method, options={})
|
28
|
+
MultiResponse.run do |r|
|
29
|
+
r.process { authorize(amount, payment_method, options) }
|
30
|
+
r.process { capture(amount, r.authorization, options) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def authorize(amount, payment_method, options={})
|
35
|
+
post = {}
|
36
|
+
add_invoice(post, amount, options)
|
37
|
+
add_payment_method(post, payment_method)
|
38
|
+
add_customer_data(post, options)
|
39
|
+
|
40
|
+
commit("ProcessAuthorise", post)
|
41
|
+
end
|
42
|
+
|
43
|
+
def capture(amount, authorization, options={})
|
44
|
+
post = {}
|
45
|
+
add_invoice(post, amount, options)
|
46
|
+
add_reference(post, authorization)
|
47
|
+
add_customer_data(post, options)
|
48
|
+
|
49
|
+
commit("ProcessCapture", post)
|
50
|
+
end
|
51
|
+
|
52
|
+
def refund(amount, authorization, options={})
|
53
|
+
post = {}
|
54
|
+
add_invoice(post, amount, options)
|
55
|
+
add_reference(post, authorization)
|
56
|
+
add_customer_data(post, options)
|
57
|
+
|
58
|
+
commit("ProcessRefund", post)
|
59
|
+
end
|
60
|
+
|
61
|
+
def supports_scrubbing?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def scrub(transcript)
|
66
|
+
transcript.
|
67
|
+
gsub(%r((<Password>)[^<]+(<))i, '\1[FILTERED]\2').
|
68
|
+
gsub(%r((<CardNumber>)[^<]+(<))i, '\1[FILTERED]\2').
|
69
|
+
gsub(%r((<CardCSC>)[^<]+(<))i, '\1[FILTERED]\2')
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
CURRENCY_CODES = Hash.new{|h,k| raise ArgumentError.new("Unsupported currency: #{k}")}
|
75
|
+
CURRENCY_CODES["NZD"] = "554"
|
76
|
+
|
77
|
+
def add_invoice(post, money, options)
|
78
|
+
post[:Amount] = amount(money)
|
79
|
+
post[:Reference] = options[:order_id]
|
80
|
+
post[:Particular] = options[:description]
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_payment_method(post, payment_method)
|
84
|
+
post[:CardNumber] = payment_method.number
|
85
|
+
post[:CardType] = BRAND_MAP[payment_method.brand.to_s]
|
86
|
+
post[:CardExpiry] = format(payment_method.month, :two_digits) + format(payment_method.year, :two_digits)
|
87
|
+
post[:CardHolderName] = payment_method.name
|
88
|
+
post[:CardCSC] = payment_method.verification_value
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_customer_data(post, options)
|
92
|
+
if(billing_address = (options[:billing_address] || options[:address]))
|
93
|
+
post[:Email] = billing_address[:email]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_reference(post, authorization)
|
98
|
+
post[:OriginalTransactionId] = authorization
|
99
|
+
end
|
100
|
+
|
101
|
+
def commit(action, post)
|
102
|
+
post[:Username] = @options[:username]
|
103
|
+
post[:Password] = @options[:password]
|
104
|
+
post[:AccountId] = @options[:account_id]
|
105
|
+
|
106
|
+
data = build_request(action, post)
|
107
|
+
begin
|
108
|
+
raw = parse(ssl_post(url, data, headers(action)), action)
|
109
|
+
rescue ActiveMerchant::ResponseError => e
|
110
|
+
if(e.response.code == "500" && e.response.body.start_with?("<?xml"))
|
111
|
+
raw = parse(e.response.body, action)
|
112
|
+
else
|
113
|
+
raise
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
succeeded = success_from(raw[:status])
|
118
|
+
Response.new(
|
119
|
+
succeeded,
|
120
|
+
message_from(succeeded, raw),
|
121
|
+
raw,
|
122
|
+
:authorization => authorization_from(action, raw[:transaction_id], post[:OriginalTransactionId]),
|
123
|
+
:error_code => error_code_from(succeeded, raw),
|
124
|
+
:test => test?
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def headers(action)
|
129
|
+
{
|
130
|
+
'Content-Type' => 'application/soap+xml; charset=utf-8',
|
131
|
+
'SOAPAction' => %{"http://www.flo2cash.co.nz/webservices/paymentwebservice/#{action}"}
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def build_request(action, post)
|
136
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
137
|
+
post.each do |field, value|
|
138
|
+
xml.tag!(field, value)
|
139
|
+
end
|
140
|
+
body = xml.target!
|
141
|
+
envelope_wrap(action, body)
|
142
|
+
end
|
143
|
+
|
144
|
+
def envelope_wrap(action, body)
|
145
|
+
<<-EOS
|
146
|
+
<?xml version="1.0" encoding="utf-8"?>
|
147
|
+
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
|
148
|
+
<soap12:Body>
|
149
|
+
<#{action} xmlns="http://www.flo2cash.co.nz/webservices/paymentwebservice">
|
150
|
+
#{body}
|
151
|
+
</#{action}>
|
152
|
+
</soap12:Body>
|
153
|
+
</soap12:Envelope>
|
154
|
+
EOS
|
155
|
+
end
|
156
|
+
|
157
|
+
def url
|
158
|
+
(test? ? test_url : live_url)
|
159
|
+
end
|
160
|
+
|
161
|
+
def parse(body, action)
|
162
|
+
response = {}
|
163
|
+
xml = REXML::Document.new(body)
|
164
|
+
root = (REXML::XPath.first(xml, "//#{action}Response") || REXML::XPath.first(xml, "//detail"))
|
165
|
+
|
166
|
+
root.elements.to_a.each do |node|
|
167
|
+
parse_element(response, node)
|
168
|
+
end if root
|
169
|
+
|
170
|
+
response
|
171
|
+
end
|
172
|
+
|
173
|
+
def parse_element(response, node)
|
174
|
+
if node.has_elements?
|
175
|
+
node.elements.each{|element| parse_element(response, element) }
|
176
|
+
else
|
177
|
+
response[node.name.underscore.to_sym] = node.text
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def success_from(response)
|
182
|
+
response == 'SUCCESSFUL'
|
183
|
+
end
|
184
|
+
|
185
|
+
def message_from(succeeded, response)
|
186
|
+
if succeeded
|
187
|
+
"Succeeded"
|
188
|
+
else
|
189
|
+
response[:message] || response[:errormessage] || "Unable to read error message"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def authorization_from(action, current, original)
|
194
|
+
# Refunds require the authorization from the authorize() of the MultiResponse.
|
195
|
+
if action == 'ProcessCapture'
|
196
|
+
original
|
197
|
+
else
|
198
|
+
current
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
STANDARD_ERROR_CODE_MAPPING = {
|
203
|
+
'Transaction Declined - Expired Card' => STANDARD_ERROR_CODE[:expired_card],
|
204
|
+
'Bank Declined Transaction' => STANDARD_ERROR_CODE[:card_declined],
|
205
|
+
'Insufficient Funds' => STANDARD_ERROR_CODE[:card_declined],
|
206
|
+
'Transaction Declined - Bank Error' => STANDARD_ERROR_CODE[:processing_error],
|
207
|
+
'No Reply from Bank' => STANDARD_ERROR_CODE[:processing_error],
|
208
|
+
}
|
209
|
+
|
210
|
+
def error_code_from(succeeded, response)
|
211
|
+
succeeded ? nil : STANDARD_ERROR_CODE_MAPPING[response[:message]]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|