Class: Payment::Gateways::CreditCard

Inherits:
BasePaymentGateway
  • Object
show all
Defined in:
app/services/payment/gateways/credit_card.rb

Defined Under Namespace

Classes: ReceiptResult, Result

Constant Summary collapse

DETACHED_PM_PATTERNS =
[
  'was previously used with a PaymentIntent without Customer attachment',
  'was detached from a Customer'
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(payment = nil, _delivery = nil) ⇒ CreditCard

Returns a new instance of CreditCard.



9
10
11
# File 'app/services/payment/gateways/credit_card.rb', line 9

def initialize(payment = nil, _delivery = nil)
  @payment = payment
end

Instance Method Details

#apply_pm_details(vault, pm, card, billing) ⇒ Object



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'app/services/payment/gateways/credit_card.rb', line 490

def apply_pm_details(vault, pm, card, billing)
  vault.vault_id = pm.id
  vault.card_type = card.brand
  vault.number = "....#{card.last4}"
  vault.name = pm.billing_details&.name
  vault.exp_month = card.exp_month
  vault.exp_year = card.exp_year

  if billing
    vault.address_line1 = billing.line1
    vault.address_line2 = billing.line2 if billing.line2.present?
    vault.address_city = billing.city
    vault.address_state = billing.state
    vault.zip_code = billing.postal_code
    vault.address_country = billing.country
  end

  vault.address_line1_check = card.checks&.address_line1_check
  vault.address_zip_check = card.checks&.address_postal_code_check
  vault.cvc_check = card.checks&.cvc_check
end

#authorizeObject



13
14
15
16
17
18
19
# File 'app/services/payment/gateways/credit_card.rb', line 13

def authorize
  if @payment.vault_id.present?
    authorize_with_saved_card
  else
    verify_confirmed_payment_intent
  end
end

#cancel_authorization(report_fraud = false) ⇒ Object



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'app/services/payment/gateways/credit_card.rb', line 335

def cancel_authorization(report_fraud = false)
  begin
    reason = report_fraud ? 'fraudulent' : 'requested_by_customer'
    result_obj = Payment::Apis::Stripe.cancel_payment_intent(
      @payment.authorization_code,
      currency: @payment.currency,
      reason: reason
    )
    tx = OrderTransaction.record_stripe(action: 'void', stripe_object: result_obj)
    success = result_obj.status.in?(%w[canceled succeeded pending])
  rescue ::Stripe::InvalidRequestError => e
    if e.message =~ /No such payment_intent/ || e.message =~ /has already been/
      success = true
      tx = OrderTransaction.record_stripe_error(action: 'void', error: e)
    else
      success = false
      tx = OrderTransaction.record_stripe_error(action: 'void', error: e)
    end
  rescue ::Stripe::StripeError => e
    success = false
    tx = OrderTransaction.record_stripe_error(action: 'void', error: e)
  end

  @payment.transactions.push(tx)

  if success
    backfill_payment_intent_id(result_obj&.id)
    logger.info("#{Time.current}: CC Void of Payment ID #{@payment.id} succeeded")
    @payment.payment_voided!
  else
    logger.info("#{Time.current}: CC Void of Payment ID #{@payment.id} failed")
  end
  Result.new(success: success, message: tx&.message)
end

#capture(capture_amount, options = {}) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/services/payment/gateways/credit_card.rb', line 113

def capture(capture_amount, options = {})
  funds_captured = false

  if @payment.captured_on_stripe?
    stripe_captured_cents = @payment.amount_captured_on_stripe.to_i
    our_captured_cents = (@payment.total_captured * 100).to_i
    if stripe_captured_cents > our_captured_cents
      funds_captured = true
      capture_amount = (stripe_captured_cents - our_captured_cents) / 100.0
      logger.info("#{Time.current}: Payment #{@payment.id} was captured externally on Stripe — syncing #{capture_amount}")
    else
      logger.info("#{Time.current}: Payment #{@payment.id} already fully captured on Stripe (#{stripe_captured_cents} cents), remaining authorization released")
      return Result.new(success: false, message: 'The remaining authorization has been released by Stripe. No additional funds can be captured on this payment.')
    end
  else
    resolver = Payment::StrategyResolver.new(@payment)
    order = @payment.order
    capture_opts = order ? resolver.capture_options(order: order) : Payment::StrategyResolver::CaptureOptions.new
    final_capture = options.key?(:final_capture) ? options[:final_capture] : capture_opts.final_capture
    pi_id = @payment.stripe_payment_intent_id.presence || @payment.authorization_code

    begin
      pi = Payment::Apis::Stripe.capture_payment_intent(
        pi_id,
        currency: @payment.currency,
        amount_cents: (capture_amount * 100).to_i,
        final_capture: final_capture
      )
      tx = OrderTransaction.record_stripe(action: 'capture', stripe_object: pi, amount_cents: (capture_amount * 100).to_i)
      funds_captured = pi.status.in?(%w[succeeded requires_capture])
      backfill_payment_intent_id(pi.id) if funds_captured
    rescue ::Stripe::StripeError => e
      tx = OrderTransaction.record_stripe_error(action: 'capture', error: e, amount_cents: (capture_amount * 100).to_i)
      funds_captured = false
      @payment.transactions.push(tx)

      if e.message.include?('capture amount is greater than the amount you can capture')
        retry_result = retry_capture_with_available_amount(pi_id, capture_amount, final_capture)
        if retry_result
          tx = retry_result[:tx]
          capture_amount = retry_result[:capture_amount]
          funds_captured = true
        end
      end
    end
    @payment.transactions.push(tx) unless @payment.transactions.include?(tx)
  end

  if funds_captured
    logger.info("#{Time.current}: #{@payment.authorization_type} payment of #{capture_amount} captured for payment id: #{@payment.id}")
    @payment.reload
    if @payment.total_captured >= @payment.amount
      @payment.payment_captured!
    elsif !final_capture
      logger.info("#{Time.current}: Partial capture (multicapture) for payment id: #{@payment.id}, remaining authorized")
    else
      @payment.payment_captured!
    end
    create_receipt(@payment.invoice, capture_amount, nil)
  else
    logger.info("#{Time.current}: Problem capturing #{@payment.authorization_type} funds for payment id: #{@payment.id}, please review transactions log")
    if @payment.total_captured.positive?
      logger.warn("#{Time.current}: Capture failed but payment #{@payment.id} already has #{@payment.total_captured} captured — keeping current state")
    else
      @payment.transaction_declined!
    end
  end
  Result.new(success: funds_captured)
end

#copy_vault_attrs(source, target) ⇒ Object



512
513
514
515
516
517
# File 'app/services/payment/gateways/credit_card.rb', line 512

def copy_vault_attrs(source, target)
  %i[card_type number name exp_month exp_year address_line1 address_line2
     address_city address_state zip_code address_country].each do |attr|
    target.send(:"#{attr}=", source.send(attr))
  end
end

#create_receipt(invoice, amount, _balance) ⇒ Object



525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# File 'app/services/payment/gateways/credit_card.rb', line 525

def create_receipt(invoice, amount, _balance)
  resource = invoice
  resource ||= @payment.order

  new_receipt = @payment.receipts.new(company: resource.company,
                                      customer: resource.customer,
                                      category: 'Credit Card',
                                      amount: amount,
                                      reference: @payment.reference,
                                      card_type: @payment.card_type,
                                      currency: @payment.currency,
                                      payment: @payment,
                                      bank_account: resource.company.,
                                      gl_date: Date.current,
                                      receipt_date: Date.current,
                                      email: @payment.email,
                                      remark: @payment.authorization_code.present? ? "Auth Code: #{@payment.authorization_code}" : nil)

  if resource.is_a?(Invoice)
    new_receipt.receipt_details << ReceiptDetail.new(category: 'Invoice', invoice: resource, amount: amount,
                                                     gl_date: Date.current)
  end

  begin
    new_receipt.save!
    logger.info("#{Time.current}: Created new receipt id: #{new_receipt.id}")
    success = true
  rescue StandardError => e
    success = false
    report_exception e, payment_id: @payment.id, message: 'Payment purchase completed but no receipt'
  end
  ReceiptResult.new(success: success, receipt: new_receipt)
end

#creditObject



370
371
372
# File 'app/services/payment/gateways/credit_card.rb', line 370

def credit
  # Not in use
end

#detach_old_pm(old_vault_id, currency) ⇒ Object



519
520
521
522
523
# File 'app/services/payment/gateways/credit_card.rb', line 519

def detach_old_pm(old_vault_id, currency)
  Payment::Apis::Stripe.detach_payment_method(stripe_payment_method_id(old_vault_id), currency: currency)
rescue ::Stripe::StripeError => e
  logger.warn("#{Time.current}: Failed to detach old PM #{old_vault_id}: #{e.message}")
end

#find_matching_hidden_vault(customer, card) ⇒ Object



481
482
483
484
485
486
487
488
# File 'app/services/payment/gateways/credit_card.rb', line 481

def find_matching_hidden_vault(customer, card)
  customer.credit_card_vaults.hidden_without_consent.find_by(
    number: "....#{card.last4}",
    exp_month: card.exp_month,
    exp_year: card.exp_year,
    card_type: card.brand
  )
end

#increment_authorization(new_payment_amount) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'app/services/payment/gateways/credit_card.rb', line 183

def increment_authorization(new_payment_amount)
  return Result.new(success: false, message: 'No PaymentIntent ID') unless @payment.stripe_payment_intent_id.present?

  if any_capture_on_shared_pi?
    return Result.new(success: false, message: 'Cannot increment authorization after a capture has been made on this PaymentIntent')
  end

  return Result.new(success: false, message: 'New amount must be greater than current payment amount') if new_payment_amount <= @payment.amount

  target_pi_cents = (new_payment_amount * 100).to_i

  begin
    current_pi = Payment::Apis::Stripe.retrieve_payment_intent(
      @payment.stripe_payment_intent_id,
      currency: @payment.currency,
      expand: []
    )

    if current_pi.amount >= target_pi_cents
      @payment.update!(amount: new_payment_amount)
      store_stripe_capabilities(current_pi)
      logger.info("#{Time.current}: Payment #{@payment.id} amount updated to #{new_payment_amount} — Stripe PI already at #{current_pi.amount / 100.0}, no increment needed")
      return Result.new(success: true)
    end

    pi = Payment::Apis::Stripe.increment_authorization(
      @payment.stripe_payment_intent_id,
      amount_cents: target_pi_cents,
      currency: @payment.currency,
      description: "Incremented for order #{@payment.order&.reference_number}"
    )
    tx = OrderTransaction.record_stripe(
      action: 'incremental_authorization',
      stripe_object: pi,
      amount_cents: target_pi_cents
    )
  rescue ::Stripe::StripeError => e
    tx = OrderTransaction.record_stripe_error(
      action: 'incremental_authorization',
      error: e,
      amount_cents: target_pi_cents
    )
  end

  @payment.transactions.push(tx)

  if tx.success?
    @payment.update!(amount: new_payment_amount)
    store_stripe_capabilities(pi)
    logger.info("#{Time.current}: Incremental auth for payment #{@payment.id} increased to #{new_payment_amount} (PI total: #{target_pi_cents / 100.0})")
    Result.new(success: true)
  else
    logger.info("#{Time.current}: Incremental auth failed for payment #{@payment.id}: #{tx.message}")
    Result.new(success: false, message: tx.message)
  end
end

#purchase(receipt, options = {}) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/services/payment/gateways/credit_card.rb', line 21

def purchase(receipt, options = {})
  client_confirmed = @payment.card_token&.start_with?('pi_') && @payment.vault_id.nil?
  confirmed_pi = nil

  if @payment.vault_id.nil? && @payment.card_token.present? && @payment.store_card.to_b
    card_token_for_vault = @payment.card_token

    if client_confirmed
      confirmed_pi = Payment::Apis::Stripe.retrieve_payment_intent(@payment.card_token, currency: @payment.currency)
      pm = confirmed_pi.payment_method
      card_token_for_vault = pm.is_a?(String) ? pm : pm&.id
    end

    if card_token_for_vault.present?
      cc_vault = @payment.customer.credit_card_vaults.new(
        card_token: card_token_for_vault, description: @payment.store_card_name,
        hidden: false, issuer_number: @payment.issuer_number,
        consent_given_at: Time.current,
        consent_channel: @payment.try(:consent_channel) || 'crm_phone'
      )
      res = cc_vault.store_card(@payment.email)
      if res == false
        return Result.new(success: false, message: "Error processing card: #{cc_vault.errors_to_s}")
      end
      @payment.vault_id = cc_vault.vault_id
    end
  end

  begin
    if client_confirmed
      pi = confirmed_pi || Payment::Apis::Stripe.retrieve_payment_intent(@payment.card_token, currency: @payment.currency)
    else
      pi = Payment::Apis::Stripe.create_payment_intent(
        amount_cents: (@payment.amount * 100).to_i,
        currency: @payment.currency,
        customer_id: resolve_stripe_customer_id,
        payment_method: stripe_payment_method_id(@payment.vault_id.presence) || @payment.card_token,
        capture_method: 'automatic',
        confirm: true,
        off_session: true,
        metadata: ,
        description: stripe_description,
        statement_descriptor: stripe_statement_descriptor
      )
    end
    tx = OrderTransaction.record_stripe(action: 'purchase', stripe_object: pi, amount_cents: pi.amount)
  rescue ::Stripe::StripeError => e
    tx = OrderTransaction.record_stripe_error(action: 'purchase', error: e, amount_cents: (@payment.amount * 100).to_i)
    mark_vault_detached_if_applicable(e) unless client_confirmed
  end

  @payment.transactions.push(tx)
  process_stripe_tx_results(tx, pi)

  if tx.success?
    logger.info("#{Time.current}: CC Purchase of Payment ID #{@payment.id} Amount #{@payment.amount} succeeded")
    @payment.purchase_complete!
    if receipt.nil?
      logger.info("#{Time.current}: Skipping receipt for Payment ID #{@payment.id} as this is for a Payment")
      receipt_ok = true
      message = 'ok'
    else
      receipt.reference = @payment.reference
      receipt.card_type = @payment.card_type
      if @payment.authorization_code.present?
        trans_id = "Auth Code: #{@payment.authorization_code}"
        receipt.remark = (receipt.remark.present? ? "#{receipt.remark} / #{trans_id}" : trans_id)
      end
      receipt.payment = @payment
      message = 'ok'
      begin
        receipt.save!
        receipt_ok = true
        logger.info("#{Time.current}: Created new receipt id: #{receipt.id}")
      rescue StandardError => e
        receipt_ok = false
        report_exception e, payment_id: @payment.id, message: 'Payment purchase completed but no receipt'
      end
    end
  else
    receipt_ok = false
    logger.info("#{Time.current}: CC Purchase of Payment ID #{@payment.id} Amount #{@payment.amount} failed")
    @payment.transaction_declined!
    @payment.last_response = tx.message
    message = tx.message
  end
  Result.new(success: (tx.success? && receipt_ok == true), message: message, receipt: receipt)
rescue ActiveRecord::ActiveRecordError => e
  decline_payment_safely(tx&.message || e.message)
  Result.new(success: false, message: tx&.message || e.message)
end

#reauthorizeObject



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'app/services/payment/gateways/credit_card.rb', line 256

def reauthorize
  order = @payment.order
  return Result.new(success: false, message: 'not authorized') unless @payment.authorized?
  return Result.new(success: false, message: 'no vault on file') unless @payment.credit_card_vault.present?

  if @payment.captured_on_stripe?
    return Result.new(success: false, message: 'already captured on Stripe')
  end

  attrs = {
    currency: @payment.currency,
    category: @payment.category,
    po_number: @payment.po_number,
    amount: @payment.amount,
    authorization_type: @payment.authorization_type,
    vault_id: @payment.vault_id,
    email: @payment.email,
    remote_ip_address: @payment.remote_ip_address
  }
  res = Payment::OrderProcessor.new(order, attrs).process

  if res.result.in?(%w[fully_authorized partially_authorized])
    cancel_old_authorization
    @payment.payment_expired!
    Result.new(success: true)
  else
    logger.warn("Payment #{@payment.id}: reauth failed (#{res.error}), capturing existing authorization as fallback")
    capture(@payment.amount, final_capture: true)
  end
end

#refund(refund_amount, credit_memo) ⇒ Object



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'app/services/payment/gateways/credit_card.rb', line 374

def refund(refund_amount, credit_memo)
  begin
    refund_obj = Payment::Apis::Stripe.create_refund(
      payment_intent_id: @payment.authorization_code,
      currency: @payment.currency,
      amount_cents: (refund_amount * 100).to_i
    )
    tx = OrderTransaction.record_stripe(action: 'refund', stripe_object: refund_obj, amount_cents: (refund_amount * 100).to_i)
  rescue ::Stripe::StripeError => e
    tx = OrderTransaction.record_stripe_error(action: 'refund', error: e, amount_cents: (refund_amount * 100).to_i)
  end

  @payment.transactions.push(tx)

  if tx.success?
    backfill_payment_intent_id(refund_obj&.payment_intent)
    logger.info("#{Time.current}: #{@payment.authorization_type} Refund of Payment ID #{@payment.id} Amount #{refund_amount} succeeded")
    @payment.payment_refunded!
     = @payment.invoice&.company&. || credit_memo.company.
    new_receipt = Receipt.new(company: credit_memo.company,
                              customer: credit_memo.customer,
                              category: 'Credit Card',
                              amount: -refund_amount,
                              reference: @payment.reference,
                              card_type: @payment.card_type,
                              currency: @payment.currency,
                              payment: @payment,
                              bank_account: ,
                              gl_date: Date.current,
                              receipt_date: Date.current)
    new_receipt.receipt_details << ReceiptDetail.new(category: 'Credit Memo', credit_memo: credit_memo, amount: -refund_amount,
                                                     gl_date: Date.current)
    begin
      new_receipt.save!
      receipt_ok = true
      logger.info("#{Time.current}: Created new receipt id: #{new_receipt.id}")
    rescue StandardError => e
      receipt_ok = false
      report_exception e, payment_id: @payment.id, message: 'Payment refunded but no receipt'
    end
  else
    logger.info("#{Time.current}: Refund of Payment ID #{@payment.id} Amount #{refund_amount} failed")
  end
  Result.new(success: (tx.success? && receipt_ok))
end

#register_shared_authorization(payment_intent, amount_cents:) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'app/services/payment/gateways/credit_card.rb', line 240

def register_shared_authorization(payment_intent, amount_cents:)
  tx = OrderTransaction.record_stripe(
    action: 'authorization',
    stripe_object: payment_intent,
    amount_cents: amount_cents
  )
  @payment.transactions.push(tx)
  process_stripe_tx_results(tx, payment_intent)
  store_stripe_capabilities(payment_intent)
  @payment.payment_authorized!
  Result.new(success: true)
rescue StandardError => e
  logger.error("Failed to register shared auth for payment #{@payment.id}: #{e.message}")
  Result.new(success: false, message: e.message)
end

#release_remaining_authorizationObject



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'app/services/payment/gateways/credit_card.rb', line 297

def release_remaining_authorization
  pi_id = @payment.stripe_payment_intent_id.presence || @payment.authorization_code

  begin
    if Payment::Apis::Stripe.payment_intent?(pi_id)
      result_obj = Payment::Apis::Stripe.capture_payment_intent(
        pi_id,
        currency: @payment.currency,
        amount_cents: 0,
        final_capture: true
      )
      tx = OrderTransaction.record_stripe(action: 'void', stripe_object: result_obj, amount_cents: 0)
      success = result_obj.status.in?(%w[succeeded canceled])
    else
      result_obj = Payment::Apis::Stripe.refund_charge(
        pi_id,
        currency: @payment.currency
      )
      tx = OrderTransaction.record_stripe(action: 'void', stripe_object: result_obj, amount_cents: 0)
      success = result_obj.status.in?(%w[succeeded pending])
    end
  rescue ::Stripe::StripeError => e
    tx = OrderTransaction.record_stripe_error(action: 'void', error: e)
    success = false
  end

  @payment.transactions.push(tx)

  if success
    backfill_payment_intent_id(result_obj&.id)
    logger.info("#{Time.current}: Released remaining authorization for payment #{@payment.id} (captured: #{@payment.total_captured})")
    @payment.payment_voided!
  else
    logger.info("#{Time.current}: Failed to release remaining authorization for payment #{@payment.id}")
  end
  Result.new(success: success, message: tx&.message)
end

#store(cc_vault, email) ⇒ Object



420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'app/services/payment/gateways/credit_card.rb', line 420

def store(cc_vault, email)
  currency = cc_vault.customer.catalog&.currency || 'USD'
  customer_id = cc_vault.customer.stripe_customer_id

  if customer_id.blank?
    begin
      stripe_customer = Payment::Apis::Stripe.create_customer(
        currency: currency,
        email: email,
        name: cc_vault.customer.full_name,
        metadata: { customer_id: cc_vault.customer.id }
      )
      customer_id = stripe_customer.id
      cc_vault.customer.update(stripe_customer_id: customer_id)
    rescue ::Stripe::StripeError => e
      cc_vault.errors.add(:base, e.message)
      return Result.new(success: false, message: e.message)
    end
  end

  begin
    pm = Payment::Apis::Stripe.attach_payment_method(
      cc_vault.card_token, customer_id: customer_id, currency: currency
    )

    card = pm.card
    billing = pm.billing_details&.address

    existing = find_matching_hidden_vault(cc_vault.customer, card) if cc_vault.consented?
    if existing
      old_vault_id = existing.vault_id
      apply_pm_details(existing, pm, card, billing)
      existing.hidden = false
      existing.description = cc_vault.description if cc_vault.description.present?
      existing.consent_given_at = cc_vault.consent_given_at
      existing.consent_channel = cc_vault.consent_channel
      success = existing.save
      detach_old_pm(old_vault_id, currency) if success && old_vault_id != pm.id
      cc_vault.vault_id = existing.vault_id
      copy_vault_attrs(existing, cc_vault)
      Result.new(success: success, message: success ? 'ok' : existing.errors.full_messages.join(', '))
    else
      apply_pm_details(cc_vault, pm, card, billing)
      success = cc_vault.save
      Result.new(success: success, message: success ? 'ok' : cc_vault.errors.full_messages.join(', '))
    end
  rescue ::Stripe::StripeError => e
    cc_vault.errors.add(:base, e.message)
    Result.new(success: false, message: e.message)
  end
end

#unstore(cc_vault) ⇒ Object



472
473
474
475
476
477
478
479
# File 'app/services/payment/gateways/credit_card.rb', line 472

def unstore(cc_vault)
  return if cc_vault.vault_id.blank?

  currency = cc_vault.customer&.catalog&.currency || 'USD'
  Payment::Apis::Stripe.detach_payment_method(stripe_payment_method_id(cc_vault.vault_id), currency: currency)
rescue ::Stripe::StripeError => e
  logger.warn("#{Time.current}: Failed to detach PaymentMethod #{cc_vault.vault_id}: #{e.message}")
end

#void(report_fraud = false) ⇒ Object



287
288
289
290
291
292
293
294
295
# File 'app/services/payment/gateways/credit_card.rb', line 287

def void(report_fraud = false)
  return Result.new(success: false) unless @payment.authorized?

  if @payment.total_captured.positive?
    release_remaining_authorization
  else
    cancel_authorization(report_fraud)
  end
end