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. We would love to chat.