Oct 24, 2023
51 min read
Accepting online payments is now a universal must-have, catering to everyone from solo entrepreneurs to massive global corporations.
PayPal Checkout allows for seamless integration of PayPal’s Payment Buttons component into your e-commerce app, granting you the power to accept online payments. In this guide, I am going to show how to add PayPal Checkout to a simple shopping app, built with HTML and NodeJS.
Part 1. Prerequisites
Part 2a. Basic Integration — Code Setup
Part 2b. Basic Integration — Checkout Flow
Part 3a. Custom Integration — Code Setup
Part 3b. Custom Integration — Checkout Flow
Part 4. Conclusion
Part 5. Additional PayPal Payment Integrations
To complete this integration, you will need the following information:
You can grab the code for our completed Checkout Integration demo from the PayPal Developer GitHub repository.
Navigate to PayPal’s Integrate Checkout documentation. The documentation shows you how to set your development environment up: installing npm, verifying your package.json file, and setting up your .env file.
Next, you will set up your back-end server and front-end code. The documentation presents the contents of three files: server.js (for the back-end server), app.js (for the client-side logic) and checkout.html (for the user interface).
Grab these code snippets and paste them into your IDE (Integrated Development Environment). I strongly recommend that you implement the basic integration first, and ensure that you are able to complete the PayPal Checkout Flow successfully, before adding your custom e-commerce code. I find that this approach reduces potential errors and associated frustrations. 😅
In the checkout.html file, replace the word “test” within the Software Development Kit (SDK) script tag with your Client ID.
<script src="https://www.paypal.com/sdk/js?client-id=CLIENT_ID"></script>
In the app.js file, note that the “cart” object in the response body is hard-coded with one product’s SKU and quantity information. For important security reasons, we do not pass any transaction or price amounts from the front- end. Instead, we use the SKU/ID and quantity information to look up the product(s) in a datastore and use these product details to calculate the total cart price in our back-end.
body: JSON.stringify({
cart: [
{
id: "YOUR_PRODUCT_ID",
quantity: "YOUR_PRODUCT_QUANTITY",
},
],
})
In the server.js file, note that the transaction quantity (“100.00”) is hardcoded into the createOrder call.
const createOrder = async (cart) => {
console.log(
"shopping cart information passed from the frontend createOrder() callback:",
cart,
);
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const payload = {
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "100.00",
},
},
],
};
... more code ...
}
Start your server; on the command line, run “node server/server.js” or whichever shortcut is defined in your package.json file. You should see the PayPal Buttons component displayed on the front- end.
Click the first PayPal button, which displays the Checkout modal. Note that the URL in the modal shows that we are in a sandbox environment. Sign in using your Personal Sandbox account (if you haven’t already).
You are then presented with the transaction amount (at this point, this should be $100.00) and several payment options.
Complete the transaction with the PayPal Balance option. A confirmation message should appear in your browser; its location depends on where you place the element with the id “result-message” in your application.
<p id="result-message"></p>
Now, let’s add our custom e-commerce code!
In the checkout.html file, I added the code for three products being sold at the LiftOff Shop. I styled the code with TailwindCSS:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PayPal JS SDK Standard Integration Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="p-6">
<h1 class="text-center text-4xl font-medium">LiftOff Shop</h1>
<main class="products flex flex-wrap justify-center">
<div
class="product text-center m-3 p-3 border border-slate-400 rounded-md"
>
<img src="https://picsum.photos/id/21/200" class="rounded-md" />
<h2 class="text-xl my-2">Galaxy Shoes</h2>
<p class="text-green-600 font-medium text-lg mb-2">$100</p>
<div class="mb-3">
<label class="text-lg" for="quantity1">Quantity:</label>
<select class="text-lg" id="quantity1">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<button
class="product-btn py-1 px-3 rounded-md bg-indigo-200 hover:bg-indigo-300"
data-product-id="hdqgapq9"
data-quantity-id="quantity1"
>
Add to Cart
</button>
</div>
<div
class="product text-center m-3 p-3 border border-slate-400 rounded-md"
>
<img src="https://picsum.photos/id/104/200" class="rounded-md" />
<h2 class="text-xl my-2">Spaceship Earrings</h2>
<p class="text-green-600 font-medium text-lg mb-2">$40</p>
<div class="mb-3">
<label class="text-lg" for="quantity2">Quantity:</label>
<select class="text-lg" id="quantity2">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<button
class="product-btn py-1 px-3 rounded-md bg-indigo-200 hover:bg-indigo-300"
data-product-id="rnfjwsy0"
data-quantity-id="quantity2"
>
Add to Cart
</button>
</div>
<div
class="product text-center m-3 p-3 border border-slate-400 rounded-md"
>
<img src="https://picsum.photos/id/342/200" class="rounded-md" />
<h2 class="text-xl my-2">Martian Tote</h2>
<p class="text-green-600 font-medium text-lg mb-2">$75</p>
<div class="mb-3">
<label class="text-lg" for="quantity3">Quantity:</label>
<select class="text-lg" id="quantity3">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<button
class="product-btn py-1 px-3 rounded-md bg-indigo-200 hover:bg-indigo-300"
data-product-id="zytkddw5"
data-quantity-id="quantity3"
>
Add to Cart
</button>
</div>
</main>
<p id="result-message" class="mx-auto text-center text-red-600 w-1/2"></p>
<section class="mt-8 mx-auto flex flex-wrap w-1/2">
<div class="mb-3 w-1/2 text-center" id="cart">
<h2 class="text-xl font-medium">Cart</h2>
<ul id="cart-items"></ul>
<p>TOTAL $<span id="cart-total">0</span></p>
<button
id="clear-cart"
class="py-1 px-3 mt-3 rounded-md bg-gray-200 hover:bg-gray-300"
>
Clear Cart
</button>
</div>
<div class="text-center" id="paypal-button-container"></div>
</section>
<script src="https://www.paypal.com/sdk/js?client-id=AYCd0wKOWs9itZ23_2XEZUhZpbZdHT_c1qcMCjqTnb6fxFE7tQBnUQGSPh3_PVQkgoBamFV1KgPUI9B4¤cy=USD"></script>
<script type="module" src="app.js"></script>
</body>
</html>
In the app.js file, I added code that brings the Add-To-Cart, Calculate-Cart-Total and Clear-Cart functionalities. To calculate my cart total, I imported my product information from the products.js file, which serves as my makeshift datastore. I also updated the cart object in the response body to the cartItems array (instead of the hard-coded product from earlier).
import { PRODUCTS } from "./products.js";
const cartItems = [];
let cartTotal = 0;
function addToCart(productId, quantityId) {
const quantitySelect = document.getElementById(quantityId);
const selectedQuantity = parseInt(quantitySelect.value, 10);
if (selectedQuantity > 0) {
cartItems.push({
id: productId,
quantity: selectedQuantity,
});
cartTotal = calculateCartTotal(cartItems);
updateCart(cartItems);
}
}
function calculateCartTotal(cartItems) {
let total = 0;
cartItems.forEach((item) => {
total += item.quantity * PRODUCTS[item.id].price;
});
return total;
}
function updateCart(cartItems) {
const cartItemsList = document.getElementById("cart-items");
const cartTotalElement = document.getElementById("cart-total");
cartItemsList.innerHTML = "";
cartTotalElement.textContent = cartTotal;
cartItems.forEach((item) => {
const li = document.createElement("li");
li.textContent = `${PRODUCTS[item.id].name}: ${item.quantity}`;
cartItemsList.appendChild(li);
});
}
window.paypal
.Buttons({
async createOrder() {
try {
const response = await fetch("/api/orders", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
// use the "body" param to optionally pass additional order information
// like product ids and quantities
body: JSON.stringify({
cart: cartItems,
}),
});
... more code ...
... more code ...
})
.render("#paypal-button-container");
// Example function to show a result to the user. Your site's UI library can be used instead.
function resultMessage(message) {
const container = document.querySelector("#result-message");
container.innerHTML = message;
}
// Event Listeners
const productBtns = document.querySelectorAll(".product-btn");
productBtns.forEach((btn) => {
btn.addEventListener("click", (e) => {
const productId = e.target.dataset.productId;
const quantityId = e.target.dataset.quantityId;
addToCart(productId, quantityId);
});
});
// clear cart
document.getElementById("clear-cart").addEventListener("click", () => {
// clear cart items, without reassigning the array
cartItems.length = 0;
cartTotal = 0;
updateCart(cartItems);
});
In the server.js file, I imported my products from products.js and added a function that calculates the transaction amount using the cart contents. In the createOrder call, I replaced the hardcoded transaction value with the calculated cart value.
import express from "express";
import fetch from "node-fetch";
import "dotenv/config";
import path from "path";
import { PRODUCTS } from "../client/products.js";
... more code ...
... more code ...
function calculateTotalPrice(cart) {
let totalPrice = 0;
for (let i = 0; i < cart.length; i++) {
totalPrice += PRODUCTS[cart[i].id].price * cart[i].quantity;
}
return totalPrice.toString();
}
/**
* Create an order to start the transaction.
* @see https://developer.paypal.com/docs/api/orders/v2/#orders_create
*/
const createOrder = async (cart) => {
// use the cart information passed from the front-end to calculate the purchase unit details
console.log(
"shopping cart information passed from the frontend createOrder() callback:",
cart
);
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const payload = {
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: calculateTotalPrice(cart),
},
},
],
};
... more code ...
};
... more code ...
Restart the server and add some products to the cart; I chose one of each item, totaling $215.
Click the gold PayPal button to activate Checkout (signing in with your Personal Sandbox account, if necessary). You should see that the transaction amount is the value of your cart.
Go ahead and complete the purchase. You should see a confirmation message in the browser.
This demo introduced a straightforward integration of PayPal Checkout, using the PayPal Buttons component. You may customize your further, to meet your use cases. Read our Checkout Documentation for examples of these customizations. The PayPal Developer GitHub repository also has several examples of integrations within various code environments, such as React and Angular.
3 min read
4 min read