activemerchant 1.66.0 → 1.67.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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[:cavv] if options[:xid]
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