如何在Rails中干掉Webhook处理程序

时间:2017-01-05 04:29:54

标签: ruby-on-rails ruby

我一直在开发Stripe Webhook处理程序,以根据值创建/更新记录。

这不是很难,如果它像下面这样简单;

StripeEvent.configure do |events|

      events.subscribe 'charge.succeeded' do |event|
        charge = event.data.object
        StripeMailer.receipt(charge).deliver
        StripeMailer.admin_charge_succeeded(charge).deliver
      end
    end

但是,如果我需要有条件地存储数据,那可能会有点麻烦。 在这里,我提取了每个Webhook处理程序并定义了类似stripe_handlers/blahblah_handler.rb

的内容
class InvoicePaymentFailed
      def call(event)
        invoice_obj = event.data.object
        charge_obj = retrieve_charge_obj_of(invoice_obj)
        invoice = Invoice.find_by(stripe_invoice_id: charge_obj[:invoice])

        # common execution for subscription
        invoice.account.subscription.renew_billing_period(start_at: invoice_obj[:period_start], end_at: invoice_obj[:period_end])

        case invoice.state
        when 'pending'
          invoice.fail!(:processing,
                        amount_due: invoice[:amount_due],
                        error: {
                          code: charge_obj[:failure_code],
                          message: charge_obj[:failure_message]
                        })
        when 'past_due'
          invoice.failed_final_attempt!
        end

        invoice.next_attempt_at = Utils.unix_time_to_utc(invoice_obj[:next_payment_attempt].to_i)
        invoice.attempt_count = invoice_obj[:attempt_count].to_i
        invoice.save
      end


      private

      def retrieve_charge_obj_of(invoice)
        charge_obj = Stripe::Charge.retrieve(id: invoice.charge)
        return charge_obj
      rescue Stripe::InvalidRequestError, Stripe::AuthenticationError, Stripe::APIConnectionError, Stripe::StripeError => e
        logger.error e
        logger.error e.backtrace.join("\n")
      end
    end
end

我只是想知道如何干掉这个Webhook处理程序。

是否有一些最佳做法可以接受这个或任何想法?

1 个答案:

答案 0 :(得分:1)

  1. 我建议在retrieve_charge_obj_of中重新引发异常,因为稍后您将获得nil引用异常,这会产生误导。 (原样,您也可以让异常冒出来,让专用的错误处理系统进行抢救,记录并返回有意义的500错误。)

    一个。如果您想要返回500,那么您有一个错误b / c retrieve_charge_obj_of 将在拯救异常后返回nil 。如果charge_objnil,那么此服务将提升NPE,从而产生500.

  2. 如果invoice_obj[:next_payment_attempt]可以是!present?blank?),那么Utils.unix_time_to_utc(invoice_obj[:next_payment_attempt].to_i)应该是什么意思?

    一个。如果是nilfalse'',则#to_i会返回0 - 是否有意? ([] / {}也是blank?但会加注)

  3. 从概念上讲,这个处理程序需要在Invoice上发出状态转换,所以这个逻辑的一部分可以放在模型中:

    class Invoice < ApplicationRecord
      # this method is "internal" to your application, so incoming params should be already "clean"
      def mark_payment_failed!(err_code, err_msg, attempt_count, next_payment_at)
        transaction do # payment processing usually needs to be transactional
          case self.state
          when 'pending'
            err = { code: err_code, message: err_msg }
            self.fail!(:processing, amount_due: self.amount_due, error: err)
          when 'past_due'
            self.failed_final_attempt!
          else
            ex_msg = "some useful data #{state} #{err_code}"
            raise InvalidStateTransition, ex_msg
          end
    
          self.next_attempt_at = next_payment_at
          self.attempt_count = attempt_count
          self.save
        end
      end
    
      class InvalidStateTransition < StandardError; end
    end
    

    注意:我建议在状态和状态之前使用正式的状态机实现(例如state_machine)。过渡失控。

    数据提取,验证和转换应该在处理程序中进行(这就是“处理程序”的用途),它们应该在应用程序中更深入地进行之前发生。在采取任何行动之前,最好及早发现错误,并尽早停止执行。

    我看到还有一些其他边缘情况没有真正处理过。