Posts

Simple Paypal checkout in Ruby on Rails using Orders API v2

Recently, Paypal archived most of their SDK repositories that uses v1 of the API and added a deprecation notice. This includes the PayPal-Ruby-SDK.

In this tutorial, we would be moving away from PayPal-Ruby-SDK and use Checkout-Ruby-SDK as this is the recommended according to the Paypal developer documentation. The Checkout-Ruby-SDK uses v2 of the API instead of the deprecated v1.

We will make this tutorial as simple as possible, we will hard code everything from products to orders. It is up to you on how will you optimize and implement a better UI for this.

Step 1: Initialize a Rails application

You can initalize a Rails application by running the following commands in the terminal.

rails new paypal-checkout
cd paypal-checkout

Step 2: Install the gem

In your Gemfile add the following:

gem 'paypal-checkout-sdk'

And then run the following in the terminal.

bundle install

Step 3: Create an Order model

This will be the model where we’ll store the transactions.

rails g model Order

This will create a model file and a migration. In the migration add the following:

def change
  create_table :orders do |t|
    t.boolean :paid, :default => false
    t.string :token
    t.integer :price
    t.timestamps
  end
end

The paid will determine if the order is paid or not. The token will be your Paypal reference. The price will the total amount of the transaction (sum of prices of all products), we will just hard code it for simplicity and also used integer to make it simple. Please use appropriate data type on your project.

rails db:migrate

This will migrate our migration and create table in the database for our records.

Step 4: Create the routes

In your config/routes.rb, add the following:

Rails.application.routes.draw do
  get '/', :to => 'orders#index'
  post :create_order, :to => 'orders#create_order'
  post :capture_order, :to => 'orders#capture_order'
end

Step 5: Create the controller

This will be the controller for the routes that we created in the previous step.

rails g controller Orders

In app/controllers/orders_controller.rb, add the following methods:

class OrdersController < ApplicationController
  skip_before_action :verify_authenticity_token

  def index; end

  def create_order
    # PAYPAL CREATE ORDER
  end

  def capture_order
    # PAYPAL CAPTURE ORDER
  end
end

Step 6: Add the Paypal smart payment button

You can see the Paypal smart button interactive demo to customize your button.

In app/views/orders/index.html.erb, add the following: (create the file if it not exists)

<div id="paypal-button-container"></div>

<script src="https://www.paypal.com/sdk/js?client-id=YOUR-PAYPAL-CLIENT-ID"></script>
<script>
  paypal.Buttons({
    env: 'sandbox', // Valid values are sandbox and live.
    createOrder: async () => {},
    onApprove: async (data) => {}
  }).render('#paypal-button-container');
</script>

Replace YOUR-PAYPAL-CLIENT-ID with your client id. If you don’t have one, you can create by clicking this link. You can also create sandbox accounts using this link.

Step 7: Connecting the button to the backend

Let us first initialize our Checkout SDK.

In app/controllers/orders_controller.rb, add the following methods:

class OrdersController < ApplicationController
  skip_before_action :verify_authenticity_token

  before_action :paypal_init, :except => [:index]

  def index; end

  def create_order
    # PAYPAL CREATE ORDER
  end

  def capture_order
    # PAYPAL CAPTURE ORDER
  end



  private

  def paypal_init
    client_id = 'YOUR-CLIENT-ID'
    client_secret = 'YOUR-CLIENT-SECRET'

    environment = PayPal::SandboxEnvironment.new client_id, client_secret
    @client = PayPal::PayPalHttpClient.new environment
  end
end

This will make @client available in our create_order and capture_order methods.

Inside create_order, we can now generate a token for the order using the following code:

def create_order
  price = '100.00'
  request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
  request.request_body({
    :intent => 'CAPTURE',
    :purchase_units => [
      {
        :amount => {
          :currency_code => 'USD',
          :value => price
        }
      }
    ]
  })

  begin
    response = @client.execute request
    order = Order.new
    order.price = price.to_i
    order.token = response.result.id

    if order.save
      return render :json => {:token => response.result.id}, :status => :ok
    end
  rescue PayPalHttp::HttpError => ioe
    # HANDLE THE ERROR
  end
end

This will create an order and returns us a token from Paypal, while doing that we are also creating a record in our database using the Order model. Click this for more info.

Now in the frontend, we can now use this token. In the paypal.Buttons, inside createOrder function, we must return this token for our button to process.

createOrder: async () => {
  const response = await fetch('/create_order', {method: 'POST'});
  const responseData = await response.json();

  return responseData.token;
}

The next would be the onApprove function where the data will be passed after the payment has been approved.

onApprove: async (data) => {
  const response = await fetch('/capture_order', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({order_id: data.orderID})
  });
  const responseData = await response.json();

  if (responseData.status === 'COMPLETED') {
    alert('COMPLETED');
    // REDIRECT TO SUCCESS PAGE
  }
}

If the backend gives a COMPLETED response, we can now redirect the user to the success page.

Let us create the backend that would capture the order. In app/controllers/orders_controller.rb, inside capture_order add the following:

def capture_order
  request = PayPalCheckoutSdk::Orders::OrdersCaptureRequest::new params[:order_id]

  begin
    response = @client.execute request
    order = Order.find_by :token => params[:order_id]
    order.paid = response.result.status == 'COMPLETED'

    if order.save
      return render :json => {:status => response.result.status}, :status => :ok
    end
  rescue PayPalHttp::HttpError => ioe
    # HANDLE THE ERROR
  end
end

This will capture the order and give a status if the order has been completed. Click this for more info.

If the status is now completed then we can set the order as paid, and return a COMPLETED in the frontend to know if the process is success.

Step 8: Test the app

In your terminal.

rails s

In your browser go to http://localhost:3000/ and you should see a Paypal button if everything is successful.

If you clicked the Paypal button, a new tab should appear and an Order must be created in your database that has token, price, and paid set as false.

Once you complete the payment, the tab should close and the order record paid column now must be set to true, and an alert the says COMPLETED should appear.

That’s it. We have now created a simple Paypal checkout in Ruby on Rails.

Rails authentication with Devise

In this tutorial, we will be dealing with Rails authentication with Devise.

If you don’t want to use Devise for authentication, you may refer to the article create basic authentication in Ruby on Rails instead.

Devise has been around for some time now and have been used and reviewed thousands of times and still receives security updates to date.

Not only that, it is also composed of other features such as reset password, email integration, and many more that can be a pain when creating your own authentication system.

Step 1: Initialize a Rails application

You can initalize a Rails application by running the following commands in the terminal.

rails new devise-app
cd devise-app

Step 2: Install the Devise gem

In your Gemfile add the following:

gem 'devise'

And run the following in the terminal.

bundle install
rails g devise:install

This will install and initialize the Devise gem in our Rails application.

Step 3: Create your model for authentication

In this example, we would be using User as our model for authentication.

Run the following commands in the terminal.

rails g devise user
rails db:migrate

This will create a migration file for our User model then migrate it to complete.

Step 4: Add messages and navigation link to views

In your app/views/layouts/application.html.erb, add the following:

<% if user_signed_in? %>
  Logged in as <strong><%= current_user.email %></strong>.
  <%= link_to 'Edit Profile', edit_user_registration_path %> |
  <%= link_to 'Sign out', destroy_user_session_path, :method => :delete %>
<% else %>
  <%= link_to 'Sign up', new_user_registration_path %> |
  <%= link_to 'Sign in', new_user_session_path %>
<% end %>
<%= notice if notice.present? %>
<%= alert if alert.present? %>

This will show the current signed in user, a link to edit profile, a link to sign-out if there is a current user and will show a sign-up link and sign-in link instead otherwise.

The notice and alert are used to notify the user for the actions that have been made.

Step 5: Register a user

Now that we have a model, we can now register a new User. The Devise gem already created everything we need such as routes, models, etc. for our authentication to work.

Run your Rails server by running the following in the terminal.

rails s

Navigate to http://localhost:3000/users/sign_up in your browser and you should see a form.

Fill up the form and click Sign up.

It is up to you now to create your own views and to see all available routes generated by Devise you may run the following command in the terminal.

rake routes

That’s it, we have now created a Rails authentication with Devise.

Rails file upload using Active Storage

In this tutorial, we would be learning to upload files in Rails. Rails file upload have become easier since the release of Active Storage. It allows us to handle file uploads without using any gem.

This tutorial aims to deliver the simplest way to integrate Rails file upload. We would be skipping the details on how Active Storage works in the back and focus on how to integrate it correctly.

If you are already on an existing project and has a model that already has a CRUD operation then you may skip to Step 3.

Step 1: Create a new Rails project.

In your terminal run the following commands.

rails new file-upload
cd file-upload

This will simply create a new Rails project and change our current directory in the newly created project.

Step 2: Create a CRUD.

Since our focus is on file upload, we would be using scaffold to create our CRUD operations.

Scaffolding requires the following command in the terminal.

rails g scaffold post title:string content:text

This will create a Post model with a title and content columns, a database migration, and all views.

Step 3: Install Active Storage.

You need to run the following command in the terminal.

rails active_storage:install
rake db:migrate

The command creates a database migration that hold the necessary data and makes file uploading possible. Since we have a new database migration, we would need to migrate it.

Step 4: Add the necessary association.

In the file app/models/post.rb add the following association.

class Post < ApplicationRecord

  has_one_attached :image

end

This will tell that our model allows an attachment image. The word image is user-defined and can be changed to anything you want.

Since we are using has_one_attached, we are only expected to attach one image per record. If you want to attach many files in a single record you may want to use has_many_attached instead.

Step 5: Add the file upload field in the form.

The scaffold the we ran in Step 2 includes the views needed in our CRUD.

In the file app/views/posts/_form.html.erb, inside the form_with, add the following.

<div class="field">
  <%= form.label :image %>
  <%= form.file_field :image %>
</div>

This will add a file upload field in the form.

Step 6: Process the new field in the controller.

All changes in this step will be done in the file app/controllers/posts_controller.rb.

First, we would need to whitelist the new field in the parameters. This is necessary to prevent attackers from passing malicious parameters to our controller.

def post_params
  params.require(:post).permit(:title, :content, :image)
end

Second, we would need to attach our image in the controller’s create method.

# POST /posts
# POST /posts.json
def create
  @post = Post.new(post_params)
  @post.image.attach(post_params[:image])

  respond_to do |format|
    if @post.save
      format.html { redirect_to @post, notice: 'Post was successfully created.' }
      format.json { render :show, status: :created, location: @post }
    else
      format.html { render :new }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

Notice that we just added @post.image.attach(post_params[:image]) in the create method.

Third, we also need to attach our image in the update method.

# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
  @post.image.purge
  @post.image.attach(post_params[:image])

  respond_to do |format|
    if @post.update(post_params)
      format.html { redirect_to @post, notice: 'Post was successfully updated.' }
      format.json { render :show, status: :ok, location: @post }
    else
      format.html { render :edit }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

In the update method we added @post.image.purge to ensure that the old attachments are deleted before we attach the new file. The next line after purge is the same as what we did in create method.

Step 7: Show the uploaded file in the view.

Now that we are able to attach files on our records, we would want to view them in the view right?

In your file app/views/posts/show.html.erb, add the following.

<p>
  <% if @post.image.attached? %>
    <p>
      <strong>Image:</strong>
      <br>
      <%= image_tag @post.image %>
    </p>
  <% end %>
</p>

It will check if there is a file attached and if it does it will show the file.

You may want to run your app using rails s and navigate to http://localhost:3000/posts to confirm that everything works.

That’s it, we have now implemented a Rails file upload using Active Storage. Take note that all files will be saved on your local disk, you can also change this but that would be for another tutorial as this aims to deliver the most basic Rails file upload.

How to create basic authentication in Ruby on Rails

In this tutorial, we will be creating a basic authentication in Ruby on Rails.

Step 1: Adding a password_digest field to the model.

This field will be used for authenticating the user.

Step 1.1-A: Coming from a fresh Rails project.

If you are doing this from a fresh project then you must first create a User model by running rails g model User in the terminal.

This will create a file in db/migrate/xxxxx_create_users.rb where xxxxx is the current date and time you ran the command.

In that file add the following:

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :username
      t.string :password_digest
      t.timestamps
    end
  end
end

Return to your terminal and run:

rake db:migrate

This will create a users table in the database with columns username and password digest.

Step 1.1-B: Coming from an existing Rails project.

If you are doing this from an existing project then you must add password_digest field to your existing model.

In this tutorial, we will be using User as the model. Feel free to change it on your preference.

Run the following command in the terminal:

rails g migration add_password_digest_to_users

This will create a file in db/migrate/xxxxx_add_password_digest_to_users.rb

In that file add the following:

class AddPasswordDigestToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :password_digest, :string
  end
end

Return to your terminal and run:

rake db:migrate

This will add a string column password_digest to your users table in the database.

Note:

In this step, you may notice that ActiveRecord::Migration[5.2] has 5.2 in it, you must change it to the correct version, otherwise, this might not work.

You might want to check your Rails version by running the following command in the terminal:

rails -v

This tutorial is created using Rails 5.2.3.

Step 2: Creating routes for the app.

Open config/routes.rb in your text editor.

In this file, we will define the links in our app. For this tutorial, we will be creating six (6) routes. one (1) that needs authentication and five (5) unauthenticated routes.

Rails.application.routes.draw do

  # AUTHENTICATED ROUTES
  get '/dashboard' => 'dashboard#index' # THIS WILL BE THE DASHBOARD OF THE USER

  # UNAUTHENTICATED ROUTES
  get '/sign-up' => 'users#new' # THIS IS WHERE THE USER WILL REGISTER
  post '/save-user' => 'users#create' # THIS WILL BE THE PROCESS OF REGISTERING THE USER

  get '/' => 'sessions#new' # THIS IS WHERETHE USER WILL SIGN IN
  post '/' => 'sessions#create' # THIS WILL BE THE PROCESS OF SIGNING IN THE USER
  delete '/sign-out' => 'sessions#destroy' # THIS WILL BE THE PROCESS OF SIGNING OUT THE USER

end

Step 3: Creating controllers for the routes.

At this point, our app will raise an error because the routes we defined doesn’t have a matching controller.

We can see that we used three (3) different controllers in Step 2. These are the dashboard, users, and sessions.

To create a matching controller for these routes you can run the following command in the terminal. One (1) at a time.

rails g controller dashboard
rails g controller users
rails g controller sessions

This will create files in app/controllers directory.

In the dashboard controller app/controllers/dashboard_controller.rb add the following method:

def index
end

In the users controller app/controllers/users_controller.rb add the following method:

def new
end

def create
end

In the sessions controller app/controllers/sessions_controller.rb add the following method:

def new
end

def create
end

def destroy
end

If you observe the routes, it does not only matches to the controller but also matches to the methods in that controller.

The creation of method above ensures that our controller matches our routes.

Step 4: Creating the dashboard page.

In the app/views/dashboard/index.html.erb paste the following code (create the file if it does not exists):

DASHBOARD

This page will simply return a text DASHBOARD when /dashboard is accessed in the browser.

Step 5: Creating a sign up page.

From the steps above, we know that the users controller handles these processes.

In your app/controllers/users_controller.rb, inside the new method, add the following:

@user = User.new

In the app/views/users/new.html.erb paste the following code (create the file if it does not exists):

<h1>Sign Up</h1>

<%= form_with :model => @user, :url => '/save-user' do |f| %>
  <%= f.label :username %>
  <br>
  <%= f.text_field :username %>
  <br>
  <br>
  <%= f.label :password %>
  <br>
  <%= f.password_field :password %>
  <br>
  <br>
  <%= f.label :password_confirmation %>
  <br>
  <%= f.password_field :password_confirmation %>
  <br>
  <br>
  <%= f.submit %>
<% end %>

This will show an HTML form when we access the link /sign-up in our browser that will make a POST request to /save-user when submitted.

Now, we are going to edit the method that will handle the data from our form.

In the same controller file add the following method:

private

def user_params
  params.require(:user).permit :username, :password, :password_confirmation
end

This is to let to know that our app only permits username, password, and password_confirmation which is coming from our form to prevent malicious parameters to be processed.

In the create method add the following code:

@user = User.new user_params

if @user.save
  session[:user_id] = @user.id

  redirect_to '/dashboard'
else
  redirect_to '/sign-up'
end

Basically, we are just creating a new instance of User and its parameters.

If it was saved successfully then we will set the newly registered user to be the current user and redirect it the dashboard but returns back to sign up page if the registration fails.

Step 6: Creating the sign in page.

In the app/views/sessions/new.html.erb paste the following code (create the file if it does not exists):

<h1>Sign In</h1>

<%= form_with :url => '/' do |f| %>
  <%= f.label :username %>
  <br>
  <%= f.text_field :username %>
  <br>
  <br>
  <%= f.label :password %>
  <br>
  <%= f.password_field :password %>
  <br>
  <br>
  <%= f.submit %>
<% end %>

Now that we created the form, we now have to authenticate the credentials if it matches a record in the database.

In your app/controllers/sessions_controller.rb, inside the create method, add the following:

@user = User.find_by :username => params[:username]

if @user && @user.authenticate(params[:password])
  session[:user_id] = @user.id

  redirect_to '/dashboard'
else
  redirect_to '/'
end

What the code does is that it finds the user with the supplied username and authenticates its password.

It sets the session and redirects to the dashboard if it succeeds then redirects back it to the sign in page if it doesn’t.

Step 7: Creating the sign out function.

In your app/controllers/sessions_controller.rb, inside the destroy method, add the following:

session[:user_id] = nil

redirect_to '/'

We are just going to set the session[:user_id] that we defined in the sign up and sign in back to its original value which is nil and redirects back the user to the sign in page.

Step 8: Creating the helper methods.

In your app/controllers/application_controller.rb, add the following:

helper_method :current_user



def current_user
  @current_user ||= User.find_by :id => session[:user_id]
end

def authorize
  redirect_to '/' unless current_user
end

A helper method is a method that we can access in our views. The helper method current user holds the data of the currently authenticated user.

The authorize method is a method that we can call to our routes that needs a user to be authenticated before allowing access to a resource.

In this case, we defined /dashboard to be the only route that needs authentication before being accessed.

In your app/controllers/dashboard_controller.rb, add the following:

class DashboardController < ApplicationController

  def index
    authorize

    # YOUR OTHER CODE HERE
  end

end

This simply calls authorize method and redirects back to the user to the sign in page if no user is authenticated.

If you want the whole controller to be authenticated instead:

class DashboardController < ApplicationController

  before_action :authorize



  def index
  end

  def method1
  end

  def method2
  end

end

This will authenticate not just the index method but also method1 and method2.

Step 9: Adding the links in the view.

We created all this routes, pages, and stuff but we didn’t have links to access this pages at all without manually typing the links in the browser.

In your app/views/layouts/application.html.erb, add the following inside the <body> above the <%= yield %>:

<% if current_user %>
  Hi <%= current_user.username %>! | <%= link_to 'Sign Out', '/sign-out', :method => :delete %>
<% else %>
  <%= link_to 'Sign In', '/' %> | <%= link_to 'Sign Up', '/sign-up' %>
<% end %>
<br>
<br>

What it does is printing the name of user and a sign out link if it’s authenticated and sign in and sign up links if it’s unauthenticated.

Step 10: Installing bcrypt gem.

Our app may look complete at this point, but if you run it at this state, you will just encounter a lot of errors because we are lacking the core function of this tutorial.

In your Gemfile uncomment the following:

gem 'bcrypt', '~> 3.1.7'

But if it does not exists, add it instead.

In your terminal run:

bundle install

In your app/models/user.rbadd the following:

class User < ApplicationRecord

  has_secure_password

end

Step 11: Run the app.

In your terminal run:

rails s

You should be able to see the sign in page as your startup page if you access http://localhost:3000/ in your browser.

That’s it. This is just a basic authentication in Rails for learning purposes.