How do I make my Stripe Charges more robust [Background Worker & Idempotent]?
One thing I don't want to have to deal with are refunds because a customer happened to get charged twice. I want to ensure that I've designed my Rails app to be robust in the face of failure before I release it to production.
What is the best way to process Charge requests in the background? Currently I'm using sidekiq to invoke similar jobs (see Mailer example below). Moreover, should I be making idempotent requests? What's the best way to approach this? This way I don't have to worry about creating multiple charges, and also don't start blocking up the server with Charge requests.
Sidekiq Example
What is the best way to process Charge requests in the background? Currently I'm using sidekiq to invoke similar jobs (see Mailer example below). Moreover, should I be making idempotent requests? What's the best way to approach this? This way I don't have to worry about creating multiple charges, and also don't start blocking up the server with Charge requests.
Sidekiq Example
def send_job_post_email
JobMailer.send_job_post_email(user).deliver_later
end
JobsController < ApplicationController
def create
@job = current_user.jobs.build jobs_params.merge(stripe_token: stripe_params["stripeToken"])if @job.create_with_stripe(params[:stripeToken]) if @job.save @order = Order.create( :job_id => @job.id, :stripe_token => @job.stripe_token ) end redirect_to job_order_path(@job, @order), notice: 'Registration was successfully created.' else render_form end
end
Job < ApplicationRecord
def create_with_stripe(token)
Stripe.api_key = Rails.application.secrets.stripe_secret_keyif valid? Stripe::Charge.create( :amount => 999, :currency => "cad", :source => token ) else false end
rescue Stripe::CardError, Stripe::InvalidRequestError => e
errors.add :base, "Whoops! We were unable to process your card. #{e.message}"
false
end
Thanks for the reply @jack...I'm using Charge objects. How did you (or would I) go about setting the Service objects with Sidekiq to process in the background? I'm familiar with how Sidekiq and background jobs work, but I ask because the examples online show you having to poll the connection, which is over my head.
Moreover, how are you setting the idempotency_key? Is it simple as setting it in the Object and it automagically works?
Chris, let me know if you have any insight on this with steps that I can implement.
Moreover, how are you setting the idempotency_key? Is it simple as setting it in the Object and it automagically works?
Chris, let me know if you have any insight on this with steps that I can implement.
Taylor,
You really don't need to poll for the payment and respond to the customer that it was a success. I think most customers realize that when you input your card information, there's going to be a charge that happens at some point, and most don't really care for a confirmation that the charge went through (within your interface). This means you can just capture the card token and process the charge in the background, if successful update your Payment model (if keeping invoice data) that payment went through. If it fails, prompt the customer with an email to update card details and try the charge again.
In one implementation we charge customers on a nightly job based on a pre-defined schedule. Stripe never struggles with us throwing requests at it like crazy at midnight.
You're on the right track with moving the processing of the charge to a background job. I guess you need to answer whether or not the confirmation is important to your on-boarding flow.
You really don't need to poll for the payment and respond to the customer that it was a success. I think most customers realize that when you input your card information, there's going to be a charge that happens at some point, and most don't really care for a confirmation that the charge went through (within your interface). This means you can just capture the card token and process the charge in the background, if successful update your Payment model (if keeping invoice data) that payment went through. If it fails, prompt the customer with an email to update card details and try the charge again.
In one implementation we charge customers on a nightly job based on a pre-defined schedule. Stripe never struggles with us throwing requests at it like crazy at midnight.
You're on the right track with moving the processing of the charge to a background job. I guess you need to answer whether or not the confirmation is important to your on-boarding flow.
Thanks for the response eminkel. In regards to processing the job in a background worker, can I enqueue the job just as I would any other - is there anything special that I need to do? If I understand correctly, I can just skip the ajax call made to check the status of the payment, as customers expect there to be processed at some point.
Hey Taylor,
Yeah, you can process the payment like any normal background job. Don't allow retry if the job fails (for fear of multiple payments), and return and update some sort of charge id on success to your database.
Yeah, you can process the payment like any normal background job. Don't allow retry if the job fails (for fear of multiple payments), and return and update some sort of charge id on success to your database.
Awesome,
That helps a bunch! I know I can disable retry support for a particular worker by appending:
That helps a bunch! I know I can disable retry support for a particular worker by appending:
sidekiq_options :retry => false # job will be discarded immediately if failed
In my controller, I create an Order object with the :stripe_token and :job_id if @job.save. If I add the :charge_id to the Order as well, will that be sufficient or is there more I can do? (See the code block in the main post)