Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make the transaction rollback if the API call fails and vice versa?

Two things I want:

a) I want to be able to save a record in the db only if the API call succeeds

b) I want to execute the API call only if the db record saves successfully

The goal, is to keep data stored locally (in the DB) consistent with that of the data on 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

The above following will work, because if the record (on line 5) doesn't save, the rest of the code doesn't execute.

But what if I needed the @payment object saved after the API call, because I need to assign the @payment object with values from the API results. Take for example:

@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

You notice @payment.save! happens after the API call. This could be a problem, because the API call ran, before the DB tried to save the record. Which could mean, a successful API call, but a failed DB commit.

Any ideas / suggestions for this scenario?

like image 953
Christian Fazzini Avatar asked Feb 20 '14 21:02

Christian Fazzini


People also ask

Can a transaction rollback fail?

If a rollback fails, then you would have a serious problem. The reliability of the database cannot be guaranteed. In other words; you probably have some sort of corruption in your transaction log and will end up with an inconsistent database.

What does it mean to rollback a transaction?

A rollback is the operation of restoring a database to a previous state by canceling a specific transaction or transaction set. Rollbacks are either performed automatically by database systems or manually by users.

What is meant by transaction rollback what is meant by cascading rollback?

A cascading rollback occurs in database systems when a transaction (T1) causes a failure and a rollback must be performed. Other transactions dependent on T1's actions must also be rollbacked due to T1's failure, thus causing a cascading effect. That is, one transaction's failure causes many to fail.


1 Answers

You can't execute API => DB and DB => API at the same time (sounds like an infinite execution conditions), at least I can't image how you can achieve this workflow. I understand your data consistency needs, so I propose:

  • Check if record is valid @payment.valid? (probably with a custom method like valid_without_payment?)
  • Run api call
  • Save record (with payment_id) only if api call succeds

Alternatively:

  • Save record without payment_id
  • Run api call
  • Update record with payment_id (api response) if call succeds
  • Run a task (script) periodically (cron) to check inconsistent instances (where(payment_id: nil)) and delete it

I think both options are acceptable and your data will remain consistent.

like image 131
markets Avatar answered Sep 19 '22 13:09

markets