How can I make the transaction rollback if the API call fails and vice versa?
ruby, ruby-on-rails, ruby-on-rails-4, stripe-payments, transactions
Solution
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.
Problem
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?