DEV Community

Cover image for Accept E-Commerce Payments Easily with PayPal’s Buttons Component! 💰
Arit Developer
Arit Developer

Posted on • Originally published at Medium

Accept E-Commerce Payments Easily with PayPal’s Buttons Component! 💰

Accepting online payments is now a universal must-have, catering to everyone from solo entrepreneurs to massive global corporations.

PayPal’s Standard 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 Standard Checkout to a simple shopping app, built with HTML and NodeJS.

Guide Overview

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


Part 1. Prerequisites

To complete this integration, you will need the following information:

  • API Credentials (Client ID and Secret) for a PayPal REST app: paste these from your Developer Dashboard. Since we are working in a development environment in this demo, ensure that the Sandbox Toggle is on.

Obtain Test API Credentials

  • The username and password for a Personal Sandbox account: you will need this to complete the checkout process.

Navigate to Sandbox Accounts under Testing Tools

Obtain Personal Sandbox Account login details


Part 2a. Basic Integration — Code Setup

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).

Obtain the Required Code

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 Standard 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&currency=USD"></script>
Enter fullscreen mode Exit fullscreen mode

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,
    },
  ],
})
Enter fullscreen mode Exit fullscreen mode

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 ...
}
Enter fullscreen mode Exit fullscreen mode

Part 2b. Basic Integration — Checkout Flow

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.

PayPal Button Component — Basic Integration

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).

The Checkout Modal starts the checkout process

You are then presented with the transaction amount (at this point, this should be $100.00) and several payment options.

The transaction amount is hardcoded to $100

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>
Enter fullscreen mode Exit fullscreen mode

Status message after completed checkout process<br>


Part 3a. Custom Integration — Code Setup

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&currency=USD"></script>
    <script type="module" src="app.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

E-Commerce App with PayPal Buttons Component

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);
});
Enter fullscreen mode Exit fullscreen mode

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 ...
Enter fullscreen mode Exit fullscreen mode

Part 3b. Custom Integration — Checkout Flow

Restart the server and add some products to the cart; I chose one of each item, totaling $215.

Cart total showing total amount of cart items

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.

Transaction value in the Checkout modal is equal to the cart amount

Go ahead and complete the purchase. You should see a confirmation message in the browser.

Confirmation of successful checkout in browser


Part 4. Conclusion

This demo introduced a straightforward integration of PayPal’s Standard 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.


Part 5. Additional PayPal Payment Integrations


Join the PayPal Developer Community!
Our Developer Community members support each other in integrating PayPal technologies, contributing to open source, expanding knowledge and networks, and improving PayPal’s products and documentation. We’d love to have you join us! 💙

Top comments (1)

Collapse
 
dotenv profile image
Dotenv

💛🌴