Class: Spree::BankPayments::ApplyTransfer
- Inherits:
-
Object
- Object
- Spree::BankPayments::ApplyTransfer
- Defined in:
- app/services/spree/bank_payments/apply_transfer.rb
Overview
The single place money actually moves. IngestTransfer calls this with no
applied_by for automatic application; the admin hand-match queue
(Task 13) calls it with the acting admin user. One transaction, one path
— no second implementation of "what applying a transfer means".
Class Method Summary collapse
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(transfer:, payment_session:, applied_by: nil) ⇒ ApplyTransfer
constructor
A new instance of ApplyTransfer.
Constructor Details
#initialize(transfer:, payment_session:, applied_by: nil) ⇒ ApplyTransfer
Returns a new instance of ApplyTransfer.
12 13 14 15 16 |
# File 'app/services/spree/bank_payments/apply_transfer.rb', line 12 def initialize(transfer:, payment_session:, applied_by: nil) @transfer = transfer @payment_session = payment_session @applied_by = applied_by end |
Class Method Details
.call ⇒ Object
8 9 10 |
# File 'app/services/spree/bank_payments/apply_transfer.rb', line 8 def self.call(...) new(...).call end |
Instance Method Details
#call ⇒ Object
18 19 20 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 |
# File 'app/services/spree/bank_payments/apply_transfer.rb', line 18 def call ActiveRecord::Base.transaction do # Payment completes before the session. Spree::PaymentSession uses # publishes_lifecycle_events, which publishes synchronously inside # this transaction — completing the session first would let a # "session completed" event (plausibly a customer receipt) escape # before we know the payment itself will actually complete. If # payment.complete! raises, the DB rolls back but that event # cannot be recalled. payment = payment_session.find_or_create_payment! # find_or_create_payment! (Spree core) stamps the *session's* # amount onto the payment -- correct on the automatic path, where # IngestTransfer only ever gets here on an exact amount/currency # match, so this is a no-op there. On the manual admin path (Task # 13) an admin can confirm applying a transfer to a session whose # amount doesn't match what actually arrived; crediting the # session's amount in that case would mark the order 'paid' for # money it never received. Re-stamp the payment with what the # transfer says actually arrived before completing it, so Spree's # own payment_state computation reflects the real balance # (balance_due/credit_owed) instead of a false 'paid'. # # Only mutate while the payment is still editable (pre-completion): # Payment#editable? is checkout/pending, and completing flips # payment_total via the state machine, so touching amount after # completion would need a second recalculation this class doesn't # own. `find_or_create_payment!` always returns either a payment it # just created (checkout state) or one it found by the same # response_code -- if that existing payment is already completed # (shouldn't happen: the `applied?` guard upstream prevents # re-applying), leave its amount alone rather than mutate a # finalized record. if !payment.completed? && payment.amount != transfer.amount payment.update!(amount: transfer.amount) end payment.complete! unless payment.completed? payment_session.complete! unless payment_session.completed? attrs = { state: 'applied', payment_session: payment_session } if applied_by attrs[:applied_by_id] = applied_by.id attrs[:applied_at] = Time.current end transfer.update!(attrs) end transfer end |