activemerchant 1.58.0 → 1.59.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +54 -0
  3. data/README.md +3 -3
  4. data/lib/active_merchant/billing/check.rb +3 -0
  5. data/lib/active_merchant/billing/credit_card.rb +7 -2
  6. data/lib/active_merchant/billing/credit_card_methods.rb +5 -1
  7. data/lib/active_merchant/billing/gateway.rb +5 -3
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +3 -3
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +34 -5
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +348 -0
  12. data/lib/active_merchant/billing/gateways/braintree_blue.rb +6 -3
  13. data/lib/active_merchant/billing/gateways/card_stream.rb +33 -15
  14. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -0
  15. data/lib/active_merchant/billing/gateways/cyber_source.rb +7 -3
  16. data/lib/active_merchant/billing/gateways/global_collect.rb +293 -0
  17. data/lib/active_merchant/billing/gateways/jetpay.rb +11 -8
  18. data/lib/active_merchant/billing/gateways/latitude19.rb +416 -0
  19. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +13 -0
  20. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +10 -7
  21. data/lib/active_merchant/billing/gateways/mercury.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/metrics_global.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/moneris.rb +8 -1
  24. data/lib/active_merchant/billing/gateways/nmi.rb +25 -9
  25. data/lib/active_merchant/billing/gateways/openpay.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/orbital.rb +5 -3
  27. data/lib/active_merchant/billing/gateways/paymill.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -6
  29. data/lib/active_merchant/billing/gateways/payu_in.rb +3 -2
  30. data/lib/active_merchant/billing/gateways/s5.rb +8 -5
  31. data/lib/active_merchant/billing/gateways/sage.rb +1 -7
  32. data/lib/active_merchant/billing/gateways/sage_pay.rb +0 -4
  33. data/lib/active_merchant/billing/gateways/secure_net.rb +0 -5
  34. data/lib/active_merchant/billing/gateways/secure_pay.rb +1 -1
  35. data/lib/active_merchant/billing/gateways/securion_pay.rb +46 -17
  36. data/lib/active_merchant/billing/gateways/stripe.rb +5 -8
  37. data/lib/active_merchant/billing/gateways/tns.rb +1 -1
  38. data/lib/active_merchant/billing/gateways/trans_first.rb +1 -2
  39. data/lib/active_merchant/billing/gateways/vanco.rb +1 -1
  40. data/lib/active_merchant/billing/gateways/visanet_peru.rb +218 -0
  41. data/lib/active_merchant/billing/gateways/world_net.rb +344 -0
  42. data/lib/active_merchant/billing/gateways/worldpay.rb +8 -11
  43. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +4 -0
  44. data/lib/active_merchant/country.rb +0 -2
  45. data/lib/active_merchant/version.rb +1 -1
  46. metadata +7 -2
@@ -21,12 +21,11 @@ module ActiveMerchant #:nodoc:
21
21
  'unchecked' => 'P'
22
22
  }
23
23
 
24
- CURRENCIES_WITHOUT_FRACTIONS = %w(BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF)
25
-
26
24
  self.supported_countries = %w(AT AU BE CA CH DE DK ES FI FR GB IE IT LU NL NO SE US)
27
25
  self.default_currency = 'USD'
28
26
  self.money_format = :cents
29
27
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
28
+ self.currencies_without_fractions = %w(BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF)
30
29
 
31
30
  self.homepage_url = 'https://stripe.com/'
32
31
  self.display_name = 'Stripe'
@@ -341,7 +340,8 @@ module ActiveMerchant #:nodoc:
341
340
  card = {}
342
341
  if emv_payment?(creditcard)
343
342
  add_emv_creditcard(post, creditcard.icc_data)
344
- post[:card][:read_method] = "contactless" if creditcard.contactless
343
+ post[:card][:read_method] = "contactless" if creditcard.contactless_emv
344
+ post[:card][:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
345
345
  if creditcard.encrypted_pin_cryptogram.present? && creditcard.encrypted_pin_ksn.present?
346
346
  post[:card][:encrypted_pin] = creditcard.encrypted_pin_cryptogram
347
347
  post[:card][:encrypted_pin_key_id] = creditcard.encrypted_pin_ksn
@@ -350,7 +350,8 @@ module ActiveMerchant #:nodoc:
350
350
  if creditcard.respond_to?(:track_data) && creditcard.track_data.present?
351
351
  card[:swipe_data] = creditcard.track_data
352
352
  card[:fallback_reason] = creditcard.fallback_reason if creditcard.fallback_reason
353
- card[:read_method] = "contactless" if creditcard.contactless
353
+ card[:read_method] = "contactless" if creditcard.contactless_emv
354
+ post[:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
354
355
  else
355
356
  card[:number] = creditcard.number
356
357
  card[:exp_month] = creditcard.month
@@ -534,10 +535,6 @@ module ActiveMerchant #:nodoc:
534
535
  end
535
536
  end
536
537
 
537
- def non_fractional_currency?(currency)
538
- CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s)
539
- end
540
-
541
538
  def emv_payment?(payment)
542
539
  payment.respond_to?(:emv?) && payment.emv?
543
540
  end
@@ -122,7 +122,7 @@ module ActiveMerchant #:nodoc:
122
122
  billing[:phone] = billing_address[:phone]
123
123
 
124
124
  customer[:email] = options[:email] if options[:email]
125
- customer[:ipaddress] = options[:ip] if options[:ip]
125
+ customer[:ipAddress] = options[:ip] if options[:ip]
126
126
  end
127
127
 
128
128
  if(shipping_address = options[:shipping_address])
@@ -40,7 +40,7 @@ module ActiveMerchant #:nodoc:
40
40
  add_amount(post, money)
41
41
  add_payment(post, payment)
42
42
  add_address(post, options)
43
- add_invoice(post, options) if payment.is_a?(CreditCard)
43
+ add_invoice(post, options) if payment.credit_card?
44
44
  add_pair(post, :RefID, options[:order_id], required: true)
45
45
 
46
46
  commit((payment.is_a?(Check) ? :purchase_echeck : :purchase), post)
@@ -237,4 +237,3 @@ module ActiveMerchant #:nodoc:
237
237
  end
238
238
  end
239
239
  end
240
-
@@ -6,7 +6,7 @@ module ActiveMerchant
6
6
  include Empty
7
7
 
8
8
  self.test_url = 'https://www.vancodev.com/cgi-bin/wstest2.vps'
9
- self.live_url = 'https://www.vancoservices.com/cgi-bin/ws2.vps'
9
+ self.live_url = 'https://myvanco.vancopayments.com/cgi-bin/ws2.vps'
10
10
 
11
11
  self.supported_countries = ['US']
12
12
  self.default_currency = 'USD'
@@ -0,0 +1,218 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class VisanetPeruGateway < Gateway
4
+ include Empty
5
+ self.display_name = "VisaNet Peru Gateway"
6
+ self.homepage_url = "http://www.visanet.com.pe"
7
+
8
+ self.test_url = "https://devapi.vnforapps.com/api.tokenization/api/v2/merchant"
9
+ self.live_url = "https://api.vnforapps.com/api.tokenization/api/v2/merchant"
10
+
11
+ self.supported_countries = ["US", "PE"]
12
+ self.default_currency = "PEN"
13
+ self.money_format = :dollars
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
15
+
16
+ def initialize(options={})
17
+ requires!(options, :access_key_id, :secret_access_key, :merchant_id)
18
+ super
19
+ end
20
+
21
+ def purchase(amount, payment_method, options={})
22
+ MultiResponse.run() do |r|
23
+ r.process { authorize(amount, payment_method, options) }
24
+ r.process { capture(r.authorization, options) }
25
+ end
26
+ end
27
+
28
+ def authorize(amount, payment_method, options={})
29
+ params = {}
30
+
31
+ add_invoice(params, amount, options)
32
+ add_payment_method(params, payment_method)
33
+ add_antifraud_data(params, options)
34
+ params[:email] = options[:email] || 'unknown@email.com'
35
+
36
+ # No vaulting for now
37
+ params[:createAlias] = false
38
+
39
+ commit("authorize", params)
40
+ end
41
+
42
+ def capture(authorization, options={})
43
+ params = {}
44
+ _, purchase_number = split_authorization(authorization)
45
+ params[:purchaseNumber] = purchase_number
46
+ params[:externalTransactionId] = options[:order_id]
47
+ commit("deposit", params)
48
+ end
49
+
50
+ def void(authorization, options={})
51
+ params = {}
52
+ action, purchase_number = split_authorization(authorization)
53
+ params[:purchaseNumber] = purchase_number
54
+ params[:externalTransactionId] = options[:order_id]
55
+
56
+ case action
57
+ when "authorize"
58
+ commit("void", params)
59
+ when "deposit"
60
+ commit("cancelDeposit", params)
61
+ end
62
+ end
63
+
64
+ def verify(credit_card, options={})
65
+ MultiResponse.run(:use_first_response) do |r|
66
+ r.process { authorize(100, credit_card, options) }
67
+ r.process(:ignore_result) { void(r.authorization, options) }
68
+ end
69
+ end
70
+
71
+ def supports_scrubbing?
72
+ true
73
+ end
74
+
75
+ def scrub(transcript)
76
+ transcript.
77
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
78
+ gsub(%r((\"cardNumber\\\":\\\")\d+), '\1[FILTERED]').
79
+ gsub(%r((\"cvv2Code\\\":\\\")\d+), '\1[FILTERED]')
80
+ end
81
+
82
+ private
83
+
84
+ CURRENCY_CODES = Hash.new{|h,k| raise ArgumentError.new("Unsupported currency: #{k}")}
85
+ CURRENCY_CODES["USD"] = 840
86
+ CURRENCY_CODES["PEN"] = 604
87
+
88
+ def add_invoice(params, money, options)
89
+ # Visanet Peru expects a 9-digit numeric purchaseNumber
90
+ params[:purchaseNumber] = (SecureRandom.random_number(900_000_000) + 100_000_000).to_s
91
+ params[:externalTransactionId] = options[:order_id]
92
+ params[:amount] = amount(money)
93
+ params[:currencyId] = CURRENCY_CODES[options[:currency] || currency(money)]
94
+ end
95
+
96
+ def add_payment_method(params, payment_method)
97
+ params[:firstName] = payment_method.first_name
98
+ params[:lastName] = payment_method.last_name
99
+ params[:cardNumber] = payment_method.number
100
+ params[:cvv2Code] = payment_method.verification_value
101
+ params[:expirationYear] = format(payment_method.year, :four_digits)
102
+ params[:expirationMonth] = format(payment_method.month, :two_digits)
103
+ end
104
+
105
+ def add_antifraud_data(params, options)
106
+ antifraud = {}
107
+
108
+ if billing_address = options[:billing_address] || options[:address]
109
+ antifraud[:billTo_street1] = billing_address[:address1]
110
+ antifraud[:billTo_city] = billing_address[:city]
111
+ antifraud[:billTo_state] = billing_address[:state]
112
+ antifraud[:billTo_country] = billing_address[:country]
113
+ antifraud[:billTo_postalCode] = billing_address[:zip]
114
+ end
115
+
116
+ antifraud[:deviceFingerprintId] = options[:device_fingerprint_id] || SecureRandom.hex(16)
117
+ antifraud[:merchantDefineData] = options[:merchant_define_data] if options[:merchant_define_data]
118
+
119
+ params[:antifraud] = antifraud
120
+ end
121
+
122
+ def commit(action, params)
123
+ begin
124
+ raw_response = ssl_request(method(action), url(action, params), params.to_json, headers)
125
+ response = parse(raw_response)
126
+ rescue ResponseError => e
127
+ raw_response = e.response.body
128
+ response_error(raw_response)
129
+ rescue JSON::ParserError
130
+ unparsable_response(raw_response)
131
+ else
132
+ Response.new(
133
+ success_from(response),
134
+ message_from(response),
135
+ response,
136
+ :test => test?,
137
+ :authorization => authorization_from(action, params),
138
+ :error_code => response["errorCode"]
139
+ )
140
+ end
141
+ end
142
+
143
+ def headers
144
+ {
145
+ "Authorization" => "Basic " + Base64.strict_encode64("#{@options[:access_key_id]}:#{@options[:secret_access_key]}").strip,
146
+ "Content-Type" => "application/json"
147
+ }
148
+ end
149
+
150
+ def url(action, params)
151
+ if (action == "authorize")
152
+ url = base_url() + "/" + @options[:merchant_id]
153
+ else
154
+ url = base_url() + "/" + @options[:merchant_id] + "/" + action + "/" + params[:purchaseNumber]
155
+ end
156
+ end
157
+
158
+ def method(action)
159
+ if (action == "authorize")
160
+ method = :post
161
+ else
162
+ method = :put
163
+ end
164
+ end
165
+
166
+ def split_authorization(authorization)
167
+ authorization.split("|")
168
+ end
169
+
170
+ def authorization_from(action, params)
171
+ action + "|" + (params[:purchaseNumber] || '')
172
+ end
173
+
174
+ def base_url
175
+ test? ? test_url : live_url
176
+ end
177
+
178
+ def parse(body)
179
+ JSON.parse(body)
180
+ end
181
+
182
+ def success_from(response)
183
+ response["errorCode"] == 0
184
+ end
185
+
186
+ def message_from(response)
187
+ if empty?(response["errorMessage"]) || response["errorMessage"] == "[ ]"
188
+ response["data"]["DSC_COD_ACCION"]
189
+ else
190
+ response["errorMessage"]
191
+ end
192
+ end
193
+
194
+ def response_error(raw_response)
195
+ begin
196
+ response = parse(raw_response)
197
+ rescue JSON::ParserError
198
+ unparsable_response(raw_response)
199
+ else
200
+ return Response.new(
201
+ false,
202
+ message_from(response),
203
+ response,
204
+ :test => test?,
205
+ :authorization => response["transactionUUID"],
206
+ :error_code => response["errorCode"]
207
+ )
208
+ end
209
+ end
210
+
211
+ def unparsable_response(raw_response)
212
+ message = "Invalid JSON response received from VisanetPeruGateway. Please contact VisanetPeruGateway if you continue to receive this message."
213
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
214
+ return Response.new(false, message)
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,344 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # See https://helpdesk.worldnettps.com/support/solutions/articles/1000167298-integrator-guide
6
+ class WorldNetGateway < Gateway
7
+ self.test_url = 'https://testpayments.worldnettps.com/merchant/xmlpayment'
8
+ self.live_url = 'https://payments.worldnettps.com/merchant/xmlpayment'
9
+
10
+ self.homepage_url = 'http://worldnettps.com/'
11
+ self.display_name = 'WorldNet'
12
+
13
+ self.supported_countries = %w(IE GB US)
14
+ self.default_currency = 'EUR'
15
+
16
+ CARD_TYPES = {
17
+ visa: 'VISA',
18
+ master: 'MASTERCARD',
19
+ discover: 'DISCOVER',
20
+ american_express: 'AMEX',
21
+ maestro: 'MAESTRO',
22
+ diners_club: 'DINERS',
23
+ jcb: 'JCB',
24
+ secure_card: 'SECURECARD'
25
+ }.freeze
26
+ self.supported_cardtypes = CARD_TYPES.keys
27
+
28
+ def initialize(options = {})
29
+ requires!(options, :terminal_id, :secret)
30
+ options[:terminal_type] ||= 2 # eCommerce
31
+ super
32
+ end
33
+
34
+ def purchase(money, payment, options = {})
35
+ requires!(options, :order_id)
36
+
37
+ post = {}
38
+ add_invoice(post, money, options)
39
+ add_payment(post, payment)
40
+ add_address(post, payment, options)
41
+ add_customer_data(post, options)
42
+
43
+ commit('PAYMENT', post)
44
+ end
45
+
46
+ def authorize(money, payment, options = {})
47
+ requires!(options, :order_id)
48
+
49
+ post = {}
50
+ add_invoice(post, money, options)
51
+ add_payment(post, payment)
52
+ add_address(post, payment, options)
53
+ add_customer_data(post, options)
54
+
55
+ commit('PREAUTH', post)
56
+ end
57
+
58
+ def capture(money, authorization, options = {})
59
+ post = {}
60
+ add_invoice(post, money, options)
61
+ post[:uniqueref] = authorization
62
+
63
+ commit('PREAUTHCOMPLETION', post)
64
+ end
65
+
66
+ def refund(money, authorization, options = {})
67
+ requires!(options, :operator, :reason)
68
+
69
+ post = {}
70
+ post[:uniqueref] = authorization
71
+ add_invoice(post, money, options)
72
+ post[:operator] = options[:operator]
73
+ post[:reason] = options[:reason]
74
+
75
+ commit('REFUND', post)
76
+ end
77
+
78
+ def void(authorization, _options = {})
79
+ post = {}
80
+ post[:uniqueref] = authorization
81
+ commit('VOID', post)
82
+ end
83
+
84
+ def verify(credit_card, options = {})
85
+ MultiResponse.run(:use_first_response) do |r|
86
+ r.process { authorize(100, credit_card, options) }
87
+ r.process(:ignore_result) { void(r.authorization, options) }
88
+ end
89
+ end
90
+
91
+ def store(payment, options = {})
92
+ requires!(options, :order_id)
93
+
94
+ post = {}
95
+ post[:merchantref] = options[:order_id]
96
+ add_payment(post, payment)
97
+
98
+ commit('SECURECARDREGISTRATION', post)
99
+ end
100
+
101
+ def unstore(payment, options = {})
102
+ requires!(options, :order_id)
103
+
104
+ post = {}
105
+ post[:merchantref] = options[:order_id]
106
+ add_card_reference(post, payment)
107
+
108
+ commit('SECURECARDREMOVAL', post)
109
+ end
110
+
111
+ def supports_scrubbing?
112
+ true
113
+ end
114
+
115
+ def scrub(transcript)
116
+ transcript
117
+ .gsub(%r{(<CARDNUMBER>\d{6})\d+(\d{4}</CARDNUMBER>)}, '\1...\2')
118
+ .gsub(%r{(<CVV>)\d+(</CVV)}, '\1...\2')
119
+ end
120
+
121
+ private
122
+
123
+ def add_customer_data(post, options)
124
+ post[:email] = options[:email]
125
+ post[:ipaddress] = options[:ip]
126
+ end
127
+
128
+ def add_address(post, _creditcard, options)
129
+ address = options[:billing_address] || options[:address]
130
+ return unless address
131
+ post[:address1] = address[:address1]
132
+ post[:address2] = address[:address2]
133
+ post[:city] = address[:city]
134
+ post[:country] = address[:country] # ISO 3166-1-alpha-2 code.
135
+ post[:postcode] = address[:zip]
136
+ end
137
+
138
+ def add_invoice(post, money, options)
139
+ post[:orderid] = options[:order_id]
140
+ post[:amount] = amount(money)
141
+ post[:currency] = (options[:currency] || currency(money))
142
+ post[:description] = options[:description]
143
+ end
144
+
145
+ def add_payment(post, payment)
146
+ # a payment triggered with a secure_card (tokenised card) will not
147
+ # respond to `:number`
148
+ if payment.respond_to?(:number)
149
+ post[:cardholdername] = cardholdername(payment)
150
+ post[:cardtype] = CARD_TYPES[payment.brand.to_sym]
151
+ post[:cardnumber] = payment.number
152
+ post[:cvv] = payment.verification_value if payment.verification_value
153
+ post[:cardexpiry] = expdate(payment)
154
+ else
155
+ post[:cardtype] = CARD_TYPES[:secure_card]
156
+ post[:cardnumber] = payment
157
+ end
158
+ end
159
+
160
+ def add_card_reference(post, payment)
161
+ post[:cardreference] = payment
162
+ end
163
+
164
+ def cardholdername(payment)
165
+ [payment.first_name, payment.last_name].join(' ').slice(0, 60)
166
+ end
167
+
168
+ def parse(action, body)
169
+ results = {}
170
+ xml = Nokogiri::XML(body)
171
+ resp = xml.xpath("//#{action}RESPONSE | //ERROR")
172
+ resp.children.each do |element|
173
+ results[element.name.downcase.to_sym] = element.text
174
+ end
175
+ results
176
+ end
177
+
178
+ def commit(action, parameters)
179
+ url = (test? ? test_url : live_url)
180
+ response = parse(action, ssl_post(url, post_data(action, parameters)))
181
+
182
+ Response.new(
183
+ success_from(action, response),
184
+ message_from(response),
185
+ response,
186
+ authorization: authorization_from(action, response),
187
+ avs_result: AVSResult.new(code: response[:avs_response]),
188
+ cvv_result: CVVResult.new(response[:cvv_response]),
189
+ test: test?,
190
+ error_code: success_from(action, response) ? nil : message_to_standard_error_code_from(response)
191
+ )
192
+ end
193
+
194
+ def success_from(action, response)
195
+ case action
196
+ when 'SECURECARDREGISTRATION'
197
+ response[:cardreference].present?
198
+ when 'SECURECARDREMOVAL'
199
+ response[:datetime].present? && response[:hash].present?
200
+ else
201
+ response[:responsecode] == 'A'
202
+ end
203
+ end
204
+
205
+ def message_to_standard_error_code_from(response)
206
+ case message_from(response)
207
+ when /DECLINED/
208
+ STANDARD_ERROR_CODE[:card_declined]
209
+ when /CVV FAILURE/
210
+ STANDARD_ERROR_CODE[:incorrect_cvc]
211
+ when /Invalid CARDEXPIRY field/
212
+ STANDARD_ERROR_CODE[:invalid_expiry_date]
213
+ else
214
+ STANDARD_ERROR_CODE[:processing_error]
215
+ end
216
+ end
217
+
218
+ def message_from(response)
219
+ response[:responsetext] || response[:errorstring]
220
+ end
221
+
222
+ def authorization_from(action, response)
223
+ case action
224
+ when 'SECURECARDREGISTRATION'
225
+ response[:cardreference]
226
+ else
227
+ response[:uniqueref]
228
+ end
229
+ end
230
+
231
+ def post_data(action, parameters = {})
232
+ parameters[:terminalid] = @options[:terminal_id]
233
+ parameters[:terminaltype] = @options[:terminal_type]
234
+ parameters[:transactiontype] = 7 # eCommerce
235
+ parameters[:datetime] = create_time_stamp
236
+ parameters[:hash] = case action
237
+ when 'SECURECARDREGISTRATION'
238
+ build_store_signature(parameters)
239
+ when 'SECURECARDREMOVAL'
240
+ build_unstore_signature(parameters)
241
+ else
242
+ build_signature(parameters)
243
+ end
244
+ build_xml_request(action, fields(action), parameters)
245
+ end
246
+
247
+ def create_time_stamp
248
+ Time.now.gmtime.strftime('%d-%m-%Y:%H:%M:%S:%L')
249
+ end
250
+
251
+ def build_signature(parameters)
252
+ str = parameters[:terminalid]
253
+ str += (parameters[:uniqueref] || parameters[:orderid])
254
+ str += (parameters[:amount].to_s + parameters[:datetime])
255
+ Digest::MD5.hexdigest(str + @options[:secret])
256
+ end
257
+
258
+ def build_store_signature(parameters)
259
+ str = parameters[:terminalid]
260
+ str += parameters[:merchantref]
261
+ str += parameters[:datetime]
262
+ str += parameters[:cardnumber]
263
+ str += parameters[:cardexpiry]
264
+ str += parameters[:cardtype]
265
+ str += parameters[:cardholdername]
266
+ Digest::MD5.hexdigest(str + @options[:secret])
267
+ end
268
+
269
+ def build_unstore_signature(parameters)
270
+ str = parameters[:terminalid]
271
+ str += parameters[:merchantref]
272
+ str += parameters[:datetime]
273
+ str += parameters[:cardreference]
274
+ Digest::MD5.hexdigest(str + @options[:secret])
275
+ end
276
+
277
+ def fields(action)
278
+ # Gateway expects fields in fixed order below.
279
+ case action
280
+ when 'PAYMENT', 'PREAUTH'
281
+ [
282
+ :orderid,
283
+ :terminalid,
284
+ :amount,
285
+ :datetime,
286
+ :cardnumber, :cardtype, :cardexpiry, :cardholdername,
287
+ :hash,
288
+ :currency,
289
+ :terminaltype,
290
+ :transactiontype,
291
+ :email,
292
+ :cvv,
293
+ :address1, :address2,
294
+ :postcode,
295
+ :description,
296
+ :city, :country,
297
+ :ipaddress
298
+ ]
299
+ when 'PREAUTHCOMPLETION'
300
+ [:uniqueref, :terminalid, :amount, :datetime, :hash]
301
+ when 'REFUND'
302
+ [:uniqueref, :terminalid, :amount, :datetime, :hash,
303
+ :operator, :reason]
304
+ when 'VOID'
305
+ [:uniqueref]
306
+ when 'SECURECARDREGISTRATION'
307
+ [
308
+ :merchantref,
309
+ :terminalid,
310
+ :datetime,
311
+ :cardnumber, :cardexpiry, :cardtype, :cardholdername,
312
+ :hash,
313
+ :dontchecksecurity,
314
+ :cvv,
315
+ :issueno
316
+ ]
317
+ when 'SECURECARDREMOVAL'
318
+ [
319
+ :merchantref,
320
+ :cardreference,
321
+ :terminalid,
322
+ :datetime,
323
+ :hash
324
+ ]
325
+ end
326
+ end
327
+
328
+ def build_xml_request(action, fields, data)
329
+ xml = Builder::XmlMarkup.new indent: 2
330
+ xml.instruct!(:xml, version: '1.0', encoding: 'utf-8')
331
+ xml.tag!(action) do
332
+ fields.each do |field|
333
+ xml.tag!(field.to_s.upcase, data[field]) if data[field]
334
+ end
335
+ end
336
+ xml.target!
337
+ end
338
+
339
+ def expdate(credit_card)
340
+ sprintf('%02d%02d', credit_card.month, credit_card.year % 100)
341
+ end
342
+ end
343
+ end
344
+ end