Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

Squaring Up: Integrate Square Payments in Next.js App Router

Ready to take your Next.js app to the checkout lane? Let’s integrate Square payments and make your App Router application as smooth as a well-oiled cash register. 💰

Why Square?

Square offers a robust payment platform with developer-friendly APIs. It’s great for both online and offline payments, perfect for your Next.js e-commerce project.

Prerequisites

Before we dive in, make sure you’ve got:

  • A Next.js 14 app using the App Router
  • A Square developer account
  • Node.js and npm installed

Got all that? Let’s square up! 🔲

Step 1: Install Square SDK

First, let’s add the Square SDK to our project:

npm install square

Step 2: Set Up Environment Variables

Create a .env.local file in your project root and add your Square credentials:

SQUARE_ACCESS_TOKEN=your_access_token_here
SQUARE_LOCATION_ID=your_location_id_here

Next.js Configuration

/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    SQUARE_ACCESS_TOKEN: process.env.SQUARE_ACCESS_TOKEN,
    SQUARE_LOCATION_ID: process.env.SQUARE_LOCATION_ID,
  },
};

module.exports = nextConfig;

Step 3: Create a Square Client

Let’s create a utility file to initialize our Square client:

Square Client Utility

import { Client, Environment } from 'square';

const squareClient = new Client({
  accessToken: process.env.SQUARE_ACCESS_TOKEN,
  environment: Environment.Sandbox, // Use Environment.Production for live payments
});

export default squareClient;

Step 4: Create a Payment API Route

Now, let’s create an API route to handle payment creation:

Payment API Route

// app/api/create-payment/route.js
import { NextResponse } from 'next/server';
import squareClient from '../../../utils/squareClient';

export async function POST(req) {
  const { sourceId, amount, currency } = await req.json();

  try {
    const response = await squareClient.paymentsApi.createPayment({
      sourceId: sourceId,
      amountMoney: {
        amount: amount,
        currency: currency,
      },
      locationId: process.env.SQUARE_LOCATION_ID,
    });

    return NextResponse.json({ payment: response.result.payment });
  } catch (error) {
    console.error('Payment error:', error);
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

Step 5: Create a Payment Form Component

Let’s create a React component to handle our payment form:

Payment Form Component

'use client';

import { useState } from 'react';
import { SquarePaymentForm, CreditCardNumberInput, CreditCardExpirationDateInput, CreditCardPostalCodeInput, CreditCardCVVInput } from 'react-square-payment-form';
import 'react-square-payment-form/lib/default.css';

export default function PaymentForm() {
  const [errorMessages, setErrorMessages] = useState([]);

  const cardNonceResponseReceived = async (errors, nonce, cardData, buyerVerificationToken) => {
    if (errors) {
      setErrorMessages(errors.map(error => error.message));
      return;
    }

    setErrorMessages([]);

    try {
      const response = await fetch('/api/create-payment', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          sourceId: nonce,
          amount: 100, // Amount in cents
          currency: 'USD',
        }),
      });

      const result = await response.json();

      if (response.ok) {
        console.log('Payment successful:', result.payment);
        // Handle successful payment (e.g., show confirmation, update UI)
      } else {
        setErrorMessages([result.error]);
      }
    } catch (error) {
      setErrorMessages(['Network error. Please try again.']);
    }
  };

  return (
    <SquarePaymentForm
      applicationId="sandbox-sq0idb-YOUR-APP-ID"
      locationId={process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID}
      cardNonceResponseReceived={cardNonceResponseReceived}
    >
      <fieldset className="sq-fieldset">
        <CreditCardNumberInput />
        <div className="sq-form-third">
          <CreditCardExpirationDateInput />
        </div>
        <div className="sq-form-third">
          <CreditCardPostalCodeInput />
        </div>
        <div className="sq-form-third">
          <CreditCardCVVInput />
        </div>
      </fieldset>

      <button className="sq-button" onClick={e => e.preventDefault()}>
        Pay Now
      </button>

      {errorMessages.length > 0 && (
        <div className="sq-error-message">
          {errorMessages.map(errorMessage => (
            <p key={errorMessage}>{errorMessage}</p>
          ))}
        </div>
      )}
    </SquarePaymentForm>
  );
}

Step 6: Implement the Payment Flow

Now, let’s use our PaymentForm component in a page:

Checkout Page Component

import PaymentForm from '../components/PaymentForm';

export default function CheckoutPage() {
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-4">Checkout</h1>
      <PaymentForm />
    </div>
  );
}

Step 7: Handle Webhooks (Optional, but Recommended)

To keep your app in sync with Square events (like successful payments), you’ll want to set up a webhook endpoint:

Webhook Handler

// app/api/webhooks/square/route.js
import { NextResponse } from 'next/server';
import squareClient from '../../../../utils/squareClient';

export async function POST(req) {
  const body = await req.json();
  const signature = req.headers.get('x-square-signature');

  if (!signature) {
    return NextResponse.json({ error: 'No signature provided' }, { status: 400 });
  }

  try {
    // Verify the webhook signature
    const event = await squareClient.webhooksApi.verifyWebhook(JSON.stringify(body), signature);

    // Handle different event types
    switch (event.type) {
      case 'payment.created':
        // Handle new payment
        console.log('New payment created:', event.data.object.payment);
        break;
      // Add more cases as needed
    }

    return NextResponse.json({ received: true }, { status: 200 });
  } catch (err) {
    console.error('Webhook error:', err);
    return NextResponse.json({ error: 'Webhook error' }, { status: 400 });
  }
}

Conclusion

And there you have it! You’ve just integrated Square payments into your Next.js 14 app using the App Router. Your users can now make payments smoother than a freshly polished countertop. 💳✨

Remember to:

  • Keep your API credentials secret
  • Test thoroughly in Square’s sandbox environment
  • Handle errors gracefully
  • Implement proper security measures for your webhook endpoint

Now go forth and monetize your Next.js app like a boss! 💰

Want to level up your Next.js skills even more? Check out these cash-money articles:

Leave a Reply

Your email address will not be published. Required fields are marked *