activemerchant 1.123.0 → 1.124.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class MokaGateway < Gateway
4
- self.test_url = 'https://service.testmoka.com'
4
+ self.test_url = 'https://service.refmoka.com'
5
5
  self.live_url = 'https://service.moka.com'
6
6
 
7
7
  self.supported_countries = %w[GB TR US]
@@ -55,8 +55,10 @@ module ActiveMerchant #:nodoc:
55
55
  post[:PaymentDealerRequest] = {}
56
56
  options[:pre_auth] = 0
57
57
  add_auth_purchase(post, money, payment, options)
58
+ add_3ds_data(post, options) if options[:execute_threed]
58
59
 
59
- commit('purchase', post)
60
+ action = options[:execute_threed] ? 'three_ds_purchase' : 'purchase'
61
+ commit(action, post)
60
62
  end
61
63
 
62
64
  def authorize(money, payment, options = {})
@@ -64,8 +66,10 @@ module ActiveMerchant #:nodoc:
64
66
  post[:PaymentDealerRequest] = {}
65
67
  options[:pre_auth] = 1
66
68
  add_auth_purchase(post, money, payment, options)
69
+ add_3ds_data(post, options) if options[:execute_threed]
67
70
 
68
- commit('authorize', post)
71
+ action = options[:execute_threed] ? 'three_ds_authorize' : 'authorize'
72
+ commit(action, post)
69
73
  end
70
74
 
71
75
  def capture(money, authorization, options = {})
@@ -73,7 +77,7 @@ module ActiveMerchant #:nodoc:
73
77
  post[:PaymentDealerRequest] = {}
74
78
  add_payment_dealer_authentication(post)
75
79
  add_transaction_reference(post, authorization)
76
- add_additional_transaction_data(post, options)
80
+ add_invoice(post, money, options)
77
81
 
78
82
  commit('capture', post)
79
83
  end
@@ -83,7 +87,6 @@ module ActiveMerchant #:nodoc:
83
87
  post[:PaymentDealerRequest] = {}
84
88
  add_payment_dealer_authentication(post)
85
89
  add_transaction_reference(post, authorization)
86
- add_additional_transaction_data(post, options)
87
90
  add_void_refund_reason(post)
88
91
  add_amount(post, money)
89
92
 
@@ -95,7 +98,6 @@ module ActiveMerchant #:nodoc:
95
98
  post[:PaymentDealerRequest] = {}
96
99
  add_payment_dealer_authentication(post)
97
100
  add_transaction_reference(post, authorization)
98
- add_additional_transaction_data(post, options)
99
101
  add_void_refund_reason(post)
100
102
 
101
103
  commit('void', post)
@@ -134,6 +136,12 @@ module ActiveMerchant #:nodoc:
134
136
  add_basket_product(post, options[:basket_product]) if options[:basket_product]
135
137
  end
136
138
 
139
+ def add_3ds_data(post, options)
140
+ post[:PaymentDealerRequest][:ReturnHash] = 1
141
+ post[:PaymentDealerRequest][:RedirectUrl] = options[:redirect_url] || ''
142
+ post[:PaymentDealerRequest][:RedirectType] = options[:redirect_type] || 0
143
+ end
144
+
137
145
  def add_payment_dealer_authentication(post)
138
146
  post[:PaymentDealerAuthentication] = {
139
147
  DealerCode: @options[:dealer_code],
@@ -156,18 +164,19 @@ module ActiveMerchant #:nodoc:
156
164
  def add_payment(post, card)
157
165
  post[:PaymentDealerRequest][:CardHolderFullName] = card.name
158
166
  post[:PaymentDealerRequest][:CardNumber] = card.number
159
- post[:PaymentDealerRequest][:ExpMonth] = card.month
167
+ post[:PaymentDealerRequest][:ExpMonth] = card.month.to_s.rjust(2, '0')
160
168
  post[:PaymentDealerRequest][:ExpYear] = card.year
161
- post[:PaymentDealerRequest][:CvcNumber] = card.verification_value
169
+ post[:PaymentDealerRequest][:CvcNumber] = card.verification_value || ''
162
170
  end
163
171
 
164
172
  def add_amount(post, money)
165
- post[:PaymentDealerRequest][:Amount] = money || 0
173
+ post[:PaymentDealerRequest][:Amount] = amount(money) || 0
166
174
  end
167
175
 
168
176
  def add_additional_auth_purchase_data(post, options)
169
177
  post[:PaymentDealerRequest][:IsPreAuth] = options[:pre_auth]
170
- post[:PaymentDealerRequest][:Description] = options[:order_id] if options[:order_id]
178
+ post[:PaymentDealerRequest][:Description] = options[:description] if options[:description]
179
+ post[:PaymentDealerRequest][:InstallmentNumber] = options[:installment_number].to_i if options[:installment_number]
171
180
  post[:SubMerchantName] = options[:sub_merchant_name] if options[:sub_merchant_name]
172
181
  post[:IsPoolPayment] = options[:is_pool_payment] || 0
173
182
  end
@@ -232,6 +241,8 @@ module ActiveMerchant #:nodoc:
232
241
 
233
242
  def endpoint(action)
234
243
  case action
244
+ when 'three_ds_authorize', 'three_ds_purchase'
245
+ 'DoDirectPaymentThreeD'
235
246
  when 'purchase', 'authorize'
236
247
  'DoDirectPayment'
237
248
  when 'capture'
@@ -256,7 +267,9 @@ module ActiveMerchant #:nodoc:
256
267
  end
257
268
 
258
269
  def success_from(response)
259
- response.dig('Data', 'IsSuccessful')
270
+ return response.dig('Data', 'IsSuccessful') if response.dig('Data', 'IsSuccessful').to_s.present?
271
+
272
+ response['ResultCode']&.casecmp('success') == 0
260
273
  end
261
274
 
262
275
  def message_from(response)
@@ -236,6 +236,19 @@ module ActiveMerchant #:nodoc:
236
236
  post[:shipping_zip] = shipping_address[:zip]
237
237
  post[:shipping_phone] = shipping_address[:phone]
238
238
  end
239
+
240
+ if (descriptor = options[:descriptors])
241
+ post[:descriptor] = descriptor[:descriptor]
242
+ post[:descriptor_phone] = descriptor[:descriptor_phone]
243
+ post[:descriptor_address] = descriptor[:descriptor_address]
244
+ post[:descriptor_city] = descriptor[:descriptor_city]
245
+ post[:descriptor_state] = descriptor[:descriptor_state]
246
+ post[:descriptor_postal] = descriptor[:descriptor_postal]
247
+ post[:descriptor_country] = descriptor[:descriptor_country]
248
+ post[:descriptor_mcc] = descriptor[:descriptor_mcc]
249
+ post[:descriptor_merchant_id] = descriptor[:descriptor_merchant_id]
250
+ post[:descriptor_url] = descriptor[:descriptor_url]
251
+ end
239
252
  end
240
253
 
241
254
  def add_vendor_data(post, options)
@@ -337,7 +337,8 @@ module ActiveMerchant #:nodoc:
337
337
  gsub(%r((<CustomerMerchantID>).+(</CustomerMerchantID>)), '\1[FILTERED]\2').
338
338
  gsub(%r((<CustomerProfileMessage>).+(</CustomerProfileMessage>)), '\1[FILTERED]\2').
339
339
  gsub(%r((<CheckDDA>).+(</CheckDDA>)), '\1[FILTERED]\2').
340
- gsub(%r((<BCRtNum>).+(</BCRtNum>)), '\1[FILTERED]\2')
340
+ gsub(%r((<BCRtNum>).+(</BCRtNum>)), '\1[FILTERED]\2').
341
+ gsub(%r((<DigitalTokenCryptogram>).+(</DigitalTokenCryptogram>)), '\1[FILTERED]\2')
341
342
  end
342
343
 
343
344
  private
@@ -286,16 +286,18 @@ module ActiveMerchant #:nodoc:
286
286
  end
287
287
 
288
288
  def add_address(post, options)
289
- post['address_line1'] = options[:billing_address][:address1]
290
- post['address_line2'] = options[:billing_address][:address2]
291
- post['city'] = options[:billing_address][:city]
292
- post['state'] = options[:billing_address][:state]
293
- post['zip'] = options[:billing_address][:zip]
294
- post['country'] = options[:billing_address][:country]
289
+ return unless billing_address = options[:billing_address]
290
+
291
+ post['address_line1'] = billing_address[:address1]
292
+ post['address_line2'] = billing_address[:address2]
293
+ post['city'] = billing_address[:city]
294
+ post['state'] = billing_address[:state]
295
+ post['zip'] = billing_address[:zip]
296
+ post['country'] = billing_address[:country]
295
297
  end
296
298
 
297
299
  def add_phone(post, options)
298
- post['receipt_phone'] = options[:billing_address][:phone] if options[:billing_address][:phone]
300
+ post['phone_number'] = options[:billing_address][:phone] if options.dig(:billing_address, :phone)
299
301
  end
300
302
 
301
303
  def add_money(post, money, options)
@@ -79,7 +79,9 @@ module ActiveMerchant #:nodoc:
79
79
  force_utf8(transcript).
80
80
  gsub(%r((api_accesskey=)\w+), '\1[FILTERED]').
81
81
  gsub(%r((card_number=)\w+), '\1[FILTERED]').
82
- gsub(%r((card_verification=)\w+), '\1[FILTERED]')
82
+ gsub(%r((card_verification=)\w+), '\1[FILTERED]').
83
+ gsub(%r((bank_account_number=)\w+), '\1[FILTERED]').
84
+ gsub(%r((bank_routing_number=)\w+), '\1[FILTERED]')
83
85
  end
84
86
 
85
87
  private
@@ -289,18 +289,26 @@ module ActiveMerchant #:nodoc:
289
289
  xml.tag! 'ECI', three_d_secure[:eci] unless three_d_secure[:eci].blank?
290
290
  xml.tag! 'CAVV', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
291
291
  xml.tag! 'XID', three_d_secure[:xid] unless three_d_secure[:xid].blank?
292
- xml.tag! 'THREEDSVERSION', three_d_secure[:version] unless three_d_secure[:version].blank?
293
- xml.tag! 'DSTRANSACTIONID', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
292
+ xml.tag! 'ThreeDSVersion', three_d_secure[:version] unless three_d_secure[:version].blank?
293
+ xml.tag! 'DSTransactionID', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
294
294
  end
295
295
  end
296
296
  end
297
297
 
298
298
  def authentication_status(three_d_secure, xml)
299
- if three_d_secure[:authentication_response_status].present?
300
- xml.tag! 'Status', three_d_secure[:authentication_response_status]
301
- elsif three_d_secure[:directory_response_status].present?
302
- xml.tag! 'Status', three_d_secure[:directory_response_status]
299
+ status = if three_d_secure[:authentication_response_status].present?
300
+ three_d_secure[:authentication_response_status]
301
+ elsif three_d_secure[:directory_response_status].present?
302
+ three_d_secure[:directory_response_status]
303
303
  end
304
+ if status.present?
305
+ xml.tag! 'Status', status
306
+ xml.tag! 'AuthenticationStatus', status if version_2_or_newer?(three_d_secure)
307
+ end
308
+ end
309
+
310
+ def version_2_or_newer?(three_d_secure)
311
+ three_d_secure[:version]&.start_with?('2')
304
312
  end
305
313
 
306
314
  def credit_card_type(credit_card)
@@ -4,8 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  self.test_url = 'https://api.test.paysafe.com'
5
5
  self.live_url = 'https://api.paysafe.com'
6
6
 
7
- self.supported_countries = %w(FR)
8
- self.default_currency = 'EUR'
7
+ self.supported_countries = %w(AL AT BE BA BG CA HR CY CZ DK EE FI FR DE GR HU IS IE IT LV LI LT LU MT ME NL MK NO PL PT RO RS SK SI ES SE CH TR GB US)
9
8
  self.supported_cardtypes = %i[visa master american_express discover]
10
9
 
11
10
  self.homepage_url = 'https://www.paysafe.com/'
@@ -22,8 +21,10 @@ module ActiveMerchant #:nodoc:
22
21
  add_payment(post, payment)
23
22
  add_billing_address(post, options)
24
23
  add_merchant_details(post, options)
24
+ add_airline_travel_details(post, options)
25
25
  add_customer_data(post, payment, options) unless payment.is_a?(String)
26
26
  add_three_d_secure(post, payment, options) if options[:three_d_secure]
27
+ add_split_pay_details(post, options)
27
28
  post[:settleWithAuth] = true
28
29
 
29
30
  commit(:post, 'auths', post, options)
@@ -119,9 +120,8 @@ module ActiveMerchant #:nodoc:
119
120
  end
120
121
 
121
122
  def add_billing_address(post, options)
122
- return unless options[:billing_address] || options[:address]
123
+ return unless address = options[:billing_address] || options[:address]
123
124
 
124
- address = options[:billing_address] || options[:address]
125
125
  post[:billingDetails] = {}
126
126
  post[:billingDetails][:street] = address[:address1]
127
127
  post[:billingDetails][:city] = address[:city]
@@ -134,21 +134,19 @@ module ActiveMerchant #:nodoc:
134
134
  # The add_address_for_vaulting method is applicable to the store method, as the APIs address
135
135
  # object is formatted differently from the standard transaction billing address
136
136
  def add_address_for_vaulting(post, options)
137
- return unless options[:billing_address || options[:address]]
137
+ return unless address = options[:billing_address] || options[:address]
138
138
 
139
- address = options[:billing_address] || options[:address]
140
- post[:billingAddress] = {}
141
- post[:billingAddress][:street] = address[:address1]
142
- post[:billingAddress][:city] = address[:city]
143
- post[:billingAddress][:zip] = address[:zip]
144
- post[:billingAddress][:country] = address[:country]
145
- post[:billingAddress][:state] = address[:state] if address[:state]
139
+ post[:card][:billingAddress] = {}
140
+ post[:card][:billingAddress][:street] = address[:address1]
141
+ post[:card][:billingAddress][:street2] = address[:address2]
142
+ post[:card][:billingAddress][:city] = address[:city]
143
+ post[:card][:billingAddress][:zip] = address[:zip]
144
+ post[:card][:billingAddress][:country] = address[:country]
145
+ post[:card][:billingAddress][:state] = address[:state] if address[:state]
146
146
  end
147
147
 
148
148
  # This data is specific to creating a profile at the gateway's vault level
149
149
  def add_profile_data(post, payment, options)
150
- address = options[:billing_address] || options[:address]
151
-
152
150
  post[:firstName] = payment.first_name
153
151
  post[:lastName] = payment.last_name
154
152
  post[:dateOfBirth] = {}
@@ -156,8 +154,13 @@ module ActiveMerchant #:nodoc:
156
154
  post[:dateOfBirth][:month] = options[:date_of_birth][:month]
157
155
  post[:dateOfBirth][:day] = options[:date_of_birth][:day]
158
156
  post[:email] = options[:email] if options[:email]
159
- post[:phone] = (address[:phone] || options[:phone]) if address[:phone] || options[:phone]
160
157
  post[:ip] = options[:ip] if options[:ip]
158
+
159
+ if options[:phone]
160
+ post[:phone] = options[:phone]
161
+ elsif address = options[:billing_address] || options[:address]
162
+ post[:phone] = address[:phone] if address[:phone]
163
+ end
161
164
  end
162
165
 
163
166
  def add_store_data(post, payment, options)
@@ -203,6 +206,84 @@ module ActiveMerchant #:nodoc:
203
206
  post[:authentication][:directoryServerTransactionId] = three_d_secure[:ds_transaction_id] unless payment.is_a?(String) || payment.brand != 'mastercard'
204
207
  end
205
208
 
209
+ def add_airline_travel_details(post, options)
210
+ return unless options[:airline_travel_details]
211
+
212
+ post[:airlineTravelDetails] = {}
213
+ post[:airlineTravelDetails][:passengerName] = options[:airline_travel_details][:passenger_name] if options[:airline_travel_details][:passenger_name]
214
+ post[:airlineTravelDetails][:departureDate] = options[:airline_travel_details][:departure_date] if options[:airline_travel_details][:departure_date]
215
+ post[:airlineTravelDetails][:origin] = options[:airline_travel_details][:origin] if options[:airline_travel_details][:origin]
216
+ post[:airlineTravelDetails][:computerizedReservationSystem] = options[:airline_travel_details][:computerized_reservation_system] if options[:airline_travel_details][:computerized_reservation_system]
217
+ post[:airlineTravelDetails][:customerReferenceNumber] = options[:airline_travel_details][:customer_reference_number] if options[:airline_travel_details][:customer_reference_number]
218
+
219
+ add_ticket_details(post, options)
220
+ add_travel_agency_details(post, options)
221
+ add_trip_legs(post, options)
222
+ end
223
+
224
+ def add_ticket_details(post, options)
225
+ return unless ticket = options[:airline_travel_details][:ticket]
226
+
227
+ post[:airlineTravelDetails][:ticket] = {}
228
+ post[:airlineTravelDetails][:ticket][:ticketNumber] = ticket[:ticket_number] if ticket[:ticket_number]
229
+ post[:airlineTravelDetails][:ticket][:isRestrictedTicket] = ticket[:is_restricted_ticket] if ticket[:is_restricted_ticket]
230
+ end
231
+
232
+ def add_travel_agency_details(post, options)
233
+ return unless agency = options[:airline_travel_details][:travel_agency]
234
+
235
+ post[:airlineTravelDetails][:travelAgency] = {}
236
+ post[:airlineTravelDetails][:travelAgency][:name] = agency[:name] if agency[:name]
237
+ post[:airlineTravelDetails][:travelAgency][:code] = agency[:code] if agency[:code]
238
+ end
239
+
240
+ def add_trip_legs(post, options)
241
+ return unless trip_legs = options[:airline_travel_details][:trip_legs]
242
+
243
+ trip_legs_hash = {}
244
+ trip_legs.each.with_index(1) do |leg, i|
245
+ my_leg = "leg#{i}".to_sym
246
+ details = add_leg_details(my_leg, leg[1])
247
+
248
+ trip_legs_hash[my_leg] = details
249
+ end
250
+ post[:airlineTravelDetails][:tripLegs] = trip_legs_hash
251
+ end
252
+
253
+ def add_leg_details(obj, leg)
254
+ details = {}
255
+ add_flight_details(details, obj, leg)
256
+ details[:serviceClass] = leg[:service_class] if leg[:service_class]
257
+ details[:isStopOverAllowed] = leg[:is_stop_over_allowed] if leg[:is_stop_over_allowed]
258
+ details[:destination] = leg[:destination] if leg[:destination]
259
+ details[:fareBasis] = leg[:fare_basis] if leg[:fare_basis]
260
+ details[:departureDate] = leg[:departure_date] if leg[:departure_date]
261
+
262
+ details
263
+ end
264
+
265
+ def add_flight_details(details, obj, leg)
266
+ details[:flight] = {}
267
+ details[:flight][:carrierCode] = leg[:flight][:carrier_code] if leg[:flight][:carrier_code]
268
+ details[:flight][:flightNumber] = leg[:flight][:flight_number] if leg[:flight][:flight_number]
269
+ end
270
+
271
+ def add_split_pay_details(post, options)
272
+ return unless options[:split_pay]
273
+
274
+ split_pay = []
275
+ options[:split_pay].each do |pmnt|
276
+ split = {}
277
+
278
+ split[:linkedAccount] = pmnt[:linked_account]
279
+ split[:amount] = pmnt[:amount].to_i if pmnt[:amount]
280
+ split[:percent] = pmnt[:percent].to_i if pmnt[:percent]
281
+
282
+ split_pay << split
283
+ end
284
+ post[:splitpay] = split_pay
285
+ end
286
+
206
287
  def parse(body)
207
288
  JSON.parse(body)
208
289
  end
@@ -217,7 +298,7 @@ module ActiveMerchant #:nodoc:
217
298
  success,
218
299
  message_from(success, response),
219
300
  response,
220
- authorization: authorization_from(response),
301
+ authorization: authorization_from(action, response),
221
302
  avs_result: AVSResult.new(code: response['avsResponse']),
222
303
  cvv_result: CVVResult.new(response['cvvVerification']),
223
304
  test: test?,
@@ -265,8 +346,12 @@ module ActiveMerchant #:nodoc:
265
346
  "Error(s)- code:#{response['error']['code']}, message:#{response['error']['message']}"
266
347
  end
267
348
 
268
- def authorization_from(response)
269
- response['id']
349
+ def authorization_from(action, response)
350
+ if action == 'profiles'
351
+ response['cards'].first['paymentToken']
352
+ else
353
+ response['id']
354
+ end
270
355
  end
271
356
 
272
357
  def post_data(parameters = {}, options = {})
@@ -151,6 +151,7 @@ module ActiveMerchant
151
151
  else
152
152
  add_three_d_secure(xml, options)
153
153
  end
154
+ add_stored_credential(xml, options)
154
155
  add_comments(xml, options)
155
156
  add_address_and_customer_info(xml, options)
156
157
  end
@@ -323,6 +324,23 @@ module ActiveMerchant
323
324
  end
324
325
  end
325
326
 
327
+ def add_stored_credential(xml, options)
328
+ return unless stored_credential = options[:stored_credential]
329
+
330
+ xml.tag! 'storedcredential' do
331
+ xml.tag! 'type', stored_credential_type(stored_credential[:reason_type])
332
+ xml.tag! 'initiator', stored_credential[:initiator]
333
+ xml.tag! 'sequence', stored_credential[:initial_transaction] ? 'first' : 'subsequent'
334
+ xml.tag! 'srd', stored_credential[:network_transaction_id]
335
+ end
336
+ end
337
+
338
+ def stored_credential_type(reason)
339
+ return 'oneoff' if reason == 'unscheduled'
340
+
341
+ reason
342
+ end
343
+
326
344
  def format_address_code(address)
327
345
  code = [address[:zip].to_s, address[:address1].to_s + address[:address2].to_s]
328
346
  code.collect { |e| e.gsub(/\D/, '') }.reject(&:empty?).join('|')
@@ -62,6 +62,7 @@ module ActiveMerchant #:nodoc:
62
62
  post[:sg_CCToken] = token
63
63
  post[:sg_ExpMonth] = exp_month
64
64
  post[:sg_ExpYear] = exp_year
65
+ post[:sg_Email] = options[:email]
65
66
 
66
67
  commit(post)
67
68
  end
@@ -126,9 +127,11 @@ module ActiveMerchant #:nodoc:
126
127
  private
127
128
 
128
129
  def add_transaction_data(trans_type, post, money, options)
130
+ currency = options[:currency] || currency(money)
131
+
129
132
  post[:sg_TransType] = trans_type
130
- post[:sg_Currency] = (options[:currency] || currency(money))
131
- post[:sg_Amount] = amount(money)
133
+ post[:sg_Currency] = currency
134
+ post[:sg_Amount] = localized_amount(money, currency)
132
135
  post[:sg_ClientLoginID] = @options[:client_login_id]
133
136
  post[:sg_ClientPassword] = @options[:client_password]
134
137
  post[:sg_ResponseFormat] = '4'
@@ -286,7 +286,8 @@ module ActiveMerchant #:nodoc:
286
286
  gsub(%r(((\[card\]|card)\[encrypted_pin\]=)[^&]+(&?)), '\1[FILTERED]\3').
287
287
  gsub(%r(((\[card\]|card)\[encrypted_pin_key_id\]=)[\w=]+(&?)), '\1[FILTERED]\3').
288
288
  gsub(%r(((\[card\]|card)\[number\]=)\d+), '\1[FILTERED]').
289
- gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3')
289
+ gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3').
290
+ gsub(%r(((\[bank_account\]|bank_account)\[account_number\]=)\d+), '\1[FILTERED]')
290
291
  end
291
292
 
292
293
  def supports_network_tokenization?
@@ -379,6 +380,7 @@ module ActiveMerchant #:nodoc:
379
380
  add_destination(post, options)
380
381
  add_level_three(post, options)
381
382
  add_connected_account(post, options)
383
+ add_radar_data(post, options)
382
384
  post
383
385
  end
384
386
 
@@ -570,6 +572,14 @@ module ActiveMerchant #:nodoc:
570
572
  post[:application_fee_amount] = options[:application_fee_amount] if options[:application_fee_amount]
571
573
  end
572
574
 
575
+ def add_radar_data(post, options = {})
576
+ return unless options[:radar_session_id]
577
+
578
+ post[:radar_options] = {
579
+ session: options[:radar_session_id]
580
+ }
581
+ end
582
+
573
583
  def parse(body)
574
584
  JSON.parse(body)
575
585
  end
@@ -20,18 +20,20 @@ module ActiveMerchant #:nodoc:
20
20
  add_capture_method(post, options)
21
21
  add_confirmation_method(post, options)
22
22
  add_customer(post, options)
23
- payment_method = add_payment_method_token(post, payment_method, options)
24
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
23
+ result = add_payment_method_token(post, payment_method, options)
24
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
25
25
 
26
26
  add_external_three_d_secure_auth_data(post, options)
27
27
  add_metadata(post, options)
28
28
  add_return_url(post, options)
29
29
  add_connected_account(post, options)
30
+ add_radar_data(post, options)
30
31
  add_shipping_address(post, options)
31
32
  setup_future_usage(post, options)
32
33
  add_exemption(post, options)
33
34
  add_stored_credentials(post, options)
34
35
  add_ntid(post, options)
36
+ add_claim_without_transaction_id(post, options)
35
37
  add_error_on_requires_action(post, options)
36
38
  request_three_d_secure(post, options)
37
39
 
@@ -48,8 +50,8 @@ module ActiveMerchant #:nodoc:
48
50
 
49
51
  def confirm_intent(intent_id, payment_method, options = {})
50
52
  post = {}
51
- payment_method = add_payment_method_token(post, payment_method, options)
52
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
53
+ result = add_payment_method_token(post, payment_method, options)
54
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
53
55
 
54
56
  CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
55
57
  add_whitelisted_attribute(post, options, attribute)
@@ -58,13 +60,13 @@ module ActiveMerchant #:nodoc:
58
60
  end
59
61
 
60
62
  def create_payment_method(payment_method, options = {})
61
- post_data = create_payment_method_data(payment_method, options)
63
+ post_data = add_payment_method_data(payment_method, options)
62
64
 
63
65
  options = format_idempotency_key(options, 'pm')
64
66
  commit(:post, 'payment_methods', post_data, options)
65
67
  end
66
68
 
67
- def create_payment_method_data(payment_method, options = {})
69
+ def add_payment_method_data(payment_method, options = {})
68
70
  post_data = {}
69
71
  post_data[:type] = 'card'
70
72
  post_data[:card] = {}
@@ -73,6 +75,7 @@ module ActiveMerchant #:nodoc:
73
75
  post_data[:card][:exp_year] = payment_method.year
74
76
  post_data[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
75
77
  add_billing_address(post_data, options)
78
+ add_name_only(post_data, payment_method) if post_data[:billing_details].nil?
76
79
  post_data
77
80
  end
78
81
 
@@ -80,8 +83,8 @@ module ActiveMerchant #:nodoc:
80
83
  post = {}
81
84
  add_amount(post, money, options)
82
85
 
83
- payment_method = add_payment_method_token(post, payment_method, options)
84
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
86
+ result = add_payment_method_token(post, payment_method, options)
87
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
85
88
 
86
89
  add_payment_method_types(post, options)
87
90
  add_customer(post, options)
@@ -98,8 +101,8 @@ module ActiveMerchant #:nodoc:
98
101
  def create_setup_intent(payment_method, options = {})
99
102
  post = {}
100
103
  add_customer(post, options)
101
- payment_method = add_payment_method_token(post, payment_method, options)
102
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
104
+ result = add_payment_method_token(post, payment_method, options)
105
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
103
106
 
104
107
  add_metadata(post, options)
105
108
  add_return_url(post, options)
@@ -178,8 +181,8 @@ module ActiveMerchant #:nodoc:
178
181
  # If customer option is provided, create a payment method and attach to customer id
179
182
  # Otherwise, create a customer, then attach
180
183
  if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
181
- payment_method = add_payment_method_token(params, payment_method, options)
182
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
184
+ result = add_payment_method_token(params, payment_method, options)
185
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
183
186
 
184
187
  if options[:customer]
185
188
  customer_id = options[:customer]
@@ -250,35 +253,36 @@ module ActiveMerchant #:nodoc:
250
253
  end
251
254
 
252
255
  def add_payment_method_token(post, payment_method, options)
253
- return if payment_method.nil?
254
-
255
- if payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
256
- if off_session_request?(options)
257
- post[:payment_method_data] = create_payment_method_data(payment_method, options)
258
- return
259
- else
260
- p = create_payment_method(payment_method, options)
261
- return p unless p.success?
262
-
263
- payment_method = p.params['id']
264
- end
265
- end
266
-
267
256
  case payment_method
268
257
  when StripePaymentToken
269
258
  post[:payment_method] = payment_method.payment_data['id']
270
259
  when String
271
- if payment_method.include?('|')
272
- customer_id, payment_method_id = payment_method.split('|')
273
- token = payment_method_id
274
- post[:customer] = customer_id
275
- else
276
- token = payment_method
277
- end
278
- post[:payment_method] = token
260
+ extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
261
+ when ActiveMerchant::Billing::CreditCard
262
+ get_payment_method_data_from_card(post, payment_method, options)
279
263
  end
264
+ end
280
265
 
281
- post
266
+ def extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
267
+ if payment_method.include?('|')
268
+ customer_id, payment_method = payment_method.split('|')
269
+ post[:customer] = customer_id
270
+ end
271
+
272
+ post[:payment_method] = payment_method
273
+ end
274
+
275
+ def get_payment_method_data_from_card(post, payment_method, options)
276
+ return create_payment_method_and_extract_token(post, payment_method, options) unless off_session_request?(options)
277
+
278
+ post[:payment_method_data] = add_payment_method_data(payment_method, options)
279
+ end
280
+
281
+ def create_payment_method_and_extract_token(post, payment_method, options)
282
+ payment_method_response = create_payment_method(payment_method, options)
283
+ return payment_method_response if payment_method_response.failure?
284
+
285
+ add_payment_method_token(post, payment_method_response.params['id'], options)
282
286
  end
283
287
 
284
288
  def add_payment_method_types(post, options)
@@ -326,6 +330,19 @@ module ActiveMerchant #:nodoc:
326
330
  post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = options[:network_transaction_id] if options[:network_transaction_id]
327
331
  end
328
332
 
333
+ def add_claim_without_transaction_id(post, options = {})
334
+ return if options[:stored_credential] || options[:network_transaction_id] || options[:ds_transaction_id]
335
+ return unless options[:claim_without_transaction_id]
336
+
337
+ post[:payment_method_options] ||= {}
338
+ post[:payment_method_options][:card] ||= {}
339
+ post[:payment_method_options][:card][:mit_exemption] = {}
340
+
341
+ # Stripe PI accepts claim_without_transaction_id for transactions without transaction ids.
342
+ # Gateway validation for this field occurs through a different service, before the transaction request is sent to the gateway.
343
+ post[:payment_method_options][:card][:mit_exemption][:claim_without_transaction_id] = options[:claim_without_transaction_id]
344
+ end
345
+
329
346
  def add_error_on_requires_action(post, options = {})
330
347
  return unless options[:confirm]
331
348
 
@@ -375,6 +392,13 @@ module ActiveMerchant #:nodoc:
375
392
  post[:billing_details][:phone] = billing[:phone] if billing[:phone]
376
393
  end
377
394
 
395
+ def add_name_only(post, payment_method)
396
+ post[:billing_details] = {} unless post[:billing_details]
397
+
398
+ name = [payment_method.first_name, payment_method.last_name].compact.join(' ')
399
+ post[:billing_details][:name] = name
400
+ end
401
+
378
402
  def add_shipping_address(post, options = {})
379
403
  return unless shipping = options[:shipping]
380
404