activemerchant 1.63.0 → 1.64.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -46,7 +46,7 @@ module ActiveMerchant #:nodoc:
46
46
 
47
47
  def refund(money, authorization, options={})
48
48
  post = nestable_hash
49
- add_amount(post, money, options={})
49
+ add_amount(post, money, options)
50
50
  add_refund_customer_data(post, options)
51
51
  commit(:refund, post, authorization)
52
52
  end
@@ -99,7 +99,7 @@ module ActiveMerchant #:nodoc:
99
99
  }
100
100
  end
101
101
 
102
- def add_amount(post, money, options)
102
+ def add_amount(post, money, options={})
103
103
  post["amountOfMoney"] = {
104
104
  "amount" => amount(money),
105
105
  "currencyCode" => options[:currency] || currency(money)
@@ -0,0 +1,217 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class KushkiGateway < Gateway
4
+ self.display_name = "Kushki"
5
+ self.homepage_url = "https://www.kushkipagos.com"
6
+
7
+ self.test_url = "https://api-uat.kushkipagos.com/v1/"
8
+ self.live_url = "https://api.kushkipagos.com/v1/"
9
+
10
+ self.supported_countries = ["CO", "EC"]
11
+ self.default_currency = "USD"
12
+ self.money_format = :dollars
13
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club]
14
+
15
+ def initialize(options={})
16
+ requires!(options, :public_merchant_id, :private_merchant_id)
17
+ super
18
+ end
19
+
20
+ def purchase(amount, payment_method, options={})
21
+ MultiResponse.run() do |r|
22
+ r.process { tokenize(amount, payment_method, options) }
23
+ r.process { charge(amount, r.authorization, options) }
24
+ end
25
+ end
26
+
27
+ def void(authorization, options={})
28
+ action = "void"
29
+
30
+ post = {}
31
+ post[:ticketNumber] = authorization
32
+
33
+ commit(action, post)
34
+ end
35
+
36
+ def supports_scrubbing?
37
+ true
38
+ end
39
+
40
+ def scrub(transcript)
41
+ transcript.
42
+ gsub(%r((Private-Merchant-Id: )\d+), '\1[FILTERED]').
43
+ gsub(%r((\"card\\\":{\\\"number\\\":\\\")\d+), '\1[FILTERED]').
44
+ gsub(%r((\"cvv\\\":\\\")\d+), '\1[FILTERED]')
45
+ end
46
+
47
+ private
48
+
49
+ def tokenize(amount, payment_method, options)
50
+ action = "tokenize"
51
+
52
+ post = {}
53
+ add_invoice(action, post, amount, options)
54
+ add_payment_method(post, payment_method, options)
55
+
56
+ commit(action, post)
57
+ end
58
+
59
+ def charge(amount, authorization, options)
60
+ action = "charge"
61
+
62
+ post = {}
63
+ add_reference(post, authorization, options)
64
+ add_invoice(action, post, amount, options)
65
+
66
+ commit(action, post)
67
+ end
68
+
69
+ def add_invoice(action, post, money, options)
70
+ if action == "tokenize"
71
+ post[:totalAmount] = amount(money).to_f
72
+ post[:currency] = options[:currency] || currency(money)
73
+ post[:isDeferred] = false
74
+ else
75
+ sum = {}
76
+ sum[:currency] = options[:currency] || currency(money)
77
+ add_amount_defaults(sum, money, options)
78
+ add_amount_by_country(sum, options)
79
+ post[:amount] = sum
80
+ end
81
+ end
82
+
83
+ def add_amount_defaults(sum, money, options)
84
+ sum[:subtotalIva] = amount(money).to_f
85
+ sum[:iva] = 0
86
+ sum[:subtotalIva0] = 0
87
+
88
+ if sum[:currency] == "COP"
89
+ extra_taxes = {}
90
+ extra_taxes[:propina] = 0
91
+ extra_taxes[:tasaAeroportuaria] = 0
92
+ extra_taxes[:agenciaDeViaje] = 0
93
+ extra_taxes[:iac] = 0
94
+ sum[:extraTaxes] = extra_taxes
95
+ else
96
+ sum[:ice] = 0
97
+ end
98
+ end
99
+
100
+ def add_amount_by_country(sum, options)
101
+ if amount = options[:amount]
102
+ sum[:subtotalIva] = amount[:subtotal_iva].to_f if amount[:subtotal_iva]
103
+ sum[:iva] = amount[:iva].to_f if amount[:iva]
104
+ sum[:subtotalIva0] = amount[:subtotal_iva_0].to_f if amount[:subtotal_iva_0]
105
+ sum[:ice] = amount[:ice].to_f if amount[:ice]
106
+ if extra_taxes = amount[:extra_taxes] && sum[:currency] == "COP"
107
+ sum[:extraTaxes][:propina] = extra_taxes[:propina].to_f if extra_taxes[:propina]
108
+ sum[:extraTaxes][:tasaAeroportuaria] = extra_taxes[:tasa_aeroportuaria].to_f if extra_taxes[:tasa_aeroportuaria]
109
+ sum[:extraTaxes][:agenciaDeViaje] = extra_taxes[:agencia_de_viaje].to_f if extra_taxes[:agencia_de_viaje]
110
+ sum[:extraTaxes][:iac] = extra_taxes[:iac].to_f if extra_taxes[:iac]
111
+ end
112
+ end
113
+ end
114
+
115
+ def add_payment_method(post, payment_method, options)
116
+ card = {}
117
+ card[:number] = payment_method.number
118
+ card[:cvv] = payment_method.verification_value
119
+ card[:expiryMonth] = format(payment_method.month, :two_digits)
120
+ card[:expiryYear] = format(payment_method.year, :two_digits)
121
+ card[:name] = payment_method.name
122
+ post[:card] = card
123
+ end
124
+
125
+ def add_reference(post, authorization, options)
126
+ post[:token] = authorization
127
+ end
128
+
129
+ ENDPOINT = {
130
+ "tokenize" => "tokens",
131
+ "charge" => "charges",
132
+ "void" => "charges"
133
+ }
134
+
135
+ def commit(action, params)
136
+ response = begin
137
+ parse(ssl_invoke(action, params))
138
+ rescue ResponseError => e
139
+ parse(e.response.body)
140
+ end
141
+
142
+ success = success_from(response)
143
+
144
+ Response.new(
145
+ success,
146
+ message_from(success, response),
147
+ response,
148
+ authorization: success ? authorization_from(response) : nil,
149
+ error_code: success ? nil : error_from(response),
150
+ test: test?
151
+ )
152
+ end
153
+
154
+ def ssl_invoke(action, params)
155
+ if action == "void"
156
+ ssl_request(:delete, url(action, params), nil, headers(action))
157
+ else
158
+ ssl_post(url(action, params), post_data(params), headers(action))
159
+ end
160
+ end
161
+
162
+ def headers(action)
163
+ hfields = {}
164
+ hfields["Public-Merchant-Id"] = @options[:public_merchant_id] if action == "tokenize"
165
+ hfields["Private-Merchant-Id"] = @options[:private_merchant_id] unless action == "tokenize"
166
+ hfields["Content-Type"] = "application/json"
167
+ hfields
168
+ end
169
+
170
+ def post_data(params)
171
+ params.to_json
172
+ end
173
+
174
+ def url(action, params)
175
+ base_url = test? ? test_url : live_url
176
+
177
+ if action == "void"
178
+ base_url + ENDPOINT[action] + "/" + params[:ticketNumber]
179
+ else
180
+ base_url + ENDPOINT[action]
181
+ end
182
+ end
183
+
184
+ def parse(body)
185
+ begin
186
+ JSON.parse(body)
187
+ rescue JSON::ParserError
188
+ message = "Invalid JSON response received from KushkiGateway. Please contact KushkiGateway if you continue to receive this message."
189
+ message += " (The raw response returned by the API was #{body.inspect})"
190
+ {
191
+ "message" => message
192
+ }
193
+ end
194
+ end
195
+
196
+ def success_from(response)
197
+ return true if response["token"] || response["ticketNumber"]
198
+ end
199
+
200
+ def message_from(succeeded, response)
201
+ if succeeded
202
+ "Succeeded"
203
+ else
204
+ response["message"]
205
+ end
206
+ end
207
+
208
+ def authorization_from(response)
209
+ response["token"] || response["ticketNumber"]
210
+ end
211
+
212
+ def error_from(response)
213
+ response["code"]
214
+ end
215
+ end
216
+ end
217
+ end
@@ -143,9 +143,9 @@ module ActiveMerchant #:nodoc:
143
143
  :pem => LinkpointGateway.pem_file
144
144
  }.update(options)
145
145
 
146
- @options[:pem].strip!
147
-
148
146
  raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @options[:pem].blank?
147
+
148
+ @options[:pem].strip!
149
149
  end
150
150
 
151
151
  # Send a purchase request with periodic options
@@ -14,18 +14,20 @@ module ActiveMerchant #:nodoc:
14
14
  self.live_url = self.test_url = API_URL
15
15
 
16
16
  # Currency supported by Omise
17
- # * Thai Baht with Satang, i.e. 9000 => 90 THB
17
+ # * Thai Baht with Satang, 50000 (THB500.00)
18
+ # * Japanese Yen, 500 (JPY500)
18
19
  self.default_currency = 'THB'
19
20
  self.money_format = :cents
20
21
 
21
22
  #Country supported by Omise
22
23
  # * Thailand
23
- self.supported_countries = %w( TH )
24
+ self.supported_countries = %w( TH JP )
24
25
 
25
26
  # Credit cards supported by Omise
26
27
  # * VISA
27
28
  # * MasterCard
28
- self.supported_cardtypes = [:visa, :master]
29
+ # * JCB
30
+ self.supported_cardtypes = [:visa, :master, :jcb]
29
31
 
30
32
  # Omise main page
31
33
  self.homepage_url = 'https://www.omise.co/'
@@ -39,8 +41,10 @@ module ActiveMerchant #:nodoc:
39
41
  #
40
42
  # ==== Options
41
43
  #
42
- # * <tt>:public_key</tt> -- Omise's public key (REQUIRED).
43
- # * <tt>:secret_key</tt> -- Omise's secret key (REQUIRED).
44
+ # * <tt>:public_key</tt> -- Omise's public key (REQUIRED).
45
+ # * <tt>:secret_key</tt> -- Omise's secret key (REQUIRED).
46
+ # * <tt>:api_version</tt> -- Omise's API Version (OPTIONAL), default version is '2014-07-27'
47
+ # See version at page https://dashboard.omise.co/api-version/edit
44
48
 
45
49
  def initialize(options={})
46
50
  requires!(options, :public_key, :secret_key)
@@ -17,7 +17,7 @@ module ActiveMerchant #:nodoc:
17
17
  super
18
18
  end
19
19
 
20
- def purchase(money, payment_method, options = {})
20
+ def purchase(money, payment_method, options={})
21
21
  action_with_token(:purchase, money, payment_method, options)
22
22
  end
23
23
 
@@ -48,7 +48,7 @@ module ActiveMerchant #:nodoc:
48
48
  end
49
49
 
50
50
  def store(credit_card, options={})
51
- save_card(credit_card)
51
+ save_card(credit_card, options)
52
52
  end
53
53
 
54
54
  def supports_scrubbing
@@ -74,11 +74,15 @@ module ActiveMerchant #:nodoc:
74
74
 
75
75
  private
76
76
 
77
- def add_credit_card(post, credit_card)
77
+ def add_credit_card(post, credit_card, options)
78
+ post['account.holder'] = (credit_card.try(:name) || "")
78
79
  post['account.number'] = credit_card.number
79
80
  post['account.expiry.month'] = sprintf("%.2i", credit_card.month)
80
81
  post['account.expiry.year'] = sprintf("%.4i", credit_card.year)
81
82
  post['account.verification'] = credit_card.verification_value
83
+ post['account.email'] = (options[:email] || nil)
84
+ post['presentation.amount3D'] = (options[:money] || nil)
85
+ post['presentation.currency3D'] = (options[:currency] || currency( options[:money]))
82
86
  end
83
87
 
84
88
  def headers
@@ -122,12 +126,13 @@ module ActiveMerchant #:nodoc:
122
126
  end
123
127
 
124
128
  def action_with_token(action, money, payment_method, options)
129
+ options[:money] = money
125
130
  case payment_method
126
- when String
127
- self.send("#{action}_with_token", money, payment_method, options)
128
- else
129
- MultiResponse.run do |r|
130
- r.process { save_card(payment_method) }
131
+ when String
132
+ self.send("#{action}_with_token", money, payment_method, options)
133
+ else
134
+ MultiResponse.run do |r|
135
+ r.process { save_card(payment_method, options) }
131
136
  r.process { self.send("#{action}_with_token", money, r.authorization, options) }
132
137
  end
133
138
  end
@@ -153,10 +158,10 @@ module ActiveMerchant #:nodoc:
153
158
  commit(:post, 'preauthorizations', post)
154
159
  end
155
160
 
156
- def save_card(credit_card)
161
+ def save_card(credit_card, options)
157
162
  post = {}
158
163
 
159
- add_credit_card(post, credit_card)
164
+ add_credit_card(post, credit_card, options)
160
165
  post['channel.id'] = @options[:public_key]
161
166
  post['jsonPFunction'] = 'jsonPFunction'
162
167
  post['transaction.mode'] = (test? ? 'CONNECTOR_TEST' : 'LIVE')
@@ -219,6 +224,7 @@ module ActiveMerchant #:nodoc:
219
224
  20203 => "Reversed due to complaint by buyer",
220
225
  20204 => "Payment has been refunded",
221
226
  20300 => "Reversal has been canceled",
227
+ 22000 => "Initiation of transaction successful",
222
228
 
223
229
  30000 => "Transaction still in progress",
224
230
  30100 => "Transaction has been accepted",
@@ -258,6 +264,8 @@ module ActiveMerchant #:nodoc:
258
264
  40420 => "Problem with address data",
259
265
  40500 => "Permission error with acquirer API",
260
266
  40510 => "Rate limit reached for acquirer API",
267
+ 42000 => "Initiation of transaction failed",
268
+ 42410 => "Initiation of transaction expired",
261
269
 
262
270
  50000 => "Problem with back end",
263
271
  50001 => "Country blacklisted",
@@ -65,7 +65,7 @@ module ActiveMerchant #:nodoc:
65
65
  commit('void', post)
66
66
  end
67
67
 
68
- def refund(authorization, options={})
68
+ def refund(amount, authorization, options={})
69
69
  post = {}
70
70
 
71
71
  add_credentials(post, 'SUBMIT_TRANSACTION')
@@ -321,6 +321,8 @@ module ActiveMerchant #:nodoc:
321
321
  response["code"] == "SUCCESS" && response["creditCardToken"] && response["creditCardToken"]["creditCardTokenId"].present?
322
322
  when 'verify_credentials'
323
323
  response["code"] == "SUCCESS"
324
+ when 'refund'
325
+ response["code"] == "SUCCESS" && response["transactionResponse"] && (response["transactionResponse"]["state"] == "PENDING" || response["transactionResponse"]["state"] == "APPROVED")
324
326
  else
325
327
  response["code"] == "SUCCESS" && response["transactionResponse"] && (response["transactionResponse"]["state"] == "APPROVED")
326
328
  end
@@ -1,6 +1,8 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class SageGateway < Gateway
4
+ include Empty
5
+
4
6
  self.display_name = 'http://www.sagepayments.com'
5
7
  self.homepage_url = 'Sage Payment Solutions'
6
8
  self.live_url = 'https://www.sagepayments.net/cgi-bin'
@@ -204,8 +206,8 @@ module ActiveMerchant #:nodoc:
204
206
 
205
207
  def add_invoice(post, options)
206
208
  post[:T_ordernum] = (options[:order_id] || generate_unique_id).slice(0, 20)
207
- post[:T_tax] = amount(options[:tax]) unless options[:tax].blank?
208
- post[:T_shipping] = amount(options[:shipping]) unless options[:shipping].blank?
209
+ post[:T_tax] = amount(options[:tax]) unless empty?(options[:tax])
210
+ post[:T_shipping] = amount(options[:shipping]) unless empty?(options[:shipping])
209
211
  end
210
212
 
211
213
  def add_reference(post, reference)
@@ -226,7 +228,7 @@ module ActiveMerchant #:nodoc:
226
228
 
227
229
  post[:C_address] = billing_address[:address1]
228
230
  post[:C_city] = billing_address[:city]
229
- post[:C_state] = billing_address[:state]
231
+ post[:C_state] = empty?(billing_address[:state]) ? "Outside of US" : billing_address[:state]
230
232
  post[:C_zip] = billing_address[:zip]
231
233
  post[:C_country] = billing_address[:country]
232
234
  post[:C_telephone] = billing_address[:phone]
@@ -71,7 +71,6 @@ module ActiveMerchant #:nodoc:
71
71
  requires!(options, :login)
72
72
  @api_key = options[:login]
73
73
  @fee_refund_api_key = options[:fee_refund_login]
74
-
75
74
  super
76
75
  end
77
76
 
@@ -159,6 +158,7 @@ module ActiveMerchant #:nodoc:
159
158
  def verify(payment, options = {})
160
159
  MultiResponse.run(:use_first_response) do |r|
161
160
  r.process { authorize(auth_minimum_amount(options), payment, options) }
161
+ options[:idempotency_key] = nil
162
162
  r.process(:ignore_result) { void(r.authorization, options) }
163
163
  end
164
164
  end
@@ -248,7 +248,6 @@ module ActiveMerchant #:nodoc:
248
248
  request = build_xml_transaction_request do |doc|
249
249
  add_amount(doc, amount)
250
250
  add_original_transaction_data(doc, transaction_id)
251
- add_order_number(doc, options)
252
251
  end
253
252
 
254
253
  commit(:refund, request)
@@ -338,8 +337,8 @@ module ActiveMerchant #:nodoc:
338
337
  response,
339
338
  error_code: error_code_from(succeeded, response),
340
339
  authorization: authorization_from(action, response),
341
- avs_result: AVSResult.new(code: response["AVSCode"]),
342
- cvv_result: CVVResult.new(response["CVV2Response"]),
340
+ avs_result: AVSResult.new(code: response["avsRslt"]),
341
+ cvv_result: CVVResult.new(response["secRslt"]),
343
342
  test: test?
344
343
  )
345
344
  end