Home >Web Front-end >JS Tutorial >Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (€/$), Quantity, and Location Support

Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (€/$), Quantity, and Location Support

Susan Sarandon
Susan SarandonOriginal
2024-11-01 17:11:021064browse

Commit 3c90066

Before you read any further, just as an FYI, I learn and code on my own to build what we need to run our business. So, please take the following information as is. It's a real world example we used for our own ? yellow book about coworking. At the time we couldn't find a better solution, so I build the following for our eCommerce website.

Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (€/$), Quantity, and Location Support

Selling a single product online, like a book, can be straightforward until you encounter the complexities of international shipping rates, multiple currencies, and varying quantities—especially since Stripe Checkout allows for only one shipping rate by default. In this article, let's walk through how we built a custom shipping calculator using Netlify Functions and Stripe to handle these challenges. By the end, you'll have a working solution tailored for selling up to three copies of a book, with dynamic shipping costs based on the customer's currency (EUR/USD), quantity, and location.

While this example is very specific to our needs, you can tweak it to suit your own requirements. Please feel free to share your solutions, upgrades, or any improvements you make.

? Prerequisites

Before we dive in, make sure you have the following:

  • A Netlify account with a deployed site.
  • A Stripe account with test and live API keys.
  • Basic understanding of HTML, JavaScript, and serverless functions.
  • Familiarity with environment variables.

? Overview

Let's create a seamless checkout experience that:

  • Determines shipping costs based on the customer's currency, number of items, and location.
  • Supports both EUR and USD currencies.
  • Handles different shipping rates for European and worldwide destinations.
  • Integrates seamlessly with Stripe Checkout.

Bellow I will cover both the frontend (HTML and JavaScript) and the backend (Netlify Function) components.

? Project Structure

Project should include the following folders and files:

/functions
  - create-checkout-session.js
/index.html
.env
netlify.toml
package.json
  • /functions: Directory for Netlify Functions.
  • create-checkout-session.js: The custom serverless function.
  • index.html: The frontend HTML file.
  • .env: File to store environment variables
  • netlify.toml: The configuration file for Netlify.
  • package.json: Lists dependencies like stripe.

?️ Setting Up the Backend (Netlify Function)

Create a new file in your /functions directory named create-checkout-session.js.

/functions
  - create-checkout-session.js
/index.html
.env
netlify.toml
package.json

? Code Breakdown

Importing Stripe

// functions/create-checkout-session.js

// Add Stripe secret key
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

exports.handler = async (event) => {
  // Parse the order data sent from the frontend
  const order = JSON.parse(event.body);

  // Define country groups
  const euCountries = ['AL', 'AM', 'AT', ...]; // Add the EU countries you ship to
  const worldCountries = ['AE', 'AR', 'AU', ...]; // Add worldwide countries you ship to
  let allowedCountries = [];

  // Payment methods based on currency
  let paymentMethods = [];

  // Determine shipping rates and allowed countries
  if (order.currency === 'EUR') {
    paymentMethods = ['card', 'sepa_debit', 'ideal', 'bancontact', 'p24', 'eps', 'giropay', 'sofort'];

    if (order.shippingOption === 'europe-eur') {
      allowedCountries = euCountries;
      // Set shipping rate IDs for Europe in EUR
      order.shippingRate = process.env[`SHIPPING_RATE_EUR_EU_${order.items}`];
    } else if (order.shippingOption === 'world-eur') {
      allowedCountries = worldCountries;
      // Set shipping rate IDs for World in EUR
      order.shippingRate = process.env[`SHIPPING_RATE_EUR_W_${order.items}`];
    }
  } else if (order.currency === 'USD') {
    paymentMethods = ['card'];

    if (order.shippingOption === 'europe-usd') {
      allowedCountries = euCountries;
      // Set shipping rate IDs for Europe in USD
      order.shippingRate = process.env[`SHIPPING_RATE_USD_EU_${order.items}`];
    } else if (order.shippingOption === 'world-usd') {
      allowedCountries = worldCountries;
      // Set shipping rate IDs for World in USD
      order.shippingRate = process.env[`SHIPPING_RATE_USD_W_${order.items}`];
    }
  }

  // Create the Stripe Checkout session
  const session = await stripe.checkout.sessions.create({
    payment_method_types: paymentMethods,
    line_items: [
      {
        price: order.priceId, // The price ID of your product
        quantity: order.items,
      },
    ],
    mode: 'payment',
    billing_address_collection: 'auto',
    shipping_rates: [order.shippingRate],
    shipping_address_collection: {
      allowed_countries: allowedCountries,
    },
    success_url: `${process.env.URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.URL}/cancel`,
  });

  return {
    statusCode: 200,
    body: JSON.stringify({
      sessionId: session.id,
      publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
    }),
  };
};

Initializes the Stripe SDK with your secret key.

Handling the Event

Pars the incoming order data from the frontend.

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

Defining Country Groups

exports.handler = async (event) => {
  const order = JSON.parse(event.body);
  // Rest of the code...
};
  • Lists of countries for EU and worldwide shipping.
  • allowedCountries will be set based on the shipping option.

Setting Payment Methods

Determine the available payment methods based on the currency.

const euCountries = [/* ... */];
const worldCountries = [/* ... */];
let allowedCountries = [];

Determining Shipping Rates

let paymentMethods = [];
  • Uses environment variables to set the correct shipping rate ID based on currency, region, and quantity.
  • Example environment variable: SHIPPING_RATE_EUR_EU_1 for 1 item in Europe with EUR currency.

Creating the Checkout Session

if (order.currency === 'EUR') {
  paymentMethods = [/* ... */];

  if (order.shippingOption === 'europe-eur') {
    allowedCountries = euCountries;
    order.shippingRate = process.env[`SHIPPING_RATE_EUR_EU_${order.items}`];
  } else if (order.shippingOption === 'world-eur') {
    allowedCountries = worldCountries;
    order.shippingRate = process.env[`SHIPPING_RATE_EUR_W_${order.items}`];
  }
} else if (order.currency === 'USD') {
  // Similar logic for USD
}
  • Creates a new Stripe Checkout session with dynamic configurations.

?️ Setting Up the Frontend

Below is a shortened example of the HTML and JavaScript code that interacts with our Netlify Function.

? HTML Structure (index.html)

const session = await stripe.checkout.sessions.create({
  payment_method_types: paymentMethods,
  line_items: [/* ... */],
  mode: 'payment',
  billing_address_collection: 'auto',
  shipping_rates: [order.shippingRate],
  shipping_address_collection: {
    allowed_countries: allowedCountries,
  },
  success_url: `${process.env.URL}/success?session_id={CHECKOUT_SESSION_ID}`,
  cancel_url: `${process.env.URL}/cancel`,
});

? HTML Breakdown

  • Currency Tabs: Allows users to select between EUR and USD pricing.
  • Number of Books: Users can select up to three books.
  • Shipping Destination: Dropdowns populated with countries, grouped by shipping rates.
  • Checkout Buttons: Initiates the checkout process when clicked.

? JavaScript Logic (script.js)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Book Pre-Order</title>
  <!-- Include any CSS or Meta tags here -->
</head>
<body>
  <!-- Book Purchase Section -->
  <section id="pricing">
    <div class="pricing-content">
      <!-- Currency Tabs -->
      <ul class="tabs-menu">
        <li id="active_currency_eur" class="current"><a href="#tab-1">Buy in ?? EUR</a></li>
        <li id="active_currency"><a href="#tab-2">Buy in ?? USD</a></li>
      </ul>

      <!-- EUR Tab Content -->
      <div id="tab-1" class="tab-content">
        <h3>1 Print Book</h3>
        <p>A beautiful, 350 pages book.</p>
        <p>Price: <span id="book-price-eur">€95</span></p>

        <!-- Number of Books -->
        <label for="num-books">Number of Books (Max 3)</label>
        <select name="num-books" id="num-books" required>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>

        <!-- Shipping Destination -->
        <label for="shipping-amount-eur">Select Shipping Destination</label>
        <select name="shipping-amount" id="shipping-amount-eur" required>
          <optgroup label="Europe €14">
            <option value="europe-eur">Austria</option>
            <option value="europe-eur">Belgium</option>
            <!-- Add other European countries -->
          </optgroup>
          <optgroup label="Worldwide €22">
            <option value="world-eur">United States</option>
            <option value="world-eur">Canada</option>
            <!-- Add other worldwide countries -->
          </optgroup>
        </select>

        <!-- Checkout Button -->
        <button id="checkout-button-eur" type="button">PRE-ORDER</button>
      </div>

      <!-- USD Tab Content -->
      <div id="tab-2" class="tab-content">
        <h3>1 Print Book</h3>
        <p>A beautiful, 350 pages book.</p>
        <p>Price: <span id="book-price-usd"></span></p>

        <!-- Number of Books -->
        <label for="num-books-usd">Number of Books (Max 3)</label>
        <select name="num-books-usd" id="num-books-usd" required>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>

        <!-- Shipping Destination -->
        <label for="shipping-amount-usd">Select Shipping Destination</label>
        <select name="shipping-amount" id="shipping-amount-usd" required>
          <optgroup label="Europe ">
            <option value="europe-usd">Austria</option>
            <option value="europe-usd">Belgium</option>
            <!-- Add other European countries -->
          </optgroup>
          <optgroup label="Worldwide ">
            <option value="world-usd">United States</option>
            <option value="world-usd">Canada</option>
            <!-- Add other worldwide countries -->
          </optgroup>
        </select>

        <!-- Checkout Button -->
        <button id="checkout-button-usd" type="button">PRE-ORDER</button>
      </div>
    </div>
  </section>

  <!-- Include Stripe.js -->
  <script src="https://js.stripe.com/v3/"></script>

  <!-- Include your JavaScript file -->
  <script src="script.js"></script>
</body>
</html>

? JavaScript Breakdown

  • Event Listeners: Attach click events to the checkout buttons.
  • Determining Order Details: Based on the clicked button, extract the currency, shipping option, number of books, and price ID.
  • Preparing Order Data: Create an object containing all necessary order information.
  • Fetching the Checkout Session: Send a POST request to the Netlify Function with the order data.
  • Redirecting to Stripe Checkout: Use the session ID returned from the backend to redirect the user to Stripe Checkout.

? Setting Environment Variables

Make sure to add your product and shipping prices on Stirpe Dashboard.

On Stripe:
Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (€/$), Quantity, and Location Support
On Netlify:
Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (€/$), Quantity, and Location Support

Create a .env file in the root of your project and add your environment variables(or do it on the Netlify UI as shown above Site configuration > Environment variables):

/functions
  - create-checkout-session.js
/index.html
.env
netlify.toml
package.json
  • Replace the values with your actual Stripe keys and shipping rate IDs.
  • Make sure to create these shipping rates in your Stripe dashboard.

? Updating netlify.toml

Configure Netlify to use environment variables in your functions:

// functions/create-checkout-session.js

// Add Stripe secret key
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

exports.handler = async (event) => {
  // Parse the order data sent from the frontend
  const order = JSON.parse(event.body);

  // Define country groups
  const euCountries = ['AL', 'AM', 'AT', ...]; // Add the EU countries you ship to
  const worldCountries = ['AE', 'AR', 'AU', ...]; // Add worldwide countries you ship to
  let allowedCountries = [];

  // Payment methods based on currency
  let paymentMethods = [];

  // Determine shipping rates and allowed countries
  if (order.currency === 'EUR') {
    paymentMethods = ['card', 'sepa_debit', 'ideal', 'bancontact', 'p24', 'eps', 'giropay', 'sofort'];

    if (order.shippingOption === 'europe-eur') {
      allowedCountries = euCountries;
      // Set shipping rate IDs for Europe in EUR
      order.shippingRate = process.env[`SHIPPING_RATE_EUR_EU_${order.items}`];
    } else if (order.shippingOption === 'world-eur') {
      allowedCountries = worldCountries;
      // Set shipping rate IDs for World in EUR
      order.shippingRate = process.env[`SHIPPING_RATE_EUR_W_${order.items}`];
    }
  } else if (order.currency === 'USD') {
    paymentMethods = ['card'];

    if (order.shippingOption === 'europe-usd') {
      allowedCountries = euCountries;
      // Set shipping rate IDs for Europe in USD
      order.shippingRate = process.env[`SHIPPING_RATE_USD_EU_${order.items}`];
    } else if (order.shippingOption === 'world-usd') {
      allowedCountries = worldCountries;
      // Set shipping rate IDs for World in USD
      order.shippingRate = process.env[`SHIPPING_RATE_USD_W_${order.items}`];
    }
  }

  // Create the Stripe Checkout session
  const session = await stripe.checkout.sessions.create({
    payment_method_types: paymentMethods,
    line_items: [
      {
        price: order.priceId, // The price ID of your product
        quantity: order.items,
      },
    ],
    mode: 'payment',
    billing_address_collection: 'auto',
    shipping_rates: [order.shippingRate],
    shipping_address_collection: {
      allowed_countries: allowedCountries,
    },
    success_url: `${process.env.URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.URL}/cancel`,
  });

  return {
    statusCode: 200,
    body: JSON.stringify({
      sessionId: session.id,
      publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
    }),
  };
};

? Installing Dependencies

Run the following command to install the Stripe SDK:

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

? Testing the Function

  1. Start Netlify Dev Server
exports.handler = async (event) => {
  const order = JSON.parse(event.body);
  // Rest of the code...
};
  1. Place an Order
  • Open your index.html file in the browser.
  • Select your options and click the "PRE-ORDER" button.
  • Ensure that the correct shipping rates and payment methods appear in the Stripe Checkout.
  1. Test Different Scenarios
  • Switch between EUR and USD currencies.
  • Change the shipping options and item quantities.
  • Confirm that the allowed countries match your configurations.

? Conclusion

Et voilà! You've set up a custom shipping calculator function that dynamically adjusts shipping rates based on currency, quantity, and location.

Feel free to adapt and expand upon this setup to suit your own products and shipping policies.

? Additional Resources

  • Stripe Checkout Documentation
  • Netlify Functions Documentation
  • Creating Shipping Rates in Stripe
  • Stripe.js Reference

Note: This article is based on a real-world scenario for pre-ordering/selling a single book with up to three copies and demonstrates one way to handle shipping calculations involving currency, quantity, and location variables. There might be more efficient methods depending on your specific needs.

The above is the detailed content of Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (€/$), Quantity, and Location Support. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn