Table of Contents
Scaffold our app
A basic serverless function with Netlify
Hook up Stripe
Hook it up to Nuxt
Please give us your payment details:
Success!
Gatsby Applications
Home Web Front-end CSS Tutorial Let's Build a JAMstack E-Commerce Store with Netlify Functions

Let's Build a JAMstack E-Commerce Store with Netlify Functions

Apr 17, 2025 am 10:13 AM

Let's Build a JAMstack E-Commerce Store with Netlify Functions

A lot of people are confused about what JAMstack is. The acronym stands for JavaScript, APIs, and Markup, but truly, JAMstack doesn’t have to include all three. What defines JAMstack is that it’s served without web servers. If you consider the history of computing, this type of abstraction isn’t unnatural; rather it’s the inevitable progression this industry has been moving towards.

So, if JAMstack tends to be static by definition, it can’t have dynamic functionality, server-side events, or use a JavaScript framework, right? Thankfully, not so. In this tutorial, we’ll set up a JAMstack e-commerce app and add some serverless functionality with Netlify Functions (which abstract AWS Lambda, and are super dope in my opinion).

I’ll show more directly how the Nuxt/Vue part was set up in a follow-up post, but for now we’re going to focus on the Stripe serverless function. I’ll show you how I set this one up, and we’ll even talk about how to connect to other static site generators such as Gatsby. Full disclosure, I work for Netlify, and am using their tools for this, it is possible to connect to Stripe with other services. I chose to work for Netlify in part because I enjoy some of the nice abstractions that their services offer.

This site and repo should get you started if you’d like to set something like this up yourself:

Demo Site

GitHub Repo

Scaffold our app

The very first step is to set up our app. This one is built with Nuxt to create a Vue app, but you can replace these commands with your tech stack of choice:

yarn create nuxt-app

hub create

git add -A
git commit -m “initial commit”

git push -u origin master
Copy after login

I am using yarn, hub (which allows me to create repos from the command line) and Nuxt. You may need to install these tools locally or globally before proceeding.

With these few commands, following the prompts, we can set up an entirely new Nuxt project as well as the repo.

If we log into Netlify and authenticate, it will ask us to pick a repo:

I’ll use yarn generate to create the project. With that, I can add in the site settings for Nuxt in the dist directory and hit feploy! That’s all it takes to set up CI/CD and deploy the site! Now every time I push to the master branch, not only will I deploy, but I’ll be given a unique link for that particular deploy as well. So awesome.

A basic serverless function with Netlify

So here’s the exciting part, because the setup for this kind of functionality is so quick! If you’re unfamiliar with Serverless, you can think of it like the same JavaScript functions you know and love, but executed on the server. Serverless functions are event-driven logic and their pricing is extremely low (not just on Netlify, but industry-wide) and scales with your usage. And yes, we have to add the qualifier here: serverless still uses servers, but babysitting them is no longer your job. Let’s get started.

Our very basic function looks like this. I stored mine in a folder named functions, and just called it index.js. You can truly call the folder and function what you want.

// functions/index.js
exports.handler = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "Hi there Tacos",
      event
    })
  }
}
Copy after login

We’ll also need to create a netlify.toml file at the root of the project and let it know which directory to find the function in, which in this case, is “functions.”

// netlify.toml
[build]
  functions = "functions"
Copy after login

If we push to master and go into the dashboard, you can see it pick up the function!

If you look at the endpoint listed above it’s stored here:
https://ecommerce-netlify.netlify.com/.netlify/functions/index

Really, for any site you give it, the URL will follow this pattern:
https://.netlify/functions/

When we hit that endpoint, it provides us with the message we passed in, as well as all the event data we logged as well:

I love how few steps that is! This small bit of code gives us infinite power and capabilities for rich, dynamic functionality on our sites.

Hook up Stripe

Pairing with Stripe is extremely fun because it’s easy to use, sophisticated, has great docs, and works well with serverless functions. I have other tutorials where I used Stripe because I enjoy using their service so much.

Here’s a bird’s eye view of the app we’ll be building:

First we’ll go to the Stripe dashboard and get our keys. For anyone totally scandalized right now, it’s OK, these are testing keys. You can use them, too, but you’ll learn more if you set them up on your own. (It’s two clicks and I promise it’s not hard to follow along from here.)

We’ll install a package called dotenv which will help us store our key and test it locally. Then, we’ll store our Stripe secret key to a Stripe variable. (You can call it anything, but here I’ve called it STRIPE_SECRET_KEY, and that’s pretty much industry standard.)

yarn add dotenv
Copy after login
require("dotenv").config()

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)
Copy after login

In the Netlify dashboard, we’ll go to “Build & deploy,” then “Environment” to add in Environment variables, where the STRIPE_SECRET_KEY is key, and the value will be the key that starts with sk.

We’ll also add in some headers so we don’t run into CORS issues. We’ll use these headers throughout the function we’re going to build.

const headers = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "Content-Type"
}
Copy after login

So, now we’ll create the functionality for talking to Stripe. We’ll make sure we’ll handle the cases that it’s not the HTTP method we’re expecting, and also that we’re getting the information we expect.

You can already see here, what data we’re going to be needing to send to stripe by what we check for. We’ll need the token, the total amount, and an idempotency key.

If you’re unfamiliar with idempotency keys, they are unique values that are generated by a client and sent to an API along with a request in case the connection is disrupted. If the server receives a call that it realizes is a duplicate, it ignores the request and responds with a successful status code. Oh, and it’s an impossible word to pronounce.

exports.handler = async (event, context) => {
  if (!event.body || event.httpMethod !== "POST") {
    return {
      statusCode: 400,
      headers,
      body: JSON.stringify({
        status: "invalid http method"
      })
    }
  }

  const data = JSON.parse(event.body)

  if (!data.stripeToken || !data.stripeAmt || !data.stripeIdempotency) {
    console.error("Required information is missing.")

    return {
      statusCode: 400,
      headers,
      body: JSON.stringify({
        status: "missing information"
      })
    }
  }
Copy after login

Now, we’ll kick off the Stripe payment processing! We’ll create a Stripe customer using the email and token, do a little logging, and then create the Stripe charge. We’ll specify the currency, amount, email, customer ID, and give a description while we’re at it. Finally, we’ll provide the idempotency key (pronounced eye-dem-po-ten-see), and log that it was successful.

(While it’s not shown here, we’ll also do some error handling.)

// stripe payment processing begins here
try {
  await stripe.customers
    .create({
      email: data.stripeEmail,
      source: data.stripeToken
    })
    .then(customer => {
      console.log(
        `starting the charges, amt: ${data.stripeAmt}, email: ${data.stripeEmail}`
      )
      return stripe.charges
        .create(
          {
            currency: "usd",
            amount: data.stripeAmt,
            receipt_email: data.stripeEmail,
            customer: customer.id,
            description: "Sample Charge"
          },
          {
            idempotency_key: data.stripeIdempotency
          }
        )
        .then(result => {
          console.log(`Charge created: ${result}`)
        })
    })
Copy after login

Hook it up to Nuxt

If we look back at our application, you can see we have pages and components that live inside the pages. The Vuex store is like the brain of our application. It will hold the state of the app, and that’s what will communicate with Stripe. However, we still need to communicate with our user via the client. We’ll collect the credit card data in a component called AppCard.vue that will live on the cart page.

First, we’ll install a package called vue-stripe-elements-plus, that gives us some Stripe form elements that allow us to collect credit card data, and even sets us up with a pay method that allows us to create tokens for stripe payment processing. We’ll also add a library called uuid that will allow us to generate unique keys, which we’ll use for the idempotency key.

yarn add vue-stripe-elements-plus uuid
Copy after login

The default setup they give us to work with vue-stripe-elements-plus looks like this:

<template>
  <div id="app">
    <h1 id="Please-give-us-your-payment-details">Please give us your payment details:</h1>
    <card class="stripe-card" :class="{ complete }" stripe="pk_test_XXXXXXXXXXXXXXXXXXXXXXXX" :options="stripeOptions"></card>
    <button class="pay-with-stripe" :disabled="!complete">Pay with credit card</button>
  </div>
</template>
Copy after login
<script>
import { stripeKey, stripeOptions } from './stripeConfig.json'
import { Card, createToken } from 'vue-stripe-elements-plus'

export default {
  data () {
    return {
      complete: false,
      stripeOptions: {
        // see https://stripe.com/docs/stripe.js#element-options for details
      }
    }
  },

  components: { Card },

  methods: {
    pay () {
      // createToken returns a Promise which resolves in a result object with
      // either a token or an error key.
      // See https://stripe.com/docs/api#tokens for the token object.
      // See https://stripe.com/docs/api#errors for the error object.
      // More general https://stripe.com/docs/stripe.js#stripe-create-token.
      createToken().then(data => console.log(data.token))
    }
  }
}
</script>
Copy after login

So here’s what we’re going to do. We’re going to update the form to store the customer email, and update the pay method to send that and the token or error key to the Vuex store. We’ll dispatch an action to do so.

data() {
    return {
      ...
      stripeEmail: ""
    };
  },
  methods: {
    pay() {
      createToken().then(data => {
        const stripeData = { data, stripeEmail: this.stripeEmail };
        this.$store.dispatch("postStripeFunction", stripeData);
      });
    },
 ...
Copy after login

That postStripeFunction action we dispatched looks like this:

// Vuex store
export const actions = {
  async postStripeFunction({ getters, commit }, payload) {
    commit("updateCartUI", "loading")

    try {
      await axios
        .post(
          "https://ecommerce-netlify.netlify.com/.netlify/functions/index",
          {
            stripeEmail: payload.stripeEmail,
            stripeAmt: Math.floor(getters.cartTotal * 100), //it expects the price in cents                        
            stripeToken: "tok_visa", //testing token, later we would use payload.data.token
            stripeIdempotency: uuidv1() //we use this library to create a unique id
          },
          {
            headers: {
              "Content-Type": "application/json"
            }
          }
        )
        .then(res => {
          if (res.status === 200) {
            commit("updateCartUI", "success")
            setTimeout(() => commit("clearCart"), 3000)
            …
Copy after login

We’re going to update the UI state to loading while we’re processing. Then we’ll use axios to post to our function endpoint (the URL you saw earlier in the post when we set up our function). We’ll send over the email, the amt, the token and the unique key that we built the function to expect.

Then if it was successful, we’ll update the UI state to reflect that.

One last note I’ll give is that I store the UI state in a string, rather than a boolean. I usually start it with something like “idle” and, in this case, I’ll also have “loading,” “success,” and “failure.” I don’t use boolean states because I’ve rarely encountered a situation where UI state only has two states. When you work with booleans for this purpose, you tend to need to break it out into more and more states, and checking for all of them can get increasingly complicated.

As it stands, I can reflect changes in the UI on the cart page with legible conditionals, like this:

<section v-if="cartUIStatus === 'idle'">
  <app-cart-display></app-cart-display>
</section>

<section v-else-if="cartUIStatus === 'loading'">
  <app-loader></app-loader>
</section>

<section v-else-if="cartUIStatus === 'success'">
  <h2 id="Success">Success!</h2>
  <p>Thank you for your purchase. You'll be receiving your items in 4 business days.</p>
  <p>Forgot something?</p>

  <button>
    <nuxt-link exact to="/">Back to Home</nuxt-link>
  </button>
</section>

<section v-else-if="cartUIStatus === 'failure'">
  <p>Oops, something went wrong. Redirecting you to your cart to try again.</p>
</section>
Copy after login

And there you have it! We’re all set up and running to accept payments with stripe on a Nuxt, Vue site with a Netlify function, and it wasn’t even that complicated to set up!

Gatsby Applications

We used Nuxt in this instance but if you wanted to set up the same kind of functionality with something that uses React such as Gatsby, there’s a plugin for that. (Everything is plugin in Gatsby. ☺️)

You would install it with this command:

yarn add gatsby-plugin-netlify-functions
Copy after login

…and add the plugin to your application like this:

plugins: [
  {
    resolve: `gatsby-plugin-netlify-functions`,
    options: {
      functionsSrc: `${__dirname}/src/functions`,
      functionsOutput: `${__dirname}/functions`,
    },
  },
]
Copy after login

The serverless function used in this demo is straight up JavaScript, so it’s also portable to React applications. There’s a plugin to add the Stripe script to your Gatsby app (again, everything is a plugin). Fair warning: this adds the script to every page on the site. To collect the credit card information on the client, you would use React Stripe Elements, which is similar to the Vue one we used above.

Just make sure that you’re collecting from the client and passing all the information the function is expecting:

  • The user email
  • The total amount, in cents
  • The token
  • The idempotency key
Demo Site

GitHub Repo

With such a low barrier to entry, you can see how you can make really dynamic experiences with JAMstack applications. It’s amazing how much you can accomplish without any maintenance costs from servers. Stripe and Netlify Functions make setting up payment processing in a static application such a smooth developer experience!

The above is the detailed content of Let's Build a JAMstack E-Commerce Store with Netlify Functions. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
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

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1655
14
PHP Tutorial
1254
29
C# Tutorial
1228
24
Google Fonts   Variable Fonts Google Fonts Variable Fonts Apr 09, 2025 am 10:42 AM

I see Google Fonts rolled out a new design (Tweet). Compared to the last big redesign, this feels much more iterative. I can barely tell the difference

How to Create an Animated Countdown Timer With HTML, CSS and JavaScript How to Create an Animated Countdown Timer With HTML, CSS and JavaScript Apr 11, 2025 am 11:29 AM

Have you ever needed a countdown timer on a project? For something like that, it might be natural to reach for a plugin, but it’s actually a lot more

HTML Data Attributes Guide HTML Data Attributes Guide Apr 11, 2025 am 11:50 AM

Everything you ever wanted to know about data attributes in HTML, CSS, and JavaScript.

How to select a child element with the first class name item through CSS? How to select a child element with the first class name item through CSS? Apr 05, 2025 pm 11:24 PM

When the number of elements is not fixed, how to select the first child element of the specified class name through CSS. When processing HTML structure, you often encounter different elements...

Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Apr 05, 2025 pm 05:51 PM

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...

A Proof of Concept for Making Sass Faster A Proof of Concept for Making Sass Faster Apr 16, 2025 am 10:38 AM

At the start of a new project, Sass compilation happens in the blink of an eye. This feels great, especially when it’s paired with Browsersync, which reloads

How We Created a Static Site That Generates Tartan Patterns in SVG How We Created a Static Site That Generates Tartan Patterns in SVG Apr 09, 2025 am 11:29 AM

Tartan is a patterned cloth that’s typically associated with Scotland, particularly their fashionable kilts. On tartanify.com, we gathered over 5,000 tartan

See all articles