If you have your system using Subscription via Braintree, your users might have an invoice. What is the best way to do that? How can you get the necessary information from Braintree?

Preparing to generate a PDF

Let's start by adding the necessary gems.

vim Gemfile
# PDFs
gem 'render_anywhere', require: false
gem 'pdfkit' # HTML to PDF

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: %i[mri mingw x64_mingw]
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '~> 2.13'
  gem 'selenium-webdriver'
  gem 'wkhtmltopdf-binary-edge', '~> 0.12.4.0' # PDFkit works on env development & test
end

The gem wkhtmltopdf-binary-edge will be in the group development/test, because PDFKit works on environments development and test.

Storing the Transaction Identifier

Every time we subscribe to a plan or we buy something via Braintree, it creates a Transaction. We need to have the Transaction id, and then we can get the amount of the transaction and all its details. We are not currently storing the transaction id We need to store it. The place where we are creating our Subscription is in our Subscription Controller.

We will add this field in our Subscription, as we didn't deploy our application, we will just change the migration.

class CreateSubscriptions < ActiveRecord::Migration[5.1]
  def change
    create_table :subscriptions do |t|
      t.string :plan_id
      t.money :amount
      t.string :description
      t.belongs_to :user
      t.string :braintree_subscription_id
      t.string :braintree_transaction_id
      t.string :status
      t.timestamps
    end
  end
end

We are adding the field braintree_transaction_id as a string.

Let's re-run our migration:

rake db:drop && rake db:create && rake db:migrate && rake db:seed

And in our SubscriptionsController, the place where we create the subscription, we can not get the transaction id.

  private def handle_result_subscription(result)
    if result.success?
      Subscription.create(amount: params[:amount], user: current_user,
                          plan_id: result.subscription.plan_id,
                          status: result.subscription.status,
                          braintree_transaction_id: result.subscription.transactions.last.id,
                          braintree_subscription_id: result.subscription.id)
      redirect_back(fallback_location: root_path, notice: 'Everything was fine!')
    else
      redirect_back(fallback_location: root_path, notice: 'Something went wrong! :/')
    end

Sending a PDF from the controller

Let's add a route to our PDF. We will click in our subscriptions list and we will get the PDF from this.

  get 'subscriptions/user_invoice' => 'subscriptions#invoice', as: 'invoice_pdf'

Let's create this method!

  def invoice
    if params[:transaction_id]
      transaction = Braintree::Transaction.find(params[:transaction_id])
      respond_to do |format|
        format.pdf { send_invoice_pdf(transaction) }
      end
    else
      redirect_to subscriptions_path, notice: 'No invoices'
    end
  end

We will check if we have the transaction_id in the params, and then we will get the Transaction object from Braintree. The transaction object from Braintree contains all information we need to render a good invoice. After that, we can just render a format pdf. We are calling the method send_invoice_pdf and we pass the transaction to it. Let's check out this method.

  private def send_invoice_pdf(transaction)
    invoice_pdf = BraintreeInvoice.new(transaction)
    send_file invoice_pdf.to_pdf,
      filename: invoice_pdf.filename,
      type: 'application/pdf',
      disposition: 'inline'
  end

Our method send_invoice_pdf gets the invoice pdf and uses the method from rails send_file, passing the file attributes as type and filename. The class BraintreeInvoice renders the PDF for us. Let's see how it works.

BraintreeInvoice

The responsibility of this class is to render the PDF for us. To do that we render a HTML page, and we transform it to PDF. We are using the gem RenderAnywhere that allows us to use the method render from a model for example.

We store the transaction in the initializer. In the method to_pdf we get the rendered html and transform it to a PDF using PDFKit.

The HTML is generated by the private method as_html. We pass the transaction to our view, and we also have a layout called invoice_pdf.

require 'render_anywhere'

class BraintreeInvoice
  include RenderAnywhere

  def initialize(transaction)
    @transaction = transaction
  end

  def filename
    'invoice.pdf'
  end

  def to_pdf
    kit = PDFKit.new(as_html, page_size: 'A4')
    kit.to_file("#{Rails.root}/public/invoice.pdf")
  end

  private def as_html
    render template: 'subscriptions/invoice', layout: 'invoice_pdf', locals: { transaction: @transaction, customer: @transaction.customer_details, card: @transaction.credit_card_details }
  end
end

Let's see how our view will render this PDF.

vim app/views/subscriptions/invoice.html.erb

In our HTML we are rendering some information from card, user and company.

<div class="invoice-header">
  <h1> My Company name here</h1>
</div>

<div class="invoice-billto">
  <div class="bill-to">
    <h3> Customer Information</h3>
    <ul>
      <li><b><%= customer.first_name %> <%= customer.last_name %></b></li>
      <li><b>Email:</b> <%= customer.email %></li>
      <li><b>Phone:</b> <%= customer.phone %></li>
      <li><b>Location:</b> <%= transaction.credit_card_details.customer_location %></li>
    </ul>
  </div>
</div>

<hr/>
<h3>Details</h3>
<div class="box">
  <h4>Amount: <span class="amount"><%= transaction.amount %> USD</span></h4>
  <div>
    <b>Last 4 digits:</b> <%= card.last_4 %>
    <b>Expiration date:</b> <%= card.expiration_date %>
    <img src="<%=card.image_url%>"/>
  </div>
</div>

We also have a specific layout. This is better because you can customize it.

<!DOCTYPE html>
<html>
<head>
  <title>Invoice from Braintree</title>
  <style>
    body {
      font-size: 3rem;
      font-family: "Montserrat", Helvetica, sans-serif;
    }
    .invoice-billto {
      display: flex;
      margin-left: 2%;
      margin-top: 4rem;
    }
    .invoice-billto div {
      display: inline-block;
      vertical-align: top;
      width: 31.7%;
    }
    .invoice-billto h3 {
      color: rgba(74, 74, 74, 0.6);
      font-size: 4rem;
      font-weight: 900;
    }
    .box {
      border: 2px solid;
      border-radius: 0.2rem;
      padding 1rem;
    }
    .amount {
      font-size: 5rem;
    }
  </style>
  <meta charset="utf-8">
</head>
<body>
  <%= yield %>
</body>
</html>

Let's have a link to our invoice in our Subscription list page. On this page, we are adding a link. We are passing the format as pdf, and we are passing the transaction id.

 <ul>
    <% @subscriptions.each do |subscription| %>
      <li><%= subscription.plan_id %> - <%= subscription.amount %> - <%= subscription.status %> - <%= link_to "Get Invoice", invoice_pdf_path(format: 'pdf', transaction_id: subscription.braintree_transaction_id) %></li>
      <% end %>
  </ul>

Now, once we click on it, it will call our controller method. First, it will get the information from Braintree. Then it will generate the HTML. Lastly, it will generate the PDF.

Getting the PDF

Let's try that! Let's create a subscription. Subscription created. Now we can see our new subscription in the list. Let's get the invoice of this subscription! Boom! Our invoice is here! Sweet!

Summary

This week we learned a lot about Braintree. Braintree is handy to work with Subscriptions or just simple transactions for your website. Today we saw how we can get some information from Braintree and generate a PDF to our users.

Resources