activemerchant 1.63.0 → 1.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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