BearHug Repo
As school is starting back up again, it doesn’t seem like I’ll have time to write the usual full-length walkthroughs in a single take with ease. In the future, I think I’ll start breaking future walkthroughs down into different parts. Each parts will still be a complete project by itself, but with each additional parts I’ll experiment and build more on top of the original project. With that said, let this be the first of many parts in which I experiment with the Stripe API.
Whenever I face a massive amount of Computer Science assignments that I put off till the very last minute, I always wish that a gigantic furry bear would just appear out of no where and embrace me. Unfortunately, no such service exists right now, so I’ve decided to tap into my inner entrepreneur and create one for the world. With BearHugs, users can select how many hugs they would like to receive from the bears. All the transactions will be handled through Stripe.
The sooner I finish, the sooner I can start receiving hugs from the bears, so let’s get started.
What is Stripe?
Stripe is a US-based company that allows individuals and businesses to accept payment over the web. As oppose to services like Paypal and Shopify, Stripe is a lot more focused on the transactions aspect of the e-commerce business model. In order to use Stripe, the business has to pay them 2.9 % + 30 cents transaction fees for every transactions. Currently, that is all the fee a business has to pay Stripe; there are no other additional fees (like monthly, validation, etc).
Stripe also has an incredibly clear and concise API documentation. Furthermore, there is already a Stripe Gem for us Rails developers. In BearHugs, we’ll be using the gem.
Signing Up with Stripe and Locate the API Keys
In order to use Stripe, you must have a valid Stripe account. Head over to here to either sign up or log in.
Once you log in, you should be presented with your dashboard. First we need to find the API keys. On the top right of the dashboard, locate Your Account -> Account Settings -> API Keys. In our application, we’ll first be using the Test Secret Key and the Test Publishable Key, so take a note of those two. Once you have both of them, we can start developing!
Shall We Begin
Open up your favorite text editor, fire up the terminal, and create an app called BearHugs. Of course, if you’d like to receive embraces from other types of creatures, that is entirely up to you.
Let’s take a look at the gems we’ll be adding in this project.
GEMFILE
....
gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby'
gem "figaro"
gem "haml"
....
Seems very typical. Stripe as our core transaction processor. Figaro to take care of environment variables. HAML to make everything look much nicer.
bundle install
the gems, and then call bundle exec figaro install
to install Figaro.
Securing the API Keys
Before we do anything, let’s store the Stripe API keys as Environment Variables. Upon installing Figaro, you should be presetned with a config/application.yml. Let’s store the API keys in there.
config/application.yml
stripe_test_secret_key: sk_test_your_secret_key
stripe_test_publishable_key: pk_test_your_publish_key
Great, now we can simply call ENV[“stripe_test_secret_key”] and ENV[“stripe_test_publishable_key”] when we need to access the keys.
Configuring Stripe Calls
Based on this awesome guide by Stripe, it appears we need to do some configuration first. In our config/initializers folder, create a file and name it stripe.rb. We’re going to add some code within that file.
config/initializers/stripe.rb
# Set up the API keys
Rails.configuration.stripe = {
:publishable_key => ENV['stripe_test_publishable_key'],
:secret_key => ENV['stripe_test_secret_key']
}
# Required for this app to talk to the Stripe server.
Stripe.api_key = Rails.configuration.stripe[:secret_key]
The first part of the file simply sets up the API keys. We’re telling Rails that both :publishable_key and :secret_key are pointing to their respective environmental variables.
The second part is crucial in that this is how your application is “talking” to the Stripe server. Each user has a unique secret key, and with this secret key, Stripe can verify and track what the user is doing with the Stripe services.
Both parts are required in order for Stripe to function within the app.
Creating a Huggable Model
Now that we get the configuration out of the way, let’s start by thinking what we need in our huggable service. To start, each “package” of hugs should contain at least a name, a description, and pricing. Let’s create a Hug model based on those three field.
rails g model Hug name:string description:text price:decimal
Since we are dealing with monetary values, we want to ensure that there will only be two integers after the decimal point in our price field. Open up the migration
db/migrate/timestamp_reate_hugs.rb
class CreateHugs < ActiveRecord::Migration
def change
create_table :hugs do |t|
t.string :name
t.text :description
t.decimal :price
t.timestamps null: false
end
end
end
Now run rake db:migrate
and you’re all set!
Adding some Huggable Packages
Because we don’t need users to add or delete the packages, we can simply fire up the rails console and add the packages in there. Note that as oppose to the traditional method of creating a controller and adding the model objects through the views, this method is incredibly faster.
Open up the rails console by calling rails c
in your command line.
First thing first, let’s make sure our Hug model is empty right now. Call Hug.all
and see what happens.
2.1.3 :001 > Hug.all
Hug Load (1.5ms) SELECT "hugs".* FROM "hugs"
=> #<ActiveRecord::Relation []>
Great, seems like right now it is empty as expected. Let’s add our first package using the command line. To do so, we utilize the create
method, as well as the save
method.
2.1.3 :002 > @hug = Hug.create(name:"Simply a Hug", description:"Have a Bear show up at your desired location and give you a hug!", price: 10.00 )
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "hugs" ("name", "description", "price", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["name", "Simply a Hug"], ["description", "Have a Bear show up at your desired location and give you a hug!"], ["price", 10.0], ["created_at", "2015-09-07 19:35:51.456390"], ["updated_at", "2015-09-07 19:35:51.456390"]]
(8.0ms) commit transaction
=> #<Hug id: 1, name: "Simply a Hug", description: "Have a Bear show up at your desired location and g...", price: #<BigDecimal:7fe546b90a68,'0.1E2',9(27)>, created_at: "2015-09-07 19:35:51", updated_at: "2015-09-07 19:35:51">
2.1.3 :003 > @hug.save
(0.1ms) begin transaction
(0.1ms) commit transaction
=> true
And just like that, our first huggable package is saved into our database.
Let’s create more packages. Feel free to play around, but here are my personal packages.
Name: Simply a Hug | Description: Have a Bear show up at your desired location and give you a hug! | Price: 10.00
Name: More Than a Hug | Description: Have not one, but THREE Bears show up at your desired locations and give you a hug! Timing is up to you! | Price: 15.99
Name: Group Hug! | Description: Have SEVEN Bears show up at your desired locations and give you a hug! Timing is up to you! Safety not guaranteed. | Price: 24.99
Name: LOTS OF HUGS | Description: Have TEN BEARS show up at your desired locations and give you a hug! Safety not Guaranteed. Must Sign Additional Contract. Price Includes Partial Medical Fees | Price: 99.50
After creating and saving however many package you desire, let’s move onto the controller and the views.
The Huggable Controller
Both the Controller and the views are relatively straight forward.
rails g controller Hugs
app/controllers/hugs_controller.rb
class HugsController < ApplicationController
def index
@hugs = Hug.all
end
def show
@hug = Hug.find(params[:id])
end
end
app/views/hugs/index.html.haml
- @hugs.each do |hug|
= link_to hug.name, hug
app/views/hugs/show.html.haml
%h2
Package Name:
= @hug.name
%p= @hug.description
%p
$
= @hug.price
Simple and without Styling. Feel free to add your own styling though!
Creating the Huggable Orders
Now that we have our “shop” set up, let’s create a form that will allow the users to order the bear packages they want. Within the form, we’ll use the Stripe’s Checkout.js to automatically deal with all the transactions for us.
Furthermore, I’d like to make a shout out to Stripe Engineer coopf for helping me out on FreeNode. This section took me an embarrassingly amount of time to figure out, and I probably couldn’t have done it without the help of coopf.
The code below is partially reproduced from this blog. The reason this part took me forever is because I couldn’t understand the logic behind some of the code from the blog. Instead of mindlessly copying, I had to go through the their github repo and tear apart the code base line by line.
Now, allow me to explain what the code is doing.
Good Old Controller, Model, and Views.
With that said, let’s proceed with making the controller and model for Order. What do we want form a customer who’s placing an order? Probably the name and email. There can be a lot more fields, but let’s stick with those two only for simplicity’s sake. Furthermore, remember that each order belongs to a specific Huggable Package, so also toss in the references.
rails g model Order name:string email:string hug:references
rake db:migrate
And let’s also create the Orders controller
rails g controller Orders
And don’t forget to add resources :orders
in your routes.rb
Before we play with the controller and model, let’s take a step back and imagine how the user will interact with the application. The user will most likely head to the hugs index page, click on a package for more information, and click an order button that’s somewhere on each Hug show page. Then the user will most likely expect a payment form to fill out.
We’ll address the missing “order” link first.
views/hugs/show.html.haml
%h2
Package Name:
= @hug.name
%p= @hug.description
%p
$
= @hug.price
= link\_to "Order NOW", new\_order\_path(hug_id: @hug.id)
Note how we’re also putting in the hug id at the end of the path. This will allow rails to keep track which order package we’re dealing with later in the Orders controller.
Now let’s take a look at how we’re going to code the new page
views/orders/new.html.haml
= form_for @order do |f|
= f.hidden_field :hug_id, value: @hug.id
= f.label :name
= f.text_field :name
= f.label :email
= f.text_field :email
%script.stripe-button{"data-amount" => @hug.price*100, "data-description" => @hug.name, "data-key" => Rails.configuration.stripe[:publishable_key], "data-locale" => "auto", :src => "https://checkout.stripe.com/checkout.js"}
All very standard. We have an additional = f.hidden_field :hug_id, value: @hug.id
because once again we’d like to use the information from each hug later.
You may notice the script on the bottom. This script is actually Stripe’s Checkout.js, a beautifully designed payment form. This form not only deals with credit card payment, but also payment error handling. With minimal modification, we already have a fully functional payment button with ease.
Note that the @hug.price*100
is not the price that will be transmitted through the Stripe Servers. This line of code merely dictates what numbers are shown on the form. We will see the data transmission code later.
Now, onto the controller
controllers/orders_controllers.rb
class OrdersController < ApplicationController
def new
@hug = Hug.find(params[:hug_id])
@order = Order.new
end
def create
@order = Order.new orders_param.merge(email: stripe_params["stripeEmail"],card_token: stripe_params["stripeToken"])
@order.process_payment
@order.save
end
private
def stripe_params
params.permit :stripeEmail, :stripeToken
end
def orders_param
params.require(:order).permit(:hug_id, :name, :email)
end
end
The new
method shouldn’t require much explanation. @hug
finds out which Hug package we’re dealing with through the parameters generated earlier in = link\_to "Order NOW", new\_order\_path(hug_id: @hug.id)
.
In create
, we see the function merge
that’s merging the orders_param
with two other fields, email
and card_token
. This serves two purposes. First, for the email field, this allows Stripe to actually record the users email as oppose to the default example@email.com. Second, card_token is Stripe’s way of allowing its end users to follow PCI Compliance while still have a way to accept payment on-line. By using card_token, Stripe’s end users does not store any of its customers credit card numbers.
Furthermore, by using the function merge
, rails now allow both the email and card_token field to be used in the model, which we’ll target later on to implement the process_payment
method.
The private methods are also self explanatory. stripe_params
allows rails to trust email and the token. orders_params
accepts our model’s field.
Some Quick Changes to the Database…
By introducing card_token
within the Orders Controller, we need to update our database with an extra field.
rails g migration add_card_token_to_orders card_token:string
At this point, I realize that dealing with decimals values as the prices is too much of a hassle. There are a lot of issues going on with Stripe trying to figuring out the decimal points and what not. Let’s just change the type of price in the Hugs model from decimal to integer.
To do this, we need to call the following migration and modify the file like so
rails g migration change_price_types_in_hugs
db/migrate/time_stamp_change_price_types_in_hugs.rb
class ChangePriceTypeInHugs < ActiveRecord::Migration
def self.up
change_column :hugs, :price, :integer
end
def self.down
change_column :hugs, :price, :decimal
end
end
What happened to the default def change
? Of course, we could’ve used that also and include the same code as in self.up
. However, this would mean no database rollback, so if you want to rollback toe db for any reasons, there will be issues.
Now call rake db:migrate
to apply the migrations.
… And Back to the Model
Earlier we saw the process_payment
in the Orders controller, now let’s define it. There really isn’t anything special about it. We’re just taking the code from the Stripe guide from here and putting it in our model.
app/model/order.rb
class Order < ActiveRecord::Base
belongs_to :hug
def process_payment
customer = Stripe::Customer.create(
email: email,
card: card_token
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => hug.price * 100,
:description => hug.name,
:currency => 'usd'
)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to charges_path
end
end
Note how we’re using the email and card_token field here as oppose to Stripe’s params[:stripeToken] as shown in the guide. This is because we cannot call params[] in models, only in controllers and views.
Since Stripe deals in cents, we just have to multiply whatever the price is by 100 to get the dollar amount.
Finishing Up
Great! Now head back to the hugs index (@ localhost:3000/hugs) page. Select your desired huggable package. Click “Order Now”. Enter all the information and press that lovely Stripe Button. Enter a test email, 4242-4242-4242-4242 as your test credit card number, make up some expiration date and CSV, and Viola, there is an error.
You should be redirected to a template is missing error page. I believe it is under views/orders/create.html.haml. I am not sure why the path is the way it is (probably has to do with Stripe), but just make a simple thank you page at that directory.
Now try the entire process again!!
If your transaction is successful (and it should), you should see a green check mark on the Stripe.js form and be redirected to the newly created thank you page. To make sure that your charge did indeed go through, go back to your Stripe Test Dashboard and verify the charge actually went through!
Feel free to experiment more with the Stripe Dashboard, as it is incredibly user-friendly. As for me, I’ll be ending this walkthrough right here. Hope you enjoyed it! See you next time.
PS: As I went through a lot of re-coding for this project, I may have missed something during the explanations. If you think I may be missing something, please notify me in the comments below or through email!
Some Self Reflection
Was really rushed for this project. As I have a few jobs I have to do next week and an event to attend next weekend, I figure this really is the only week that I can work on another blog post. Maybe next time I’ll take it easy and not rush as much.
A lot shorter than the other ones, but I think I’ll come back in the future and play around with Stripe some more.