DEV Community

Cover image for Building the Frontend with Astro and Integration with Stripe (Nerd Streetwear Online Store) Part II
Nebojsa Radakovic
Nebojsa Radakovic

Posted on • Edited on

Building the Frontend with Astro and Integration with Stripe (Nerd Streetwear Online Store) Part II

From ideation to examples to step-by-step setup, the side hustle nerd t-shirt store built with Astro, Crystallize, and Stripe for payment by ChatGPT and proofread for accuracy.

This is a four-piece article:
1️⃣ Bringing Idea to Life and Setting Up the Backend
2️⃣ Building the Frontend with Astro and Integration with Stripe
3️⃣ Advance Features and Deploying the Project
4️⃣ Final Testing, Going Live, and Summary

The backend is set, and the products are made. Let’s take care of the frontend and the payment.

2. Building the Frontend with Astro

🚀Astro is a modern framework that allows developers to create fast, optimized websites by leveraging the best parts of various frontend frameworks. Astro’s unique approach to building sites focuses on delivering minimal JavaScript to the client, translating to faster load times and better performance.

Key Features of Astro

  • Component-Based Architecture: Astro allows you to build pages using components from popular frameworks like React, Vue, Svelte, and others, all within the same project.
  • Zero JavaScript by Default: Astro delivers zero JavaScript to the client by default, but you can opt-in to use JavaScript where needed, ensuring only essential code is sent to the browser.
  • Static Site Generation (SSG): Astro is designed for static site generation, making it perfect for building fast and SEO-friendly e-commerce sites.
  • Server-Side Rendering (SSR): Astro also supports SSR, which can be useful for dynamic content or personalized user experiences.

Astro’s flexibility and performance make it an excellent choice for building the frontend of my nerd streetwear online store.

Setting Up an Astro Project

Setting up an Astro project is straightforward. Here’s how to get started:

  • Install Node.js: Make sure you have Node.js installed on your machine. You can download it from Node.js.

  • Create a New Astro Project: Open your terminal and run the following command to create a new Astro project:

npm create astro@latest
Enter fullscreen mode Exit fullscreen mode
  • Navigate to Your Project Directory: Follow the prompts to set up your project. You can name the project something like "disbar-store." After the project is created, navigate to the project directory:
cd disbar-store
Enter fullscreen mode Exit fullscreen mode
  • Install Dependencies: Install all the necessary dependencies for your project:
npm install
Enter fullscreen mode Exit fullscreen mode

With your Astro project set up, you’re ready to start building the front end of your store.

Connecting Astro to Crystallize

To build a dynamic e-commerce store, we need to connect our Astro frontend to the Crystallize backend. This connection lets us fetch product data, display content, and manage orders directly within our Astro site using Crystallize's powerful GraphQL API. Check out the example on Astro's website.

Step-by-Step Guide to Integrating Crystallize with Astro

  • Install Crystallize SDK: Start by installing the Crystallize JavaScript SDK, which will help you interact with the Crystallize API. In your Astro project directory, run the following command:
npm install @crystallize/js-api-client
Enter fullscreen mode Exit fullscreen mode
  • Create a Crystallize Client: To interact with your Crystallize tenant, create a client instance in your Astro project. Create a new file src/lib/crystallize.js and add the following code:
import { createClient } from '@crystallize/js-api-client';

export const client = createClient({
  tenantIdentifier: 'your-tenant-identifier', // replace with your Crystallize tenant identifier
});
Enter fullscreen mode Exit fullscreen mode
  • Replace your-tenant-identifier with the actual identifier of your Crystallize tenant.

  • Fetch Data from Crystallize: With the Crystallize client setup, you can fetch data from your backend.
    For example, to fetch all t-shirt products, create a new file src/pages/products.astro and add the following code:

---
import { client } from '../lib/crystallize';

const products = await client.catalogueAPI(
    #graphql`
      query ($language: String!, $path: String!, $version: VersionLabel) {
        catalogue(path: $path, language: $language, version: $version) {
          children {
            id
            name
            path
          }
        }
      }
    `,
     {
       Language: “en”,
       path: “/t-shirts”,
       version: version === 'draft' ? 'draft' : 'published',
     }
).catalogue;
---

<HTML>
  <head>
    <title>Products</title>
  </head>
  <body>
    <h1>Products</h1>
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          <h2>{product.name}</h2>
          <a href={product.path}>View product</a>
        </li>
      ))}
    </ul>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

This code fetches all products that match the "t-shirt" shape from your Crystallize catalog and displays them in a simple list.

  • Use Astro Components for Rendering: Astro’s strength lies in its ability to use components from various frameworks. For example, if you want to use React for parts of your page, create a React component in src/components/ProductList.jsx:
import React from 'react';

const ProductList = ({ products }) => {
  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          <h2>{product.name}</h2>
          <a href={product.path}>View product</a>
        </li>
      ))}
    </ul>
  );
};

export default ProductList;
Enter fullscreen mode Exit fullscreen mode

Then, import and use this component in your Astro page:

---
import { client } from '../lib/crystallize';
import ProductList from '../components/ProductList';

const products = await client.catalogueAPI(
    #graphql`
      query ($language: String!, $path: String!, $version: VersionLabel) {
        catalogue(path: $path, language: $language, version: $version) {
          children {
            id
            name
            path
          }
        }
      }
    `,
     {
       Language: “en”,
       path: “/t-shirts”,
       version: version === 'draft' ? 'draft' : 'published',
     }
).catalogue;
---

<html>
  <head>
    <title>Products</title>
  </head>
  <body>
    <h1>Products</h1>
    <ProductList products={products} />
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

This integration allows you to display product data dynamically fetched from Crystallize within your Astro frontend, providing a seamless connection between the backend and the user interface.

Creating Dynamic Routes

Dynamic routing is essential for an e-commerce site. It allows you to generate product pages on the fly based on data from your Crystallize backend. In Astro, dynamic routes are created using parameterized filenames.

How to Create Dynamic Product Pages in Astro

  • Create a Dynamic Route: To create dynamic routes in Astro, use square brackets in your filenames to denote a dynamic segment. For example, create a new folder src/pages/products/ and create a file named [slug].astro inside it:
---
import { client } from '../../lib/crystallize';

const { slug } = Astro.params;

const product = await client.catalogueAPI(
    #graphql`
      query ($language: String!, $path: String!, $version: VersionLabel) {
        catalogue(path: $path, language: $language, version: $version) {
          #your query based on shape
          name
          component(id: “description”){
            …on richTextContent {
               plantText
            }
          }
          …on Product {
             defaultVariant {
                price
                firstImage {
                   variants {
                     url
                   }
                }
             }
          }
        }
      }
    `,
     {
       Language: “en”,
       path: `/products/${slug}`,
       version: version === 'draft' ? 'draft' : 'published',
     }
).catalogue;

const { name, component, defaultVariant} = product.data;
---

<html>
  <head>
    <title>{name}</title>
  </head>
  <body>
    <h1>{name}</h1>
    <img src={defaultVariant.firstImage[0].url} alt={name} />
    <p>{component.description.plainText}</p>
    <p>{defaultVariant.price} USD</p>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • This file will generate a dynamic page for each product based on its slug (a unique identifier in the URL). Here is an example of fetching a product by its path from the Crystallize Dounut Astro boilerplate.

  • Link to Dynamic Pages:
    Update your product list page to link to the dynamic product pages. Modify src/pages/products.astro to include links to the individual product pages:

---
import { client } from '../lib/crystallize';

const products = await client.catalogueAPI(
    #graphql`
      query ($language: String!, $path: String!, $version: VersionLabel) {
        catalogue(path: $path, language: $language, version: $version) {
          children {
            id
            name
            path
          }
        }
      }
    `,
     {
       Language: “en”,
       path: “/t-shirts”,
       version: version === 'draft' ? 'draft' : 'published',
     }
).catalogue;
---

<html>
  <head>
    <title>Products</title>
  </head>
  <body>
    <h1>Products</h1>
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          <a href={`/products/${product.path}`}>
            <h2>{product.name}</h2>
            <img src={product.image.url} alt={product.name} />
            <p>{product.price} USD</p>
          </a>
        </li>
      ))}
    </ul>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Test Dynamic Routes: Run your Astro project locally (npm run dev) and navigate to the products page. Click on a product link to ensure that it takes you to the dynamically generated product page.

Following these steps, you’ll have a fully functional e-commerce frontend powered by Astro, with dynamic routing and content fetched from your Crystallize backend. This setup allows you to manage your online store efficiently, ensuring it is scalable and easy to maintain.

3. Integrating with Stripe

💰Stripe is a leading online payment processing platform that provides businesses with the tools they need to accept payments over the Internet. It supports various payment methods, including credit cards, debit cards, digital wallets like Apple Pay and Google Pay, and even local payment methods.

Key Features of Stripe

  • Ease of Integration: Stripe offers well-documented SDKs and APIs that make integrating with various web applications straightforward.
  • Security: Stripe handles sensitive payment data securely, complying with PCI-DSS standards, which reduces the burden on developers to meet these stringent requirements.
  • Global Reach: Stripe supports over 135 currencies and a variety of payment methods, allowing you to cater to a global customer base.
  • Comprehensive Dashboard: Stripe provides a powerful dashboard where you can manage payments, view analytics, and handle disputes and refunds.

Given its powerful capabilities and ease of use, Stripe is an excellent choice for handling payments in your DisBar nerd streetwear online store.

Integrating Stripe

To accept payments on your e-commerce site, you must integrate Stripe with your Astro frontend and, optionally, with Crystallize for order management (BTW, since Crystallize is headless, you can go with whatever payment gateway you want).

There are a couple of ways to do it. One of the best guides I’ve found is Collecting Payments with Stripe while using Astro guide. Alternatively, AI suggested a more generic React way that follows:

  • Set Up a Stripe Account: Go to the Stripe website and sign up for a free account. Once your account is created, navigate to the Stripe Dashboard to obtain your API keys (Publishable Key and Secret Key) from the Developers section.

  • Install Stripe SDK: In your Astro project, install the Stripe SDK to handle payment processing:

npm install @stripe/stripe-js
Enter fullscreen mode Exit fullscreen mode
  • Create a Payment Form: In your Astro project, create a simple payment form where users can enter their payment details. This can be done using Stripe Elements, which are pre-built UI components for collecting payment information. Create a new file src/components/PaymentForm.jsx:
import React, { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe('your-publishable-key'); // Replace with your Stripe publishable key

const PaymentForm = () => {
  const stripe = useStripe();
  const elements = useElements();
  const [error, setError] = useState(null);
  const [success, setSuccess] = useState(false);

  const handleSubmit = async (event) => {
    event.preventDefault();

    if (!stripe || !elements) {
      return;
    }

    const cardElement = elements.getElement(CardElement);
    const { error, paymentMethod } = await stripe.createPaymentMethod({
      type: 'card',
      card: cardElement,
    });

    if (error) {
      setError(error.message);
    } else {
      setError(null);
      setSuccess(true);
      // Send paymentMethod.id to your server to create a charge
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <CardElement />
      <button type="submit" disabled={!stripe}>Pay</button>
      {error && <div>{error}</div>}
      {success && <div>Payment Successful!</div>}
    </form>
  );
};

const PaymentPage = () => (
  <Elements stripe={stripePromise}>
    <PaymentForm />
  </Elements>
);

export default PaymentPage;
Enter fullscreen mode Exit fullscreen mode
  • Handling Payment on the Server: You must create a server-side endpoint to handle and confirm the payment intent. If you are using Node.js, create an endpoint in your backend (which could be part of a serverless function on platforms like Vercel or Netlify):
const express = require('express');
const Stripe = require('stripe');
const app = express();
const stripe = Stripe('your-secret-key'); // Replace with your Stripe secret key

app.post('/create-payment-intent', async (req, res) => {
  const { amount } = req.body; // Amount should be in the smallest currency unit (e.g., cents)

  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency: 'usd',
    });

    res.status(200).send({
      clientSecret: paymentIntent.client_secret,
    });
  } catch (error) {
    res.status(400).send({ error: error.message });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));
Enter fullscreen mode Exit fullscreen mode

The first line above, you don't need an express server because Astro takes care of it.

  • Integrating Stripe with Crystallize (Optional): After a successful transaction, you can send the payment confirmation to Crystallize to link Stripe payments with the company's order management. Use Crystallize’s API to create an order and associate it with the payment details (keep in mind that to access the Order API, you would need access tokens):
const { client } = require('@crystallize/js-api-client');

const crystallizeClient = client({
  tenantIdentifier: 'your-tenant-identifier',
  accessTokenId: ‘xXx’,
  accessTokenSecret: ‘xXx’
});

const createOrder = async (orderDetails) => {
  const order = await apiClient.orderApi(
        `#graphql
      mutation($input: CreateOrderInput!) {
        orders {
          create(input: $input) {
            id
          }
        }
      }
    `,
        {
            input: orderInput,
        }
    );


  return order;
};
Enter fullscreen mode Exit fullscreen mode

By following these steps, you can securely process payments on your DisBar online store using Stripe, providing a smooth checkout experience for your customers.

AND we’re at the end of part two of this guide about building a side hustle with Astro, Crystallize, and Stripe for payment. Next, look at advanced features and ways to deploy the project.

Next 👉 Advance Features and Deploying the Project

¯_(ツ)_/¯

If you enjoyed reading this, please support my efforts by recommending and sharing this page to help others find it! Let me know what you think.

Please share your feedback and suggestions in the comment section below.

Top comments (5)

Collapse
 
cookieduster_n profile image
Nebojsa Radakovic
Collapse
 
wizlee profile image
Wiz Lee • Edited

Detailed article, btw dev.to has a series feature when you publish. Thay way you will get a nice section at the start and end of the page showing links to all your articles in a series + visual indicator of which one the reader is currently reading.

Collapse
 
cookieduster_n profile image
Nebojsa Radakovic

oh... did not know about that one. thx

Collapse
 
cookieduster_n profile image
Nebojsa Radakovic
Collapse
 
nenand_milovanovic_ece70e profile image
Nenand Milovanovic

bookmarking it.