Integrating Stripe Payment Elements in Nuxt 3
Tags: stripe, nuxt, vue, javascript
Published: 2024-06-19 14:36:31

This guide will show you how to integrate Stripe's Payment Element into a Nuxt 3 application to process payments for purchasing a cat. We'll cover setting up Stripe on both the client and server sides, and handling the payment process.

## Prerequisites

- A Nuxt 3 application set up.
- Stripe account with API keys (STRIPE_SECRET_KEY and STRIPE_PUBLIC_KEY).

## Step 1 - Install Stripe

Install the necessary Stripe packages:

```npm install @stripe/stripe-js stripe```

## Step 2 - Configure Environment Variables

Add your Stripe API keys to the .env file.

```
NUXT_STRIPE_PUBLIC_KEY=your_public_key_here
STRIPE_SECRET_KEY=your_secret_key_here
```

## Step 3: Create a Server API Endpoint
Create an endpoint to handle the creation of PaymentIntents. Create a file in this directory:`server/api/stripe.js`

```
import Stripe from 'stripe';

export default defineEventHandler(async (event) => {
  const body = await readBody(event);
  const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: Number(body.amount), // Amount in cents
      currency: 'usd',
      automatic_payment_methods: { enabled: true },
    });

    return {
      client_secret: paymentIntent.client_secret
    };
  } catch (error) {
    console.error('Error creating PaymentIntent:', error);
    throw createError({
      statusCode: 500,
      statusMessage: 'Error creating payment intent',
    });
  }
});
```

## Step 4: Set Up the Basic Nuxt Page
Create a component for purchasing a cat that integrates the Payment Element and handles the payment process.

`pages/purchase/[catId].vue`

```
<template>
  <div>
    <h1>Purchase Cat {{ catId }}</h1>
    <div>
      <p>Select your favorite cat and proceed to checkout.</p>
    </div>

    <!-- Payment form for Stripe Payment Element -->
    <form @submit.prevent="pay">
      <div class="border border-gray-500 p-2 rounded-sm" id="payment-element"></div>
      <p id="payment-error" role="alert" class="text-red-700 text-center font-semibold"></p>
      <button
        :disabled="isProcessing"
        type="submit"
        class="mt-4 bg-gradient-to-r from-[#FE630C] to-[#FF3200] w-full text-white text-[21px] font-semibold p-1.5 rounded-full"
        :class="isProcessing ? 'opacity-70' : 'opacity-100'"
        id="processing"
        aria-label="loading"
      >
        <p v-if="isProcessing">I'm processing payment</p>
        <div v-else>Buy Now</div>
      </button>
    </form>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { loadStripe } from '@stripe/stripe-js';
import { useRuntimeConfig } from '#app';

// Accessing environment variables
const config = useRuntimeConfig();
const stripePk = config.public.STRIPE_PUBLIC_KEY;

const route = useRoute();
const catId = route.params.catId; // Get the cat ID from the URL
const isProcessing = ref(false);
let stripe;
let elements;
let paymentElement;
let clientSecret;

const total = 2000; // Example fixed amount for purchasing a cat in cents ($20)

onMounted(async () => {
  await initializeStripe();
});

const initializeStripe = async () => {
  stripe = await loadStripe(stripePk);

  // Create a payment intent on your server
  const res = await fetch('/api/stripe', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount: total // Fixed amount in cents
    })
  });

  const result = await res.json();
  clientSecret = result.client_secret;

  elements = stripe.elements({ clientSecret });

  // Create and mount the Payment Element
  paymentElement = elements.create('payment');
  paymentElement.mount('#payment-element');

  isProcessing.value = false;
};

const pay = async () => {
  isProcessing.value = true;

  try {
    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        payment_method_data: {
          billing_details: {
            name: 'John Doe',
            email: 'john.doe@example.com',
            phone: '+1234567890',
          },
        },
      },
      redirect: 'if_required' // Stay on the same page unless redirect is necessary
    });

    if (error) {
      console.error('Payment error:', error);
      document.querySelector('#payment-error').textContent = error.message;
      isProcessing.value = false;
    } else {
      console.log('Payment succeeded');
      // Handle post-payment success actions, like showing a success message
    }
  } catch (error) {
    console.error('Payment processing error:', error);
    document.querySelector('#payment-error').textContent = 'An error occurred. Please try again.';
    isProcessing.value = false;
  }
};
</script>

<style scoped>
/* Add any styles you want to apply to the purchase page */
</style>
```

This tutorial shows how to integrate Stripe's Payment Element into a Nuxt 3 application for a fictional online cat purchase scenario. You can further customize the form, handle additional payment methods, or expand functionality as needed.

For more detailed information, refer to the Stripe Payment Element documentation.