activemerchant 1.55.0 → 1.56.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/CONTRIBUTORS +4 -0
- data/README.md +1 -0
- data/lib/active_merchant.rb +0 -1
- data/lib/active_merchant/billing/credit_card_methods.rb +5 -0
- data/lib/active_merchant/billing/gateways/bridge_pay.rb +56 -29
- data/lib/active_merchant/billing/gateways/card_stream.rb +2 -1
- data/lib/active_merchant/billing/gateways/cardknox.rb +328 -0
- data/lib/active_merchant/billing/gateways/cashnet.rb +4 -0
- data/lib/active_merchant/billing/gateways/creditcall.rb +3 -1
- data/lib/active_merchant/billing/gateways/elavon.rb +6 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +1 -1
- data/lib/active_merchant/billing/gateways/garanti.rb +4 -2
- data/lib/active_merchant/billing/gateways/jetpay.rb +3 -2
- data/lib/active_merchant/billing/gateways/mercury.rb +10 -1
- data/lib/active_merchant/billing/gateways/micropayment.rb +20 -6
- data/lib/active_merchant/billing/gateways/omise.rb +5 -4
- data/lib/active_merchant/billing/gateways/redsys.rb +107 -21
- data/lib/active_merchant/billing/gateways/stripe.rb +17 -3
- data/lib/active_merchant/billing/gateways/trans_first.rb +107 -18
- data/lib/active_merchant/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a7fa5a42dbb05e1c335a93e8cefc85781e7cbae
|
4
|
+
data.tar.gz: 928d0561eab8bc6a0fcac4245ccc2342d4e8fa6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf1511639e9a72d13e6be69a301e0f0553cd4b52e65205a0e9f777c951e355124c16440182716a622beb5cd56037d98aea1659e1bdf49541b20d0438d2bf3802
|
7
|
+
data.tar.gz: 83dd4208bb147e5dce68f4751ff27e7298f03fffa0ef8c32f0f335fbf8fa79b1c4177d4839b236510fc7d9f66bedf98a4237085fbb2dcc94a9509d57f3004feb
|
data/CHANGELOG
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
= ActiveMerchant CHANGELOG
|
2
2
|
|
3
|
+
== Version 1.56.0 (December 1, 2015)
|
4
|
+
* Add Cardknox gateway [dlehren]
|
5
|
+
* Mercury: Add support for card present track 2 [ryanbalsdon]
|
6
|
+
* Cardstream: Improve default currency handling [duff]
|
7
|
+
* Mercury: Strip start and end sentinels on track 2 [ryanbalsdon]
|
8
|
+
* Redsys: Support new SHA256 authentication method [davidsantoso]
|
9
|
+
* Cashnet: Allow custcode override [duff]
|
10
|
+
* Add Rails 5 support [rafaelfranca]
|
11
|
+
* Set required Ruby version for install to 2 or greater [rafaelfranca]
|
12
|
+
* JetPay: Pass ud_fields in capture too [duff]
|
13
|
+
* Stripe: Correctly detect test mode refunds [aprofeit]
|
14
|
+
* Fix variables in remote gateways test template [sdball]
|
15
|
+
* Micropayment: Update fieldnames for new API [duff]
|
16
|
+
* Fix CreditCard#valid_number? erroring on non-digit characters [PatrickTulskie]
|
17
|
+
* Stripe: Correctly detect test mode voids [methodmissing]
|
18
|
+
* Garanti: Add test mode URL and update remote test credentials [cbilgili]
|
19
|
+
* Cashnet: Allow custcode override on refund [duff]
|
20
|
+
* Omise: Add a new optional api_version config [zdk]
|
21
|
+
* Elavon: Include IP address in purchase and authorize requests [aprofeit]
|
22
|
+
* TransFirst: Add support for ACH and more operations [davidsantoso]
|
23
|
+
* FirstData_e4: Fix void for even dollar transactions [duff]
|
3
24
|
|
4
25
|
== Version 1.55.0 (November 9, 2015)
|
5
26
|
* CyberSource: send customer IP address when provided [fastjames]
|
@@ -51,6 +72,7 @@
|
|
51
72
|
* PayBox Direct: Refunds and working test credentials [ivanfer]
|
52
73
|
* Vanco: Handle case of no billing_address [duff]
|
53
74
|
* BluePay: Add support for CUSTOM_ID2 field [ajporterfield]
|
75
|
+
* Creditcall: Handle no verification_value [duff]
|
54
76
|
|
55
77
|
== Version 1.54.0 (October 2, 2015)
|
56
78
|
* Beanstream: Add Network Tokenization support [girasquid]
|
data/CONTRIBUTORS
CHANGED
data/README.md
CHANGED
@@ -97,6 +97,7 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
|
|
97
97
|
* [Borgun](https://www.borgun.is/) - IS
|
98
98
|
* [Braintree](http://www.braintreepaymentsolutions.com) - US, CA, AU, AD, AT, BE, BG, CY, CZ, DK, EE, FI, FR, GI, DE, GR, HU, IS, IM, IE, IT, LV, LI, LT, LU, MT, MC, NL, NO, PL, PT, RO, SM, SK, SI, ES, SE, CH, TR, GB
|
99
99
|
* [BridgePay](http://www.bridgepaynetwork.com/) - CA, US
|
100
|
+
* [Cardknox](https://www.cardknox.com/) - US, CA, GB
|
100
101
|
* [CardSave](http://www.cardsave.net/) - GB
|
101
102
|
* [CardStream](http://www.cardstream.com/) - GB
|
102
103
|
* [Cashnet](http://www.higherone.com/) - US
|
data/lib/active_merchant.rb
CHANGED
@@ -33,7 +33,6 @@ if(!defined?(ActiveSupport::VERSION) || (ActiveSupport::VERSION::STRING < "4.1")
|
|
33
33
|
require 'active_support/core_ext/class/attribute_accessors'
|
34
34
|
end
|
35
35
|
|
36
|
-
require 'active_support/core_ext/class/delegating_attributes'
|
37
36
|
require 'active_support/core_ext/module/attribute_accessors'
|
38
37
|
|
39
38
|
require 'base64'
|
@@ -68,6 +68,7 @@ module ActiveMerchant #:nodoc:
|
|
68
68
|
def valid_number?(number)
|
69
69
|
valid_test_mode_card_number?(number) ||
|
70
70
|
valid_card_number_length?(number) &&
|
71
|
+
valid_card_number_characters?(number) &&
|
71
72
|
valid_checksum?(number)
|
72
73
|
end
|
73
74
|
|
@@ -138,6 +139,10 @@ module ActiveMerchant #:nodoc:
|
|
138
139
|
number.to_s.length >= 12
|
139
140
|
end
|
140
141
|
|
142
|
+
def valid_card_number_characters?(number) #:nodoc:
|
143
|
+
!number.to_s.match(/\D/)
|
144
|
+
end
|
145
|
+
|
141
146
|
def valid_test_mode_card_number?(number) #:nodoc:
|
142
147
|
ActiveMerchant::Billing::Base.test? &&
|
143
148
|
%w[1 2 3 success failure error].include?(number.to_s)
|
@@ -6,8 +6,8 @@ module ActiveMerchant #:nodoc:
|
|
6
6
|
self.display_name = "BridgePay"
|
7
7
|
self.homepage_url = "http://www.bridgepaynetwork.com/"
|
8
8
|
|
9
|
-
self.test_url = "https://gatewaystage.itstgate.com/SmartPayments/transact.asmx
|
10
|
-
self.live_url = "https://gateway.itstgate.com/SmartPayments/transact.asmx
|
9
|
+
self.test_url = "https://gatewaystage.itstgate.com/SmartPayments/transact.asmx"
|
10
|
+
self.live_url = "https://gateway.itstgate.com/SmartPayments/transact.asmx"
|
11
11
|
|
12
12
|
self.supported_countries = ["CA", "US"]
|
13
13
|
self.default_currency = "USD"
|
@@ -19,30 +19,30 @@ module ActiveMerchant #:nodoc:
|
|
19
19
|
super
|
20
20
|
end
|
21
21
|
|
22
|
-
def purchase(amount,
|
23
|
-
post =
|
22
|
+
def purchase(amount, payment_method, options={})
|
23
|
+
post = initialize_required_fields("Sale")
|
24
24
|
|
25
25
|
# Allow the same amount in multiple transactions.
|
26
26
|
post[:ExtData] = "<Force>T</Force>"
|
27
27
|
add_invoice(post, amount, options)
|
28
|
-
|
28
|
+
add_payment_method(post, payment_method)
|
29
29
|
add_customer_data(post, options)
|
30
30
|
|
31
31
|
commit(post)
|
32
32
|
end
|
33
33
|
|
34
|
-
def authorize(amount,
|
35
|
-
post =
|
34
|
+
def authorize(amount, payment_method, options={})
|
35
|
+
post = initialize_required_fields("Auth")
|
36
36
|
|
37
37
|
add_invoice(post, amount, options)
|
38
|
-
|
38
|
+
add_payment_method(post, payment_method)
|
39
39
|
add_customer_data(post, options)
|
40
40
|
|
41
41
|
commit(post)
|
42
42
|
end
|
43
43
|
|
44
44
|
def capture(amount, authorization, options={})
|
45
|
-
post =
|
45
|
+
post = initialize_required_fields("Force")
|
46
46
|
|
47
47
|
add_invoice(post, amount, options)
|
48
48
|
add_reference(post, authorization)
|
@@ -52,7 +52,7 @@ module ActiveMerchant #:nodoc:
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def refund(amount, authorization, options={})
|
55
|
-
post =
|
55
|
+
post = initialize_required_fields("Return")
|
56
56
|
|
57
57
|
add_invoice(post, amount, options)
|
58
58
|
add_reference(post, authorization)
|
@@ -61,7 +61,7 @@ module ActiveMerchant #:nodoc:
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def void(authorization, options={})
|
64
|
-
post =
|
64
|
+
post = initialize_required_fields("Void")
|
65
65
|
|
66
66
|
add_reference(post, authorization)
|
67
67
|
|
@@ -75,9 +75,37 @@ module ActiveMerchant #:nodoc:
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
def supports_scrubbing?
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def scrub(transcript)
|
83
|
+
transcript.
|
84
|
+
gsub(%r((&?CardNum=)[^&]*)i, '\1[FILTERED]').
|
85
|
+
gsub(%r((&?CVNum=)[^&]*)i, '\1[FILTERED]').
|
86
|
+
gsub(%r((&?Password=)[^&]*)i, '\1[FILTERED]').
|
87
|
+
gsub(%r((&?TransitNum=)[^&]*)i, '\1[FILTERED]').
|
88
|
+
gsub(%r((&?AccountNum=)[^&]*)i, '\1[FILTERED]')
|
89
|
+
end
|
90
|
+
|
78
91
|
private
|
79
92
|
|
80
|
-
def
|
93
|
+
def add_payment_method(post, payment_method)
|
94
|
+
if payment_method.respond_to? :brand
|
95
|
+
post[:NameOnCard] = payment_method.name if payment_method.name
|
96
|
+
post[:ExpDate] = expdate(payment_method)
|
97
|
+
post[:CardNum] = payment_method.number
|
98
|
+
post[:CVNum] = payment_method.verification_value
|
99
|
+
else
|
100
|
+
post[:CheckNum] = payment_method.number
|
101
|
+
post[:TransitNum] = payment_method.routing_number
|
102
|
+
post[:AccountNum] = payment_method.account_number
|
103
|
+
post[:NameOnCheck] = payment_method.name
|
104
|
+
post[:ExtData] = "<AccountType>#{payment_method.account_type.capitalize}</AccountType>"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize_required_fields(transaction_type)
|
81
109
|
post = {}
|
82
110
|
post[:TransType] = transaction_type
|
83
111
|
post[:Amount] = ""
|
@@ -92,6 +120,12 @@ module ActiveMerchant #:nodoc:
|
|
92
120
|
post[:CVNum] = ""
|
93
121
|
post[:MagData] = ""
|
94
122
|
post[:ExtData] = ""
|
123
|
+
post[:MICR] = ""
|
124
|
+
post[:DL] = ""
|
125
|
+
post[:SS] = ""
|
126
|
+
post[:DOB] = ""
|
127
|
+
post[:StateCode] = ""
|
128
|
+
post[:CheckType] = ""
|
95
129
|
post
|
96
130
|
end
|
97
131
|
|
@@ -107,13 +141,6 @@ module ActiveMerchant #:nodoc:
|
|
107
141
|
post[:InvNum] = options[:order_id]
|
108
142
|
end
|
109
143
|
|
110
|
-
def add_creditcard(post, creditcard)
|
111
|
-
post[:NameOnCard] = creditcard.name if creditcard.name
|
112
|
-
post[:ExpDate] = expdate(creditcard)
|
113
|
-
post[:CardNum] = creditcard.number
|
114
|
-
post[:CVNum] = creditcard.verification_value
|
115
|
-
end
|
116
|
-
|
117
144
|
def expdate(creditcard)
|
118
145
|
"#{format(creditcard.month, :two_digits)}#{format(creditcard.year, :two_digits)}"
|
119
146
|
end
|
@@ -137,12 +164,12 @@ module ActiveMerchant #:nodoc:
|
|
137
164
|
end
|
138
165
|
|
139
166
|
def commit(parameters)
|
140
|
-
url = (
|
167
|
+
url = url(parameters[:TransitNum] ? 'ProcessCheck' : 'ProcessCreditCard')
|
141
168
|
data = post_data(parameters)
|
142
169
|
raw = parse(ssl_post(url, data))
|
143
170
|
|
144
171
|
Response.new(
|
145
|
-
success_from(raw
|
172
|
+
success_from(raw),
|
146
173
|
message_from(raw),
|
147
174
|
raw,
|
148
175
|
authorization: authorization_from(raw),
|
@@ -150,17 +177,17 @@ module ActiveMerchant #:nodoc:
|
|
150
177
|
)
|
151
178
|
end
|
152
179
|
|
153
|
-
def
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
180
|
+
def url(action)
|
181
|
+
base = test? ? test_url : live_url
|
182
|
+
"#{base}/#{action}"
|
183
|
+
end
|
184
|
+
|
185
|
+
def success_from(response)
|
186
|
+
response[:result] == "0"
|
160
187
|
end
|
161
188
|
|
162
189
|
def message_from(response)
|
163
|
-
response[:respmsg]
|
190
|
+
response[:respmsg] || response[:message]
|
164
191
|
end
|
165
192
|
|
166
193
|
def authorization_from(response)
|
@@ -14,6 +14,7 @@ module ActiveMerchant #:nodoc:
|
|
14
14
|
|
15
15
|
CURRENCY_CODES = {
|
16
16
|
"AUD" => '036',
|
17
|
+
"BRL" => '986',
|
17
18
|
"CAD" => '124',
|
18
19
|
"CZK" => '203',
|
19
20
|
"DKK" => '208',
|
@@ -132,7 +133,7 @@ module ActiveMerchant #:nodoc:
|
|
132
133
|
|
133
134
|
def add_amount(post, money, options)
|
134
135
|
add_pair(post, :amount, amount(money), :required => true)
|
135
|
-
add_pair(post, :currencyCode, currency_code(options[:currency] || currency(money))
|
136
|
+
add_pair(post, :currencyCode, currency_code(options[:currency] || currency(money)))
|
136
137
|
end
|
137
138
|
|
138
139
|
def add_customer_data(post, options)
|
@@ -0,0 +1,328 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class CardknoxGateway < Gateway
|
4
|
+
self.live_url = 'https://x1.cardknox.com/gateway'
|
5
|
+
|
6
|
+
self.supported_countries = ['US','CA','GB']
|
7
|
+
self.default_currency = 'USD'
|
8
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
9
|
+
|
10
|
+
self.homepage_url = 'https://www.cardknox.com/'
|
11
|
+
self.display_name = 'Cardknox'
|
12
|
+
|
13
|
+
COMMANDS = {
|
14
|
+
credit_card: {
|
15
|
+
purchase: 'cc:sale',
|
16
|
+
authorization: 'cc:authonly',
|
17
|
+
capture: 'cc:capture',
|
18
|
+
refund: 'cc:refund',
|
19
|
+
void: 'cc:void',
|
20
|
+
save: 'cc:save'
|
21
|
+
},
|
22
|
+
check: {
|
23
|
+
purchase: 'check:sale',
|
24
|
+
refund: 'check:refund',
|
25
|
+
void: 'check:void',
|
26
|
+
save: 'check:save'
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
def initialize(options={})
|
31
|
+
requires!(options, :api_key)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
# There are three sources for doing a purchase transation:
|
36
|
+
# - credit card
|
37
|
+
# - check
|
38
|
+
# - cardknox token, which is returned in the the authorization string "ref_num;token;command"
|
39
|
+
|
40
|
+
def purchase(amount, source, options={})
|
41
|
+
post = {}
|
42
|
+
add_amount(post, amount, options)
|
43
|
+
add_invoice(post, options)
|
44
|
+
add_source(post, source)
|
45
|
+
add_address(post, source, options)
|
46
|
+
add_customer_data(post, options)
|
47
|
+
add_custom_fields(post, options)
|
48
|
+
commit(:purchase, source_type(source), post)
|
49
|
+
end
|
50
|
+
|
51
|
+
def authorize(amount, source, options={})
|
52
|
+
post = {}
|
53
|
+
add_amount(post, amount)
|
54
|
+
add_invoice(post, options)
|
55
|
+
add_source(post, source)
|
56
|
+
add_address(post, source, options)
|
57
|
+
add_customer_data(post, options)
|
58
|
+
add_custom_fields(post, options)
|
59
|
+
commit(:authorization, source_type(source), post)
|
60
|
+
end
|
61
|
+
|
62
|
+
def capture(amount, authorization, options = {})
|
63
|
+
post = {}
|
64
|
+
add_reference(post, authorization)
|
65
|
+
add_amount(post, amount)
|
66
|
+
commit(:capture, source_type(authorization), post)
|
67
|
+
end
|
68
|
+
|
69
|
+
def refund(amount, authorization, options={})
|
70
|
+
post = {}
|
71
|
+
add_reference(post, authorization)
|
72
|
+
add_amount(post, amount)
|
73
|
+
commit(:refund, source_type(authorization), post)
|
74
|
+
end
|
75
|
+
|
76
|
+
def void(authorization, options = {})
|
77
|
+
post = {}
|
78
|
+
add_reference(post, authorization)
|
79
|
+
commit(:void, source_type(authorization), post)
|
80
|
+
end
|
81
|
+
|
82
|
+
def verify(credit_card, options={})
|
83
|
+
MultiResponse.run(:use_first_response) do |r|
|
84
|
+
r.process { authorize(100, credit_card, options) }
|
85
|
+
r.process(:ignore_result) { void(r.authorization, options) }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def store(source, options = {})
|
90
|
+
post = {}
|
91
|
+
add_source(post, source)
|
92
|
+
add_address(post, source, options)
|
93
|
+
add_invoice(post, options)
|
94
|
+
add_customer_data(post, options)
|
95
|
+
add_custom_fields(post, options)
|
96
|
+
commit(:save, source_type(source), post)
|
97
|
+
end
|
98
|
+
|
99
|
+
def supports_scrubbing?
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
def scrub(transcript)
|
104
|
+
transcript.
|
105
|
+
gsub(%r((xCardNum=)\d+), '\1[FILTERED]').
|
106
|
+
gsub(%r((xCVV=)\d+), '\1[FILTERED]').
|
107
|
+
gsub(%r((xAccount=)\d+), '\1[FILTERED]').
|
108
|
+
gsub(%r((xRouting=)\d+), '\1[FILTERED]').
|
109
|
+
gsub(%r((xKey=)\w+), '\1[FILTERED]')
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def split_authorization(authorization)
|
115
|
+
authorization.split(";")
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_reference(post, reference)
|
119
|
+
reference, _, _ = split_authorization(reference)
|
120
|
+
post[:Refnum] = reference
|
121
|
+
end
|
122
|
+
|
123
|
+
def source_type(source)
|
124
|
+
if source.respond_to?(:brand)
|
125
|
+
:credit_card
|
126
|
+
elsif source.respond_to?(:routing_number)
|
127
|
+
:check
|
128
|
+
elsif source.kind_of?(String)
|
129
|
+
source_type_from(source)
|
130
|
+
else
|
131
|
+
raise ArgumentError, "Unknown source #{source.inspect}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def source_type_from(authorization)
|
136
|
+
_, _, source_type = split_authorization(authorization)
|
137
|
+
(source_type || "credit_card").to_sym
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_source(post, source)
|
141
|
+
if source.respond_to?(:brand)
|
142
|
+
add_credit_card(post, source)
|
143
|
+
elsif source.respond_to?(:routing_number)
|
144
|
+
add_check(post, source)
|
145
|
+
elsif source.kind_of?(String)
|
146
|
+
add_cardknox_token(post, source)
|
147
|
+
else
|
148
|
+
raise ArgumentError, "Invalid payment source #{source.inspect}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Subtotal + Tax + Tip = Amount.
|
153
|
+
|
154
|
+
def add_amount(post, money, options = {})
|
155
|
+
post[:Tip] = amount(options[:tip])
|
156
|
+
post[:Amount] = amount(money)
|
157
|
+
end
|
158
|
+
|
159
|
+
def expdate(credit_card)
|
160
|
+
year = format(credit_card.year, :two_digits)
|
161
|
+
month = format(credit_card.month, :two_digits)
|
162
|
+
"#{month}#{year}"
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_customer_data(post, options)
|
166
|
+
address = options[:billing_address] || {}
|
167
|
+
post[:Street] = address[:address1]
|
168
|
+
post[:Zip] = address[:zip]
|
169
|
+
post[:PONum] = options[:po_number]
|
170
|
+
post[:Fax] = options[:fax]
|
171
|
+
post[:Email] = options[:email]
|
172
|
+
post[:IP] = options[:ip]
|
173
|
+
end
|
174
|
+
|
175
|
+
def add_address(post, source, options)
|
176
|
+
add_address_for_type(:billing, post, source, options[:billing_address]) if options[:billing_address]
|
177
|
+
add_address_for_type(:shipping, post, source, options[:shipping_address]) if options[:shipping_address]
|
178
|
+
end
|
179
|
+
|
180
|
+
def add_address_for_type(type, post, source, address)
|
181
|
+
prefix = address_key_prefix(type)
|
182
|
+
if source.respond_to?(:first_name)
|
183
|
+
post[address_key(prefix, 'FirstName')] = source.first_name
|
184
|
+
post[address_key(prefix, 'LastName')] = source.last_name
|
185
|
+
else
|
186
|
+
post[address_key(prefix, 'FirstName')] = address[:first_name]
|
187
|
+
post[address_key(prefix, 'LastName')] = address[:last_name]
|
188
|
+
end
|
189
|
+
post[address_key(prefix, 'MiddleName')] = address[:middle_name]
|
190
|
+
|
191
|
+
post[address_key(prefix, 'Company')] = address[:company]
|
192
|
+
post[address_key(prefix, 'Street')] = address[:address1]
|
193
|
+
post[address_key(prefix, 'Street2')] = address[:address2]
|
194
|
+
post[address_key(prefix, 'City')] = address[:city]
|
195
|
+
post[address_key(prefix, 'State')] = address[:state]
|
196
|
+
post[address_key(prefix, 'Zip')] = address[:zip]
|
197
|
+
post[address_key(prefix, 'Country')] = address[:country]
|
198
|
+
post[address_key(prefix, 'Phone')] = address[:phone]
|
199
|
+
post[address_key(prefix, 'Mobile')] = address[:mobile]
|
200
|
+
end
|
201
|
+
|
202
|
+
def address_key_prefix(type)
|
203
|
+
case type
|
204
|
+
when :shipping then 'Ship'
|
205
|
+
when :billing then 'Bill'
|
206
|
+
else
|
207
|
+
raise ArgumentError, "Unknown address key prefix: #{type}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def address_key(prefix, key)
|
212
|
+
"#{prefix}#{key}".to_sym
|
213
|
+
end
|
214
|
+
|
215
|
+
def add_invoice(post, options)
|
216
|
+
post[:Invoice] = options[:invoice]
|
217
|
+
post[:OrderID] = options[:order_id]
|
218
|
+
post[:Comments] = options[:comments]
|
219
|
+
post[:Description] = options[:description]
|
220
|
+
post[:Tax] = amount(options[:tax])
|
221
|
+
end
|
222
|
+
|
223
|
+
def add_custom_fields(post, options)
|
224
|
+
options.keys.grep(/^custom(?:[01]\d|20)$/) do |key|
|
225
|
+
post[key.to_s.capitalize] = options[key]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def add_credit_card(post, credit_card)
|
230
|
+
if credit_card.track_data.present?
|
231
|
+
post[:Magstripe] = credit_card.track_data
|
232
|
+
post[:Cardpresent] = true
|
233
|
+
else
|
234
|
+
post[:CardNum] = credit_card.number
|
235
|
+
post[:CVV] = credit_card.verification_value
|
236
|
+
post[:Exp] = expdate(credit_card)
|
237
|
+
post[:Name] = credit_card.name
|
238
|
+
post[:CardPresent] = true if credit_card.manual_entry
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def add_check(post, check)
|
243
|
+
post[:Routing] = check.routing_number
|
244
|
+
post[:Account] = check.account_number
|
245
|
+
post[:Name] = check.name
|
246
|
+
post[:CheckNum] = check.number
|
247
|
+
end
|
248
|
+
|
249
|
+
def add_cardknox_token(post, authorization)
|
250
|
+
_, token, _ = split_authorization(authorization)
|
251
|
+
|
252
|
+
post[:Token] = token
|
253
|
+
end
|
254
|
+
|
255
|
+
def parse(body)
|
256
|
+
fields = {}
|
257
|
+
for line in body.split('&')
|
258
|
+
key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
|
259
|
+
fields[key] = CGI.unescape(value.to_s)
|
260
|
+
end
|
261
|
+
|
262
|
+
{
|
263
|
+
result: fields['xResult'],
|
264
|
+
status: fields['xStatus'],
|
265
|
+
error: fields['xError'],
|
266
|
+
auth_code: fields['xAuthCode'],
|
267
|
+
ref_num: fields['xRefNum'],
|
268
|
+
current_ref_num: fields['xRefNumCurrent'],
|
269
|
+
token: fields['xToken'],
|
270
|
+
batch: fields['xBatch'],
|
271
|
+
avs_result: fields['xAvsResult'],
|
272
|
+
avs_result_code: fields['xAvsResultCode'],
|
273
|
+
cvv_result: fields['xCvvResult'],
|
274
|
+
cvv_result_code: fields['xCvvResultCode'],
|
275
|
+
remaining_balance: fields['xRemainingBalance'],
|
276
|
+
amount: fields['xAuthAmount'],
|
277
|
+
masked_card_num: fields['xMaskedCardNumber'],
|
278
|
+
masked_account_number: fields['MaskedAccountNumber']
|
279
|
+
}.delete_if{|k, v| v.nil?}
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
def commit(action, source_type, parameters)
|
284
|
+
response = parse(ssl_post(live_url, post_data(COMMANDS[source_type][action], parameters)))
|
285
|
+
|
286
|
+
Response.new(
|
287
|
+
(response[:status] == 'Approved'),
|
288
|
+
message_from(response),
|
289
|
+
response,
|
290
|
+
authorization: authorization_from(response, source_type),
|
291
|
+
avs_result: { code: response[:avs_result_code] },
|
292
|
+
cvv_result: response[:cvv_result_code]
|
293
|
+
)
|
294
|
+
end
|
295
|
+
|
296
|
+
def message_from(response)
|
297
|
+
if response[:status] == "Approved"
|
298
|
+
"Success"
|
299
|
+
elsif response[:error].blank?
|
300
|
+
"Unspecified error"
|
301
|
+
else
|
302
|
+
response[:error]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def authorization_from(response, source_type)
|
307
|
+
"#{response[:ref_num]};#{response[:token]};#{source_type}"
|
308
|
+
end
|
309
|
+
|
310
|
+
def post_data(command, parameters = {})
|
311
|
+
initial_parameters = {
|
312
|
+
Key: @options[:api_key],
|
313
|
+
Version: "4.5.4",
|
314
|
+
SoftwareName: 'Active Merchant',
|
315
|
+
SoftwareVersion: "#{ActiveMerchant::VERSION}",
|
316
|
+
Command: command,
|
317
|
+
}
|
318
|
+
|
319
|
+
seed = SecureRandom.hex(32).upcase
|
320
|
+
hash = Digest::SHA1.hexdigest("#{initial_parameters[:command]}:#{@options[:pin]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
|
321
|
+
initial_parameters[:Hash] = "s/#{seed}/#{hash}/n" unless @options[:pin].blank?
|
322
|
+
parameters = initial_parameters.merge(parameters)
|
323
|
+
|
324
|
+
parameters.reject{|k, v| v.blank?}.collect{ |key, value| "x#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|