ruby-on-rails - 如果 API 调用失败,我该如何回滚事务,反之亦然?

标签 ruby-on-rails ruby ruby-on-rails-4 transactions stripe-payments

我想要两件事:

a) 我希望仅当 API 调用成功时才能够在数据库中保存一条记录

b) 我只想在数据库记录成功保存时执行 API 调用

目标是使存储在本地(在数据库中)的数据与 Stripe 上的数据保持一致。

@payment = Payment.new(...)

begin
  Payment.transaction do
    @payment.save!

    stripe_customer = Stripe::Customer.retrieve(manager.customer_id)

    charge = Stripe::Charge.create(
      amount:   @plan.amount_in_cents,
      currency: 'usd',
      customer: stripe_customer.id
    )
  end
# https://stripe.com/docs/api#errors
rescue Stripe::CardError, Stripe::InvalidRequestError, Stripe::APIError => error
  @payment.errors.add :base, 'There was a problem processing your credit card. Please try again.'
  render :new
rescue => error
  render :new
else
  redirect_to dashboard_root_path, notice: 'Thank you. Your payment is being processed.'
end

下面的代码会起作用,因为如果记录(第 5 行)不保存,则其余代码不会执行。

但是,如果我需要在 API 调用后保存 @payment 对象怎么办,因为我需要为 @payment 对象分配来自 API 结果的值。举个例子:

@payment = Payment.new(...)

begin
  Payment.transaction do
    stripe_customer = Stripe::Customer.retrieve(manager.customer_id)

    charge = Stripe::Charge.create(
      amount:   @plan.amount_in_cents,
      currency: 'usd',
      customer: stripe_customer.id
    )

    @payment.payment_id = charge[:id]

    @payment.activated_at = Time.now.utc
    @payment.save!
  end
# https://stripe.com/docs/api#errors
rescue Stripe::CardError, Stripe::InvalidRequestError, Stripe::APIError => error
  @payment.errors.add :base, 'There was a problem processing your credit card. Please try again.'
  render :new
rescue => error
  render :new
else
  redirect_to dashboard_root_path, notice: 'Thank you. Your payment is being processed.'
end

您注意到 @payment.save! 发生在 API 调用之后。这可能是个问题,因为 API 调用在数据库尝试保存记录之前就已运行。这可能意味着 API 调用成功,但数据库提交失败。

对这个场景有什么想法/建议吗?

最佳答案

你不能同时执行 API => DB 和 DB => API(听起来像是一个无限执行的条件),至少我无法想象你是如何实现这个工作流程的。我了解您的数据一致性需求,因此我建议:

  • 检查记录是否有效 @payment.valid?(可能使用像 valid_without_payment? 这样的自定义方法)
  • 运行 api 调用
  • 仅当 api 调用成功时才保存记录(使用 payment_id)

或者:

  • 不带payment_id保存记录
  • 运行 api 调用
  • 如果调用成功,用payment_id更新记录(api响应)
  • 定期(cron)运行任务(脚本)以检查不一致的实例(where(payment_id: nil))并删除它

我认为这两种选择都可以接受,您的数据将保持一致。

关于ruby-on-rails - 如果 API 调用失败,我该如何回滚事务,反之亦然?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21919553/

相关文章:

ruby-on-rails - Rails 4 和 Turbolinks - 元标签不变

ruby-on-rails - 如何在不泄露应用程序 key 和凭据的情况下开源我的 Rails 应用程序

ruby-on-rails - RSpec 3.6 和 Rails 5.0.1 - ArgumentError : wrong number of arguments (given 0, 预期 1)

ruby-on-rails - 如何在 Rails 中删除通配符 cookie?

javascript - 如何使用 guard-livereload 编写 JavasScript 项目?

ruby - 在 Ruby 中构建一个 "Semi-Natural Language"DSL

ruby-on-rails - 脚手架生成的 rspec Controller 测试在工厂创建的模型上返回 nil

php - 如何将 128 CFB mcrypt 转换为 Ruby?

css - 如何使用 active_link_to 将边框直接定位在事件链接上? rails 4

ruby-on-rails - 使用带有回形针附件的 FactoryGirl 的 attributes_for