Reverse Engineering to Learn Forward

I believe that you can learn the basics of whatever you would like to implement, by looking out in to the open source world for muse codebases. The purpose of finding a muse codebase is not to rip or steal, it is to learn and inspect. It’s like asking a teacher to show you something but looking into the teachers brain as opposed to directly asking the teacher. Its harder to do but hard things teach more meaningful and memorable lessons.

In this particular post, I will be reverse engineering a code base on Github which is making use of the Stripe Connect API. Personally, I’ve always wanted to use omniauth to develop but I’ve always been scared of it for some reason. Here I will break a fear and learn a new skill and at the same time share a new skill.

I found a person on Github who had a personal project at: https://github.com/dylandrop/givespend-v3/commits/master

Project Overview

This project seems to allow a person to sell items for a charitable cause. It has the idea of a buyer and also a seller. It was not a completely finished product but there are lessons that it teaches about dealing with Omniauth and the Stripe Api in its seller implementation. So Lets break it down.

Code Walkthrough

I was always told, when trying to get familiar with a rails app its a good idea to take a look at the routes file first. The routes file can show you what the rails app is capable of. Here is what I saw:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  resources :verifications, only: [:new]
  resources :items, only: [:new, :create, :show]
  resources :transactions, only: [:new, :create]
  resource :seller_dashboard, only: [:show]

  devise_for :users, :controllers => { :registrations => "registrations", :omniauth_callbacks => "authentications", :sessions => "sessions" }

  get "pages/index"

  get "pages/about"

  get "pages/contact"

  root to: "pages#index"

This routes file is simple and shows very clearly that there is a seller dashboard, the ability to make a transaction, the ability to make new items and show items, the ability to register, the ability to process callbacks with omniauth, and last but not least it has 3 different static page routes processed by a Pages Controller. If I was to make a story line out of what I am seeing; this says to me that a seller will post an item and show it and a buyer will probably buy something from the seller. Once the item is bought there will be a record stored about the bought item being shown in the seller dashboard.

Keep in mind: this is a small routes file. On some routes files, there is an even bigger story to tell and takes much more mental work to really grok what the application is doing. We got lucky here. lol

So now I want to track the Stripe Api feature usage on this Application. A trick that I picked up for tracking features on github is to just look at any projects commit history. The commits tell a story in segments that mimic the actual feature development cycle. On github right below the repo description, there is a link that shows how many commits have been made. So I decided to click it and then search through the commit messages to find something that tells me which particular commit is a stripe feature commit.

I pretty much found a commit message that read: “verification with Stripe working”. Bingo!

This file starts with a changelog showing the addition of 3 gems to the gemfile.

1
2
3
gem 'omniauth'
gem 'omniauth-stripe-connect'
gem 'stripe'

The next significant update was a controller called AuthenticationsController which was inheriting from Devise::OmniauthCallbacksController. It had one single method in it:

1
2
3
4
5
6
7
8
+class AuthenticationsController < Devise::OmniauthCallbacksController
 +  def stripe_connect
 +    omniauth = request.env["omniauth.auth"]
 +    current_user.apply_omniauth(omniauth)
 +    current_user.save!
 +    redirect_to new_item_path
 +  end
 +end

This method is named after the API its using. Its setting a request env hash value called omniauth.auth to a variable called omniauth. It is then passing the omniauth variable to a method on the user object called apply_omniauth. Then it saves the results on the user and redirects to a new item.

The next file that I see as being changed is the user model. Basically the User file has a few interesting pieces of code to look at.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 devise :database_authenticatable, :registerable, :omniauthable

 +  def stripe_parameters
 +    {
 +      'stripe_user[business_type]' => 'sole_prop',
 +      'stripe_user[currency]' => 'usd'
 +    }
 +  end

 def apply_omniauth(omniauth)
 +    self.secret_key = omniauth['credentials']['token']
 +    self.public_key = omniauth['info']['stripe_publishable_key']
 +    self.uid = omniauth['uid']
 +  end

The first thing I notice is the omniauthable module being included to the devise class method at the top of the file. This means that devise will be making its Omniauth feature available to this application. The second thing I noticed was the apply_omniauth method. This method was called in the stripe connect method in the authentication controller before the user was saved. When I check out this method, I see that it is setting the Secret key, public key and the uid to the user object. When its done setting, the stripe_connect method saves the current users updated fields.

The last thing I noticed was the stripe_paramters method. Its holding a json like structure with 2 hashes inside of it. One hash to specify the business type and the other to specify the currency type. That was pretty much it for the user model.

The next file that was modified was from the verifications new view. Its a html.erb file that has a progress bar a short description paragraph tag and then a verification link.

1
2
3
4
5
 +<%= render partial: "layouts/progress_bar", locals: { status: 2, size: 3 } %><br><br>
 +<div class="description">
<p>'We need you to create a Stripe account so we can pay out to your bank account. Stripe is Givespends payment processor. You will be briefly redirected to Stripe, and returned to Givespend once you are done.'</p>
 +</div>
 +<%= link_to "Verify with Stripe", omniauth_authorize_path(:user, :stripe_connect, current_user.stripe_parameters), class: 'call-to-action' %>

In rails, partials are a method of extraction for verbose view logic in order to make the views easier to understand. The most important parts of this particular view is really the ‘verify with stripe’ link and the description paragraph. The link is using a route helper method called omniauth_authorize_path and it is passing a symbol called user and a symbol called stripe connect and the stripe parameters of the current user. It also has a class on it, called call to action.

This is a fairly loaded link helper. But what it’s doing is using a method from devise which creates the call to stripe and returns the result of that call to the callback method specified in the Authentications controller. Its doing all of this with a metaprogrammed method called - omniauth_authorize_path. And it takes three arguments: (resource, provider and additional arguments). see below:

1
2
3
4
def omniauth_authorize_path(resource_or_scope, provider, *args)
  scope = Devise::Mapping.find_scope!(resource_or_scope)
  _devise_route_context.send("#{scope}_#{provider}_omniauth_authorize_path", *args)
end

Now, after following this workflow I wanted to check the accuracy of my reading and so I read the OmniAuth-stripe-connect gem wiki and it pretty much confirms that this workflow above is how a developer can connect a persons stripe account to the rails app. Once this “connection” is made, a user can use it to make transactions to the stripe account through the app, using the info made available from the request.env[‘omni.auth’]hash.

–Note: One part of the flow here that is missing is the part where the developer signs up for stripe and specifies the callback url and gets the public and secret keys from Stripe. Thats not something that you can see on github, you just have to know to do it.

I will probably post some more about using the stripe API. I like the Stripe Api myself, but thats mostly because its a great way as a developer to get paid. lol