activemerchant 1.62.0 → 1.63.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7495e19fc84deca42bc3878d9463044899a33b2e
4
- data.tar.gz: bc44737020062ba81039dfc19956b7c5a128ee40
3
+ metadata.gz: a16e6e0f311ac3b77069a4f05f5f257b6a69fc60
4
+ data.tar.gz: 666b86bb155b6ff98a608bf8c38714a54af46627
5
5
  SHA512:
6
- metadata.gz: 3fb5132fdfd23c6f1d772755285638231bef754099790c47c69f963133b7c1057c0d3c3f19b82c8b72226f5b61a294c7d2d30e283c570d2a5f81701886db491a
7
- data.tar.gz: be95c7d4600244ab3f2607f2a67ff1f4a325db60da1deb57834b5ea11eead17392a4d5d758d5595fcd76710c2a3e141a51e61e0de67191db8b2be58b1b07e867
6
+ metadata.gz: bd479af93c39120f8a447058524d56de326189f0f3fc26ace5af2681166761fc81a967a66368f9d50e4d7a5a0b64c7a4867c7f61dc2b37e368b0aa19141c87c2
7
+ data.tar.gz: b9f8ea0507a585f672f9abce5cf4c2359dbeeb781fe8cdda5e05413aa24fa638fc25a0f83279c4143e72278f8cf4e82f12a1987190390640a7c21ff52f488389
data/CHANGELOG CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.63.0 (February 2, 2017)
6
+ * Authorize.net: Add #unstore support [jimryan] #2293
7
+ * AuthorizeNet: Fix line items quirk [shasum]
8
+ * CardStream: Add dynamic descriptor option fields [curiousepic]
9
+ * CardStream: Support PEN currency [shasum]
10
+ * Culqi: Add new gateway [shasum]
11
+ * CyberSource: Add Lebanon to supported countries [shasum]
12
+ * Element: Add AVS and CVV codes to response [shasum]
13
+ * Firstdata E4 (Payeezy): Set correct ECI value for card present swipes [jasonwebster] #2318
14
+ * GlobalCollect: On purchase skip capture if not required [davidsantoso]
15
+ * PaymentExpress: Update supported countries [shasum]
16
+ * Remove leading or trailing whitespace from credit card name [davidsantoso]
17
+ * Remove support for Ruby 2.0 [jasonwebster]
18
+ * Secure Pay AU: Add scrubbing support to Secure Pay AU [bruno] #2253
19
+ * Stripe: Fix error in handling of track-only contactless EMV data [jasonwebster]
20
+ * Vanco: Update test URL [davidsantoso]
21
+ * WePay: Build fee structure correctly [curiousepic]
22
+ * WePay: Remove null address fields from request [davidsantoso]
23
+ * WePay: Update WePay to API version 2016-12-07 [davidsantoso]
24
+ * Wirecard: Send customer data in requests [davidsantoso]
25
+ * Worldpay: Add session id attribute [shasum]
26
+ * Worldpay: Do not default address when not provided [shasum]
27
+
5
28
  == Version 1.62.0 (December 5, 2016)
6
29
  * AuthorizeNet: Map to standard AVSResult codes [shasum]
7
30
  * CitrusPay: Add 3DSecureId field [davidsantoso]
@@ -245,7 +245,7 @@ module ActiveMerchant #:nodoc:
245
245
  #
246
246
  # @return [String] the full name of the card holder
247
247
  def name
248
- [first_name, last_name].compact.join(' ')
248
+ "#{first_name} #{last_name}".strip
249
249
  end
250
250
 
251
251
  def name=(full_name)
@@ -159,7 +159,6 @@ module ActiveMerchant
159
159
  add_payment_source(xml, payment)
160
160
  add_invoice(xml, options)
161
161
  add_customer_data(xml, payment, options)
162
- add_line_items(xml, options)
163
162
  add_settings(xml, payment, options)
164
163
  add_user_fields(xml, amount, options)
165
164
  end
@@ -181,6 +180,12 @@ module ActiveMerchant
181
180
  end
182
181
  end
183
182
 
183
+ def unstore(authorization)
184
+ customer_profile_id, _, _ = split_authorization(authorization)
185
+
186
+ delete_customer_profile(customer_profile_id)
187
+ end
188
+
184
189
  def verify_credentials
185
190
  response = commit(:verify_credentials) { }
186
191
  response.success?
@@ -233,7 +238,6 @@ module ActiveMerchant
233
238
  add_invoice(xml, options)
234
239
  add_customer_data(xml, payment, options)
235
240
  add_market_type_device_type(xml, payment, options)
236
- add_line_items(xml, options)
237
241
  add_settings(xml, payment, options)
238
242
  add_user_fields(xml, amount, options)
239
243
  end
@@ -347,19 +351,6 @@ module ActiveMerchant
347
351
  end
348
352
  end
349
353
 
350
- def add_line_items(xml, options)
351
- return unless options[:line_items]
352
- xml.lineItems do
353
- options[:line_items].each do |line_item|
354
- xml.lineItem do
355
- line_item.each do |key, value|
356
- xml.send(camel_case_lower(key), value)
357
- end
358
- end
359
- end
360
- end
361
- end
362
-
363
354
  def camel_case_lower(key)
364
355
  String(key).split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
365
356
  end
@@ -575,6 +566,19 @@ module ActiveMerchant
575
566
  xml.invoiceNumber(truncate(options[:order_id], 20))
576
567
  xml.description(truncate(options[:description], 255))
577
568
  end
569
+
570
+ # Authorize.net API requires lineItems to be placed directly after order tag
571
+ if options[:line_items]
572
+ xml.lineItems do
573
+ options[:line_items].each do |line_item|
574
+ xml.lineItem do
575
+ line_item.each do |key, value|
576
+ xml.send(camel_case_lower(key), value)
577
+ end
578
+ end
579
+ end
580
+ end
581
+ end
578
582
  end
579
583
 
580
584
  def create_customer_payment_profile(credit_card, options)
@@ -616,6 +620,12 @@ module ActiveMerchant
616
620
  end
617
621
  end
618
622
 
623
+ def delete_customer_profile(customer_profile_id)
624
+ commit(:cim_store_delete_customer) do |xml|
625
+ xml.customerProfileId(customer_profile_id)
626
+ end
627
+ end
628
+
619
629
  def names_from(payment_source, address, options)
620
630
  if payment_source && !payment_source.is_a?(PaymentToken) && !payment_source.is_a?(String)
621
631
  first_name, last_name = split_names(address[:name])
@@ -683,6 +693,8 @@ module ActiveMerchant
683
693
  "createCustomerProfileRequest"
684
694
  elsif action == :cim_store_update
685
695
  "createCustomerPaymentProfileRequest"
696
+ elsif action == :cim_store_delete_customer
697
+ "deleteCustomerProfileRequest"
686
698
  elsif action == :verify_credentials
687
699
  "authenticateTestRequest"
688
700
  elsif is_cim_action?(action)
@@ -827,7 +839,7 @@ module ActiveMerchant
827
839
  end
828
840
 
829
841
  def cim?(action)
830
- (action == :cim_store) || (action == :cim_store_update)
842
+ (action == :cim_store) || (action == :cim_store_update) || (action == :cim_store_delete_customer)
831
843
  end
832
844
 
833
845
  def transaction_id_from(authorization)
@@ -28,6 +28,7 @@ module ActiveMerchant #:nodoc:
28
28
  "MXN" => "484",
29
29
  "NOK" => "578",
30
30
  "NZD" => "554",
31
+ "PEN" => "604",
31
32
  "SEK" => "752",
32
33
  "SGD" => "702",
33
34
  "USD" => "840",
@@ -154,6 +155,8 @@ module ActiveMerchant #:nodoc:
154
155
  def add_invoice(post, credit_card_or_reference, money, options)
155
156
  add_pair(post, :transactionUnique, options[:order_id], :required => true)
156
157
  add_pair(post, :orderRef, options[:description] || options[:order_id], :required => true)
158
+ add_pair(post, :statementNarrative1, options[:merchant_name]) if options[:merchant_name]
159
+ add_pair(post, :statementNarrative2, options[:dynamic_descriptor]) if options[:dynamic_descriptor]
157
160
  if credit_card_or_reference.respond_to?(:number)
158
161
  if ['american_express', 'diners_club'].include?(card_brand(credit_card_or_reference).to_s)
159
162
  add_pair(post, :item1Quantity, 1)
@@ -0,0 +1,279 @@
1
+ require 'digest/md5'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # Important note:
6
+ # ===
7
+ # Culqi merchant accounts are configured for either purchase or auth/capture
8
+ # modes. This is configured by Culqi when setting up a merchant account and
9
+ # largely depends on the transaction acquiring bank. Be sure to understand how
10
+ # your account was configured prior to using this gateway.
11
+ class CulqiGateway < Gateway
12
+ self.display_name = "Culqi"
13
+ self.homepage_url = "https://www.culqi.com"
14
+
15
+ self.test_url = "https://staging.paymentz.com/transaction/"
16
+ self.live_url = "https://secure.culqi.com/transaction/"
17
+
18
+ self.supported_countries = ["PE"]
19
+ self.default_currency = "PEN"
20
+ self.money_format = :dollars
21
+ self.supported_cardtypes = [:visa, :master, :diners_club, :american_express]
22
+
23
+ def initialize(options={})
24
+ requires!(options, :merchant_id, :terminal_id, :secret_key)
25
+ super
26
+ end
27
+
28
+ def purchase(amount, payment_method, options={})
29
+ authorize(amount, payment_method, options)
30
+ end
31
+
32
+ def authorize(amount, payment_method, options={})
33
+ if payment_method.is_a?(String)
34
+ action = :tokenpay
35
+ else
36
+ action = :authorize
37
+ end
38
+ post = {}
39
+ add_credentials(post)
40
+ add_invoice(action, post, amount, options)
41
+ add_payment_method(post, payment_method, action, options)
42
+ add_customer_data(post, options)
43
+ add_checksum(action, post)
44
+
45
+ commit(action, post)
46
+ end
47
+
48
+ def capture(amount, authorization, options={})
49
+ action = :capture
50
+ post = {}
51
+ add_credentials(post)
52
+ add_invoice(action, post, amount, options)
53
+ add_reference(post, authorization)
54
+ add_checksum(action, post)
55
+
56
+ commit(action, post)
57
+ end
58
+
59
+ def void(authorization, options={})
60
+ action = :void
61
+ post = {}
62
+ add_credentials(post)
63
+ add_invoice(action, post, nil, options)
64
+ add_reference(post, authorization)
65
+ add_checksum(action, post)
66
+
67
+ commit(action, post)
68
+ end
69
+
70
+ def refund(amount, authorization, options={})
71
+ action = :refund
72
+ post = {}
73
+ add_credentials(post)
74
+ add_invoice(action, post, amount, options)
75
+ add_reference(post, authorization)
76
+ add_checksum(action, post)
77
+
78
+ commit(action, post)
79
+ end
80
+
81
+ def verify(credit_card, options={})
82
+ MultiResponse.run(:use_first_response) do |r|
83
+ r.process { authorize(1000, credit_card, options) }
84
+ r.process(:ignore_result) { void(r.authorization, options) }
85
+ end
86
+ end
87
+
88
+ def verify_credentials
89
+ response = void("0", order_id: "0")
90
+ response.message.include? "Transaction not found"
91
+ end
92
+
93
+ def store(credit_card, options={})
94
+ action = :tokenize
95
+ post = {}
96
+ post[:partnerid] = options[:partner_id] if options[:partner_id]
97
+ post[:cardholderid] = options[:cardholder_id] if options[:cardholder_id]
98
+ add_credentials(post)
99
+ add_payment_method(post, credit_card, action, options)
100
+ add_customer_data(post, options)
101
+ add_checksum(action, post)
102
+
103
+ commit(action, post)
104
+ end
105
+
106
+ def invalidate(authorization, options={})
107
+ action = :invalidate
108
+ post = {}
109
+ post[:partnerid] = options[:partner_id] if options[:partner_id]
110
+ add_credentials(post)
111
+ post[:token] = authorization
112
+ add_checksum(action, post)
113
+
114
+ commit(action, post)
115
+ end
116
+
117
+ def supports_scrubbing?
118
+ true
119
+ end
120
+
121
+ def scrub(transcript)
122
+ transcript.
123
+ gsub(%r((cardnumber=)\d+), '\1[FILTERED]').
124
+ gsub(%r((cvv=)\d+), '\1[FILTERED]')
125
+ end
126
+
127
+ private
128
+
129
+ def add_credentials(post)
130
+ post[:toid] = @options[:merchant_id]
131
+ post[:totype] = 'Culqi'
132
+ post[:terminalid] = @options[:terminal_id]
133
+ post[:language] = 'ENG'
134
+ end
135
+
136
+ def add_invoice(action, post, money, options)
137
+ case action
138
+ when :capture
139
+ post[:captureamount] = amount(money)
140
+ when :refund
141
+ post[:refundamount] = amount(money)
142
+ post[:reason] = 'none'
143
+ else
144
+ post[:amount] = amount(money)
145
+ end
146
+
147
+ post[:description] = options[:order_id]
148
+ post[:redirecturl] = options[:redirect_url] || 'http://www.example.com'
149
+ end
150
+
151
+ def add_payment_method(post, payment_method, action, options)
152
+ if payment_method.is_a?(String)
153
+ post[:token] = payment_method
154
+ post[:cvv] = options[:cvv] if options[:cvv]
155
+ else
156
+ post[:cardnumber] = payment_method.number
157
+ post[:cvv] = payment_method.verification_value
158
+ post[:firstname], post[:lastname] = payment_method.name.split(' ')
159
+ if action == :tokenize
160
+ post[:expirymonth] = format(payment_method.month, :two_digits)
161
+ post[:expiryyear] = format(payment_method.year, :four_digits)
162
+ else
163
+ post[:expiry_month] = format(payment_method.month, :two_digits)
164
+ post[:expiry_year] = format(payment_method.year, :four_digits)
165
+ end
166
+ end
167
+ end
168
+
169
+ def add_customer_data(post, options)
170
+ post[:emailaddr] = options[:email] || 'unspecified@example.com'
171
+ if (billing_address = options[:billing_address] || options[:address])
172
+ post[:street] = [billing_address[:address1], billing_address[:address2]].join(' ')
173
+ post[:city] = billing_address[:city]
174
+ post[:state] = billing_address[:state]
175
+ post[:countrycode] = billing_address[:country]
176
+ post[:zip] = billing_address[:zip]
177
+ post[:telno] = billing_address[:phone]
178
+ post[:telnocc] = options[:telephone_country_code] || '051'
179
+ end
180
+ end
181
+
182
+ def add_checksum(action, post)
183
+ checksum_elements = case action
184
+ when :capture; [post[:toid], post[:trackingid], post[:captureamount], @options[:secret_key]]
185
+ when :void; [post[:toid], post[:description], post[:trackingid], @options[:secret_key]]
186
+ when :refund; [post[:toid], post[:trackingid], post[:refundamount], @options[:secret_key]]
187
+ when :tokenize; [post[:partnerid], post[:cardnumber], post[:cvv], @options[:secret_key]]
188
+ when :invalidate; [post[:partnerid], post[:token], @options[:secret_key]]
189
+ else [post[:toid], post[:totype], post[:amount], post[:description], post[:redirecturl],
190
+ post[:cardnumber] || post[:token], @options[:secret_key]]
191
+ end
192
+
193
+ post[:checksum] = Digest::MD5.hexdigest(checksum_elements.compact.join('|'))
194
+ end
195
+
196
+ def add_reference(post, authorization)
197
+ post[:trackingid] = authorization
198
+ end
199
+
200
+ ACTIONS = {
201
+ authorize: "SingleCallGenericServlet",
202
+ capture: "SingleCallGenericCaptureServlet",
203
+ void: "SingleCallGenericVoid",
204
+ refund: "SingleCallGenericReverse",
205
+ tokenize: "SingleCallTokenServlet",
206
+ invalidate: "SingleCallInvalidateToken",
207
+ tokenpay: "SingleCallTokenTransaction",
208
+ }
209
+
210
+ def commit(action, params)
211
+ response = begin
212
+ parse(ssl_post(url + ACTIONS[action], post_data(action, params), headers))
213
+ rescue ResponseError => e
214
+ parse(e.response.body)
215
+ end
216
+
217
+ success = success_from(response)
218
+
219
+ Response.new(
220
+ success,
221
+ message_from(response),
222
+ response,
223
+ authorization: success ? authorization_from(response) : nil,
224
+ cvv_result: success ? cvvresult_from(response) : nil,
225
+ error_code: success ? nil : error_from(response),
226
+ test: test?
227
+ )
228
+ end
229
+
230
+ def headers
231
+ {
232
+ "Accept" => "application/json",
233
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
234
+ }
235
+ end
236
+
237
+ def post_data(action, params)
238
+ params.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
239
+ end
240
+
241
+ def url
242
+ test? ? test_url : live_url
243
+ end
244
+
245
+ def parse(body)
246
+ begin
247
+ JSON.parse(body)
248
+ rescue JSON::ParserError
249
+ message = "Invalid JSON response received from CulqiGateway. Please contact CulqiGateway if you continue to receive this message."
250
+ message += "(The raw response returned by the API was #{body.inspect})"
251
+ {
252
+ "status" => "N",
253
+ "statusdescription" => message
254
+ }
255
+ end
256
+ end
257
+
258
+ def success_from(response)
259
+ response['status'] == 'Y'
260
+ end
261
+
262
+ def message_from(response)
263
+ response['statusdescription'] || response['statusDescription']
264
+ end
265
+
266
+ def authorization_from(response)
267
+ response['trackingid'] || response['token']
268
+ end
269
+
270
+ def cvvresult_from(response)
271
+ CVVResult.new(response['cvvresult'])
272
+ end
273
+
274
+ def error_from(response)
275
+ response['resultcode']
276
+ end
277
+ end
278
+ end
279
+ end
@@ -27,7 +27,7 @@ module ActiveMerchant #:nodoc:
27
27
  XSD_VERSION = "1.121"
28
28
 
29
29
  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
30
- self.supported_countries = %w(US BR CA CN DK FI FR DE JP MX NO SE GB SG)
30
+ self.supported_countries = %w(US BR CA CN DK FI FR DE JP MX NO SE GB SG LB)
31
31
 
32
32
  self.default_currency = 'USD'
33
33
  self.currencies_without_fractions = %w(JPY)
@@ -274,6 +274,8 @@ module ActiveMerchant #:nodoc:
274
274
  message_from(response),
275
275
  response,
276
276
  authorization: authorization_from(action, response, amount),
277
+ avs_result: success_from(response) ? avs_from(response) : nil,
278
+ cvv_result: success_from(response) ? cvv_from(response) : nil,
277
279
  test: test?
278
280
  )
279
281
  end
@@ -294,6 +296,14 @@ module ActiveMerchant #:nodoc:
294
296
  response["expressresponsemessage"]
295
297
  end
296
298
 
299
+ def avs_from(response)
300
+ AVSResult.new(code: response["card"]["avsresponsecode"]) if response["card"]
301
+ end
302
+
303
+ def cvv_from(response)
304
+ CVVResult.new(response["card"]["cvvresponsecode"]) if response["card"]
305
+ end
306
+
297
307
  def split_authorization(authorization)
298
308
  authorization.split("|")
299
309
  end
@@ -226,9 +226,9 @@ module ActiveMerchant #:nodoc:
226
226
  end
227
227
 
228
228
  def add_credit_card(xml, credit_card, options)
229
-
230
229
  if credit_card.respond_to?(:track_data) && credit_card.track_data.present?
231
230
  xml.tag! "Track1", credit_card.track_data
231
+ xml.tag! "Ecommerce_Flag", "R"
232
232
  else
233
233
  xml.tag! "Card_Number", credit_card.number
234
234
  xml.tag! "Expiry_Date", expdate(credit_card)
@@ -23,7 +23,7 @@ module ActiveMerchant #:nodoc:
23
23
  def purchase(money, payment, options={})
24
24
  MultiResponse.run do |r|
25
25
  r.process { authorize(money, payment, options) }
26
- r.process { capture(money, r.authorization, options) }
26
+ r.process { capture(money, r.authorization, options) } unless capture_requested?(r)
27
27
  end
28
28
  end
29
29
 
@@ -290,6 +290,10 @@ EOS
290
290
  def nestable_hash
291
291
  Hash.new {|h,k| h[k] = Hash.new(&h.default_proc) }
292
292
  end
293
+
294
+ def capture_requested?(response)
295
+ response.params.try(:[], "payment").try(:[], "status") == "CAPTURE_REQUESTED"
296
+ end
293
297
  end
294
298
  end
295
299
  end
@@ -16,7 +16,7 @@ module ActiveMerchant #:nodoc:
16
16
  # However, regular accounts with DPS only support VISA and Mastercard
17
17
  self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb ]
18
18
 
19
- self.supported_countries = %w[ AU CA DE ES FR GB HK IE MY NL NZ SG US ZA ]
19
+ self.supported_countries = %w[ AU FJ GB HK IE MY NZ PG SG US ]
20
20
 
21
21
  self.homepage_url = 'http://www.paymentexpress.com/'
22
22
  self.display_name = 'PaymentExpress'
@@ -103,6 +103,18 @@ module ActiveMerchant #:nodoc:
103
103
  commit_periodic(build_periodic_item(:remove_triggered, options[:amount], nil, options))
104
104
  end
105
105
 
106
+ def supports_scrubbing?
107
+ true
108
+ end
109
+
110
+ def scrub(transcript)
111
+ transcript.
112
+ gsub(%r((<merchantID>).+(</merchantID>)), '\1[FILTERED]\2').
113
+ gsub(%r((<password>).+(</password>)), '\1[FILTERED]\2').
114
+ gsub(%r((<cardNumber>).+(</cardNumber>)), '\1[FILTERED]\2').
115
+ gsub(%r((<cvv>).+(</cvv>)), '\1[FILTERED]\2')
116
+ end
117
+
106
118
  private
107
119
 
108
120
  def build_purchase_request(money, credit_card, options)
@@ -380,7 +380,7 @@ module ActiveMerchant #:nodoc:
380
380
  card[:swipe_data] = creditcard.track_data
381
381
  card[:fallback_reason] = creditcard.fallback_reason if creditcard.fallback_reason
382
382
  card[:read_method] = "contactless" if creditcard.contactless_emv
383
- post[:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
383
+ card[:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
384
384
  else
385
385
  card[:number] = creditcard.number
386
386
  card[:exp_month] = creditcard.month
@@ -5,7 +5,7 @@ module ActiveMerchant
5
5
  class VancoGateway < Gateway
6
6
  include Empty
7
7
 
8
- self.test_url = 'https://www.vancodev.com/cgi-bin/wstest2.vps'
8
+ self.test_url = 'https://uat.vancopayments.com/cgi-bin/ws2.vps'
9
9
  self.live_url = 'https://myvanco.vancopayments.com/cgi-bin/ws2.vps'
10
10
 
11
11
  self.supported_countries = ['US']
@@ -10,6 +10,8 @@ module ActiveMerchant #:nodoc:
10
10
  self.default_currency = 'USD'
11
11
  self.display_name = 'WePay'
12
12
 
13
+ API_VERSION = "2016-12-07"
14
+
13
15
  def initialize(options = {})
14
16
  requires!(options, :client_id, :account_id, :access_token)
15
17
  super(options)
@@ -18,38 +20,47 @@ module ActiveMerchant #:nodoc:
18
20
  def purchase(money, payment_method, options = {})
19
21
  post = {}
20
22
  if payment_method.is_a?(String)
21
- purchase_with_token(post, money, payment_method, options)
23
+ MultiResponse.run do |r|
24
+ r.process { authorize_with_token(post, money, payment_method, options) }
25
+ r.process { capture(money, r.authorization, options) }
26
+ end
22
27
  else
23
28
  MultiResponse.run do |r|
24
29
  r.process { store(payment_method, options) }
25
- r.process { purchase_with_token(post, money, split_authorization(r.authorization).first, options) }
30
+ r.process { authorize_with_token(post, money, r.authorization, options) }
31
+ r.process { capture(money, r.authorization, options) }
26
32
  end
27
33
  end
28
34
  end
29
35
 
30
36
  def authorize(money, payment_method, options = {})
31
- post = {auto_capture: 0}
37
+ post = {}
32
38
  if payment_method.is_a?(String)
33
- purchase_with_token(post, money, payment_method, options)
39
+ authorize_with_token(post, money, payment_method, options)
34
40
  else
35
41
  MultiResponse.run do |r|
36
42
  r.process { store(payment_method, options) }
37
- r.process { purchase_with_token(post, money, split_authorization(r.authorization).first, options) }
43
+ r.process { authorize_with_token(post, money, r.authorization, options) }
38
44
  end
39
45
  end
40
46
  end
41
47
 
42
48
  def capture(money, identifier, options = {})
49
+ checkout_id, original_amount = split_authorization(identifier)
50
+
43
51
  post = {}
44
- post[:checkout_id] = split_authorization(identifier).first
45
- commit('/checkout/capture', post)
52
+ post[:checkout_id] = checkout_id
53
+ if(money && (original_amount != amount(money)))
54
+ post[:amount] = amount(money)
55
+ end
56
+ commit('/checkout/capture', post, options)
46
57
  end
47
58
 
48
59
  def void(identifier, options = {})
49
60
  post = {}
50
61
  post[:checkout_id] = split_authorization(identifier).first
51
62
  post[:cancel_reason] = (options[:description] || "Void")
52
- commit('/checkout/cancel', post)
63
+ commit('/checkout/cancel', post, options)
53
64
  end
54
65
 
55
66
  def refund(money, identifier, options = {})
@@ -61,10 +72,9 @@ module ActiveMerchant #:nodoc:
61
72
  post[:amount] = amount(money)
62
73
  end
63
74
  post[:refund_reason] = (options[:description] || "Refund")
64
- post[:app_fee] = options[:application_fee] if options[:application_fee]
65
75
  post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message]
66
76
  post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message]
67
- commit("/checkout/refund", post)
77
+ commit("/checkout/refund", post, options)
68
78
  end
69
79
 
70
80
  def store(creditcard, options = {})
@@ -76,53 +86,46 @@ module ActiveMerchant #:nodoc:
76
86
  post[:email] = options[:email] || "unspecified@example.com"
77
87
  post[:cc_number] = creditcard.number
78
88
  post[:cvv] = creditcard.verification_value unless options[:recurring]
79
- post[:expiration_month] = "#{creditcard.month}"
80
- post[:expiration_year] = "#{creditcard.year}"
89
+ post[:expiration_month] = creditcard.month
90
+ post[:expiration_year] = creditcard.year
81
91
  post[:original_ip] = options[:ip] if options[:ip]
82
92
  post[:original_device] = options[:device_fingerprint] if options[:device_fingerprint]
93
+
83
94
  if(billing_address = (options[:billing_address] || options[:address]))
84
- post[:address] = {
85
- "address1" => billing_address[:address1],
86
- "city" => billing_address[:city],
87
- "country" => billing_address[:country]
88
- }
89
- if(post[:country] == "US")
90
- post[:address]["zip"] = billing_address[:zip]
91
- post[:address]["state"] = billing_address[:state]
92
- else
93
- post[:address]["region"] = billing_address[:state]
94
- post[:address]["postcode"] = billing_address[:zip]
95
- end
95
+ post[:address] = {}
96
+ post[:address]["address1"] = billing_address[:address1] if billing_address[:address1]
97
+ post[:address]["city"] = billing_address[:city] if billing_address[:city]
98
+ post[:address]["country"] = billing_address[:country] if billing_address[:country]
99
+ post[:address]["region"] = billing_address[:state] if billing_address[:state]
100
+ post[:address]["postal_code"] = billing_address[:zip]
96
101
  end
97
102
 
98
103
  if options[:recurring] == true
99
104
  post[:client_secret] = @options[:client_secret]
100
- commit('/credit_card/transfer', post)
105
+ commit('/credit_card/transfer', post, options)
101
106
  else
102
- commit('/credit_card/create', post)
107
+ commit('/credit_card/create', post, options)
103
108
  end
104
109
  end
105
110
 
106
111
  private
107
112
 
108
- def purchase_with_token(post, money, token, options)
113
+ def authorize_with_token(post, money, token, options)
109
114
  add_token(post, token)
110
115
  add_product_data(post, money, options)
111
- commit('/checkout/create', post)
116
+ commit('/checkout/create', post, options)
112
117
  end
113
118
 
114
119
  def add_product_data(post, money, options)
115
120
  post[:account_id] = @options[:account_id]
116
121
  post[:amount] = amount(money)
117
122
  post[:short_description] = (options[:description] || "Purchase")
118
- post[:type] = (options[:type] || "GOODS")
123
+ post[:type] = (options[:type] || "goods")
119
124
  post[:currency] = (options[:currency] || currency(money))
120
125
  post[:long_description] = options[:long_description] if options[:long_description]
121
126
  post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message]
122
127
  post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message]
123
128
  post[:reference_id] = options[:order_id] if options[:order_id]
124
- post[:app_fee] = options[:application_fee] if options[:application_fee]
125
- post[:fee_payer] = options[:fee_payer] if options[:fee_payer]
126
129
  post[:redirect_uri] = options[:redirect_uri] if options[:redirect_uri]
127
130
  post[:callback_uri] = options[:callback_uri] if options[:callback_uri]
128
131
  post[:fallback_uri] = options[:fallback_uri] if options[:fallback_uri]
@@ -133,11 +136,26 @@ module ActiveMerchant #:nodoc:
133
136
  post[:preapproval_id] = options[:preapproval_id] if options[:preapproval_id]
134
137
  post[:prefill_info] = options[:prefill_info] if options[:prefill_info]
135
138
  post[:funding_sources] = options[:funding_sources] if options[:funding_sources]
139
+ add_fee(post, options)
136
140
  end
137
141
 
138
142
  def add_token(post, token)
139
- post[:payment_method_id] = token
140
- post[:payment_method_type] = "credit_card"
143
+ payment_method = {}
144
+ payment_method[:type] = "credit_card"
145
+ payment_method[:credit_card] = {
146
+ id: token,
147
+ auto_capture: false
148
+ }
149
+
150
+ post[:payment_method] = payment_method
151
+ end
152
+
153
+ def add_fee(post, options)
154
+ if options[:application_fee] || options[:fee_payer]
155
+ post[:fee] = {}
156
+ post[:fee][:app_fee] = options[:application_fee] if options[:application_fee]
157
+ post[:fee][:fee_payer] = options[:fee_payer] if options[:fee_payer]
158
+ end
141
159
  end
142
160
 
143
161
  def parse(response)
@@ -149,7 +167,7 @@ module ActiveMerchant #:nodoc:
149
167
  response = parse(ssl_post(
150
168
  ((test? ? test_url : live_url) + action),
151
169
  params.to_json,
152
- headers
170
+ headers(options)
153
171
  ))
154
172
  rescue ResponseError => e
155
173
  response = parse(e.response.body)
@@ -178,7 +196,8 @@ module ActiveMerchant #:nodoc:
178
196
  def authorization_from(response, params)
179
197
  return response["credit_card_id"].to_s if response["credit_card_id"]
180
198
 
181
- [response["checkout_id"], params[:amount]].join('|')
199
+ original_amount = response["amount"].nil? ? nil : sprintf("%0.02f", response["amount"])
200
+ [response["checkout_id"], original_amount].join('|')
182
201
  end
183
202
 
184
203
  def split_authorization(authorization)
@@ -192,14 +211,18 @@ module ActiveMerchant #:nodoc:
192
211
  return Response.new(false, message)
193
212
  end
194
213
 
195
- def headers
214
+ def headers(options)
196
215
  {
197
216
  "Content-Type" => "application/json",
198
217
  "User-Agent" => "ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
199
- "Authorization" => "Bearer #{@options[:access_token]}"
218
+ "Authorization" => "Bearer #{@options[:access_token]}",
219
+ "Api-Version" => api_version(options)
200
220
  }
201
221
  end
222
+
223
+ def api_version(options)
224
+ options[:version] || API_VERSION
225
+ end
202
226
  end
203
227
  end
204
228
  end
205
-
@@ -239,6 +239,7 @@ module ActiveMerchant #:nodoc:
239
239
  when :reversal
240
240
  xml.tag! 'GuWID', options[:preauthorization]
241
241
  end
242
+ add_customer_data(xml, options)
242
243
  end
243
244
  end
244
245
  end
@@ -223,8 +223,11 @@ module ActiveMerchant #:nodoc:
223
223
 
224
224
  add_address(xml, (options[:billing_address] || options[:address]))
225
225
  end
226
- if options[:ip]
227
- xml.tag! 'session', 'shopperIPAddress' => options[:ip]
226
+ if options[:ip] && options[:session_id]
227
+ xml.tag! 'session', 'shopperIPAddress' => options[:ip], 'id' => options[:session_id]
228
+ else
229
+ xml.tag! 'session', 'shopperIPAddress' => options[:ip] if options[:ip]
230
+ xml.tag! 'session', 'id' => options[:session_id] if options[:session_id]
228
231
  end
229
232
  end
230
233
  end
@@ -238,6 +241,8 @@ module ActiveMerchant #:nodoc:
238
241
  end
239
242
 
240
243
  def add_address(xml, address)
244
+ return unless address
245
+
241
246
  address = address_with_defaults(address)
242
247
 
243
248
  xml.tag! 'cardAddress' do
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.62.0"
2
+ VERSION = "1.63.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.62.0
4
+ version: 1.63.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-05 00:00:00.000000000 Z
11
+ date: 2017-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -202,6 +202,7 @@ files:
202
202
  - lib/active_merchant/billing/gateways/conekta.rb
203
203
  - lib/active_merchant/billing/gateways/creditcall.rb
204
204
  - lib/active_merchant/billing/gateways/credorax.rb
205
+ - lib/active_merchant/billing/gateways/culqi.rb
205
206
  - lib/active_merchant/billing/gateways/cyber_source.rb
206
207
  - lib/active_merchant/billing/gateways/data_cash.rb
207
208
  - lib/active_merchant/billing/gateways/dibs.rb
@@ -395,7 +396,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
395
396
  requirements:
396
397
  - - ">="
397
398
  - !ruby/object:Gem::Version
398
- version: '2'
399
+ version: '2.1'
399
400
  required_rubygems_version: !ruby/object:Gem::Requirement
400
401
  requirements:
401
402
  - - ">="