activemerchant 1.66.0 → 1.67.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
- data/CHANGELOG +28 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +74 -7
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +1 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +8 -1
- data/lib/active_merchant/billing/gateways/cyber_source.rb +14 -7
- data/lib/active_merchant/billing/gateways/ebanx.rb +234 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +23 -92
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +1 -27
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +5 -3
- data/lib/active_merchant/billing/gateways/jetpay_v2.rb +410 -0
- data/lib/active_merchant/billing/gateways/nmi.rb +10 -1
- data/lib/active_merchant/billing/gateways/opp.rb +124 -114
- data/lib/active_merchant/billing/gateways/orbital.rb +15 -4
- data/lib/active_merchant/billing/gateways/payeezy.rb +9 -6
- data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +19 -14
- data/lib/active_merchant/billing/gateways/realex.rb +2 -5
- data/lib/active_merchant/billing/gateways/safe_charge.rb +2 -1
- data/lib/active_merchant/billing/gateways/wepay.rb +11 -6
- data/lib/active_merchant/version.rb +1 -1
- metadata +4 -2
@@ -2,33 +2,6 @@ require 'active_merchant/billing/gateways/viaklix'
|
|
2
2
|
|
3
3
|
module ActiveMerchant #:nodoc:
|
4
4
|
module Billing #:nodoc:
|
5
|
-
# = Elavon Virtual Merchant Gateway
|
6
|
-
#
|
7
|
-
# == Example use:
|
8
|
-
#
|
9
|
-
# gateway = ActiveMerchant::Billing::ElavonGateway.new(
|
10
|
-
# :login => "my_virtual_merchant_id",
|
11
|
-
# :password => "my_virtual_merchant_pin",
|
12
|
-
# :user => "my_virtual_merchant_user_id" # optional
|
13
|
-
# )
|
14
|
-
#
|
15
|
-
# # set up credit card obj as in main ActiveMerchant example
|
16
|
-
# creditcard = ActiveMerchant::Billing::CreditCard.new(
|
17
|
-
# :type => 'visa',
|
18
|
-
# :number => '41111111111111111',
|
19
|
-
# :month => 10,
|
20
|
-
# :year => 2011,
|
21
|
-
# :first_name => 'Bob',
|
22
|
-
# :last_name => 'Bobsen'
|
23
|
-
# )
|
24
|
-
#
|
25
|
-
# # run request
|
26
|
-
# response = gateway.purchase(1000, creditcard) # authorize and capture 10 USD
|
27
|
-
#
|
28
|
-
# puts response.success? # Check whether the transaction was successful
|
29
|
-
# puts response.message # Retrieve the message returned by Elavon
|
30
|
-
# puts response.authorization # Retrieve the unique transaction ID returned by Elavon
|
31
|
-
#
|
32
5
|
class ElavonGateway < Gateway
|
33
6
|
include Empty
|
34
7
|
|
@@ -55,23 +28,11 @@ module ActiveMerchant #:nodoc:
|
|
55
28
|
:update => 'CCUPDATETOKEN',
|
56
29
|
}
|
57
30
|
|
58
|
-
# Initialize the Gateway
|
59
|
-
#
|
60
|
-
# The gateway requires that a valid login and password be passed
|
61
|
-
# in the +options+ hash.
|
62
|
-
#
|
63
|
-
# ==== Options
|
64
|
-
#
|
65
|
-
# * <tt>:login</tt> -- Merchant ID
|
66
|
-
# * <tt>:password</tt> -- PIN
|
67
|
-
# * <tt>:user</tt> -- Specify a subuser of the account (optional)
|
68
|
-
# * <tt>:test => +true+ or +false+</tt> -- Force test transactions
|
69
31
|
def initialize(options = {})
|
70
32
|
requires!(options, :login, :password)
|
71
33
|
super
|
72
34
|
end
|
73
35
|
|
74
|
-
# Make a purchase
|
75
36
|
def purchase(money, payment_method, options = {})
|
76
37
|
form = {}
|
77
38
|
add_salestax(form, options)
|
@@ -85,16 +46,9 @@ module ActiveMerchant #:nodoc:
|
|
85
46
|
add_customer_data(form, options)
|
86
47
|
add_test_mode(form, options)
|
87
48
|
add_ip(form, options)
|
88
|
-
commit(:purchase, money, form)
|
49
|
+
commit(:purchase, money, form, options)
|
89
50
|
end
|
90
51
|
|
91
|
-
# Authorize a credit card for a given amount.
|
92
|
-
#
|
93
|
-
# ==== Parameters
|
94
|
-
# * <tt>money</tt> - The amount to be authorized as an Integer value in cents.
|
95
|
-
# * <tt>credit_card</tt> - The CreditCard details for the transaction.
|
96
|
-
# * <tt>options</tt>
|
97
|
-
# * <tt>:billing_address</tt> - The billing address for the cardholder.
|
98
52
|
def authorize(money, creditcard, options = {})
|
99
53
|
form = {}
|
100
54
|
add_salestax(form, options)
|
@@ -104,16 +58,9 @@ module ActiveMerchant #:nodoc:
|
|
104
58
|
add_customer_data(form, options)
|
105
59
|
add_test_mode(form, options)
|
106
60
|
add_ip(form, options)
|
107
|
-
commit(:authorize, money, form)
|
61
|
+
commit(:authorize, money, form, options)
|
108
62
|
end
|
109
63
|
|
110
|
-
# Capture authorized funds from a credit card.
|
111
|
-
#
|
112
|
-
# ==== Parameters
|
113
|
-
# * <tt>money</tt> - The amount to be captured as an Integer value in cents.
|
114
|
-
# * <tt>authorization</tt> - The approval code returned from the initial authorization.
|
115
|
-
# * <tt>options</tt>
|
116
|
-
# * <tt>:credit_card</tt> - The CreditCard details from the initial transaction (required).
|
117
64
|
def capture(money, authorization, options = {})
|
118
65
|
form = {}
|
119
66
|
if options[:credit_card]
|
@@ -130,45 +77,23 @@ module ActiveMerchant #:nodoc:
|
|
130
77
|
add_partial_shipment_flag(form, options)
|
131
78
|
add_test_mode(form, options)
|
132
79
|
end
|
133
|
-
commit(action, money, form)
|
134
|
-
end
|
135
|
-
|
136
|
-
# Refund a transaction.
|
137
|
-
#
|
138
|
-
# This transaction indicates to the gateway that
|
139
|
-
# money should flow from the merchant to the customer.
|
140
|
-
#
|
141
|
-
# ==== Parameters
|
142
|
-
#
|
143
|
-
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
144
|
-
# * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
|
145
|
-
# * <tt>options</tt> -- A hash of parameters.
|
80
|
+
commit(action, money, form, options)
|
81
|
+
end
|
82
|
+
|
146
83
|
def refund(money, identification, options = {})
|
147
84
|
form = {}
|
148
85
|
add_txn_id(form, identification)
|
149
86
|
add_test_mode(form, options)
|
150
|
-
commit(:refund, money, form)
|
87
|
+
commit(:refund, money, form, options)
|
151
88
|
end
|
152
89
|
|
153
|
-
# Void a previous transaction
|
154
|
-
#
|
155
|
-
# ==== Parameters
|
156
|
-
#
|
157
|
-
# * <tt>authorization</tt> - The authorization returned from the previous request.
|
158
90
|
def void(identification, options = {})
|
159
91
|
form = {}
|
160
92
|
add_txn_id(form, identification)
|
161
93
|
add_test_mode(form, options)
|
162
|
-
commit(:void, nil, form)
|
94
|
+
commit(:void, nil, form, options)
|
163
95
|
end
|
164
96
|
|
165
|
-
# Make a credit to a card. Use the refund method if you'd like to credit using
|
166
|
-
# previous transaction
|
167
|
-
#
|
168
|
-
# ==== Parameters
|
169
|
-
# * <tt>money</tt> - The amount to be credited as an Integer value in cents.
|
170
|
-
# * <tt>creditcard</tt> - The credit card to be credited.
|
171
|
-
# * <tt>options</tt>
|
172
97
|
def credit(money, creditcard, options = {})
|
173
98
|
if creditcard.is_a?(String)
|
174
99
|
raise ArgumentError, "Reference credits are not supported. Please supply the original credit card or use the #refund method."
|
@@ -180,7 +105,7 @@ module ActiveMerchant #:nodoc:
|
|
180
105
|
add_address(form, options)
|
181
106
|
add_customer_data(form, options)
|
182
107
|
add_test_mode(form, options)
|
183
|
-
commit(:credit, money, form)
|
108
|
+
commit(:credit, money, form, options)
|
184
109
|
end
|
185
110
|
|
186
111
|
def verify(credit_card, options = {})
|
@@ -198,7 +123,7 @@ module ActiveMerchant #:nodoc:
|
|
198
123
|
add_test_mode(form, options)
|
199
124
|
add_verification(form, options)
|
200
125
|
form[:add_token] = 'Y'
|
201
|
-
commit(:store, nil, form)
|
126
|
+
commit(:store, nil, form, options)
|
202
127
|
end
|
203
128
|
|
204
129
|
def update(token, creditcard, options = {})
|
@@ -208,7 +133,7 @@ module ActiveMerchant #:nodoc:
|
|
208
133
|
add_address(form, options)
|
209
134
|
add_customer_data(form, options)
|
210
135
|
add_test_mode(form, options)
|
211
|
-
commit(:update, nil, form)
|
136
|
+
commit(:update, nil, form, options)
|
212
137
|
end
|
213
138
|
|
214
139
|
private
|
@@ -255,6 +180,11 @@ module ActiveMerchant #:nodoc:
|
|
255
180
|
form[:email] = truncate(options[:email], 100) unless empty?(options[:email])
|
256
181
|
form[:customer_code] = truncate(options[:customer], 10) unless empty?(options[:customer])
|
257
182
|
form[:customer_number] = options[:customer_number] unless empty?(options[:customer_number])
|
183
|
+
if options[:custom_fields]
|
184
|
+
options[:custom_fields].each do |key, value|
|
185
|
+
form[key.to_s] = value
|
186
|
+
end
|
187
|
+
end
|
258
188
|
end
|
259
189
|
|
260
190
|
def add_salestax(form, options)
|
@@ -313,11 +243,11 @@ module ActiveMerchant #:nodoc:
|
|
313
243
|
!response.has_key?('errorMessage')
|
314
244
|
end
|
315
245
|
|
316
|
-
def commit(action, money, parameters)
|
246
|
+
def commit(action, money, parameters, options)
|
317
247
|
parameters[:amount] = amount(money)
|
318
248
|
parameters[:transaction_type] = self.actions[action]
|
319
249
|
|
320
|
-
response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters)) )
|
250
|
+
response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters, options)) )
|
321
251
|
|
322
252
|
Response.new(response['result'] == '0', message_from(response), response,
|
323
253
|
:test => @options[:test] || test?,
|
@@ -327,21 +257,22 @@ module ActiveMerchant #:nodoc:
|
|
327
257
|
)
|
328
258
|
end
|
329
259
|
|
330
|
-
def post_data(parameters)
|
260
|
+
def post_data(parameters, options)
|
331
261
|
result = preamble
|
332
262
|
result.merge!(parameters)
|
333
|
-
result.collect { |key, value| post_data_string(key, value) }.join("&")
|
263
|
+
result.collect { |key, value| post_data_string(key, value, options) }.join("&")
|
334
264
|
end
|
335
265
|
|
336
|
-
def post_data_string(key, value)
|
337
|
-
if custom_field?(key)
|
266
|
+
def post_data_string(key, value, options)
|
267
|
+
if custom_field?(key, options)
|
338
268
|
"#{key}=#{CGI.escape(value.to_s)}"
|
339
269
|
else
|
340
270
|
"ssl_#{key}=#{CGI.escape(value.to_s)}"
|
341
271
|
end
|
342
272
|
end
|
343
273
|
|
344
|
-
def custom_field?(field_name)
|
274
|
+
def custom_field?(field_name, options)
|
275
|
+
return true if options[:custom_fields] && options[:custom_fields].include?(field_name.to_sym)
|
345
276
|
field_name == :customer_number
|
346
277
|
end
|
347
278
|
|
@@ -14,23 +14,11 @@ module ActiveMerchant #:nodoc:
|
|
14
14
|
self.homepage_url = 'https://www.fatzebra.com.au/'
|
15
15
|
self.display_name = 'Fat Zebra'
|
16
16
|
|
17
|
-
# Setup a new instance of the gateway.
|
18
|
-
#
|
19
|
-
# The options hash should include :username and :token
|
20
|
-
# You can find your username and token at https://dashboard.fatzebra.com.au
|
21
|
-
# Under the Your Account section
|
22
17
|
def initialize(options = {})
|
23
18
|
requires!(options, :username, :token)
|
24
19
|
super
|
25
20
|
end
|
26
21
|
|
27
|
-
# To create a purchase on a credit card use:
|
28
|
-
#
|
29
|
-
# purchase(money, creditcard)
|
30
|
-
#
|
31
|
-
# To charge a tokenized card
|
32
|
-
#
|
33
|
-
# purchase(money, "abzy87u", :cvv => "123")
|
34
22
|
def purchase(money, creditcard, options = {})
|
35
23
|
post = {}
|
36
24
|
|
@@ -65,11 +53,6 @@ module ActiveMerchant #:nodoc:
|
|
65
53
|
commit(:post, "purchases/#{CGI.escape(authorization)}/capture", post)
|
66
54
|
end
|
67
55
|
|
68
|
-
# Refund a transaction
|
69
|
-
#
|
70
|
-
# amount - Integer - the amount to refund
|
71
|
-
# txn_id - String - the original transaction to be refunded
|
72
|
-
# reference - String - your transaction reference
|
73
56
|
def refund(money, txn_id, options={})
|
74
57
|
post = {}
|
75
58
|
|
@@ -81,9 +64,6 @@ module ActiveMerchant #:nodoc:
|
|
81
64
|
commit(:post, "refunds", post)
|
82
65
|
end
|
83
66
|
|
84
|
-
# Tokenize a credit card
|
85
|
-
#
|
86
|
-
# The token is returned in the Response#authorization
|
87
67
|
def store(creditcard, options={})
|
88
68
|
post = {}
|
89
69
|
add_creditcard(post, creditcard)
|
@@ -104,14 +84,12 @@ module ActiveMerchant #:nodoc:
|
|
104
84
|
|
105
85
|
private
|
106
86
|
|
107
|
-
# Add the money details to the request
|
108
87
|
def add_amount(post, money, options)
|
109
88
|
post[:currency] = (options[:currency] || currency(money))
|
110
89
|
post[:currency] = post[:currency].upcase if post[:currency]
|
111
90
|
post[:amount] = money
|
112
91
|
end
|
113
92
|
|
114
|
-
# Add the credit card details to the request
|
115
93
|
def add_creditcard(post, creditcard, options = {})
|
116
94
|
if creditcard.respond_to?(:number)
|
117
95
|
post[:card_number] = creditcard.number
|
@@ -134,7 +112,7 @@ module ActiveMerchant #:nodoc:
|
|
134
112
|
extra = {}
|
135
113
|
extra[:ecm] = "32" if options[:recurring]
|
136
114
|
extra[:cavv] = options[:cavv] if options[:cavv]
|
137
|
-
extra[:xid] = options[:
|
115
|
+
extra[:xid] = options[:xid] if options[:xid]
|
138
116
|
extra[:sli] = options[:sli] if options[:sli]
|
139
117
|
extra[:name] = options[:merchant] if options[:merchant]
|
140
118
|
extra[:location] = options[:merchant_location] if options[:merchant_location]
|
@@ -149,7 +127,6 @@ module ActiveMerchant #:nodoc:
|
|
149
127
|
post[:customer_ip] = options[:ip] || "127.0.0.1"
|
150
128
|
end
|
151
129
|
|
152
|
-
# Post the data to the gateway
|
153
130
|
def commit(method, uri, parameters=nil)
|
154
131
|
response = begin
|
155
132
|
parse(ssl_request(method, get_url(uri), parameters.to_json, headers))
|
@@ -194,7 +171,6 @@ module ActiveMerchant #:nodoc:
|
|
194
171
|
end
|
195
172
|
end
|
196
173
|
|
197
|
-
# Parse the returned JSON, if parse errors are raised then return a detailed error.
|
198
174
|
def parse(response)
|
199
175
|
begin
|
200
176
|
JSON.parse(response)
|
@@ -209,13 +185,11 @@ module ActiveMerchant #:nodoc:
|
|
209
185
|
end
|
210
186
|
end
|
211
187
|
|
212
|
-
# Build the URL based on the AM mode and the URI
|
213
188
|
def get_url(uri)
|
214
189
|
base = test? ? self.test_url : self.live_url
|
215
190
|
base + "/" + uri
|
216
191
|
end
|
217
192
|
|
218
|
-
# Builds the auth and U-A headers for the request
|
219
193
|
def headers
|
220
194
|
{
|
221
195
|
"Authorization" => "Basic " + Base64.strict_encode64(@options[:username].to_s + ":" + @options[:token].to_s).strip,
|
@@ -34,6 +34,8 @@ module ActiveMerchant #:nodoc:
|
|
34
34
|
|
35
35
|
E4_BRANDS = BRANDS.merge({:mastercard => "Mastercard"})
|
36
36
|
|
37
|
+
DEFAULT_ECI = "07"
|
38
|
+
|
37
39
|
self.supported_cardtypes = BRANDS.keys
|
38
40
|
self.supported_countries = ["CA", "US"]
|
39
41
|
self.default_currency = "USD"
|
@@ -235,6 +237,9 @@ module ActiveMerchant #:nodoc:
|
|
235
237
|
xml.tag! "CardHoldersName", credit_card.name
|
236
238
|
xml.tag! "CardType", card_type(credit_card.brand)
|
237
239
|
|
240
|
+
eci = (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
|
241
|
+
xml.tag! "Ecommerce_Flag", eci
|
242
|
+
|
238
243
|
add_credit_card_verification_strings(xml, credit_card, options)
|
239
244
|
end
|
240
245
|
end
|
@@ -256,8 +261,6 @@ module ActiveMerchant #:nodoc:
|
|
256
261
|
end
|
257
262
|
|
258
263
|
def add_network_tokenization_credit_card(xml, credit_card)
|
259
|
-
xml.tag!("Ecommerce_Flag", credit_card.eci)
|
260
|
-
|
261
264
|
case card_brand(credit_card).to_sym
|
262
265
|
when :visa
|
263
266
|
xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
|
@@ -275,7 +278,6 @@ module ActiveMerchant #:nodoc:
|
|
275
278
|
def add_card_authentication_data(xml, options)
|
276
279
|
xml.tag! "CAVV", options[:cavv]
|
277
280
|
xml.tag! "XID", options[:xid]
|
278
|
-
xml.tag! "Ecommerce_Flag", options[:eci]
|
279
281
|
end
|
280
282
|
|
281
283
|
def add_credit_card_token(xml, store_authorization)
|
@@ -0,0 +1,410 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class JetpayV2Gateway < Gateway
|
4
|
+
self.test_url = 'https://test1.jetpay.com/jetpay'
|
5
|
+
self.live_url = 'https://gateway20.jetpay.com/jetpay'
|
6
|
+
|
7
|
+
self.money_format = :cents
|
8
|
+
self.default_currency = 'USD'
|
9
|
+
self.supported_countries = ['US', 'CA']
|
10
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
11
|
+
|
12
|
+
self.homepage_url = 'http://www.jetpay.com'
|
13
|
+
self.display_name = 'JetPay'
|
14
|
+
|
15
|
+
API_VERSION = '2.2'
|
16
|
+
|
17
|
+
ACTION_CODE_MESSAGES = {
|
18
|
+
"000" => "Approved.",
|
19
|
+
"001" => "Refer to card issuer.",
|
20
|
+
"002" => "Refer to card issuer, special condition.",
|
21
|
+
"003" => "Invalid merchant or service provider.",
|
22
|
+
"004" => "Pick up card.",
|
23
|
+
"005" => "Do not honor.",
|
24
|
+
"006" => "Error.",
|
25
|
+
"007" => "Pick up card, special condition.",
|
26
|
+
"008" => "Honor with ID (Show ID).",
|
27
|
+
"010" => "Partial approval.",
|
28
|
+
"011" => "VIP approval.",
|
29
|
+
"012" => "Invalid transaction.",
|
30
|
+
"013" => "Invalid amount or exceeds maximum for card program.",
|
31
|
+
"014" => "Invalid account number (no such number).",
|
32
|
+
"015" => "No such issuer.",
|
33
|
+
"019" => "Re-enter Transaction.",
|
34
|
+
"021" => "No action taken (unable to back out prior transaction).",
|
35
|
+
"025" => "Transaction Not Found.",
|
36
|
+
"027" => "File update field edit error.",
|
37
|
+
"028" => "File is temporarily unavailable.",
|
38
|
+
"030" => "Format error.",
|
39
|
+
"039" => "No credit account.",
|
40
|
+
"041" => "Pick up card (lost card).",
|
41
|
+
"043" => "Pick up card (stolen card).",
|
42
|
+
"051" => "Insufficient funds.",
|
43
|
+
"052" => "No checking account.",
|
44
|
+
"053" => "Mp savomgs accpimt.",
|
45
|
+
"054" => "Expired Card.",
|
46
|
+
"055" => "Incorrect PIN.",
|
47
|
+
"057" => "Transaction not permitted to cardholder.",
|
48
|
+
"058" => "Transaction not allowed at terminal.",
|
49
|
+
"061" => "Exceeds withdrawal limit.",
|
50
|
+
"062" => "Restricted card (eg, Country Exclusion).",
|
51
|
+
"063" => "Security violation.",
|
52
|
+
"065" => "Activity count limit exceeded.",
|
53
|
+
"068" => "Response late.",
|
54
|
+
"070" => "Contact card issuer.",
|
55
|
+
"071" => "PIN not changed.",
|
56
|
+
"075" => "Allowable number of PIN-entry tries exceeded.",
|
57
|
+
"076" => "Unable to locate previous message (no matching retrieval reference number).",
|
58
|
+
"077" => "Repeat or reversal data are inconsistent with original message.",
|
59
|
+
"078" => "Blocked (first use), or non-existent account.",
|
60
|
+
"079" => "Key exchange validation failed.",
|
61
|
+
"080" => "Credit issuer unavailable or invalid date.",
|
62
|
+
"081" => "PIN cryptographic error found.",
|
63
|
+
"082" => "Negative online CVV results.",
|
64
|
+
"084" => "Invalid auth life cycle.",
|
65
|
+
"085" => "No reason to decline - CVV or AVS approved.",
|
66
|
+
"086" => "Cannot verify PIN.",
|
67
|
+
"087" => "Cashback not allowed.",
|
68
|
+
"089" => "Issuer Down.",
|
69
|
+
"091" => "Issuer Down.",
|
70
|
+
"092" => "Unable to route transaction.",
|
71
|
+
"093" => "Transaction cannot be completed - violation of law.",
|
72
|
+
"094" => "Duplicate transmission.",
|
73
|
+
"096" => "System error.",
|
74
|
+
"100" => "Deny.",
|
75
|
+
"101" => "Expired Card.",
|
76
|
+
"103" => "Deny - Invalid manual Entry 4DBC.",
|
77
|
+
"104" => "Deny - New card issued.",
|
78
|
+
"105" => "Deny - Account Cancelled.",
|
79
|
+
"106" => "Exceeded PIN Attempts.",
|
80
|
+
"107" => "Please Call Issuer.",
|
81
|
+
"109" => "Invalid merchant.",
|
82
|
+
"110" => "Invalid amount.",
|
83
|
+
"111" => "Invalid account.",
|
84
|
+
"115" => "Service not permitted.",
|
85
|
+
"117" => "Invalid PIN.",
|
86
|
+
"119" => "Card member not enrolled.",
|
87
|
+
"122" => "Invalid card (CID) security code.",
|
88
|
+
"125" => "Invalid effective date.",
|
89
|
+
"181" => "Format error.",
|
90
|
+
"182" => "Please wait.",
|
91
|
+
"183" => "Invalid currency code.",
|
92
|
+
"187" => "Deny - new card issued.",
|
93
|
+
"188" => "Deny - Expiration date required.",
|
94
|
+
"189" => "Deny - Cancelled or Closed Merchant/SE.",
|
95
|
+
"200" => "Deny - Pick up card.",
|
96
|
+
"400" => "Reversal accepted.",
|
97
|
+
"601" => "Reject - EMV Chip Declined Transaction.",
|
98
|
+
"602" => "Reject - Suspected Fraud.",
|
99
|
+
"603" => "Reject - Communications Error.",
|
100
|
+
"604" => "Reject - Insufficient Approval.",
|
101
|
+
"750" => "Velocity Check Fail.",
|
102
|
+
"899" => "Misc Decline.",
|
103
|
+
"900" => "Invalid Message Type.",
|
104
|
+
"901" => "Invalid Merchant ID.",
|
105
|
+
"903" => "Debit not supported.",
|
106
|
+
"904" => "Private label not supported.",
|
107
|
+
"905" => "Invalid card type.",
|
108
|
+
"906" => "Unit not active.",
|
109
|
+
"908" => "Manual card entry invalid.",
|
110
|
+
"909" => "Invalid track information.",
|
111
|
+
"911" => "Master merchant not found.",
|
112
|
+
"912" => "Invalid card format.",
|
113
|
+
"913" => "Invalid card type.",
|
114
|
+
"914" => "Invalid card length.",
|
115
|
+
"917" => "Expired card.",
|
116
|
+
"919" => "Invalid entry type.",
|
117
|
+
"920" => "Invalid amount.",
|
118
|
+
"921" => "Invalid messge format.",
|
119
|
+
"923" => "Invalid ABA.",
|
120
|
+
"924" => "Invalid DDA.",
|
121
|
+
"925" => "Invalid TID.",
|
122
|
+
"926" => "Invalid Password.",
|
123
|
+
"930" => "Invalid zipcode.",
|
124
|
+
"931" => "Invalid Address.",
|
125
|
+
"932" => "Invalid ZIP and Address.",
|
126
|
+
"933" => "Invalid CVV2.",
|
127
|
+
"934" => "Program Not Allowed.",
|
128
|
+
"935" => "Invalid Device/App.",
|
129
|
+
"940" => "Record Not Found.",
|
130
|
+
"941" => "Merchant ID error.",
|
131
|
+
"942" => "Refund Not Allowed.",
|
132
|
+
"943" => "Refund denied.",
|
133
|
+
"955" => "Invalid PIN block.",
|
134
|
+
"956" => "Invalid KSN.",
|
135
|
+
"958" => "Bad Status.",
|
136
|
+
"959" => "Seek Record limit exceeded.",
|
137
|
+
"960" => "Internal Key Database Error.",
|
138
|
+
"961" => "TRANS not Supported. Cash Disbursement required a specific MCC.",
|
139
|
+
"962" => "Invalid PIN key (Unknown KSN).",
|
140
|
+
"981" => "Invalid AVS.",
|
141
|
+
"987" => "Issuer Unavailable.",
|
142
|
+
"988" => "System error SD.",
|
143
|
+
"989" => "Database Error.",
|
144
|
+
"992" => "Transaction Timeout.",
|
145
|
+
"996" => "Bad Terminal ID.",
|
146
|
+
"997" => "Message rejected by association.",
|
147
|
+
"999" => "Communication failure",
|
148
|
+
nil => "No response returned (missing credentials?)."
|
149
|
+
}
|
150
|
+
|
151
|
+
def initialize(options = {})
|
152
|
+
requires!(options, :login)
|
153
|
+
super
|
154
|
+
end
|
155
|
+
|
156
|
+
def purchase(money, credit_card, options = {})
|
157
|
+
commit(money, build_sale_request(money, credit_card, options))
|
158
|
+
end
|
159
|
+
|
160
|
+
def authorize(money, credit_card, options = {})
|
161
|
+
commit(money, build_authonly_request(money, credit_card, options))
|
162
|
+
end
|
163
|
+
|
164
|
+
def capture(money, reference, options = {})
|
165
|
+
split_authorization = reference.split(";")
|
166
|
+
transaction_id = split_authorization[0]
|
167
|
+
token = split_authorization[3]
|
168
|
+
commit(money, build_capture_request(transaction_id, money, options), token)
|
169
|
+
end
|
170
|
+
|
171
|
+
def void(reference, options = {})
|
172
|
+
transaction_id, approval, amount, token = reference.split(";")
|
173
|
+
commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options), token)
|
174
|
+
end
|
175
|
+
|
176
|
+
def credit(money, credit_card, options = {})
|
177
|
+
commit(money, build_credit_request(money, nil, credit_card, nil, options))
|
178
|
+
end
|
179
|
+
|
180
|
+
def refund(money, reference, options = {})
|
181
|
+
split_authorization = reference.split(";")
|
182
|
+
transaction_id = split_authorization[0]
|
183
|
+
token = split_authorization[3]
|
184
|
+
commit(money, build_credit_request(money, transaction_id, nil, token, options))
|
185
|
+
end
|
186
|
+
|
187
|
+
def verify(credit_card, options={})
|
188
|
+
authorize(0, credit_card, options)
|
189
|
+
end
|
190
|
+
|
191
|
+
def supports_scrubbing
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
def scrub(transcript)
|
196
|
+
transcript.
|
197
|
+
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
|
198
|
+
gsub(%r((>)\d+(</CardNum>)), '\1[FILTERED]\2').
|
199
|
+
gsub(%r((<CVV2>)\d+(</CVV2>)), '\1[FILTERED]\2')
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def build_xml_request(transaction_type, options = {}, transaction_id = nil, &block)
|
205
|
+
xml = Builder::XmlMarkup.new
|
206
|
+
xml.tag! 'JetPay', 'Version' => API_VERSION do
|
207
|
+
# Basic values needed for any request
|
208
|
+
xml.tag! 'TerminalID', @options[:login]
|
209
|
+
xml.tag! 'TransactionType', transaction_type
|
210
|
+
xml.tag! 'TransactionID', transaction_id.nil? ? generate_unique_id.slice(0, 18) : transaction_id
|
211
|
+
xml.tag! 'Origin', options[:origin] || 'INTERNET'
|
212
|
+
xml.tag! 'IndustryInfo', 'Type' => options[:industry_info] || 'ECOMMERCE'
|
213
|
+
xml.tag! 'Application', (options[:application] || 'n/a'), {'Version' => options[:application_version] || '1.0'}
|
214
|
+
xml.tag! 'Device', (options[:device] || 'n/a'), {'Version' => options[:device_version] || '1.0'}
|
215
|
+
xml.tag! 'Library', 'VirtPOS SDK', 'Version' => '1.5'
|
216
|
+
xml.tag! 'Gateway', 'JetPay'
|
217
|
+
xml.tag! 'DeveloperID', options[:developer_id] || 'n/a'
|
218
|
+
|
219
|
+
if block_given?
|
220
|
+
yield xml
|
221
|
+
else
|
222
|
+
xml.target!
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def build_sale_request(money, credit_card, options)
|
228
|
+
build_xml_request('SALE', options) do |xml|
|
229
|
+
add_credit_card(xml, credit_card)
|
230
|
+
add_addresses(xml, options)
|
231
|
+
add_customer_data(xml, options)
|
232
|
+
add_invoice_data(xml, options)
|
233
|
+
add_user_defined_fields(xml, options)
|
234
|
+
xml.tag! 'TotalAmount', amount(money)
|
235
|
+
|
236
|
+
xml.target!
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def build_authonly_request(money, credit_card, options)
|
241
|
+
build_xml_request('AUTHONLY', options) do |xml|
|
242
|
+
add_credit_card(xml, credit_card)
|
243
|
+
add_addresses(xml, options)
|
244
|
+
add_customer_data(xml, options)
|
245
|
+
add_invoice_data(xml, options)
|
246
|
+
add_user_defined_fields(xml, options)
|
247
|
+
xml.tag! 'TotalAmount', amount(money)
|
248
|
+
|
249
|
+
xml.target!
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def build_capture_request(transaction_id, money, options)
|
254
|
+
build_xml_request('CAPT', options, transaction_id) do |xml|
|
255
|
+
add_invoice_data(xml, options)
|
256
|
+
add_user_defined_fields(xml, options)
|
257
|
+
xml.tag! 'TotalAmount', amount(money)
|
258
|
+
|
259
|
+
xml.target!
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def build_void_request(money, transaction_id, approval, token, options)
|
264
|
+
build_xml_request('VOID', options, transaction_id) do |xml|
|
265
|
+
xml.tag! 'Approval', approval
|
266
|
+
xml.tag! 'TotalAmount', amount(money)
|
267
|
+
xml.tag! 'Token', token if token
|
268
|
+
|
269
|
+
xml.target!
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def build_credit_request(money, transaction_id, card, token, options)
|
274
|
+
build_xml_request('CREDIT', options, transaction_id) do |xml|
|
275
|
+
add_credit_card(xml, card) if card
|
276
|
+
add_invoice_data(xml, options)
|
277
|
+
add_addresses(xml, options)
|
278
|
+
add_customer_data(xml, options)
|
279
|
+
add_user_defined_fields(xml, options)
|
280
|
+
xml.tag! 'TotalAmount', amount(money)
|
281
|
+
xml.tag! 'Token', token if token
|
282
|
+
|
283
|
+
xml.target!
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def commit(money, request, token = nil)
|
288
|
+
response = parse(ssl_post(url, request))
|
289
|
+
|
290
|
+
success = success?(response)
|
291
|
+
Response.new(success,
|
292
|
+
success ? 'APPROVED' : message_from(response),
|
293
|
+
response,
|
294
|
+
:test => test?,
|
295
|
+
:authorization => authorization_from(response, money, token),
|
296
|
+
:avs_result => AVSResult.new(:code => response[:avs]),
|
297
|
+
:cvv_result => CVVResult.new(response[:cvv2]),
|
298
|
+
:error_code => success ? nil : error_code_from(response)
|
299
|
+
)
|
300
|
+
end
|
301
|
+
|
302
|
+
def url
|
303
|
+
test? ? test_url : live_url
|
304
|
+
end
|
305
|
+
|
306
|
+
def parse(body)
|
307
|
+
return {} if body.blank?
|
308
|
+
|
309
|
+
xml = REXML::Document.new(body)
|
310
|
+
|
311
|
+
response = {}
|
312
|
+
xml.root.elements.to_a.each do |node|
|
313
|
+
parse_element(response, node)
|
314
|
+
end
|
315
|
+
response
|
316
|
+
end
|
317
|
+
|
318
|
+
def parse_element(response, node)
|
319
|
+
if node.has_elements?
|
320
|
+
node.elements.each{|element| parse_element(response, element) }
|
321
|
+
else
|
322
|
+
response[node.name.underscore.to_sym] = node.text
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def format_exp(value)
|
327
|
+
format(value, :two_digits)
|
328
|
+
end
|
329
|
+
|
330
|
+
def success?(response)
|
331
|
+
response[:action_code] == "000"
|
332
|
+
end
|
333
|
+
|
334
|
+
def message_from(response)
|
335
|
+
ACTION_CODE_MESSAGES[response[:action_code]]
|
336
|
+
end
|
337
|
+
|
338
|
+
def authorization_from(response, money, previous_token)
|
339
|
+
original_amount = amount(money) if money
|
340
|
+
[ response[:transaction_id], response[:approval], original_amount, (response[:token] || previous_token)].join(";")
|
341
|
+
end
|
342
|
+
|
343
|
+
def error_code_from(response)
|
344
|
+
response[:action_code]
|
345
|
+
end
|
346
|
+
|
347
|
+
def add_credit_card(xml, credit_card)
|
348
|
+
xml.tag! 'CardNum', credit_card.number, "CardPresent" => false, "Tokenize" => true
|
349
|
+
xml.tag! 'CardExpMonth', format_exp(credit_card.month)
|
350
|
+
xml.tag! 'CardExpYear', format_exp(credit_card.year)
|
351
|
+
|
352
|
+
if credit_card.first_name || credit_card.last_name
|
353
|
+
xml.tag! 'CardName', [credit_card.first_name,credit_card.last_name].compact.join(' ')
|
354
|
+
end
|
355
|
+
|
356
|
+
unless credit_card.verification_value.nil? || (credit_card.verification_value.length == 0)
|
357
|
+
xml.tag! 'CVV2', credit_card.verification_value
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def add_addresses(xml, options)
|
362
|
+
if billing_address = options[:billing_address] || options[:address]
|
363
|
+
xml.tag! 'Billing' do
|
364
|
+
xml.tag! 'Address', [billing_address[:address1], billing_address[:address2]].compact.join(" ")
|
365
|
+
xml.tag! 'City', billing_address[:city]
|
366
|
+
xml.tag! 'StateProv', billing_address[:state]
|
367
|
+
xml.tag! 'PostalCode', billing_address[:zip]
|
368
|
+
xml.tag! 'Country', lookup_country_code(billing_address[:country])
|
369
|
+
xml.tag! 'Phone', billing_address[:phone]
|
370
|
+
xml.tag! 'Email', options[:email] if options[:email]
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
if shipping_address = options[:shipping_address]
|
375
|
+
xml.tag! 'Shipping' do
|
376
|
+
xml.tag! 'Name', shipping_address[:name]
|
377
|
+
xml.tag! 'Address', [shipping_address[:address1], shipping_address[:address2]].compact.join(" ")
|
378
|
+
xml.tag! 'City', shipping_address[:city]
|
379
|
+
xml.tag! 'StateProv', shipping_address[:state]
|
380
|
+
xml.tag! 'PostalCode', shipping_address[:zip]
|
381
|
+
xml.tag! 'Country', lookup_country_code(shipping_address[:country])
|
382
|
+
xml.tag! 'Phone', shipping_address[:phone]
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def add_customer_data(xml, options)
|
388
|
+
xml.tag! 'UserIPAddress', options[:ip] if options[:ip]
|
389
|
+
end
|
390
|
+
|
391
|
+
def add_invoice_data(xml, options)
|
392
|
+
xml.tag! 'OrderNumber', options[:order_id] if options[:order_id]
|
393
|
+
if tax_amount = options[:tax_amount]
|
394
|
+
xml.tag! 'TaxAmount', tax_amount, {'ExemptInd' => options[:tax_exemption] || "false"}
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def add_user_defined_fields(xml, options)
|
399
|
+
xml.tag! 'UDField1', options[:ud_field_1] if options[:ud_field_1]
|
400
|
+
xml.tag! 'UDField2', options[:ud_field_2] if options[:ud_field_2]
|
401
|
+
xml.tag! 'UDField3', options[:ud_field_3] if options[:ud_field_3]
|
402
|
+
end
|
403
|
+
|
404
|
+
def lookup_country_code(code)
|
405
|
+
country = Country.find(code) rescue nil
|
406
|
+
country && country.code(:alpha3)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|