Teeps

Getting Started with Stripe - Part 1

05 June 2017 by Chayel Heinsen

8cf4eeba22eab31f096351f9b6c5f8bb8d7c9384

Why Stripe?

Stripe is an extremely powerful and easy to use tool for processing payments. Stripe handles pretty much everything for you; from a simple single payment to subscriptions. We had to implement Stripe for one of our clients not too long ago and it was a breeze. In most cases, clients prefer Stripe because of their pricing plan. Stripe only charges you per successful transaction as only 2.9% of the charge plus 30¢.

In this post we will first look a the required API implementation, and how to integrate an iOS app with this API in the next post.

Getting started on the API

Let's start off with the required routes that your apps would be calling. The examples provided will be in Rails, but Stripe provides many libraries and amazing documentation. You can visit their site to find documentation for your language.

To get started, add the Stripe gem to your Gemfile; gem stripe. Then run bundle install.

Now that you have Stripe installed, create an initializer and add your secret key. Note, Stripe provides a live and a test key. Make sure to use the test in development; it should start with sk_test.

# config/initializers/stripe.rb
Stripe.api_key = ENV.fetch("STRIPE_SECRET_KEY")

We wanted customers to be able to save and reuse their card information, so we opted in to creating customer objects on Stripe. When we create a user, we make an API call to Stripe to also create a customer on their end.

We are using interactors here. If you haven't used them, I highly recommend you take a look. You can find it on GitHub at interactor-rails.

First thing is to create our user.

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def create
    result = CreateUser.call(params: user_params)
    if result.success?
      render json: result.user
    else
      render json: { errors: result.error }, status: :unprocessable_entity
     end
  end
end

CreateUser is an Interactor Organizer. Run rails g interactor:organizer create_user create_local_user create_stripe_customer to create this file.

# app/interactors/create_user.rb
class CreateUser
  include Interactor::Organizer

  organize CreateLocalUser, CreateStripeCustomer
end

When calling CreateUser#call, CreateUser will pass that message to the first interactor in the organize method. Here we can see that CreateLocalUser will be called first, then passed to CreateStripeCustomer.

Now lets create our interactors for actually creating the user. You can run rails g interactor create_local_user to create this file.

# app/interactors/create_local_user.rb
class CreateLocalUser
  include Interactor

  def call
    user = User.new(context.params)

    if user.save
      context.user = user
    else
      context.fail!(error: user.errors)
    end
  end

  def rollback
    context.user.destroy
  end
end

CreateLocalUser#rollback will be called if our next interactor in the organizer fails. If it does, we want to remove our user.

Our next interactor is for creating the customer on Stripe. You can run rails g interactor create_stripe_customer to create this file.

# app/interactors/create_stripe_customer.rb
class CreateStripeCustomer
  include Interactor

  def call
    save_customer_id(new_customer)
  end

  private

  def new_customer
    Stripe::Customer.create(email: context.user.email)
  rescue Stripe::StripeError => e
    context.fail!(error: e.message)
  end

  def save_customer_id(customer)
    return if context.user.update(customer_id: customer.id)
    context.fail!(error: context.user.errors)
  end
end

CreateStripeCustomer#new_customer will make the API call to Stripe to make the customer and returns the customer object. If it fails, we call fail! which will allow the previous interactor, in this case CreateLocalUser to rollback its changes. The same thing happens in CreateStripeCustomer#save_customer_id if we couldn't update the user with the customer id.

Now that our user has a customer associated with it, we can create the required routes for that the mobile apps will need. These routes will interact directly with the STPBackendAPIAdapter protocol on iOS that we'll show you in part 2.

Getting a Customer

The app will need to fetch the customer object from Stripe. This is a pretty simple route. In your UsersController, add a customer method.

# app/controllers/users_controller.rb
# route - GET /users/customer
def customer
  render json: current_user.customer_info.to_json
end

User#customer_info should return the Customer object from Stripe.

# app/models/user.rb
def customer_info
  Stripe::Customer.retrieve(customer_id)
end

Sources

The next route to implement allows a customer to add a card or "source". This can be in the form of a debit/credit card or a card token. A token is a unique string that represents a card. Stripe provides a way to generate tokens on the client. You can read more about creating a token in part 2. We only accept tokens of a card and not actual card information to stay PCI compliant. In your UsersController, add a customer_sources method.

# app/controllers/users_controller.rb
# route - POST /users/customer/sources
def customer_sources
  current_user.update_customer_source(source: customer_source_params[:token])
  head :no_content
end

def customer_source_params
  params.require(:stripe_source).permit(:token)
end

The next route to implement allows the app to set the default source for the customer.

# app/controllers/users_controller.rb
# route - POST /users/customer/default_source
def customer_default_source
  customer = current_user.customer_info
  customer.default_source = params[:default_source]
  customer.save
  head :no_content
end

Now that we have all the routes to manage a customer, we can finally start processing payments! Now, I've seen a few people handle this incorrectly. You do NOT want to accept a price from the app. The server should define the price of whatever it is the user is purchasing. This makes the purchase much more secure for you.

Purchasing

We start by creating a Product model. This will define the thing to purchase as well as its price.

Here is the schema for our Product.

Table name: products

id            :integer          not null, primary key
name          :string           not null
cost          :integer          not null
created_at    :datetime         not null
updated_at    :datetime         not null

You can see that our schema is fairly simple. A Product should have a name and how much it costs (in cents), i.e a Product with a cost of 1000 will charge the customer $10 this Product.

Now we can create two routes, one to fetch the Product and one to purchase.

This will look something like

# config/routes.rb
resources :products, only: [:index] do
  post :purchase
end

Then in our ProductsController, we can create those methods. The ProductsController#purchase will be called when a user wants to make a purchase. Again, we are using interactors (PurchaseProduct) to cleanly handle the addition of credits to the users account and processing the payment.

class ProductsController < APIController
  def index
    render json: Product.all
  end

  def purchase
    result = PurchaseProduct.call(
      product_id: params[:product_id],
      current_user: current_user,
      stripe_source: purchase_params[:stripe_source]
    )

    if result.success?
      render json: current_user
    else
      render json: { errors: result.error }, status: :unprocessable_entity
    end
  end

  private

  def purchase_params
    params.permit(:stripe_source)
  end
end

Now we can attempt to charge the customer. Stripe#charge can raise many exceptions that you should obviously handle. You can find the possible errors in Stripe's documentation.

# app/interators/purchase_product.rb
class PurchaseProduct
  include Interactor

  def call
    context.product = Product.find(context.product_id)
    charge_customer
  end

  private

  def charge_customer
    context.charge = charge
  rescue Stripe::CardError => e
    context.fail!(error: e.json_body[:error])
  rescue Stripe::StripeError => e
    context.fail!(error: e.message)
  end

  def charge
    Stripe::Charge.create(
      amount: context.product.cost,
      currency: "usd",
      source: context.stripe_source,
      customer: context.current_user.customer_id,
      receipt_email: context.current_user.email,
      description: "#{context.product.name} purchased for #{context.current_user.email}",
      statement_descriptor: "MyApp Credit"
    )
  end
end

And we're done! We can now create a customer, manage their cards and process payments with only a few lines of code. Testing these routes are also simple. Stripe provides test sources for you to use. They even provide sources that will return specific responses and errors. You can find these cards in the Stripe documentation.

Look for part 2 which will go over how to take advantage of these routes in an iOS app.

Getting started with Stripe - Part 2


Need an app that would require Stripe or have a question? Fill out our Contact form or give us a call at 321-316-4724. We would love to chat.

Let's build your next big thing.

Contact Us