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
| Feature | Cost |
|---|---|
| Online payments | 2.9% + $0.30 per transaction |
| In-person payments | 2.6% + $0.10 per tap/dip |
| Invoices | 3.3% + $0.30 per invoice payment |
| Recurring billing | 3.5% + $0.15 per payment |
| Monthly fee | $0 (no subscription required) |
Square vs Stripe
| Feature | Square | Stripe |
|---|---|---|
| Online rate | 2.9% + $0.30 | 2.9% + $0.30 |
| In-person | 2.6% + $0.10 | 2.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 docs | Good | Excellent |
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Not using idempotency keys | Duplicate charges on retry | Always include idempotencyKey |
| Using Sandbox keys in production | Payments don't process | Switch to Production environment |
| Processing BigInt amounts wrong | Wrong charge amount | Amounts are in cents as BigInt |
| Not handling card tokenization errors | Silent failures | Check result.status before sending token |
| Missing location ID | API calls fail | Every payment needs a locationId |
Choosing a payment API? Compare Square vs Stripe vs PayPal on APIScout — pricing, features, and developer experience.