activemerchant 1.46.0 → 1.47.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 +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
|