2017-01-05 3 views
-1

Я разрабатывал обработчик Stripe Webhook для создания/обновления записей в зависимости от значений.Как высушить обработчики Webhook в Rails

Это не очень сложно, если это так просто, как показано ниже;

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

Я голосую, чтобы закрыть этот вопрос как не относящийся к теме, потому что вопросы о работе с рефакторингом лучше подходят для http://codereview.stackexchange.com/. –

ответ

1
  1. Я предлагаю вновь поднять исключение в retrieve_charge_obj_of, так как вы просто получите nil ссылочный исключение в дальнейшем, которая вводит в заблуждение. (Как есть, вы могли бы также позволить исключению пузыря вверх и позволить специальной системе обработки ошибок спасти, зарегистрировать и вернуть значимую ошибку 500.)

    a. Если вы не хотите вернуть 500, значит у вас есть ошибка b/c retrieve_charge_obj_ofвернет ноль после того, как исключение будет спасено. И если charge_obj это nil, то эта услуга поднимет NPE, в результате чего в 500

  2. если invoice_obj[:next_payment_attempt] может быть !present? (blank?), то что же Utils.unix_time_to_utc(invoice_obj[:next_payment_attempt].to_i) должно означать?

    a. Если это было nil, false, или '', #to_i возвращает 0 - это предназначено? ([]/{} также blank? но поднимет)

Концептуально, этот обработчик должен выдать состояние перехода на 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) перед состояниями & переходы выходят из-под контроля.

В обработчике должно выполняться извлечение, проверка и преобразование данных (для этого нужны «обработчики»), и они должны произойти до того, как вы будете углубляться в ваше приложение. Ошибки лучше всего поймать досрочно, и выполнение остановилось раньше, прежде чем предпринять какие-либо действия.

Есть еще некоторые другие случаи, которые я вижу, которые на самом деле не обрабатываются.

+0

Спасибо за совет. Для 1, да, я согласен. Полоса повторяет веб-чек, если ответ не 4xx. Однако было бы лучше запросить Stripe API явно. Для 2 метод Utils.unix_time_utc - это метод утилиты, который преобразует время unix в utc datetime. – Tosh

+0

Я думаю, вы пропустили самые важные моменты обоих моих комментариев. Я добавил некоторые подробности, пожалуйста, взгляните еще раз. – Kache

+0

Еще раз спасибо. Для 1, вы имеете в виду, если 'retrieve_charge_obj_of' возвращает' nil', я мог бы повторно создать исключение? Для 2, это моя ошибка. 'invoice_obj [: next_payment_attempt]' всегда заполняется, если существует 'charge_obj'. – Tosh

Смежные вопросы