August 23, 2016

Taking a deferred payment with Rails and Stripe

In an earlier post I wrote about how to take a one off payment with Stripe from your Rails app. But it is likely to happen that there will be times when you will need to be able to take payment from your customer at a later time. Maybe you have a thing on back order, maybe you only charge when an item is shipped to your customer. However the tokens that are obtained from Stripe are made for immediate, one time use. So what do we do??

Stripe Customer

This is where the Stripe Customer object comes into play, and can very easily work with your existing Customer code in your system.

Assuming that you have a Customer class in your application, you are going to have to add a column to your customers table to be able to store what the customer id you get from stripe is. So you're going to need a database migration:

class AddStripeCustomerId < ActiveRecord::Migration

  def change
    add_column :customers, :stripe_customer_id, :string  
  end

end

The id that we get from Stripe is a string, so we'll get a string column added for when we create them with Stripe. The next part of what we need to do will depend on the flow of your application. I imagine that it belongs in a controller that takes care of your customers information. If you are capturing more stuff about the payment than just a stripe customer id, it may warrant it's own model and controller, but as all we are doing is storing that id, we can do it with a Customer Update action.

class CustomerProfilesController < ApplicationController

  def update
    @customer = Customer.find(params[:id])
    if @customer.update(permitted_params)
      redirect_to profile_path(@customer)
    else
      render :edit
    end
  end

end

This is a standard CRUD controller flow, and part of it would include a stripe card token obtained with Stripe.js like in my earlier post. As part of our update to our Customer object then, we want to create a Stripe Customer with a payment source.

class Customer < ActiveRecord::Base

  attr_accessor :payment_token

  before_save :create_stripe_customer, if: :card_supplied_and_stripe_customer_blank?
  before_save :update_stripe_payment_source, if: :card_supplied_and_stripe_customer_present?

  private
    def card_supplied_and_stripe_customer_blank
      payemt_token.present? && stripe_customer_id.blank?
    end

    def card_supplied_and_stripe_customer_present?
      payment_token.present? && stripe_customer_id.present?
    end
end

Some simple callbacks here will allow us to decide if we need to do anything with Stripe, and what it is we need to do. When we are getting a payment_token from the Customers form and they do not have a stripe customer id, then we need to create a customer with Stripe that we can charge.

class Customer < ActiveRecord::Base

  ### omitted for brevity

  private

    def create_stripe_customer
      stripe_customer = Stripe::Customer.create(
        description: "This is a customer with email: #{email}",
        source: payment_token
      )
      self.stripe_customer_id = stripe_customer.id
    end
end

Pretty simple, create a Stripe customer object, take the id that it returns and assign it to our new field in the database. But about our second scenario. What if we already have a card and our customer supplies a card via their edit form. Then we need to update it with Stripe:

class Customer < ActiveRecord::Base

  ### omitted for brevity
  private  

    def update_stripe_payment_source
      stripe_customer = Stripe::Customer.retrieve(stripe_customer_id)
      stripe_customer.source = payment_token
      stripe_customer.save
    end
end

So all we had to do was call the customer that has been stored with Stripe, supply the new payment token as the source and save the new value with the customer.

Charging the Customer

Now that we have our stripe customer with a card attached to them, we can take a payment from them. This could be something as simple as our PurchasesController again, but now our customer would be part of the purchase, our customer could have many purchases:

class Customer < ActiveRecord::Base

  has_many :purchases

end

So of course our Purchase will belong to a customer:

class Purchase

  belongs_to :customer

end

And so now when we call our controller code to make a purchase:

class PurchasesController < ApplicationController

  def create
    @purchase = Purchase.new(purchase_params)

    if @purchase.save_and_make_payment
      redirect_to confirmation_path(@purchase)
    else
      render :new
    end
  end

end

We would now have this in our Purchase class:

class Purchase < ActiveRecord::Base

  attr_accessor :payment_token

  def save_and_make_payment
    if valid?
      begin
        charge = Stripe::Charge.create(
          amount: price_in_cents,
          currency: currency,
          customer: customer.stripe_customer_id
        )
        save
      rescue Stripe::CardError => e
        errors.add :credit_card, e.message
        false
      end
    end
  end

end

So now instead of just providing a payment token that we obtained, we provide the stripe customer id that has got a token already associated with them on the Stripe system, and can be charged whenever they make a new purchase without having to provide their card details everytime, and, again, without you haing to go through the exhaustive and painful process of PCI compliance.

A word on Callbacks

The sample code that I've used here to illustrate how these Stripe commands work are triggered with ActiveRecord Callbacks, which if used carelessly can certain create a bit of a mess if your classes are too big and too many of them are triggered. For instance, on this example Customer class, if there were more callbacks being called I would need to make sure that my stripe calls are last as I wouldn't want to bother trying to make them is if some other part of my callback chain failed as I'd end up updating my Stripe object, but not persisting that success to my database necessarily. There certainly would be other ways to manage the hookup with Stripe for these, possibly straight in the controller, or in a service object, but going into those were beyond the scope of what I was talking about.

Now you have the information you need to both charge for a purchase immediately, or now to provide your customers with the chance to pay you without entering their details on every purchase if they wish. Of course if you need more help with the, please let me know in the comments or send me an email.