module Shoppe class Payment < ActiveRecord::Base # Additional callbacks extend ActiveModel::Callbacks define_model_callbacks :refund # The associated order # # @return [Shoppe::Order] belongs_to :order, class_name: 'Shoppe::Order' # An associated payment (only applies to refunds) # # @return [Shoppe::Payment] belongs_to :parent, class_name: "Shoppe::Payment", foreign_key: "parent_payment_id" # Validations validates :amount, numericality: true validates :reference, presence: true validates :method, presence: true # Payments can have associated properties key_value_store :properties # Callbacks after_create :cache_amount_paid after_destroy :cache_amount_paid before_destroy { self.parent.update_attribute(:amount_refunded, self.parent.amount_refunded + amount) if self.parent } # Is this payment a refund? # # @return [Boolean] def refund? self.amount < BigDecimal(0) end # Has this payment had any refunds taken from it? # # @return [Boolean] def refunded? self.amount_refunded > BigDecimal(0) end # How much of the payment can be refunded # # @return [BigDecimal] def refundable_amount refundable? ? (self.amount - self.amount_refunded) : BigDecimal(0) end # Process a refund from this payment. # # @param amount [String] the amount which should be refunded # @return [Boolean] def refund!(amount) run_callbacks :refund do amount = BigDecimal(amount) if refundable_amount >= amount transaction do self.class.create(parent: self, order_id: self.order_id, amount: 0-amount, method: self.method, reference: reference) self.update_attribute(:amount_refunded, self.amount_refunded + amount) true end else raise Shoppe::Errors::RefundFailed, message: I18n.t('.refund_failed', refundable_amount: refundable_amount) end end end # Return a transaction URL for viewing further information about this # payment. # # @return [String] def transaction_url nil end private def cache_amount_paid self.order.update_attribute(:amount_paid, self.order.payments.sum(:amount)) end end end