Class: Payment::StrategyResolver
- Inherits:
-
BaseService
- Object
- BaseService
- Payment::StrategyResolver
- Defined in:
- app/services/payment/strategy_resolver.rb
Overview
Decides capture/reauthorization strategy for a Payment based on
upstream gateway capabilities (Stripe multicapture, incremental and
extended auth) and PayPal's 29-day window. Encapsulates the deadline
math the model needs without bloating it.
Defined Under Namespace
Classes: CaptureOptions, Deadline, ReauthResult, Result
Constant Summary collapse
- REAUTH_BUFFER_DAYS =
2- LEGACY_AUTH_WINDOW_DAYS =
6- EXTENDED_AUTH_WINDOW_DAYS =
28- PAYPAL_AUTH_WINDOW_DAYS =
3
Instance Attribute Summary
Attributes inherited from BaseService
Instance Method Summary collapse
-
#authorization_deadline ⇒ Payment::StrategyResolver::Deadline
When the payment must be captured by.
-
#capture_options(order:) ⇒ Payment::StrategyResolver::CaptureOptions
Decide whether the next capture against
ordershould be markedfinal_capture(releasing remaining auth) or kept open. -
#increase_strategy(new_amount) ⇒ Payment::StrategyResolver::Result
Pick the cheapest path to increase a Payment's authorized total to
new_amount: an incremental authorization on Stripe when the PI supports it, a void-and-reauth against the stored vault otherwise, or a brand-new payment if no vault is on file. -
#initialize(payment) ⇒ StrategyResolver
constructor
A new instance of StrategyResolver.
- #needs_reauthorization? ⇒ Boolean
- #supports_extended_authorization? ⇒ Boolean
- #supports_incremental_authorization? ⇒ Boolean
- #supports_multicapture? ⇒ Boolean
Methods inherited from BaseService
#log_debug, #log_error, #log_info, #log_warning, #logger, #process, #tagged_logger
Constructor Details
#initialize(payment) ⇒ StrategyResolver
Returns a new instance of StrategyResolver.
28 29 30 |
# File 'app/services/payment/strategy_resolver.rb', line 28 def initialize(payment) @payment = payment end |
Instance Method Details
#authorization_deadline ⇒ Payment::StrategyResolver::Deadline
When the payment must be captured by. Prefers the gateway-reported
capture_before; otherwise uses a heuristic window based on the
gateway and capability flags (PayPal 3d, Stripe extended-auth 28d,
legacy Stripe 6d).
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'app/services/payment/strategy_resolver.rb', line 110 def if @payment.capture_before.present? remaining = ((@payment.capture_before - Time.current) / 1.day).ceil source = paypal? ? :paypal_api : :stripe_api Deadline.new(capture_before: @payment.capture_before, days_remaining: remaining, source: source) else fallback_days = if paypal? PAYPAL_AUTH_WINDOW_DAYS elsif EXTENDED_AUTH_WINDOW_DAYS else LEGACY_AUTH_WINDOW_DAYS end fallback = @payment.created_at + fallback_days.days remaining = ((fallback - Time.current) / 1.day).ceil Deadline.new(capture_before: fallback, days_remaining: remaining, source: :heuristic) end end |
#capture_options(order:) ⇒ Payment::StrategyResolver::CaptureOptions
Decide whether the next capture against order should be marked
final_capture (releasing remaining auth) or kept open. Returns a
final_capture: true option for non-CC payments, single deliveries,
or gateways without multicapture; otherwise leaves the auth open.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'app/services/payment/strategy_resolver.rb', line 51 def (order:) unless credit_card? || paypal? return CaptureOptions.new(final_capture: true, reason: 'non-gateway payment') end if credit_card? && !supports_multicapture? return CaptureOptions.new(final_capture: true, reason: 'multicapture not available') end if cross_order_siblings_pending? return CaptureOptions.new(final_capture: false, reason: 'cross-order siblings still authorized') end unshipped = order.deliveries.select { |d| !d.shipped? && !d.invoiced? } if unshipped.size > 1 CaptureOptions.new(final_capture: false, reason: "#{unshipped.size} deliveries still pending") else CaptureOptions.new(final_capture: true, reason: 'last or only delivery') end end |
#increase_strategy(new_amount) ⇒ Payment::StrategyResolver::Result
Pick the cheapest path to increase a Payment's authorized total
to new_amount: an incremental authorization on Stripe when the PI
supports it, a void-and-reauth against the stored vault otherwise,
or a brand-new payment if no vault is on file.
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'app/services/payment/strategy_resolver.rb', line 79 def increase_strategy(new_amount) return Result.new(strategy: :new_payment, reason: 'not a credit card') unless credit_card? if && @payment. && @payment.stripe_payment_intent_id.present? Result.new(strategy: :increment, payment: @payment, reason: 'incremental authorization available') elsif @payment.credit_card_vault.present? Result.new(strategy: :void_and_reauth, payment: @payment, vault: @payment.credit_card_vault, reason: 'no incremental auth; vault available for void+reauth') else Result.new(strategy: :new_payment, reason: 'no incremental auth and no vault') end end |
#needs_reauthorization? ⇒ Boolean
92 93 94 95 96 97 98 99 100 101 102 |
# File 'app/services/payment/strategy_resolver.rb', line 92 def return ReauthResult.new(reauth_needed: false, reason: 'not authorized') unless @payment. return ReauthResult.new(reauth_needed: false, reason: 'not a credit card') unless credit_card? deadline = if deadline.capture_before.present? && deadline.days_remaining <= REAUTH_BUFFER_DAYS ReauthResult.new(reauth_needed: true, reason: "capture_before in #{deadline.days_remaining} days (#{deadline.source})") else ReauthResult.new(reauth_needed: false, reason: "#{deadline.days_remaining} days remaining (#{deadline.source})") end end |
#supports_extended_authorization? ⇒ Boolean
40 41 42 |
# File 'app/services/payment/strategy_resolver.rb', line 40 def credit_card? && capability_value(:extended_authorization) == 'enabled' end |
#supports_incremental_authorization? ⇒ Boolean
36 37 38 |
# File 'app/services/payment/strategy_resolver.rb', line 36 def credit_card? && capability_value(:incremental_authorization) == 'available' end |
#supports_multicapture? ⇒ Boolean
32 33 34 |
# File 'app/services/payment/strategy_resolver.rb', line 32 def supports_multicapture? credit_card? && capability_value(:multicapture) == 'available' end |