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.
Comments for Generating Invoice PDF from Braintree