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?
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.
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.
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.
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:
@payment.valid?
(probably with a custom method like valid_without_payment?
) payment_id
) only if api call succedsAlternatively:
payment_id
payment_id
(api response) if call succedswhere(payment_id: nil)
) and delete itI think both options are acceptable and your data will remain consistent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With