activemerchant 1.54.0 → 1.55.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +51 -0
  3. data/CONTRIBUTORS +24 -0
  4. data/README.md +8 -2
  5. data/lib/active_merchant/billing/base.rb +11 -22
  6. data/lib/active_merchant/billing/credit_card.rb +24 -1
  7. data/lib/active_merchant/billing/gateway.rb +18 -8
  8. data/lib/active_merchant/billing/gateways/allied_wallet.rb +1 -1
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +12 -14
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +3 -2
  11. data/lib/active_merchant/billing/gateways/bogus.rb +63 -17
  12. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +1 -1
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +19 -10
  14. data/lib/active_merchant/billing/gateways/cams.rb +230 -0
  15. data/lib/active_merchant/billing/gateways/card_stream.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/clearhaus.rb +238 -0
  17. data/lib/active_merchant/billing/gateways/creditcall.rb +1 -1
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +1 -0
  19. data/lib/active_merchant/billing/gateways/elavon.rb +21 -18
  20. data/lib/active_merchant/billing/gateways/eway_rapid.rb +7 -7
  21. data/lib/active_merchant/billing/gateways/ezic.rb +2 -12
  22. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +15 -9
  23. data/lib/active_merchant/billing/gateways/forte.rb +32 -21
  24. data/lib/active_merchant/billing/gateways/inspire.rb +1 -1
  25. data/lib/active_merchant/billing/gateways/jetpay.rb +141 -38
  26. data/lib/active_merchant/billing/gateways/komoju.rb +115 -0
  27. data/lib/active_merchant/billing/gateways/litle.rb +7 -3
  28. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/micropayment.rb +18 -14
  30. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +1 -3
  31. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  32. data/lib/active_merchant/billing/gateways/netbilling.rb +10 -11
  33. data/lib/active_merchant/billing/gateways/omise.rb +2 -2
  34. data/lib/active_merchant/billing/gateways/optimal_payment.rb +3 -2
  35. data/lib/active_merchant/billing/gateways/orbital.rb +1 -0
  36. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +1 -1
  37. data/lib/active_merchant/billing/gateways/paybox_direct.rb +2 -1
  38. data/lib/active_merchant/billing/gateways/payeezy.rb +238 -0
  39. data/lib/active_merchant/billing/gateways/payflow.rb +16 -3
  40. data/lib/active_merchant/billing/gateways/paymill.rb +101 -44
  41. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +2 -2
  42. data/lib/active_merchant/billing/gateways/paypal_express.rb +8 -0
  43. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +1 -1
  44. data/lib/active_merchant/billing/gateways/sage_pay.rb +6 -14
  45. data/lib/active_merchant/billing/gateways/secure_net.rb +3 -4
  46. data/lib/active_merchant/billing/gateways/securion_pay.rb +238 -0
  47. data/lib/active_merchant/billing/gateways/stripe.rb +9 -7
  48. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +1 -2
  49. data/lib/active_merchant/billing/gateways/tns.rb +2 -8
  50. data/lib/active_merchant/billing/gateways/transact_pro.rb +224 -0
  51. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -2
  52. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +5 -7
  53. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +12 -11
  54. data/lib/active_merchant/billing/gateways/vanco.rb +8 -3
  55. data/lib/active_merchant/billing/gateways/viaklix.rb +1 -9
  56. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +22 -8
  57. data/lib/active_merchant/posts_data.rb +2 -2
  58. data/lib/active_merchant/version.rb +1 -1
  59. metadata +8 -2
@@ -0,0 +1,115 @@
1
+ require 'json'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class KomojuGateway < Gateway
6
+ self.test_url = "https://sandbox.komoju.com/api/v1"
7
+ self.live_url = "https://komoju.com/api/v1"
8
+ self.supported_countries = ['JP']
9
+ self.default_currency = 'JPY'
10
+ self.money_format = :cents
11
+ self.homepage_url = 'https://www.komoju.com/'
12
+ self.display_name = 'Komoju'
13
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb]
14
+
15
+ STANDARD_ERROR_CODE_MAPPING = {
16
+ "bad_verification_value" => "incorrect_cvc",
17
+ "card_expired" => "expired_card",
18
+ "card_declined" => "card_declined",
19
+ "invalid_number" => "invalid_number"
20
+ }
21
+
22
+ def initialize(options = {})
23
+ requires!(options, :login)
24
+ super
25
+ end
26
+
27
+ def purchase(money, payment, options = {})
28
+ post = {}
29
+ post[:amount] = amount(money)
30
+ post[:description] = options[:description]
31
+ add_payment_details(post, payment, options)
32
+ post[:currency] = options[:currency] || default_currency
33
+ post[:external_order_num] = options[:order_id] if options[:order_id]
34
+ post[:tax] = options[:tax] if options[:tax]
35
+ add_fraud_details(post, options)
36
+
37
+ commit("/payments", post)
38
+ end
39
+
40
+ def refund(money, identification, options = {})
41
+ commit("/payments/#{identification}/refund", {})
42
+ end
43
+
44
+ private
45
+
46
+ def add_payment_details(post, payment, options)
47
+ details = {}
48
+
49
+ details[:type] = 'credit_card'
50
+ details[:number] = payment.number
51
+ details[:month] = payment.month
52
+ details[:year] = payment.year
53
+ details[:verification_value] = payment.verification_value
54
+ details[:given_name] = payment.first_name
55
+ details[:family_name] = payment.last_name
56
+ details[:email] = options[:email] if options[:email]
57
+
58
+ post[:payment_details] = details
59
+ end
60
+
61
+ def add_fraud_details(post, options)
62
+ details = {}
63
+
64
+ details[:customer_ip] = options[:ip] if options[:ip]
65
+ details[:customer_email] = options[:email] if options[:email]
66
+ details[:browser_language] = options[:browser_language] if options[:browser_language]
67
+ details[:browser_user_agent] = options[:browser_user_agent] if options[:browser_user_agent]
68
+
69
+ post[:fraud_details] = details unless details.empty?
70
+ end
71
+
72
+ def api_request(path, data)
73
+ raw_response = nil
74
+ begin
75
+ raw_response = ssl_post("#{url}#{path}", data, headers)
76
+ rescue ResponseError => e
77
+ raw_response = e.response.body
78
+ end
79
+
80
+ JSON.parse(raw_response)
81
+ end
82
+
83
+ def commit(path, params)
84
+ response = api_request(path, params.to_json)
85
+ success = !response.key?("error")
86
+ message = (success ? "Transaction succeeded" : response["error"]["message"])
87
+ Response.new(
88
+ success,
89
+ message,
90
+ response,
91
+ test: test?,
92
+ error_code: (success ? nil : error_code(response["error"]["code"])),
93
+ authorization: (success ? response["id"] : nil)
94
+ )
95
+ end
96
+
97
+ def error_code(code)
98
+ STANDARD_ERROR_CODE_MAPPING[code] || code
99
+ end
100
+
101
+ def url
102
+ test? ? self.test_url : self.live_url
103
+ end
104
+
105
+ def headers
106
+ {
107
+ "Authorization" => "Basic " + Base64.encode64(@options[:login].to_s + ":").strip,
108
+ "Accept" => "application/json",
109
+ "Content-Type" => "application/json",
110
+ "User-Agent" => "Komoju/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}"
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -103,13 +103,17 @@ module ActiveMerchant #:nodoc:
103
103
  commit(void_type(kind), request)
104
104
  end
105
105
 
106
- def store(creditcard, options = {})
106
+ def store(payment_method, options = {})
107
107
  request = build_xml_request do |doc|
108
108
  add_authentication(doc)
109
109
  doc.registerTokenRequest(transaction_attributes(options)) do
110
110
  doc.orderId(truncate(options[:order_id], 24))
111
- doc.accountNumber(creditcard.number)
112
- doc.cardValidationNum(creditcard.verification_value) if creditcard.verification_value
111
+ if payment_method.is_a?(String)
112
+ doc.paypageRegistrationId(payment_method)
113
+ else
114
+ doc.accountNumber(payment_method.number)
115
+ doc.cardValidationNum(payment_method.verification_value) if payment_method.verification_value
116
+ end
113
117
  end
114
118
  end
115
119
 
@@ -20,7 +20,7 @@ module ActiveMerchant #:nodoc:
20
20
  :reference_purchase => 'RepeatSale',
21
21
  :authorize => "PreAuthorizationKeyed",
22
22
  :capture => "PostAuthorization",
23
- :void => "VoidPreAuthorization",
23
+ :void => "Void",
24
24
  :refund => "Refund"
25
25
  }
26
26
 
@@ -5,13 +5,14 @@ module ActiveMerchant #:nodoc:
5
5
  self.display_name = "micropayment"
6
6
  self.homepage_url = "https://www.micropayment.de/"
7
7
 
8
- self.test_url = self.live_url = "https://sipg.micropayment.de/public/creditcard/v1.5.2/nvp/"
8
+ self.test_url = self.live_url = "https://sipg.micropayment.de/public/creditcardpsp/v1/nvp/"
9
9
 
10
10
  self.supported_countries = %w(DE)
11
11
  self.default_currency = "EUR"
12
12
  self.money_format = :cents
13
13
  self.supported_cardtypes = [:visa, :master, :american_express]
14
14
 
15
+
15
16
  def initialize(options={})
16
17
  requires!(options, :access_key)
17
18
  super
@@ -20,40 +21,41 @@ module ActiveMerchant #:nodoc:
20
21
  def purchase(amount, payment_method, options={})
21
22
  post = {}
22
23
  add_invoice(post, amount, options)
23
- add_payment_method(post, payment_method)
24
+ add_payment_method(post, payment_method, options)
24
25
  add_customer_data(post, options)
25
- commit("shortTransactionPurchase", post)
26
+ commit("purchase", post)
26
27
  end
27
28
 
28
29
  def authorize(amount, payment_method, options={})
29
30
  post = {}
30
31
  add_invoice(post, amount, options)
31
- add_payment_method(post, payment_method)
32
+ add_payment_method(post, payment_method, options)
32
33
  add_customer_data(post, options)
33
- commit("shortTransactionAuthorization", post)
34
+ commit("authorize", post)
34
35
  end
35
36
 
36
37
  def capture(amount, authorization, options={})
37
38
  post = {}
38
39
  add_reference(post, authorization)
39
40
  add_invoice(post, amount, options)
40
- commit("transactionCapture", post)
41
+ commit("capture", post)
41
42
  end
42
43
 
43
44
  def void(authorization, options={})
44
45
  post = {}
45
46
  add_reference(post, authorization)
46
- commit("transactionReversal", post)
47
+ commit("void", post)
47
48
  end
48
49
 
49
50
  def refund(amount, authorization, options={})
50
51
  post = {}
51
52
  add_reference(post, authorization)
52
53
  add_invoice(post, amount, options)
53
- commit("transactionRefund", post)
54
+ commit("refund", post)
54
55
  end
55
56
 
56
57
  def verify(credit_card, options={})
58
+
57
59
  MultiResponse.run(:use_first_response) do |r|
58
60
  r.process { authorize(250, credit_card, options) }
59
61
  r.process(:ignore_result) { void(r.authorization, options) }
@@ -78,13 +80,14 @@ module ActiveMerchant #:nodoc:
78
80
  post[:amount] = amount(money)
79
81
  post[:currency] = options[:currency] || currency(money)
80
82
  end
81
- post[:project] = options[:project] || "sprdly"
83
+ post[:project] = options[:project] if options[:project]
82
84
  end
83
85
 
84
- def add_payment_method(post, payment_method)
86
+ def add_payment_method(post, payment_method, options={})
85
87
  post[:firstname] = payment_method.first_name
86
88
  post[:surname] = payment_method.last_name
87
89
  post[:number] = payment_method.number
90
+ post[:recurring] = 1 if options[:recurring] == true
88
91
  post[:cvc2] = payment_method.verification_value
89
92
  post[:expiryYear] = format(payment_method.year, :four_digits)
90
93
  post[:expiryMonth] = format(payment_method.month, :two_digits)
@@ -103,9 +106,9 @@ module ActiveMerchant #:nodoc:
103
106
  end
104
107
 
105
108
  def commit(action, params)
106
-
107
109
  params[:testMode] = 1 if test?
108
110
  params[:accessKey] = @options[:access_key]
111
+ params[:apiKey] = @options[:api_key] || "af1fd841af792f4c50131414ff76e004"
109
112
 
110
113
  response = parse(ssl_post(url(action), post_data(action, params), headers))
111
114
 
@@ -113,7 +116,7 @@ module ActiveMerchant #:nodoc:
113
116
  succeeded = success_from(response),
114
117
  message_from(succeeded, response),
115
118
  response,
116
- authorization: authorization_from(response),
119
+ authorization: authorization_from(response, params),
117
120
  avs_result: AVSResult.new(code: response["some_avs_result_key"]),
118
121
  cvv_result: CVVResult.new(response["some_cvv_result_key"]),
119
122
  test: test?
@@ -159,8 +162,9 @@ module ActiveMerchant #:nodoc:
159
162
  authorization.split("|")
160
163
  end
161
164
 
162
- def authorization_from(response)
163
- "#{response["sessionId"]}|#{response["transactionId"]}"
165
+ def authorization_from(response, request_params)
166
+ session_id = response["sessionId"] ? response["sessionId"] : request_params[:sessionId]
167
+ "#{session_id}|#{response["transactionId"]}"
164
168
  end
165
169
  end
166
170
  end
@@ -87,9 +87,7 @@ module ActiveMerchant #:nodoc:
87
87
  address = options[:billing_address] || options[:address] || {}
88
88
 
89
89
  if name = address[:name]
90
- segments = name.split(' ')
91
- post[:lastName] = segments.pop
92
- post[:firstName] = segments.join(' ')
90
+ post[:firstName], post[:lastName] = split_names(name)
93
91
  else
94
92
  post[:firstName] = address[:first_name]
95
93
  post[:lastName] = address[:last_name]
@@ -138,7 +138,7 @@ module ActiveMerchant #:nodoc:
138
138
  commit(request)
139
139
  end
140
140
 
141
- # Private: Execute operation that depends on athorization code from previous purchase or authorize operation
141
+ # Private: Execute operation that depends on authorization code from previous purchase or authorize operation
142
142
  def execute_dependant(action, money, authorization, options)
143
143
  request = build_request do |xml|
144
144
  add_identification_authorization(xml, authorization, options)
@@ -46,6 +46,7 @@ module ActiveMerchant #:nodoc:
46
46
  add_payment_source(post, payment_source)
47
47
  add_address(post, payment_source, options)
48
48
  add_customer_data(post, options)
49
+ add_user_data(post, options)
49
50
 
50
51
  commit(:authorization, post)
51
52
  end
@@ -57,6 +58,7 @@ module ActiveMerchant #:nodoc:
57
58
  add_payment_source(post, payment_source)
58
59
  add_address(post, payment_source, options)
59
60
  add_customer_data(post, options)
61
+ add_user_data(post, options)
60
62
 
61
63
  commit(:purchase, post)
62
64
  end
@@ -81,6 +83,7 @@ module ActiveMerchant #:nodoc:
81
83
  add_credit_card(post, credit_card)
82
84
  add_address(post, credit_card, options)
83
85
  add_customer_data(post, options)
86
+ add_user_data(post, options)
84
87
 
85
88
  commit(:credit, post)
86
89
  end
@@ -142,10 +145,7 @@ module ActiveMerchant #:nodoc:
142
145
  end
143
146
 
144
147
  if shipping_address = options[:shipping_address]
145
- first_name, last_name = parse_first_and_last_name(shipping_address[:name])
146
-
147
- post[:ship_name1] = first_name
148
- post[:ship_name2] = last_name
148
+ post[:ship_name1], post[:ship_name2] = split_names(shipping_address[:name])
149
149
  post[:ship_street] = shipping_address[:address1]
150
150
  post[:ship_zip] = shipping_address[:zip]
151
151
  post[:ship_city] = shipping_address[:city]
@@ -166,6 +166,12 @@ module ActiveMerchant #:nodoc:
166
166
  end
167
167
  end
168
168
 
169
+ def add_user_data(post, options)
170
+ if options[:order_id]
171
+ post[:user_data] = "order_id:#{options[:order_id]}"
172
+ end
173
+ end
174
+
169
175
  def add_transaction_id(post, transaction_id)
170
176
  post[:card_number] = 'CS:' + transaction_id
171
177
  end
@@ -222,13 +228,6 @@ module ActiveMerchant #:nodoc:
222
228
  parameters.reject{|k,v| v.blank?}.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
223
229
  end
224
230
 
225
- def parse_first_and_last_name(value)
226
- name = value.to_s.split(' ')
227
-
228
- last_name = name.pop || ''
229
- first_name = name.join(' ')
230
- [ first_name, last_name ]
231
- end
232
231
  end
233
232
  end
234
233
  end
@@ -15,7 +15,7 @@ module ActiveMerchant #:nodoc:
15
15
  self.live_url = self.test_url = API_URL
16
16
 
17
17
  # Currency supported by Omise
18
- # * Thai Baht with Satang, ie. 9000 => 90 THB
18
+ # * Thai Baht with Satang, i.e. 9000 => 90 THB
19
19
  self.default_currency = 'THB'
20
20
  self.money_format = :cents
21
21
 
@@ -145,7 +145,7 @@ module ActiveMerchant #:nodoc:
145
145
  commit(:delete, "customers/#{CGI.escape(customer_id)}")
146
146
  end
147
147
 
148
- # Enable scrubbling sensitive information
148
+ # Enable scrubbing sensitive information
149
149
  def supports_scrubbing?
150
150
  true
151
151
  end
@@ -283,8 +283,9 @@ module ActiveMerchant #:nodoc:
283
283
 
284
284
  def build_address(xml, addr)
285
285
  if addr[:name]
286
- xml.tag! 'firstName', addr[:name].split(' ').first
287
- xml.tag! 'lastName' , addr[:name].split(' ').last
286
+ first_name, last_name = split_names(addr[:name])
287
+ xml.tag! 'firstName', first_name
288
+ xml.tag! 'lastName' , last_name
288
289
  end
289
290
  xml.tag! 'street' , addr[:address1] if addr[:address1].present?
290
291
  xml.tag! 'street2', addr[:address2] if addr[:address2].present?
@@ -668,6 +668,7 @@ module ActiveMerchant #:nodoc:
668
668
  end
669
669
 
670
670
  def build_customer_request_xml(creditcard, options = {})
671
+ ActiveMerchant.deprecated "Customer Profile support in Orbital is non-conformant to the ActiveMerchant API and will be removed in its current form in a future version. Please contact the ActiveMerchant maintainers if you have an interest in modifying it to conform to the store/unstore/update API."
671
672
  xml = xml_envelope
672
673
  xml.tag! :Request do
673
674
  xml.tag! :Profile do
@@ -52,7 +52,7 @@ module ActiveMerchant #:nodoc:
52
52
  #
53
53
  # PayGateXML Field ActiveMerchant Use
54
54
  #
55
- # pgid use :login value to gateway instantation
55
+ # pgid use :login value to gateway instantiation
56
56
  # pwd use :password value to gateway instantiation
57
57
  #
58
58
  # cname credit_card.name
@@ -113,6 +113,7 @@ module ActiveMerchant #:nodoc:
113
113
  post = {}
114
114
  add_invoice(post, options)
115
115
  add_reference(post, identification)
116
+ add_amount(post, money, options)
116
117
  commit('refund', money, post)
117
118
  end
118
119
 
@@ -180,7 +181,7 @@ module ActiveMerchant #:nodoc:
180
181
  :dateq => Time.now.strftime('%d%m%Y%H%M%S'),
181
182
  :numquestion => unique_id(parameters[:order_id]),
182
183
  :site => @options[:login].to_s[0,7],
183
- :rang => @options[:login].to_s[7..-1],
184
+ :rang => @options[:rang] || @options[:login].to_s[7..-1],
184
185
  :cle => @options[:password],
185
186
  :pays => '',
186
187
  :archivage => parameters[:order_id]
@@ -0,0 +1,238 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ class PayeezyGateway < Gateway
4
+ class_attribute :integration_url
5
+
6
+ self.test_url = 'https://api-cert.payeezy.com/v1/transactions'
7
+ self.integration_url = 'https://api-cat.payeezy.com/v1/transactions'
8
+ self.live_url = 'https://api.payeezy.com/v1/transactions'
9
+
10
+ self.default_currency = 'USD'
11
+ self.money_format = :cents
12
+ self.supported_countries = %w(US CA)
13
+
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
15
+
16
+ self.homepage_url = 'https://developer.payeezy.com/'
17
+ self.display_name = 'Payeezy'
18
+
19
+ CREDIT_CARD_BRAND = {
20
+ 'visa' => 'Visa',
21
+ 'master' => 'Mastercard',
22
+ 'american_express' => 'American Express',
23
+ 'discover' => 'Discover',
24
+ 'jcb' => 'JCB',
25
+ 'diners_club' => 'Diners Club'
26
+ }
27
+
28
+ def initialize(options = {})
29
+ requires!(options, :apikey, :apisecret, :token)
30
+ super
31
+ end
32
+
33
+ def purchase(amount, creditcard, options = {})
34
+ params = {transaction_type: 'purchase'}
35
+
36
+ add_invoice(params, options)
37
+ add_creditcard(params, creditcard)
38
+ add_address(params, options)
39
+ add_amount(params, amount, options)
40
+
41
+ commit(params, options)
42
+ end
43
+
44
+ def authorize(amount, creditcard, options = {})
45
+ params = {transaction_type: 'authorize'}
46
+
47
+ add_invoice(params, options)
48
+ add_creditcard(params, creditcard)
49
+ add_address(params, options)
50
+ add_amount(params, amount, options)
51
+
52
+ commit(params, options)
53
+ end
54
+
55
+ def capture(amount, authorization, options = {})
56
+ params = {transaction_type: 'capture'}
57
+
58
+ add_authorization_info(params, authorization)
59
+ add_amount(params, amount, options)
60
+
61
+ commit(params, options)
62
+ end
63
+
64
+ def refund(amount, authorization, options = {})
65
+ params = {transaction_type: 'refund'}
66
+
67
+ add_authorization_info(params, authorization)
68
+ add_amount(params, (amount || amount_from_authorization(authorization)), options)
69
+
70
+ commit(params, options)
71
+ end
72
+
73
+ def void(authorization, options = {})
74
+ params = {transaction_type: 'refund'}
75
+
76
+ add_authorization_info(params, authorization)
77
+ add_amount(params, amount_from_authorization(authorization), options)
78
+
79
+ commit(params, options)
80
+ end
81
+
82
+ private
83
+
84
+ def add_invoice(params, options)
85
+ params[:merchant_ref] = options[:order_id]
86
+ end
87
+
88
+ def amount_from_authorization(authorization)
89
+ authorization.split('|').last.to_i
90
+ end
91
+
92
+ def add_authorization_info(params, authorization)
93
+ transaction_id, transaction_tag, method, _ = authorization.split('|')
94
+ params[:transaction_id] = transaction_id
95
+ params[:transaction_tag] = transaction_tag
96
+ params[:method] = method
97
+ end
98
+
99
+ def add_creditcard(params, creditcard)
100
+ credit_card = {}
101
+
102
+ credit_card[:type] = CREDIT_CARD_BRAND[creditcard.brand]
103
+ credit_card[:cardholder_name] = creditcard.name
104
+ credit_card[:card_number] = creditcard.number
105
+ credit_card[:exp_date] = "#{format(creditcard.month, :two_digits)}#{format(creditcard.year, :two_digits)}"
106
+ credit_card[:cvv] = creditcard.verification_value if creditcard.verification_value?
107
+
108
+ params[:method] = 'credit_card'
109
+ params[:credit_card] = credit_card
110
+ end
111
+
112
+ def add_address(params, options)
113
+ address = options[:billing_address]
114
+ return unless address
115
+
116
+ billing_address = {}
117
+ billing_address[:street] = address[:address1] if address[:address1]
118
+ billing_address[:city] = address[:city] if address[:city]
119
+ billing_address[:state_province] = address[:state] if address[:state]
120
+ billing_address[:zip_postal_code] = address[:zip] if address[:zip]
121
+ billing_address[:country] = address[:country] if address[:country]
122
+
123
+ params[:billing_address] = billing_address
124
+ end
125
+
126
+ def add_amount(params, money, options)
127
+ params[:currency_code] = (options[:currency] || default_currency).upcase
128
+ params[:amount] = amount(money)
129
+ end
130
+
131
+ def commit(params, options)
132
+ url = if options[:integration]
133
+ integration_url
134
+ elsif test?
135
+ test_url
136
+ else
137
+ live_url
138
+ end
139
+
140
+ if transaction_id = params.delete(:transaction_id)
141
+ url = "#{url}/#{transaction_id}"
142
+ end
143
+
144
+ success = false
145
+ begin
146
+ body = params.to_json
147
+ raw_response = ssl_post(url, body, headers(body))
148
+ response = parse(raw_response)
149
+ success = (response['transaction_status'] == 'approved')
150
+ rescue ResponseError => e
151
+ response = response_error(e.response.body)
152
+ rescue JSON::ParserError
153
+ response = json_error(raw_response)
154
+ end
155
+
156
+ Response.new(
157
+ success,
158
+ handle_message(response, success),
159
+ response,
160
+ test: test?,
161
+ authorization: authorization_from(params, response),
162
+ avs_result: {code: response['avs']},
163
+ cvv_result: response['cvv2'],
164
+ error_code: error_code(response, success)
165
+ )
166
+ end
167
+
168
+ def authorization_from(params, response)
169
+ [
170
+ response['transaction_id'],
171
+ response['transaction_tag'],
172
+ params[:method],
173
+ (response['amount'] && response['amount'].to_i)
174
+ ].join('|')
175
+ end
176
+
177
+ def generate_hmac(nonce, current_timestamp, payload)
178
+ message = [
179
+ @options[:apikey],
180
+ nonce.to_s,
181
+ current_timestamp.to_s,
182
+ @options[:token],
183
+ payload
184
+ ].join("")
185
+ hash = Base64.strict_encode64(OpenSSL::HMAC.hexdigest('sha256', @options[:apisecret], message))
186
+ hash
187
+ end
188
+
189
+ def headers(payload)
190
+ nonce = (SecureRandom.random_number * 10_000_000_000)
191
+ current_timestamp = (Time.now.to_f * 1000).to_i
192
+ {
193
+ 'Content-Type' => 'application/json',
194
+ 'apikey' => options[:apikey],
195
+ 'token' => options[:token],
196
+ 'nonce' => nonce.to_s,
197
+ 'timestamp' => current_timestamp.to_s,
198
+ 'Authorization' => generate_hmac(nonce, current_timestamp, payload)
199
+ }
200
+ end
201
+
202
+ def error_code(response, success)
203
+ return if success
204
+ response['Error'].to_h['messages'].to_a.map { |e| e['code'] }.join(', ')
205
+ end
206
+
207
+ def handle_message(response, success)
208
+ if success
209
+ "#{response['gateway_message']} - #{response['bank_message']}"
210
+ elsif %w(401 403).include?(response['code'])
211
+ response['message']
212
+ elsif response.key?('Error')
213
+ response['Error']['messages'].first['description']
214
+ elsif response.key?('error')
215
+ response['error']
216
+ elsif response.key?('fault')
217
+ response['fault'].to_h['faultstring']
218
+ else
219
+ response['bank_message']
220
+ end
221
+ end
222
+
223
+ def parse(body)
224
+ JSON.parse(body)
225
+ end
226
+
227
+ def response_error(raw_response)
228
+ parse(raw_response)
229
+ rescue JSON::ParserError
230
+ json_error(raw_response)
231
+ end
232
+
233
+ def json_error(raw_response)
234
+ {"error" => "Unable to parse response: #{raw_response.inspect}"}
235
+ end
236
+ end
237
+ end
238
+ end