Paddle Billing
Search

Build an inline checkout

Get a step-by-step overview of how to build a complete inline checkout — including initializing Paddle.js, passing settings and items, updating on-page information, and next steps.

The checkout is where customers make purchases. For SaaS businesses, it's the process where customers enter their details and payment information, and confirm that they'd like to sign up for a subscription with you.

You can use Paddle.js to integrate an inline checkout into your app. Inline checkout lets you embed a checkout and display information about items and totals in your own interface, creating checkout experiences that are fully integrated with your app.

Grab the code and test using CodePen

CodePen is a platform for building and sharing frontend code. Explore the code for this tutorial and test right away using our overlay checkout pen.

What are we building?

In this tutorial, we'll build a page that embeds an inline checkout for three items in our product catalog. We'll display information about items and totals in a table on the page, add a way for customers to switch to annual plan during checkout, then we'll extend it by passing customer information.

Illustration showing an inline checkout implementation. The Paddle Checkout frame is on the left and is on the payment page. It shows buttons for Apple Pay and PayPal, followed by a card payment form. On the right is an items list and totals. The grand total is $3600 billed monthly and is at the top.

We'll learn how to:

  • Include and set up Paddle.js using a client-side token
  • Pass settings to Paddle.js to embed an inline checkout in our page
  • Pass items to inline checkout using Paddle.Checkout.open()
  • Display and update information about items and totals using checkout events
  • Take a test payment
  • Update items for an opened checkout using Paddle.Checkout.updateCheckout()

If you like, you can copy-paste the sample code in your editor or view on CodePen and follow along.

Before you begin

Choose a type of checkout

This tutorial walks through creating an inline checkout. You can also create overlay checkouts, which let you launch a checkout in just a few lines of code.

We recommend building an overlay checkout if you're new to Paddle. Inline checkouts use the same JavaScript methods as overlay checkouts, so you can switch to an inline checkout later.

Create products and prices

Paddle Checkout works with products and prices to say what you're billing for, so you'll need to create a product and at least one related price to pass to your checkout.

Set your default payment link

You'll also need to:

  • Set your default payment link under Paddle > Checkout > Checkout settings > Default payment link.
  • Get your default payment link domain approved, if you're working with the live environment.

We recommend starting the domain approval early in your integration process, so your domains are approved for when you're ready to go-live.

Get started

Add an inline checkout to your website or app in five steps:

  1. Include and initialize Paddle.js

    Add Paddle.js to your app or website, so you can securely capture payment information and build subscription billing experiences.

  2. Embed and pass checkout settings and items

    Pass settings to determine how your checkout opens and how it works, then pass items to say what your checkout is for.

  3. Show and update on-page information

    Inline checkout handles capturing payment securely. Display an items list and totals on your page, and update using event callbacks.

  4. Take a test payment

    Make sure that your checkout loads successfully, then take a test payment.

  5. Update your checkout — optional

    Dynamically update items and other information for your opened checkout.

1. Include and initialize Paddle.js

Paddle.js is a lightweight JavaScript library that lets you build rich, integrated subscription billing experiences using Paddle. We can use Paddle.js to securely work with products and prices in our Paddle system, as well as opening checkouts and capturing payment information.

Include Paddle.js script

Start with a blank webpage, or an existing page on your website. Then, include Paddle.js by adding this script to the <head>:

Set environment (optional)

We recommend signing up for a sandbox account to test and build your integration, then switching to a live account later when you're ready to go live.

If you're testing with the sandbox, call Paddle.Environment.set() and set your environment to sandbox:

Pass a client-side token

Next, go to Paddle > Developer tools > Authentication and generate a client-side token. Client-side tokens let you interact with the Paddle platform in frontend code, like webpages or mobile apps. They have limited access to the data in your system, so they're safe to publish.

In your page, call Paddle.Initialize() and pass your client-side token as token. For best performance, do this just after calling Paddle.Environment.set(), like this:

Client-side tokens are separate for your sandbox and live accounts. You'll need to generate a new client-side token for your live account. Sandbox tokens start with test_ to make them easy to distinguish.

2. Embed and pass checkout settings

Next, we'll set an element on our page as a container for Paddle Checkout and set up Paddle.js for inline checkout.

Inline checkout works by embedding a frame that contains Paddle Checkout into your website or app. The Paddle Checkout frame handles securely capturing payment information, letting you display information about items and totals elsewhere on the page.

Create checkout container

Create an empty <div> for the Paddle Checkout frame and give it a unique class, for example checkout-container:

Pass settings

Now, we'll pass the class of this empty <div> to tell Paddle.js where to embed the checkout frame. We'll also pass checkout settings to tell Paddle.js to load an inline checkout and say how our inline checkout should work.

There are two ways we can do this:

Paddle.Initialize() method

Code illustration showing the Paddle.Initialize function with settings. It's cut off, so you can't make out the full code.

  • Pass settings to Paddle.Initialize() when initializing Paddle.js.
  • Settings apply to all checkouts opened on this page.
  • Recommended in most cases.

Paddle.Checkout.open() method

Code illustration showing an open checkout function with settings. It's cut off, so you can't make out the full code.

  • Pass settings to Paddle.Checkout.open() when opening a checkout.
  • Settings only apply to the opened checkout.
  • Recommended where you have multiple checkouts on a page with different options.

If all the checkouts you use have the same settings, we recommend using the Paddle.Initialize() method. This means you don't need to pass the same settings for every checkout that you'd like to open.

Update your Paddle.Initialize() method call so that it includes checkout.settings. These settings are applied to all checkouts opened on this page.

In our sample, we pass these settings to checkout.settings:

displayModeDetermines whether Paddle.js should open an inline or overlay checkout.We set to inline.
frameTargetSets the element where Paddle Checkout should be loaded.We passed our checkout-container class name.
frameInitialHeightSets the initial height of the dev element where Paddle Checkout is loaded.We set this to 450, which is our recommendation.
frameStyleCSS properties to apply to the checkout container.We passed some simple CSS styles here.

We covered the required settings for an inline checkout, but you may also pass locale, theme, and other settings that control how Paddle Checkout works. See: Pass checkout settings

Pass items

Checkouts must be for one or more items. If we were to try to open a checkout so far, Paddle.js would throw an error.

To pass items, we can use the Paddle.Checkout.open() method.

In our sample, we've created a function called openCheckout() to open a checkout. Here's how it works:

  1. We create a variable called monthItemsList and pass an array of objects, where each object contains a priceId and quantity. In our case, there are two prices that recur monthly and a single one-time price.

  2. We create a function called openCheckout() that takes a parameter called items.

  3. In our openCheckout() function, we call Paddle.Checkout.open(), passing the value of items as the items list for the checkout.

If you already used the Paddle.Checkout.open() method in the previous step to pass settings, work this into your existing openCheckout() function.

Recurring items on a checkout must have the same billing interval. For example, you can't have a checkout with some prices that are billed monthly and some products that are billed annually.

Set openCheckout() to run on page load

Right now, we've written a function to open a checkout, but we haven't set it to run.

We can add onLoad to our <body> tag to run our openCheckout() function immediately after the page has loaded, passing in our monthItemsList variable as a parameter:

Test your work

Save your page, then open it in your browser. Paddle Checkout should load in place of the checkout container <div> element we created earlier.

You'll notice that the Paddle Checkout frame doesn't include any information about what the checkout is for. That's our next step.

Screenshot showing an inline checkout. It's on the first page of checkout, with fields for email and country. There is no items list or totals.

3. Show and update on-page information

The inline checkout frame doesn't include a breakdown of items or totals. It's designed to handle capturing customer and payment information, giving you the flexibility to show items and totals in your frontend in a way that fits with our design.

To do this, we can use an event callback function. Paddle.js emits events throughout the checkout process when key things happen. An event callback function is some code that we run when a specific event occurs.

For example, when checkout is first loaded, Paddle.js emits a checkout.loaded event that contains information about the items and totals on a checkout. We can build an event callback function to update our items and totals table with data contained in the event.

Add tables to hold items and totals

First, we need to add some HTML for a couple of tables to hold items and totals information. You might use something more visual when building an app or website, but tables work for our tutorial.

Add this to the <body> of your page.

In this sample, we have two tables for our items and our totals:

  • Our items table has a header row and a body row that's got some zero values in. We'll add a row for each item on our checkout later.
  • Our totals table has a header column for the totals we'd like to show to our customer. There are ids set on the <td> elements that should contain totals. We'll use these IDs to replace the contents of these elements with totals later.

Update items table

Next, we'll build an event callback function to take data about our items from events and display it in our items table.

In our sample, we created a function called updateTable() that takes a parameter called event. Then, we pass the event payload to our function as event. Here's how it works:

  1. First, we exclude events that don't return a name field and print the data payload of events to the console. This is useful for us to see which events are emitted and how they look while we're testing.

  2. We create a variable called items and set this to event.data.items in our event payload. We'll use this variable to populate our items table.

  3. We call another function as part of this event callback function: updateItemsTable(), where we pass items as a parameter.

  4. We create the updateItemsTable() function that we called in our event callback, setting it up to accept a parameter called items. It finds and selects our items table body (.items-table tbody), clears out any rows, then iterates through each item in the items array we passed.

  5. When iterating through each item, we call another function called createTableRow(). We define this underneath, and it accepts productName, priceName, quantity, and total — a parameter for each of the columns in our items table.

  6. createTableRow() returns an HTML table row element with our product name, price name, quantity, and total. This newly created row is appended to our table body in our updateItemsTable() function.

  7. We update our Paddle.Initialize() method, passing updateTable as the eventCallback. This means this function is run every time an event is emitted by Paddle.js.

Update totals table

Let's update our event callback function so that it displays totals from our events in our totals table.

In our sample, we updated our updateTable() function so that it calls another function, updateSummaryTable(), to update our totals table. Here's how it works:

  1. We create some additional variables called totals and recurringTotals, setting these to values in our event payload.

  2. We add a call to another function as part of this event callback function: updateSummaryTable(), where we pass totals and recurringTotals as parameters.

  3. We create the updateSummaryTable() function that we called in our event callback, setting it up to accept parameters called totals and recurringTotals.

  4. updateSummaryTable() gets cells in our totals table using the IDs that we gave them earlier, then replaces the contents with values from the totals and recurringTotals arrays that we passed in as parameters. We calculate the one-time total by subtracting the subtotal of recurring items from the overall subtotal.

For simplicity, we use the built-in .toFixed() JavaScript method to format values to two decimal places in our sample. Paddle supports 25 currencies, some of which use a different number of decimal places. Consider using a currency library like currency.js to format currencies correctly.

4. Take a test payment

We're now ready to test! Save your page, then open it in your browser. Paddle.js should open an inline checkout for the items that we passed. You should see items and totals in the tables we created.

If you're using the sandbox environment, you can take a test payment using our test card details:

Email addressAn email address you own
CountryAny valid country supported by Paddle
ZIP code (if required)Any valid ZIP or postal code
Card number4242 4242 4242 4242
Name on cardAny name
Expiration dateAny valid date in the future.
Security code100

Short animation showing launching an inline checkout, entering contact information, and entering test payment details.

5. Update items on the checkout (optional)

What if we want to update our checkout now it's opened? For example, we might want to let customers adjust the quantity of items or upsell them addons. Paddle.js includes the Paddle.Checkout.updateCheckout() method to let us dynamically update items, customer information, and discount on a checkout.

For this tutorial, we'll add a button that we can click to switch to annual plan. When customers click this button, we'll swap monthly items on the checkout for annual plan items.

Define list of prices

Paddle.Checkout.updateCheckout() has an items parameter. When we pass an items list, Paddle.js replaces the items on the checkout with the new items list we passed.

First, we'll define a new variable called yearItemsList. Like monthItemsList, we'll pass an array of objects, where each object contains a priceId and quantity. In this case, there are two prices that recur yearly and a single one-time price.

Keep in mind that the entire items list is replaced — any omitted items are removed from the checkout entirely.

Recurring items on a checkout must have the same billing interval. For example, you can't have a checkout with some prices that are billed monthly and some products that are billed annually.

Update items

Next, we'll build a function to replace items on our checkout

In our sample, we created a function called switchPlan(). Here's how it works:

  1. We create a variable called isMonthly and set it to true. This variable tracks whether the current plan is monthly or yearly, and it's set to true initially because we pass monthItemsList to our checkout when the page loads.

  2. We create a function called switchPlan(), then set a variable called updatedItems to our yearItemList array if isMonthly is true, and monthItemList if isMonthly is false.

  3. We call the Paddle.Checkout.updateCheckout() method, passing updatedItems as the items parameter. This is our yearly items list when the plan is monthly, and our monthly items list when the plan is yearly.

  4. We toggle the value of isMonthly. If it were true, it's set to false; if it were false, it's set to true. This means that next time switchPlan() is called, it'll switch plans correctly.

Add a button to swap plan

Finally, add a button to our HTML to call our switchPlan() function.

Test your work

Save your page, then open it in your browser. Paddle Checkout should load as before. When you click the switch plan button, Paddle.js swaps items on your checkout from monthly to annual, and vice versa.

Short animation showing updating items on an inline checkout. When the switch plan button is clicked, items and totals update.

Next steps

That's it! Now you've built a checkout, you might like to extend Paddle Checkout by automatically applying a discount, passing optional checkout settings, or building a success workflow.

Automatically apply a discount

Extend your checkout by passing a discount. When our checkout is launched, Paddle automatically applies the discount (where it's valid).

Pass checkout settings

We covered passing the required settings for inline checkout, but there are a bunch of other settings you can pass that give you more control over how opened checkouts work. For example, you can set the language that Paddle Checkout uses, hide the option to add a discount, or restrict payment methods shown to customers.

Build a success workflow

When customers complete checkout, Paddle Checkout has a final screen that lets customers know that their purchase was successful. If you like, you can redirect customers to your own page or use JavaScript event callbacks to build a more advanced success workflow.