如果错误,我会尝试回退订阅更改,但是我很难确定这样做的好方法。我现在不希望使用网络挂钩。
有更好的方法吗?如您所见,我正在尝试事件,然后手动回滚每个事件。我觉得这需要很好的重构。
def change_plan(subscription:, to_plan:, coupon: nil)
require_args(subscription: subscription, to_plan: to_plan)
events = []
begin
from_plan = subscription.plan
api_sub = update_subscription_plan(subscription: subscription, to_plan: to_plan,
coupon: coupon, proration_date: get_proration_date, prorate: true)
events<<'api_updated'
subscription.update!(plan: to_plan,
coupon: coupon,
item_api_id: api_sub.items.data[0].id)
events<<'db_updated'
invoice = create_invoice(subscription)
events<<'created_invoice'
invoice = invoice.pay
events<<'invoice_paid'
rescue Stripe::StripeError => e
subscription.errors[:base] << e.message
if events.include?('api_updated')
stripe_sub = update_subscription_plan(subscription: subscription, to_plan: from_plan,
coupon: coupon, proration_date: get_proration_date, prorate: false)
end
if events.include?('db_updated')
subscription.update!(plan: from_plan,
coupon: coupon,
item_api_id: api_sub.items.data[0].id)
end
if events.include?('created_invoice')
void_invoice(invoice.id)
end
subscription.errors[:base] << "Upgrade failed, please contact us."
end
subscription
end
您可以将交互器模式与出色的interactor
宝石一起使用,您可以在此处找到https://github.com/collectiveidea/interactor。交互器就像服务,只有一种目的的小类,被一一称为。 gem提供了rollback
方法,因此您可以轻松回滚任何进程。
值得一提的是,这里有互动者和组织者。您可以将交互程序视为服务,将组织者视为按定义顺序触发的一组链接的交互程序。您可以创建ChangePlan
管理器:
class ChangePlan
include Interactor::Organizer
organize UpdateSubscriptionPlan, UpdateSubscription, CreateInvoice, PayInvoice
end
这里是您的交互器以及rollback
方法,在发生Stripe::StripeError
错误的情况下,这些方法将还原更改。
class UpdateSubscriptionPlan
include Interactor
def call
context.api_sub = update_subscription_plan(
subscription: context.subscription,
to_plan: context.to_plan,
coupon: context.coupon,
proration_date: get_proration_date,
prorate: true
)
rescue Stripe::StripeError => error
context.fail!(error: error.message)
end
def rollback
update_subscription_plan(
subscription: context.subscription,
to_plan: context.from_plan,
coupon: context.coupon,
proration_date: get_proration_date,
prorate: false
)
end
end
class UpdateSubscription
include Interactor
delegate :subscription, :to_plan, :coupon, :api_sub, :from_plan, to: :context
def call
subscription.update!(
plan: to_plan,
coupon: coupon,
item_api_id: api_sub.items.data[0].id
)
end
def rollback
subscription.update!(
plan: from_plan,
coupon: coupon,
item_api_id: api_sub.items.data[0].id
)
end
end
class CreateInvoice
include Interactor
def call
context.invoice = create_invoice(context.subscription)
rescue Stripe::StripeError => error
context.fail!(error: error.message)
end
def rollback
void_invoice(context.invoice.id)
end
end
class PayInvoice
include Interactor
def call
context.invoice.pay
end
end
具有这种结构,您现在可以这样称呼他们:
outcome = ChangePlan.call(
subscription: subscription,
to_plan: to_plan,
coupon: coupon
)
unless outcome.success?
subscription.errors[:base] = 'Upgrade failed, please contact us.'
end
您可以在https://github.com/collectiveidea/interactor#rollback上阅读有关交互器中回滚的更多信息。我只给了您伪代码,但是按此顺序操作可以使您在某个动作失败时自动回滚所有先前的动作。这是交互器的主要优点。