activemerchant 1.123.0 → 1.124.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.
@@ -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