activemerchant 1.31.1 → 1.32.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.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +25 -0
- data/CONTRIBUTORS +8 -0
- data/README.md +2 -0
- data/lib/active_merchant/billing/credit_card.rb +1 -1
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +2 -1
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +1 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +1 -0
- data/lib/active_merchant/billing/gateways/cc5.rb +160 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +3 -3
- data/lib/active_merchant/billing/gateways/finansbank.rb +22 -0
- data/lib/active_merchant/billing/gateways/iridium.rb +8 -2
- data/lib/active_merchant/billing/gateways/litle.rb +289 -101
- data/lib/active_merchant/billing/gateways/ogone.rb +1 -1
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +26 -16
- data/lib/active_merchant/billing/gateways/orbital.rb +6 -6
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +2 -1
- data/lib/active_merchant/billing/gateways/paymill.rb +13 -9
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +14 -9
- data/lib/active_merchant/billing/gateways/pin.rb +13 -5
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +15 -17
- data/lib/active_merchant/billing/gateways/stripe.rb +25 -12
- data/lib/active_merchant/billing/gateways/webpay.rb +8 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +44 -22
- data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +3 -2
- data/lib/active_merchant/billing/integrations/pxpay/helper.rb +1 -0
- data/lib/active_merchant/billing/integrations/robokassa/common.rb +1 -1
- data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +5 -1
- data/lib/active_merchant/billing/integrations/world_pay.rb +15 -8
- data/lib/active_merchant/version.rb +1 -1
- metadata +62 -60
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
= ActiveMerchant CHANGELOG
|
|
2
2
|
|
|
3
|
+
== Version 1.32.0 (April 1, 2013)
|
|
4
|
+
|
|
5
|
+
* Optimal: Submit shipping address with requests [jduff]
|
|
6
|
+
* Iridium: Enable reference transactions for authorize [ntalbott]
|
|
7
|
+
* Stripe: Add authorize and capture methods [melari]
|
|
8
|
+
* Pin: Add a default description if none is specified to fix failures [melari]
|
|
9
|
+
* Litle: Add support for passing optional fields in token based transactions [forest]
|
|
10
|
+
* Add Finansbank gateway [scamurcuoglu]
|
|
11
|
+
* Paymill: Use .com instead of .de for save card url [besi]
|
|
12
|
+
* Worldpay integration: Use more robust endpoint urls [nashbridges]
|
|
13
|
+
* Braintree Blue: Return CC token in transaction hash [cyu]
|
|
14
|
+
* Robokassa: Fix signature for empty amount [ukolovda]
|
|
15
|
+
* Worldpay gateway: Fix error messages for some failures [duff]
|
|
16
|
+
* Worldpay gateway: Allow settled payments to be refunded [dougal]
|
|
17
|
+
* Spreedly: Update urls and terminology [duff]
|
|
18
|
+
* Make card brand error more user friendly [oggy]
|
|
19
|
+
* DataCash: Update test Mastercard number [jamesshipton]
|
|
20
|
+
* DataCash: Update test response fixtures [jamesshipton]
|
|
21
|
+
* Pin: Add Pin.js card token support [nagash]
|
|
22
|
+
* PayPal Express gateway: Fix error when no address information is in response [pierre]
|
|
23
|
+
* Ogone: Use BYPSP for ALIASOPERATION [ntalbott]
|
|
24
|
+
* Paymill: Handle error storing card [duff]
|
|
25
|
+
* SagePay integration: Add referrer field [melari]
|
|
26
|
+
* Pin: Add extra headers [duff]
|
|
27
|
+
|
|
3
28
|
== Version 1.31.1 (February 25, 2013)
|
|
4
29
|
|
|
5
30
|
* Cybersource: Bug fixes [natejgreene, jduff]
|
data/CONTRIBUTORS
CHANGED
|
@@ -346,6 +346,10 @@ Balanced gateway (July 2012)
|
|
|
346
346
|
|
|
347
347
|
* Marshall Jones (mjallday)
|
|
348
348
|
|
|
349
|
+
PayFast integration (October 2012)
|
|
350
|
+
|
|
351
|
+
* Vasiliy Ermolovich (nashby)
|
|
352
|
+
|
|
349
353
|
A1Agregator (November 2012)
|
|
350
354
|
|
|
351
355
|
* Roman Ivanilov (england)
|
|
@@ -384,3 +388,7 @@ Paymill (February 2013)
|
|
|
384
388
|
EVO Canada (February 2013)
|
|
385
389
|
|
|
386
390
|
* Alex Dunae (alexdunae)
|
|
391
|
+
|
|
392
|
+
Finansbank WebPOS (March 2013)
|
|
393
|
+
|
|
394
|
+
* scamurcuoglu
|
data/README.md
CHANGED
|
@@ -100,6 +100,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
|
|
|
100
100
|
* [E-xact](http://www.e-xact.com) - CA, US
|
|
101
101
|
* [Fat Zebra](https://www.fatzebra.com.au) - AU
|
|
102
102
|
* [Federated Canada](http://www.federatedcanada.com/) - CA
|
|
103
|
+
* [Finansbank WebPOS](https://www.fbwebpos.com/) - US, TR
|
|
103
104
|
* [FirstData Global Gateway e4](http://www.firstdata.com) - CA, US
|
|
104
105
|
* [FirstPay](http://www.first-pay.com) - US
|
|
105
106
|
* [Garanti Sanal POS](https://ccpos.garanti.com.tr/ccRaporlar/garanti/ccReports) - US, TR
|
|
@@ -130,6 +131,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
|
|
|
130
131
|
* [Optimal Payments](http://www.optimalpayments.com/) - CA, US, UK
|
|
131
132
|
* [Orbital Paymentech](http://chasepaymentech.com/) - CA, US, UK, GB
|
|
132
133
|
* [PayBox Direct](http://www.paybox.com) - FR
|
|
134
|
+
* [PayFast](https://www.payfast.co.za/) - ZA
|
|
133
135
|
* [PayGate PayXML](http://paygate.co.za/) - US, ZA
|
|
134
136
|
* [PayJunction](http://www.payjunction.com/) - US
|
|
135
137
|
* [PaymentExpress](http://www.paymentexpress.com/) - AU, MY, NZ, SG, ZA, UK, US
|
|
@@ -229,7 +229,7 @@ module ActiveMerchant #:nodoc:
|
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
unless errors.on(:number) || errors.on(:brand)
|
|
232
|
-
errors.add :brand, "
|
|
232
|
+
errors.add :brand, "does not match the card number" unless CreditCard.matching_brand?(number, brand)
|
|
233
233
|
end
|
|
234
234
|
end
|
|
235
235
|
|
|
@@ -338,7 +338,8 @@ module ActiveMerchant #:nodoc:
|
|
|
338
338
|
# * <tt>:type</tt> -- The type of transaction. Can be either <tt>:auth_only</tt>, <tt>:capture_only</tt>, <tt>:auth_capture</tt>, <tt>:prior_auth_capture</tt>, <tt>:refund</tt> or <tt>:void</tt>. (REQUIRED)
|
|
339
339
|
# * <tt>:amount</tt> -- The amount for the tranaction. Formatted with a decimal. For example "4.95" (CONDITIONAL)
|
|
340
340
|
# - :type == :void (NOT USED)
|
|
341
|
-
# - :type ==
|
|
341
|
+
# - :type == :refund (OPTIONAL)
|
|
342
|
+
# - :type == (:auth_only, :capture_only, :auth_capture, :prior_auth_capture) (REQUIRED)
|
|
342
343
|
#
|
|
343
344
|
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (CONDITIONAL)
|
|
344
345
|
# - :type == (:void, :prior_auth_capture) (OPTIONAL)
|
|
@@ -340,6 +340,7 @@ module ActiveMerchant #:nodoc:
|
|
|
340
340
|
"bin" => transaction.credit_card_details.bin,
|
|
341
341
|
"last_4" => transaction.credit_card_details.last_4,
|
|
342
342
|
"card_type" => transaction.credit_card_details.card_type,
|
|
343
|
+
"token" => transaction.credit_card_details.token
|
|
343
344
|
}
|
|
344
345
|
|
|
345
346
|
{
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
if RUBY_VERSION < '1.9' && $KCODE == "NONE"
|
|
2
|
+
$KCODE = 'u'
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
module ActiveMerchant #:nodoc:
|
|
6
|
+
module Billing #:nodoc:
|
|
7
|
+
# CC5 API is used by many banks in Turkey. Extend this base class to provide
|
|
8
|
+
# concrete implementations.
|
|
9
|
+
class CC5Gateway < Gateway
|
|
10
|
+
self.default_currency = 'TRY'
|
|
11
|
+
|
|
12
|
+
CURRENCY_CODES = {
|
|
13
|
+
'TRY' => 949,
|
|
14
|
+
'YTL' => 949,
|
|
15
|
+
'TRL' => 949,
|
|
16
|
+
'TL' => 949,
|
|
17
|
+
'USD' => 840,
|
|
18
|
+
'EUR' => 978,
|
|
19
|
+
'GBP' => 826,
|
|
20
|
+
'JPY' => 392
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def initialize(options = {})
|
|
24
|
+
requires!(options, :login, :password, :client_id)
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def purchase(money, creditcard, options = {})
|
|
29
|
+
commit(build_sale_request('Auth', money, creditcard, options))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def authorize(money, creditcard, options = {})
|
|
33
|
+
commit(build_sale_request('PreAuth', money, creditcard, options))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def capture(money, authorization, options = {})
|
|
37
|
+
commit(build_capture_request(money, authorization, options))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
protected
|
|
41
|
+
|
|
42
|
+
def build_sale_request(type, money, creditcard, options = {})
|
|
43
|
+
requires!(options, :order_id)
|
|
44
|
+
|
|
45
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
|
46
|
+
|
|
47
|
+
xml.tag! 'CC5Request' do
|
|
48
|
+
add_login_tags(xml)
|
|
49
|
+
xml.tag! 'OrderId', options[:order_id]
|
|
50
|
+
xml.tag! 'Type', type
|
|
51
|
+
xml.tag! 'Number', creditcard.number
|
|
52
|
+
xml.tag! 'Expires', [format(creditcard.month, :two_digits), format(creditcard.year, :two_digits)].join('/')
|
|
53
|
+
xml.tag! 'Cvv2Val', creditcard.verification_value
|
|
54
|
+
add_amount_tags(money, options, xml)
|
|
55
|
+
xml.tag! 'Email', options[:email] if options[:email]
|
|
56
|
+
|
|
57
|
+
if(address = (options[:billing_address] || options[:address]))
|
|
58
|
+
xml.tag! 'BillTo' do
|
|
59
|
+
add_address(xml, address)
|
|
60
|
+
end
|
|
61
|
+
xml.tag! 'ShipTo' do
|
|
62
|
+
add_address(xml, address)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
xml.target!
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def build_capture_request(money, authorization, options = {})
|
|
72
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
|
73
|
+
|
|
74
|
+
xml.tag! 'CC5Request' do
|
|
75
|
+
add_login_tags(xml)
|
|
76
|
+
xml.tag! 'OrderId', authorization
|
|
77
|
+
xml.tag! 'Type', 'PostAuth'
|
|
78
|
+
add_amount_tags(money, options, xml)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add_address(xml, address)
|
|
83
|
+
xml.tag! 'Name', normalize(address[:name])
|
|
84
|
+
xml.tag! 'Street1', normalize(address[:address1])
|
|
85
|
+
xml.tag! 'Street2', normalize(address[:address2]) if address[:address2]
|
|
86
|
+
xml.tag! 'City', normalize(address[:city])
|
|
87
|
+
xml.tag! 'PostalCode', address[:zip]
|
|
88
|
+
xml.tag! 'Country', normalize(address[:country])
|
|
89
|
+
xml.tag! 'Company', normalize(address[:company])
|
|
90
|
+
xml.tag! 'TelVoice', address[:phone].to_s.gsub(/[^0-9]/, '') if address[:phone]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def add_login_tags(xml)
|
|
94
|
+
xml.tag! 'Name', @options[:login]
|
|
95
|
+
xml.tag! 'Password', @options[:password]
|
|
96
|
+
xml.tag! 'ClientId', @options[:client_id]
|
|
97
|
+
xml.tag! 'Mode', (test? ? 'T' : 'P')
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def add_amount_tags(money, options, xml)
|
|
101
|
+
xml.tag! 'Total', amount(money)
|
|
102
|
+
xml.tag! 'Currency', currency_code(options[:currency] || currency(money))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def currency_code(currency)
|
|
106
|
+
(CURRENCY_CODES[currency] || CURRENCY_CODES[default_currency])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def commit(request)
|
|
110
|
+
raw_response = ssl_post((test? ? self.test_url : self.live_url), "DATA=" + request)
|
|
111
|
+
|
|
112
|
+
response = parse(raw_response)
|
|
113
|
+
|
|
114
|
+
success = success?(response)
|
|
115
|
+
|
|
116
|
+
Response.new(
|
|
117
|
+
success,
|
|
118
|
+
(success ? 'Approved' : "Declined (Reason: #{response[:proc_return_code]} - #{response[:err_msg]})"),
|
|
119
|
+
response,
|
|
120
|
+
:test => test?,
|
|
121
|
+
:authorization => response[:order_id]
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def parse(body)
|
|
126
|
+
xml = REXML::Document.new(body)
|
|
127
|
+
|
|
128
|
+
response = {}
|
|
129
|
+
xml.root.elements.to_a.each do |node|
|
|
130
|
+
parse_element(response, node)
|
|
131
|
+
end
|
|
132
|
+
response
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def parse_element(response, node)
|
|
136
|
+
if node.has_elements?
|
|
137
|
+
node.elements.each{|element| parse_element(response, element) }
|
|
138
|
+
else
|
|
139
|
+
response[node.name.underscore.to_sym] = node.text
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def success?(response)
|
|
144
|
+
(response[:response] == "Approved")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def normalize(text)
|
|
148
|
+
return unless text
|
|
149
|
+
|
|
150
|
+
if ActiveSupport::Inflector.method(:transliterate).arity == -2
|
|
151
|
+
ActiveSupport::Inflector.transliterate(text,'')
|
|
152
|
+
elsif RUBY_VERSION >= '1.9'
|
|
153
|
+
text.gsub(/[^\x00-\x7F]+/, '')
|
|
154
|
+
else
|
|
155
|
+
ActiveSupport::Inflector.transliterate(text).to_s
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -65,7 +65,7 @@ module ActiveMerchant
|
|
|
65
65
|
# See http://www.datacash.com/services/recurring/historic.php for more details of historic transactions.
|
|
66
66
|
# * <tt>:address</tt>:: billing address for card
|
|
67
67
|
#
|
|
68
|
-
# The continuous authority reference will be available in response#params['
|
|
68
|
+
# The continuous authority reference will be available in response#params['ca_reference'] if you have requested one
|
|
69
69
|
def purchase(money, authorization_or_credit_card, options = {})
|
|
70
70
|
requires!(options, :order_id)
|
|
71
71
|
|
|
@@ -93,7 +93,7 @@ module ActiveMerchant
|
|
|
93
93
|
# See http://www.datacash.com/services/recurring/historic.php for more details of historic transactions.
|
|
94
94
|
# * <tt>:address</tt>:: billing address for card
|
|
95
95
|
#
|
|
96
|
-
# The continuous authority reference will be available in response#params['
|
|
96
|
+
# The continuous authority reference will be available in response#params['ca_reference'] if you have requested one
|
|
97
97
|
def authorize(money, authorization_or_credit_card, options = {})
|
|
98
98
|
requires!(options, :order_id)
|
|
99
99
|
|
|
@@ -448,7 +448,7 @@ module ActiveMerchant
|
|
|
448
448
|
end
|
|
449
449
|
end
|
|
450
450
|
|
|
451
|
-
# Add credit_card
|
|
451
|
+
# Add credit_card details to the passed XML Builder doc
|
|
452
452
|
#
|
|
453
453
|
# Parameters:
|
|
454
454
|
# -xml: Builder document that is being built up
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/cc5'
|
|
2
|
+
|
|
3
|
+
module ActiveMerchant #:nodoc:
|
|
4
|
+
module Billing #:nodoc:
|
|
5
|
+
class FinansbankGateway < CC5Gateway
|
|
6
|
+
self.live_url = self.test_url = 'https://www.fbwebpos.com/servlet/cc5ApiServer'
|
|
7
|
+
|
|
8
|
+
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
|
9
|
+
self.supported_countries = ['US', 'TR']
|
|
10
|
+
|
|
11
|
+
# The card types supported by the payment gateway
|
|
12
|
+
self.supported_cardtypes = [:visa, :master]
|
|
13
|
+
|
|
14
|
+
# The homepage URL of the gateway
|
|
15
|
+
self.homepage_url = 'https://www.fbwebpos.com/'
|
|
16
|
+
|
|
17
|
+
# The name of the gateway
|
|
18
|
+
self.display_name = 'Finansbank WebPOS'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -38,8 +38,14 @@ module ActiveMerchant #:nodoc:
|
|
|
38
38
|
super
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def authorize(money,
|
|
42
|
-
|
|
41
|
+
def authorize(money, payment_source, options = {})
|
|
42
|
+
setup_address_hash(options)
|
|
43
|
+
|
|
44
|
+
if payment_source.respond_to?(:number)
|
|
45
|
+
commit(build_purchase_request('PREAUTH', money, payment_source, options), options)
|
|
46
|
+
else
|
|
47
|
+
commit(build_reference_request('PREAUTH', money, payment_source, options), options)
|
|
48
|
+
end
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
def purchase(money, payment_source, options = {})
|
|
@@ -37,7 +37,7 @@ module ActiveMerchant #:nodoc:
|
|
|
37
37
|
self.test_url = 'https://www.testlitle.com/sandbox/communicator/online'
|
|
38
38
|
self.live_url = 'https://payments.litle.com/vap/communicator/online'
|
|
39
39
|
|
|
40
|
-
LITLE_SCHEMA_VERSION
|
|
40
|
+
LITLE_SCHEMA_VERSION = '8.13'
|
|
41
41
|
|
|
42
42
|
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
|
43
43
|
self.supported_countries = ['US']
|
|
@@ -46,10 +46,10 @@ module ActiveMerchant #:nodoc:
|
|
|
46
46
|
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
|
47
47
|
|
|
48
48
|
# The homepage URL of the gateway
|
|
49
|
-
self.homepage_url
|
|
49
|
+
self.homepage_url = 'http://www.litle.com/'
|
|
50
50
|
|
|
51
51
|
# The name of the gateway
|
|
52
|
-
self.display_name
|
|
52
|
+
self.display_name = 'Litle & Co.'
|
|
53
53
|
|
|
54
54
|
self.default_currency = 'USD'
|
|
55
55
|
|
|
@@ -91,8 +91,8 @@ module ActiveMerchant #:nodoc:
|
|
|
91
91
|
build_response(:void, @litle.void(to_pass))
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
def credit(money,
|
|
95
|
-
to_pass =
|
|
94
|
+
def credit(money, identification_or_token, options = {})
|
|
95
|
+
to_pass = build_credit_request(money, identification_or_token, options)
|
|
96
96
|
build_response(:credit, @litle.credit(to_pass))
|
|
97
97
|
end
|
|
98
98
|
|
|
@@ -104,30 +104,30 @@ module ActiveMerchant #:nodoc:
|
|
|
104
104
|
private
|
|
105
105
|
|
|
106
106
|
CARD_TYPE = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
'visa' => 'VI',
|
|
108
|
+
'master' => 'MC',
|
|
109
|
+
'american_express' => 'AX',
|
|
110
|
+
'discover' => 'DI',
|
|
111
|
+
'jcb' => 'DI',
|
|
112
|
+
'diners_club' => 'DI'
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
AVS_RESPONSE_CODE = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
116
|
+
'00' => 'Y',
|
|
117
|
+
'01' => 'X',
|
|
118
|
+
'02' => 'D',
|
|
119
|
+
'10' => 'Z',
|
|
120
|
+
'11' => 'W',
|
|
121
|
+
'12' => 'A',
|
|
122
|
+
'13' => 'A',
|
|
123
|
+
'14' => 'P',
|
|
124
|
+
'20' => 'N',
|
|
125
|
+
'30' => 'S',
|
|
126
|
+
'31' => 'R',
|
|
127
|
+
'32' => 'U',
|
|
128
|
+
'33' => 'R',
|
|
129
|
+
'34' => 'I',
|
|
130
|
+
'40' => 'E'
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
def url
|
|
@@ -140,22 +140,22 @@ module ActiveMerchant #:nodoc:
|
|
|
140
140
|
response = Hash.from_xml(litle_response.raw_xml.to_s)['litleOnlineResponse']
|
|
141
141
|
|
|
142
142
|
if response['response'] == "0"
|
|
143
|
-
detail
|
|
144
|
-
fraud
|
|
143
|
+
detail = response["#{kind}Response"]
|
|
144
|
+
fraud = fraud_result(detail)
|
|
145
145
|
authorization = case kind
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
when :registerToken
|
|
147
|
+
response['registerTokenResponse']['litleToken']
|
|
148
|
+
else
|
|
149
|
+
detail['litleTxnId']
|
|
150
|
+
end
|
|
151
151
|
Response.new(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
152
|
+
valid_responses.include?(detail['response']),
|
|
153
|
+
detail['message'],
|
|
154
|
+
{ :litleOnlineResponse => response },
|
|
155
|
+
:authorization => authorization,
|
|
156
|
+
:avs_result => { :code => fraud['avs'] },
|
|
157
|
+
:cvv_result => fraud['cvv'],
|
|
158
|
+
:test => test?
|
|
159
159
|
)
|
|
160
160
|
else
|
|
161
161
|
Response.new(false, response['message'], :litleOnlineResponse => response, :test => test?)
|
|
@@ -163,39 +163,83 @@ module ActiveMerchant #:nodoc:
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
def build_authorize_request(money, creditcard_or_token, options)
|
|
166
|
+
payment_method = build_payment_method(creditcard_or_token, options)
|
|
167
|
+
|
|
166
168
|
hash = create_hash(money, options)
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
add_creditcard_or_cardtoken_hash(hash, payment_method)
|
|
169
171
|
|
|
170
172
|
hash
|
|
171
173
|
end
|
|
172
174
|
|
|
173
175
|
def build_purchase_request(money, creditcard_or_token, options)
|
|
176
|
+
payment_method = build_payment_method(creditcard_or_token, options)
|
|
177
|
+
|
|
178
|
+
hash = create_hash(money, options)
|
|
179
|
+
|
|
180
|
+
add_creditcard_or_cardtoken_hash(hash, payment_method)
|
|
181
|
+
|
|
182
|
+
hash
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def build_credit_request(money, identification_or_token, options)
|
|
186
|
+
payment_method = build_payment_method(identification_or_token, options)
|
|
187
|
+
|
|
174
188
|
hash = create_hash(money, options)
|
|
175
189
|
|
|
176
|
-
|
|
190
|
+
add_identification_or_cardtoken_hash(hash, payment_method)
|
|
191
|
+
|
|
192
|
+
unless payment_method.is_a?(LitleCardToken)
|
|
193
|
+
hash['orderSource'] = nil
|
|
194
|
+
hash['orderId'] = nil
|
|
195
|
+
end
|
|
177
196
|
|
|
178
197
|
hash
|
|
179
198
|
end
|
|
180
199
|
|
|
181
|
-
def
|
|
182
|
-
|
|
183
|
-
|
|
200
|
+
def build_payment_method(payment_method, options)
|
|
201
|
+
result = payment_method
|
|
202
|
+
|
|
203
|
+
# Build instance of the LitleCardToken class for internal use if this is a token request.
|
|
204
|
+
if payment_method.is_a?(String) && options.has_key?(:token)
|
|
205
|
+
result = LitleCardToken.new(:token => payment_method)
|
|
206
|
+
result.month = options[:token][:month]
|
|
207
|
+
result.year = options[:token][:year]
|
|
208
|
+
result.verification_value = options[:token][:verification_value]
|
|
209
|
+
result.brand = options[:token][:brand]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
result
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def add_creditcard_or_cardtoken_hash(hash, creditcard_or_cardtoken)
|
|
216
|
+
if creditcard_or_cardtoken.is_a?(LitleCardToken)
|
|
217
|
+
add_cardtoken_hash(hash, creditcard_or_cardtoken)
|
|
184
218
|
else
|
|
185
|
-
|
|
219
|
+
add_creditcard_hash(hash, creditcard_or_cardtoken)
|
|
186
220
|
end
|
|
187
221
|
end
|
|
188
222
|
|
|
189
|
-
def
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
223
|
+
def add_identification_or_cardtoken_hash(hash, identification_or_cardtoken)
|
|
224
|
+
if identification_or_cardtoken.is_a?(LitleCardToken)
|
|
225
|
+
add_cardtoken_hash(hash, identification_or_cardtoken)
|
|
226
|
+
else
|
|
227
|
+
hash['litleTxnId'] = identification_or_cardtoken
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def add_cardtoken_hash(hash, cardtoken)
|
|
232
|
+
token_info = {}
|
|
233
|
+
token_info['litleToken'] = cardtoken.token
|
|
234
|
+
token_info['expDate'] = cardtoken.exp_date if cardtoken.exp_date?
|
|
235
|
+
token_info['cardValidationNum'] = cardtoken.verification_value unless cardtoken.verification_value.blank?
|
|
236
|
+
token_info['type'] = cardtoken.type unless cardtoken.type.blank?
|
|
193
237
|
|
|
194
238
|
hash['token'] = token_info
|
|
195
239
|
hash
|
|
196
240
|
end
|
|
197
241
|
|
|
198
|
-
def
|
|
242
|
+
def add_creditcard_hash(hash, creditcard)
|
|
199
243
|
cc_type = CARD_TYPE[creditcard.brand]
|
|
200
244
|
exp_date_yr = creditcard.year.to_s[2..3]
|
|
201
245
|
exp_date_mo = '%02d' % creditcard.month.to_i
|
|
@@ -213,27 +257,19 @@ module ActiveMerchant #:nodoc:
|
|
|
213
257
|
end
|
|
214
258
|
|
|
215
259
|
def create_capture_hash(money, authorization, options)
|
|
216
|
-
hash
|
|
260
|
+
hash = create_hash(money, options)
|
|
217
261
|
hash['litleTxnId'] = authorization
|
|
218
262
|
hash
|
|
219
263
|
end
|
|
220
264
|
|
|
221
|
-
def create_credit_hash(money, identification, options)
|
|
222
|
-
hash = create_hash(money, options)
|
|
223
|
-
hash['litleTxnId'] = identification
|
|
224
|
-
hash['orderSource'] = nil
|
|
225
|
-
hash['orderId'] = nil
|
|
226
|
-
hash
|
|
227
|
-
end
|
|
228
|
-
|
|
229
265
|
def create_token_hash(creditcard, options)
|
|
230
|
-
hash
|
|
266
|
+
hash = create_hash(0, options)
|
|
231
267
|
hash['accountNumber'] = creditcard.number
|
|
232
268
|
hash
|
|
233
269
|
end
|
|
234
270
|
|
|
235
271
|
def create_void_hash(identification, options)
|
|
236
|
-
hash
|
|
272
|
+
hash = create_hash(nil, options)
|
|
237
273
|
hash['litleTxnId'] = identification
|
|
238
274
|
hash
|
|
239
275
|
end
|
|
@@ -255,54 +291,54 @@ module ActiveMerchant #:nodoc:
|
|
|
255
291
|
|
|
256
292
|
if options[:billing_address]
|
|
257
293
|
bill_to_address = {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
294
|
+
'name' => options[:billing_address][:name],
|
|
295
|
+
'companyName' => options[:billing_address][:company],
|
|
296
|
+
'addressLine1' => options[:billing_address][:address1],
|
|
297
|
+
'addressLine2' => options[:billing_address][:address2],
|
|
298
|
+
'city' => options[:billing_address][:city],
|
|
299
|
+
'state' => options[:billing_address][:state],
|
|
300
|
+
'zip' => options[:billing_address][:zip],
|
|
301
|
+
'country' => options[:billing_address][:country],
|
|
302
|
+
'email' => options[:email],
|
|
303
|
+
'phone' => options[:billing_address][:phone]
|
|
268
304
|
}
|
|
269
305
|
end
|
|
270
306
|
if options[:shipping_address]
|
|
271
307
|
ship_to_address = {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
308
|
+
'name' => options[:shipping_address][:name],
|
|
309
|
+
'companyName' => options[:shipping_address][:company],
|
|
310
|
+
'addressLine1' => options[:shipping_address][:address1],
|
|
311
|
+
'addressLine2' => options[:shipping_address][:address2],
|
|
312
|
+
'city' => options[:shipping_address][:city],
|
|
313
|
+
'state' => options[:shipping_address][:state],
|
|
314
|
+
'zip' => options[:shipping_address][:zip],
|
|
315
|
+
'country' => options[:shipping_address][:country],
|
|
316
|
+
'email' => options[:email],
|
|
317
|
+
'phone' => options[:shipping_address][:phone]
|
|
282
318
|
}
|
|
283
319
|
end
|
|
284
320
|
|
|
285
321
|
hash = {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
322
|
+
'billToAddress' => bill_to_address,
|
|
323
|
+
'shipToAddress' => ship_to_address,
|
|
324
|
+
'orderId' => (options[:order_id] || @options[:order_id]),
|
|
325
|
+
'customerId' => options[:customer],
|
|
326
|
+
'reportGroup' => (options[:merchant] || @options[:merchant]),
|
|
327
|
+
'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
|
|
328
|
+
'orderSource' => (options[:order_source] || 'ecommerce'),
|
|
329
|
+
'enhancedData' => enhanced_data,
|
|
330
|
+
'fraudCheckType' => fraud_check_type,
|
|
331
|
+
'user' => (options[:user] || @options[:user]),
|
|
332
|
+
'password' => (options[:password] || @options[:password]),
|
|
333
|
+
'version' => (options[:version] || @options[:version]),
|
|
334
|
+
'url' => (options[:url] || url),
|
|
335
|
+
'proxy_addr' => (options[:proxy_addr] || @options[:proxy_addr]),
|
|
336
|
+
'proxy_port' => (options[:proxy_port] || @options[:proxy_port]),
|
|
337
|
+
'id' => (options[:id] || options[:order_id] || @options[:order_id])
|
|
302
338
|
}
|
|
303
339
|
|
|
304
|
-
if(
|
|
305
|
-
hash.merge!({'amount' => money})
|
|
340
|
+
if (!money.nil? && money.to_s.length > 0)
|
|
341
|
+
hash.merge!({ 'amount' => money })
|
|
306
342
|
end
|
|
307
343
|
hash
|
|
308
344
|
end
|
|
@@ -315,7 +351,159 @@ module ActiveMerchant #:nodoc:
|
|
|
315
351
|
|
|
316
352
|
avs_to_pass = AVS_RESPONSE_CODE[result['avsResult']] unless result['avsResult'].blank?
|
|
317
353
|
end
|
|
318
|
-
{'cvv'=>cvv_to_pass, 'avs'=>avs_to_pass}
|
|
354
|
+
{ 'cvv' => cvv_to_pass, 'avs' => avs_to_pass }
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# A +LitleCardToken+ object represents a tokenized credit card, and is capable of validating the various
|
|
358
|
+
# data associated with these.
|
|
359
|
+
#
|
|
360
|
+
# == Example Usage
|
|
361
|
+
# token = LitleCardToken.new(
|
|
362
|
+
# :token => '1234567890123456',
|
|
363
|
+
# :month => '9',
|
|
364
|
+
# :year => '2010',
|
|
365
|
+
# :brand => 'visa',
|
|
366
|
+
# :verification_value => '123'
|
|
367
|
+
# )
|
|
368
|
+
#
|
|
369
|
+
# token.valid? # => true
|
|
370
|
+
# cc.exp_date # => 0910
|
|
371
|
+
#
|
|
372
|
+
class LitleCardToken
|
|
373
|
+
include Validateable
|
|
374
|
+
|
|
375
|
+
# Returns or sets the token. (required)
|
|
376
|
+
#
|
|
377
|
+
# @return [String]
|
|
378
|
+
attr_accessor :token
|
|
379
|
+
|
|
380
|
+
# Returns or sets the expiry month for the card associated with token. (optional)
|
|
381
|
+
#
|
|
382
|
+
# @return [Integer]
|
|
383
|
+
attr_accessor :month
|
|
384
|
+
|
|
385
|
+
# Returns or sets the expiry year for the card associated with token. (optional)
|
|
386
|
+
#
|
|
387
|
+
# @return [Integer]
|
|
388
|
+
attr_accessor :year
|
|
389
|
+
|
|
390
|
+
# Returns or sets the card verification value. (optional)
|
|
391
|
+
#
|
|
392
|
+
# @return [String] the verification value
|
|
393
|
+
attr_accessor :verification_value
|
|
394
|
+
|
|
395
|
+
# Returns or sets the credit card brand. (optional)
|
|
396
|
+
#
|
|
397
|
+
# Valid card types are
|
|
398
|
+
#
|
|
399
|
+
# * +'visa'+
|
|
400
|
+
# * +'master'+
|
|
401
|
+
# * +'discover'+
|
|
402
|
+
# * +'american_express'+
|
|
403
|
+
# * +'diners_club'+
|
|
404
|
+
# * +'jcb'+
|
|
405
|
+
# * +'switch'+
|
|
406
|
+
# * +'solo'+
|
|
407
|
+
# * +'dankort'+
|
|
408
|
+
# * +'maestro'+
|
|
409
|
+
# * +'forbrugsforeningen'+
|
|
410
|
+
# * +'laser'+
|
|
411
|
+
#
|
|
412
|
+
# @return (String) the credit card brand
|
|
413
|
+
attr_accessor :brand
|
|
414
|
+
|
|
415
|
+
# Returns the Litle credit card type identifier.
|
|
416
|
+
#
|
|
417
|
+
# @return (String) the credit card type identifier
|
|
418
|
+
def type
|
|
419
|
+
CARD_TYPE[brand] unless brand.blank?
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Returns true if the expiration date is set.
|
|
423
|
+
#
|
|
424
|
+
# @return (Boolean)
|
|
425
|
+
def exp_date?
|
|
426
|
+
!month.to_i.zero? && !year.to_i.zero?
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Returns the card token expiration date in MMYY format.
|
|
430
|
+
#
|
|
431
|
+
# @return (String) the expiration date in MMYY format
|
|
432
|
+
def exp_date
|
|
433
|
+
result = ''
|
|
434
|
+
if exp_date?
|
|
435
|
+
exp_date_yr = year.to_s[2..3]
|
|
436
|
+
exp_date_mo = '%02d' % month.to_i
|
|
437
|
+
|
|
438
|
+
result = exp_date_mo + exp_date_yr
|
|
439
|
+
end
|
|
440
|
+
result
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Validates the card token details.
|
|
444
|
+
#
|
|
445
|
+
# Any validation errors are added to the {#errors} attribute.
|
|
446
|
+
def validate
|
|
447
|
+
validate_card_token
|
|
448
|
+
validate_expiration_date
|
|
449
|
+
validate_card_brand
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def check?
|
|
453
|
+
false
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
private
|
|
457
|
+
|
|
458
|
+
CARD_TYPE = {
|
|
459
|
+
'visa' => 'VI',
|
|
460
|
+
'master' => 'MC',
|
|
461
|
+
'american_express' => 'AX',
|
|
462
|
+
'discover' => 'DI',
|
|
463
|
+
'jcb' => 'DI',
|
|
464
|
+
'diners_club' => 'DI'
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
def before_validate #:nodoc:
|
|
468
|
+
self.month = month.to_i
|
|
469
|
+
self.year = year.to_i
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Litle XML Reference Guide 1.8.2
|
|
473
|
+
#
|
|
474
|
+
# The length of the original card number is reflected in the token, so a
|
|
475
|
+
# submitted 16-digit number results in a 16-digit token. Also, all tokens
|
|
476
|
+
# use only numeric characters, so you do not have to change your
|
|
477
|
+
# systems to accept alpha-numeric characters.
|
|
478
|
+
#
|
|
479
|
+
# The credit card token numbers themselves have two parts.
|
|
480
|
+
# The last four digits match the last four digits of the card number.
|
|
481
|
+
# The remaining digits (length can vary based upon original card number
|
|
482
|
+
# length) are a randomly generated.
|
|
483
|
+
def validate_card_token #:nodoc:
|
|
484
|
+
if token.to_s.length < 12 || token.to_s.match(/\A\d+\Z/).nil?
|
|
485
|
+
errors.add :token, "is not a valid card token"
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def validate_expiration_date #:nodoc:
|
|
490
|
+
if !month.to_i.zero? || !year.to_i.zero?
|
|
491
|
+
errors.add :month, "is not a valid month" unless valid_month?(month)
|
|
492
|
+
errors.add :year, "is not a valid year" unless valid_expiry_year?(year)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def validate_card_brand #:nodoc:
|
|
497
|
+
errors.add :brand, "is invalid" unless brand.blank? || CreditCard.card_companies.keys.include?(brand)
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def valid_month?(month)
|
|
501
|
+
(1..12).include?(month.to_i)
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def valid_expiry_year?(year)
|
|
505
|
+
year.to_s =~ /\A\d{4}\Z/ && year.to_i > 1987
|
|
506
|
+
end
|
|
319
507
|
end
|
|
320
508
|
end
|
|
321
509
|
end
|