module JamRuby class RecurlyTransactionWebHook < ActiveRecord::Base attr_accessible :admin_description, :jam_track_id, as: :admin belongs_to :user, class_name: 'JamRuby::User' belongs_to :sale_line_item, class_name: 'JamRuby::SaleLineItem', foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid', inverse_of: :recurly_transactions belongs_to :sale, class_name: 'JamRuby::Sale', foreign_key: 'invoice_id', primary_key: 'recurly_invoice_id', inverse_of: :recurly_transactions # when we know what JamTrack this refund is related to, we set this value belongs_to :jam_track, class_name: 'JamRuby::JamTrack' validates :recurly_transaction_id, presence: true validates :action, presence: true validates :status, presence: true validates :amount_in_cents, numericality: {only_integer: true} validates :user, presence: true SUCCESSFUL_PAYMENT = 'payment' FAILED_PAYMENT = 'failed_payment' REFUND = 'refund' VOID = 'void' HOOK_TYPES = [SUCCESSFUL_PAYMENT, FAILED_PAYMENT, REFUND, VOID] def is_credit_type? transaction_type == REFUND || transaction_type == VOID end def is_voided? transaction_type == VOID end def is_refund? transaction_type == REFUND end def self.is_transaction_web_hook?(document) return false if document.root.nil? case document.root.name when 'successful_payment_notification' true when 'successful_refund_notification' true when 'failed_payment_notification' true when 'void_payment_notification' true else false end end def admin_url APP_CONFIG.admin_root_url + "/admin/recurly_hooks/" + id end # see spec for examples of XML def self.create_from_xml(document) transaction = RecurlyTransactionWebHook.new case document.root.name when 'successful_payment_notification' transaction.transaction_type = SUCCESSFUL_PAYMENT when 'successful_refund_notification' transaction.transaction_type = REFUND when 'failed_payment_notification' transaction.transaction_type = FAILED_PAYMENT when 'void_payment_notification' transaction.transaction_type = VOID else raise 'unknown document type ' + document.root.name end transaction.recurly_transaction_id = document.at_css('transaction id').content transaction.user_id = document.at_css('account account_code').content transaction.subscription_id = document.at_css('subscription_id').content transaction.invoice_id = document.at_css('invoice_id').content transaction.invoice_number_prefix = document.at_css('invoice_number_prefix').content transaction.invoice_number = document.at_css('invoice_number').content transaction.action = document.at_css('action').content transaction.status = document.at_css('status').content transaction.transaction_at = Time.parse(document.at_css('date').content) transaction.amount_in_cents = document.at_css('amount_in_cents').content transaction.reference = document.at_css('reference').content transaction.message = document.at_css('message').content transaction.save! # now that we have the transaction saved, we also need to delete the jam_track_right if this is a refund, or voided if transaction.transaction_type == 'refund' || transaction.transaction_type == 'void' sale = Sale.find_by_recurly_invoice_id(transaction.invoice_id) if sale && sale.is_jam_track_sale? if sale.sale_line_items.length == 1 if sale.recurly_total_in_cents == transaction.amount_in_cents jam_track = sale.sale_line_items[0].product jam_track_right = jam_track.right_for_user(transaction.user) if jam_track if jam_track_right jam_track_right.destroy # associate which JamTrack we assume this is related to in this one success case transaction.jam_track = jam_track transaction.save! AdminMailer.recurly_alerts(transaction.user, { subject: "NOTICE: #{transaction.user.email} has had JamTrack: #{jam_track.name} revoked", body: "A #{transaction.transaction_type} event came from Recurly for sale with Recurly invoice ID #{sale.recurly_invoice_id}. We deleted their right to the track in our own database as a result." }).deliver else AdminMailer.recurly_alerts(transaction.user, { subject: "NOTICE: #{transaction.user.email} got a refund, but unable to find JamTrackRight to delete", body: "This should just mean the user already has no rights to the JamTrackRight when the refund came in. Not a big deal, but sort of weird..." }).deliver end else AdminMailer.recurly_alerts(transaction.user, { subject: "ACTION REQUIRED: #{transaction.user.email} got a refund it was not for total value of a JamTrack sale", body: "We received a #{transaction.transaction_type} notice for an amount that was not the same as the original sale. So, no action was taken in the database. sale total: #{sale.recurly_total_in_cents}, refund amount: #{transaction.amount_in_cents}" }).deliver end else AdminMailer.recurly_alerts(transaction.user, { subject: "ACTION REQUIRED: #{transaction.user.email} has refund on invoice with multiple JamTracks", body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks" }).deliver end else AdminMailer.recurly_alerts(transaction.user, { subject: "ACTION REQUIRED: #{transaction.user.email} has refund with no correlator to sales", body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks" }).deliver end end transaction end end end