Skip to main content

How to Process Payments with the Square API

·APIScout Team
squarepaymentspayment apitutorialapi integration

How to Process Payments with the Square API

Square handles both online and in-person payments with a single API. This guide covers online payments: embedding the payment form, creating charges, managing customers, and handling subscriptions.

What You'll Build

  • Embedded payment form (credit card)
  • Server-side payment processing
  • Customer management
  • Invoice creation
  • Recurring subscriptions

Prerequisites: Node.js 18+, Square account (free, no monthly fee).

1. Setup

Install

npm install square

Initialize

// lib/square.ts
import { Client, Environment } from 'square';

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

export const { paymentsApi, customersApi, invoicesApi } = squareClient;

Environment Variables

SQUARE_ACCESS_TOKEN=EAAAl...
SQUARE_APPLICATION_ID=sandbox-sq0idb-...
SQUARE_LOCATION_ID=L...
NEXT_PUBLIC_SQUARE_APPLICATION_ID=sandbox-sq0idb-...
NEXT_PUBLIC_SQUARE_LOCATION_ID=L...

2. Payment Form

Load Web Payments SDK

// components/SquarePayment.tsx
'use client';
import { useEffect, useRef, useState } from 'react';

export function SquarePayment({ amount }: { amount: number }) {
  const cardRef = useRef<HTMLDivElement>(null);
  const [card, setCard] = useState<any>(null);
  const [processing, setProcessing] = useState(false);

  useEffect(() => {
    const loadSquare = async () => {
      const payments = (window as any).Square.payments(
        process.env.NEXT_PUBLIC_SQUARE_APPLICATION_ID!,
        process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID!
      );

      const card = await payments.card();
      await card.attach(cardRef.current!);
      setCard(card);
    };

    // Load Square SDK script
    const script = document.createElement('script');
    script.src = 'https://sandbox.web.squarecdn.com/v1/square.js';
    script.onload = loadSquare;
    document.head.appendChild(script);
  }, []);

  const handlePayment = async () => {
    if (!card) return;
    setProcessing(true);

    try {
      const result = await card.tokenize();
      if (result.status !== 'OK') {
        throw new Error('Card tokenization failed');
      }

      // Send token to your server
      const res = await fetch('/api/payments', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          sourceId: result.token,
          amount,
        }),
      });

      const data = await res.json();
      if (data.success) {
        alert('Payment successful!');
      }
    } catch (error) {
      console.error('Payment failed:', error);
    } finally {
      setProcessing(false);
    }
  };

  return (
    <div>
      <div ref={cardRef} style={{ minHeight: '100px' }} />
      <button onClick={handlePayment} disabled={processing || !card}>
        {processing ? 'Processing...' : `Pay $${(amount / 100).toFixed(2)}`}
      </button>
    </div>
  );
}

3. Process Payment (Server)

// app/api/payments/route.ts
import { NextResponse } from 'next/server';
import { paymentsApi } from '@/lib/square';
import { randomUUID } from 'crypto';

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

  try {
    const response = await paymentsApi.createPayment({
      sourceId,
      idempotencyKey: randomUUID(),
      amountMoney: {
        amount: BigInt(amount), // Amount in cents
        currency: 'USD',
      },
      locationId: process.env.SQUARE_LOCATION_ID!,
    });

    return NextResponse.json({
      success: true,
      paymentId: response.result.payment?.id,
      status: response.result.payment?.status,
    });
  } catch (error: any) {
    return NextResponse.json(
      { success: false, error: error.message },
      { status: 400 }
    );
  }
}

4. Customer Management

// Create customer
const customer = await customersApi.createCustomer({
  givenName: 'Jane',
  familyName: 'Doe',
  emailAddress: 'jane@example.com',
  phoneNumber: '+15551234567',
  idempotencyKey: randomUUID(),
});

// Save card on file for future payments
const card = await squareClient.cardsApi.createCard({
  sourceId: tokenFromWebPaymentsSDK,
  idempotencyKey: randomUUID(),
  card: {
    customerId: customer.result.customer!.id!,
  },
});

// Charge saved card
await paymentsApi.createPayment({
  sourceId: card.result.card!.id!,
  idempotencyKey: randomUUID(),
  amountMoney: { amount: BigInt(2900), currency: 'USD' },
  customerId: customer.result.customer!.id!,
  locationId: process.env.SQUARE_LOCATION_ID!,
});

5. Invoices

// Create an invoice
const invoice = await invoicesApi.createInvoice({
  invoice: {
    locationId: process.env.SQUARE_LOCATION_ID!,
    primaryRecipient: {
      customerId: customerId,
    },
    paymentRequests: [{
      requestType: 'BALANCE',
      dueDate: '2026-04-01',
      automaticPaymentSource: 'NONE',
    }],
    deliveryMethod: 'EMAIL',
    title: 'Consulting Services',
  },
  idempotencyKey: randomUUID(),
});

// Add line items via order
const order = await squareClient.ordersApi.createOrder({
  order: {
    locationId: process.env.SQUARE_LOCATION_ID!,
    lineItems: [{
      name: 'Web Development',
      quantity: '10',
      basePriceMoney: { amount: BigInt(15000), currency: 'USD' }, // $150/hr
    }],
  },
  idempotencyKey: randomUUID(),
});

// Publish (send) the invoice
await invoicesApi.publishInvoice(invoiceId, {
  version: 0,
  idempotencyKey: randomUUID(),
});

6. Subscriptions

// Create a subscription plan (catalog item)
const plan = await squareClient.catalogApi.upsertCatalogObject({
  idempotencyKey: randomUUID(),
  object: {
    type: 'SUBSCRIPTION_PLAN',
    id: '#pro-plan',
    subscriptionPlanData: {
      name: 'Pro Plan',
      phases: [{
        cadence: 'MONTHLY',
        recurringPriceMoney: { amount: BigInt(2900), currency: 'USD' },
      }],
    },
  },
});

// Subscribe a customer
const subscription = await squareClient.subscriptionsApi.createSubscription({
  locationId: process.env.SQUARE_LOCATION_ID!,
  planId: plan.result.catalogObject!.id!,
  customerId: customerId,
  cardId: savedCardId,
  idempotencyKey: randomUUID(),
});

Pricing

FeatureCost
Online payments2.9% + $0.30 per transaction
In-person payments2.6% + $0.10 per tap/dip
Invoices3.3% + $0.30 per invoice payment
Recurring billing3.5% + $0.15 per payment
Monthly fee$0 (no subscription required)

Square vs Stripe

FeatureSquareStripe
Online rate2.9% + $0.302.9% + $0.30
In-person2.6% + $0.102.7% + $0.05
POS hardware✅ First-party❌ Third-party only
Dashboard✅ Unified online + in-person✅ Online focused
Free tier✅ No monthly fee✅ No monthly fee
Developer docsGoodExcellent

Common Mistakes

MistakeImpactFix
Not using idempotency keysDuplicate charges on retryAlways include idempotencyKey
Using Sandbox keys in productionPayments don't processSwitch to Production environment
Processing BigInt amounts wrongWrong charge amountAmounts are in cents as BigInt
Not handling card tokenization errorsSilent failuresCheck result.status before sending token
Missing location IDAPI calls failEvery payment needs a locationId

Choosing a payment API? Compare Square vs Stripe vs PayPal on APIScout — pricing, features, and developer experience.

Comments