Square REST API lets your Bolt.new app handle payments, manage product catalogs, and track inventory — both online and in physical stores. Unlike Bolt's native Stripe integration, Square requires manual setup via Next.js API routes. Use the Web Payments SDK for an embeddable checkout form and the Square Node.js SDK for server-side payment processing. All sensitive operations go through API routes, never client-side code.
Square Payments in Bolt.new: Unifying Online and In-Person Commerce
Square is the dominant payment platform for businesses with both online and physical locations. Its API covers payments, catalog management, inventory, orders, and customer data — all unified across online and in-person channels. For merchants who already use Square POS hardware, building a Bolt.new web storefront that shares the same catalog and inventory is a compelling use case.
Unlike Bolt's native Stripe integration, Square requires manual setup via npm package and API routes. The payment architecture uses the Square Web Payments SDK (client-side iframe for secure card tokenization) paired with the squareup server SDK (API route for charging the token). This split keeps card data off your server for PCI compliance.
Square provides a full sandbox environment with test credentials and card numbers, so you can build and test the complete payment flow in Bolt's WebContainer before deploying to production.
Integration method
Square payments in Bolt.new use a two-layer approach: the Square Web Payments SDK (loaded client-side) renders a secure, embeddable payment form that tokenizes card data without touching your server, and a Next.js API route uses the squareup npm package to charge that token server-side. Square credentials and the access token live in server-side environment variables. Payment flows can be tested with Square sandbox credentials in Bolt's WebContainer preview.
Prerequisites
- A Bolt.new account with a Next.js project created
- A Square Developer account at developer.squareup.com (free)
- A Square Application created in the Developer Dashboard to get sandbox credentials
- Understanding that the Web Payments SDK is loaded via script tag, not npm
- A Square Seller account if you need to access existing catalog or inventory data
Step-by-step guide
Create a Square Developer Application and Get Sandbox Credentials
Create a Square Developer Application and Get Sandbox Credentials
Go to developer.squareup.com and sign up for a Developer account (separate from a Square merchant account, though you can link them). Click '+ New Application', give it a name, and open the created application's settings. You'll see two environments — Sandbox and Production. Start with Sandbox for all development and testing. In the Sandbox tab, note your Sandbox Application ID (starts with 'sandbox-sq0idb-') and your Sandbox Access Token (starts with 'EAAAl...'). The Application ID is safe to include in client-side code (it identifies your app to the Web Payments SDK). The Access Token is highly sensitive — it has full API access to your account and must stay server-side only. Also find your Sandbox Location ID in the Sandbox → Locations tab. Square's API requires a Location ID for most transactions — it represents a physical or online business location. The sandbox provides a default test location. For testing payments, Square provides test card numbers in its documentation: 4111 1111 1111 1111 for a successful charge, 4000 0000 0000 0002 for a declined card. You don't need to enter real card details during Sandbox testing.
Pro tip: Keep your Sandbox and Production credentials clearly separated. A common mistake is accidentally using a production access token in development, which processes real charges. Name your environment variables SQUARE_SANDBOX_ACCESS_TOKEN and SQUARE_PRODUCTION_ACCESS_TOKEN to make the distinction explicit.
Expected result: You have your Square Sandbox Application ID, Sandbox Access Token, and Sandbox Location ID. You've noted the test card numbers for payment testing.
Configure Environment Variables and Install the Square SDK
Configure Environment Variables and Install the Square SDK
Add Square credentials to your .env file. The Application ID uses the NEXT_PUBLIC_ prefix because it's passed to the Web Payments SDK on the client side (needed to initialize the form). The Access Token and Location ID must never have the NEXT_PUBLIC_ prefix — they are server-side secrets used in API routes only. Install the squareup npm package, which is the official Square Node.js SDK. It provides typed clients for all Square APIs including Payments, Catalog, Orders, Inventory, and Customers. The SDK uses your access token to authenticate all API calls. Note that the Web Payments SDK (the client-side form library) is NOT installed via npm — it's loaded as a script tag from Square's CDN. This is intentional: it ensures you always have the latest version and that Square controls the secure card tokenization environment.
Install the squareup npm package. Create a lib/square.ts file that initializes the Square client using SQUARE_ACCESS_TOKEN environment variable and exports the paymentsApi, catalogApi, and inventoryApi. Add the Square Web Payments SDK script tag to the document head: <script src='https://sandbox.web.squarecdn.com/v1/square.js'></script> for sandbox, and switch to https://web.squarecdn.com/v1/square.js for production.
Paste this in Bolt.new chat
1# .env file additions2# NEXT_PUBLIC_ = client-safe (Application ID only, never the access token)3NEXT_PUBLIC_SQUARE_APP_ID=sandbox-sq0idb-your_application_id4NEXT_PUBLIC_SQUARE_LOCATION_ID=your_sandbox_location_id5# Use SQUARE_ENV=sandbox for development, production for live6NEXT_PUBLIC_SQUARE_ENV=sandbox78# Server-side only — never NEXT_PUBLIC_9SQUARE_ACCESS_TOKEN=EAAAl_your_sandbox_access_token10SQUARE_LOCATION_ID=your_sandbox_location_idPro tip: When you switch to production, update NEXT_PUBLIC_SQUARE_ENV to 'production' and update the script src URL from sandbox.web.squarecdn.com to web.squarecdn.com. The sandbox and production SDKs are completely separate.
Expected result: squareup is installed and lib/square.ts exports initialized API clients. The Web Payments SDK script loads in the app's HTML head.
Build the Square Web Payments Checkout Form
Build the Square Web Payments Checkout Form
The Web Payments SDK renders a secure card input form inside an iframe on your page. You initialize it with your Application ID and Location ID, call .card() to create a card payment method, and attach it to a DOM element. When the customer clicks Pay, call .tokenize() to get a single-use source ID that represents the card without exposing the actual number. The tokenization happens client-side between the browser and Square's servers — your application code never sees the raw card number. The source ID you receive looks like 'cnon:card-nonce-ok' in sandbox mode and 'cnon:CBASEHbuvzxbhNQRFCxf...' in production. Build this as a React component that manages the SDK state (loading, initialized, processing) and calls your API route with the source ID and payment amount on form submission.
Create a SquareCheckout React component that: 1) Loads the Square Web Payments SDK and initializes it with NEXT_PUBLIC_SQUARE_APP_ID and NEXT_PUBLIC_SQUARE_LOCATION_ID, 2) Renders the card payment form in a div with id='card-container', 3) Has a 'Pay' button that calls card.tokenize() to get a source ID, 4) POSTs the source ID and amount to /api/square/charge, 5) Shows success/error states. Use TypeScript and handle the async initialization.
Paste this in Bolt.new chat
1// components/SquareCheckout.tsx2'use client';3import { useEffect, useRef, useState } from 'react';45declare global {6 interface Window {7 Square?: {8 payments: (appId: string, locationId: string) => Promise<{9 card: () => Promise<{10 attach: (selector: string) => Promise<void>;11 tokenize: () => Promise<{ status: string; token?: string; errors?: Array<{ message: string }> }>;12 }>;13 }>;14 };15 }16}1718interface SquareCheckoutProps {19 amount: number; // in cents20 currency?: string;21 onSuccess: (paymentId: string) => void;22}2324export function SquareCheckout({ amount, currency = 'USD', onSuccess }: SquareCheckoutProps) {25 const cardRef = useRef<{ tokenize: () => Promise<{ status: string; token?: string }> } | null>(null);26 const [status, setStatus] = useState<'loading' | 'ready' | 'processing' | 'success' | 'error'>('loading');27 const [errorMessage, setErrorMessage] = useState('');2829 useEffect(() => {30 async function initializeSquare() {31 if (!window.Square) {32 setErrorMessage('Square Web Payments SDK not loaded');33 setStatus('error');34 return;35 }36 try {37 const payments = await window.Square.payments(38 process.env.NEXT_PUBLIC_SQUARE_APP_ID ?? '',39 process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID ?? ''40 );41 const card = await payments.card();42 await card.attach('#card-container');43 cardRef.current = card;44 setStatus('ready');45 } catch (err) {46 console.error('Square initialization error:', err);47 setStatus('error');48 }49 }50 initializeSquare();51 }, []);5253 async function handlePay() {54 if (!cardRef.current) return;55 setStatus('processing');56 setErrorMessage('');57 try {58 const result = await cardRef.current.tokenize();59 if (result.status !== 'OK' || !result.token) {60 setErrorMessage('Card tokenization failed. Please check your card details.');61 setStatus('ready');62 return;63 }64 const response = await fetch('/api/square/charge', {65 method: 'POST',66 headers: { 'Content-Type': 'application/json' },67 body: JSON.stringify({ sourceId: result.token, amount, currency }),68 });69 const data = await response.json();70 if (!response.ok) throw new Error(data.error ?? 'Payment failed');71 setStatus('success');72 onSuccess(data.paymentId);73 } catch (err) {74 setErrorMessage(err instanceof Error ? err.message : 'Payment failed');75 setStatus('ready');76 }77 }7879 return (80 <div className="max-w-md mx-auto p-6 border rounded-lg">81 <div id="card-container" className="mb-4 min-h-[100px]" />82 {errorMessage && <p className="text-red-500 text-sm mb-3">{errorMessage}</p>}83 <button84 onClick={handlePay}85 disabled={status !== 'ready'}86 className="w-full py-3 bg-black text-white rounded font-medium disabled:opacity-50"87 >88 {status === 'loading' ? 'Loading...' : status === 'processing' ? 'Processing...' : `Pay $${(amount / 100).toFixed(2)}`}89 </button>90 </div>91 );92}Pro tip: Add the Web Payments SDK script tag to your layout.tsx or _document.tsx head section. Use the sandbox URL (sandbox.web.squarecdn.com) during development and switch to the production URL (web.squarecdn.com) when going live.
Expected result: The SquareCheckout component renders with the Square card form in the #card-container div. The card fields appear with Square's branded styling and accept test card numbers.
Create the Server-Side Payment Processing API Route
Create the Server-Side Payment Processing API Route
The API route receives the source ID (card token) from the frontend and calls Square's Payments API to actually charge the card. This is where the real payment happens — the source ID is single-use and expires within 24 hours, so the charge must happen promptly after tokenization. The Payments API requires the source ID, the amount in the smallest currency unit (cents for USD), the currency code, and a Location ID. It also accepts an idempotency key — a unique string you generate per payment attempt that prevents double-charges if the request is retried due to network errors. Use a UUID or timestamp-based key for idempotency. On success, Square returns a Payment object with an ID, status ('COMPLETED' for successful charges), the charged amount, and the card's last 4 digits. Store the payment ID in your database to associate the transaction with the order.
Create a Next.js API route at /api/square/charge (POST) that accepts sourceId, amount (in cents), and currency. Use the squareup SDK to create a payment with the Square Payments API. Include an idempotency key using crypto.randomUUID(). Return the payment ID and status on success. Handle Square API errors and return appropriate error messages.
Paste this in Bolt.new chat
1// app/api/square/charge/route.ts2import { NextResponse } from 'next/server';3import { Client, Environment } from 'squareup';45const squareClient = new Client({6 accessToken: process.env.SQUARE_ACCESS_TOKEN ?? '',7 environment: process.env.NEXT_PUBLIC_SQUARE_ENV === 'production'8 ? Environment.Production9 : Environment.Sandbox,10});1112export async function POST(request: Request) {13 try {14 const { sourceId, amount, currency = 'USD' } = await request.json();1516 if (!sourceId) {17 return NextResponse.json({ error: 'sourceId is required' }, { status: 400 });18 }19 if (!amount || amount < 1) {20 return NextResponse.json({ error: 'Amount must be at least 1 cent' }, { status: 400 });21 }2223 const response = await squareClient.paymentsApi.createPayment({24 sourceId,25 idempotencyKey: crypto.randomUUID(),26 amountMoney: {27 amount: BigInt(amount),28 currency,29 },30 locationId: process.env.SQUARE_LOCATION_ID,31 });3233 const payment = response.result.payment;34 if (!payment) {35 return NextResponse.json({ error: 'Payment creation failed' }, { status: 500 });36 }3738 return NextResponse.json({39 success: true,40 paymentId: payment.id,41 status: payment.status,42 receiptUrl: payment.receiptUrl,43 });44 } catch (error: unknown) {45 console.error('Square payment error:', error);46 const squareError = error as { errors?: Array<{ detail?: string; code?: string }> };47 const detail = squareError.errors?.[0]?.detail ?? 'Payment processing failed';48 return NextResponse.json({ error: detail }, { status: 500 });49 }50}Pro tip: Square's amountMoney.amount field uses BigInt, not a regular number. The squareup SDK handles this internally, but if you encounter TypeScript errors about number/BigInt mismatches, use BigInt(amount) to convert.
Expected result: The /api/square/charge endpoint processes test payments using sandbox credentials. Successful charges return a payment ID and 'COMPLETED' status. Test with Square's test card 4111 1111 1111 1111.
Add Square Catalog and Inventory Queries
Add Square Catalog and Inventory Queries
One of Square's key differentiators is the Catalog API — a unified product database that syncs with Square POS, allowing your Bolt.new online store to reflect real-time inventory from physical locations. The Catalog API stores items, variations (sizes, colors), images, taxes, and discounts. The listCatalog endpoint returns all items in your Square catalog with pagination. Each catalog item has an itemData object with name, description, and variations array. Each variation has its own price and inventory. The Inventory API provides current stock counts per variation per location. For performance, cache catalog data in your database or in-memory — catalog items change infrequently, and calling the API on every page load adds latency and uses API quota unnecessarily.
Create a /api/square/catalog route that fetches all ITEM type objects from the Square Catalog API, formats them with name, description, price from the first variation, and item_id, and returns them as JSON. Also create a /api/square/inventory/[itemVariationId] route that returns the current quantity for a specific item variation at the configured location. Build a ProductGrid component that fetches and displays catalog items.
Paste this in Bolt.new chat
1// app/api/square/catalog/route.ts2import { NextResponse } from 'next/server';3import { Client, Environment } from 'squareup';45const squareClient = new Client({6 accessToken: process.env.SQUARE_ACCESS_TOKEN ?? '',7 environment: process.env.NEXT_PUBLIC_SQUARE_ENV === 'production'8 ? Environment.Production9 : Environment.Sandbox,10});1112export async function GET() {13 try {14 const response = await squareClient.catalogApi.listCatalog(15 undefined, // cursor for pagination16 'ITEM' // only fetch items, not categories/taxes/discounts17 );1819 const items = (response.result.objects ?? []).map((obj) => {20 const item = obj.itemData;21 const firstVariation = item?.variations?.[0]?.itemVariationData;22 return {23 id: obj.id,24 name: item?.name ?? 'Unnamed item',25 description: item?.description ?? '',26 price: Number(firstVariation?.priceMoney?.amount ?? 0),27 currency: firstVariation?.priceMoney?.currency ?? 'USD',28 variationId: item?.variations?.[0]?.id,29 };30 });3132 return NextResponse.json({ items });33 } catch (error) {34 console.error('Square catalog error:', error);35 return NextResponse.json({ error: 'Failed to fetch catalog' }, { status: 500 });36 }37}Pro tip: In the Square Sandbox, your catalog starts empty. Use the Square Sandbox Developer Dashboard at developer.squareup.com to add test items, or use the Catalog API to programmatically create test items in your sandbox environment.
Expected result: The /api/square/catalog endpoint returns formatted product data from Square's Catalog API. In sandbox mode, it returns items you've created in the Developer Dashboard.
Common use cases
Online Store with Shared Square Inventory
Build a web storefront that reads product listings and inventory levels from an existing Square Catalog — perfect for businesses that manage inventory in Square's POS and want their website to reflect real-time stock. When an item sells online, Square's inventory decrements automatically.
Build an online store that reads products from the Square Catalog API and allows checkout via Square Web Payments. Create an API route that fetches catalog items with their variations and pricing. Build a product grid with a cart, and a checkout page that embeds the Square Web Payments SDK form. Process payments server-side with the squareup package and update inventory on successful purchase.
Copy this prompt to try it in Bolt.new
Service Booking with Square Payments
Accept deposits or full payments for service bookings — appointments, consultations, or class reservations. Square handles the payment, while your Bolt app manages the booking calendar. This is common for salons, tutoring services, and fitness studios already on Square.
Create a service booking form that collects appointment details and payment. Use Square Web Payments SDK for the card form, and a /api/square/charge endpoint to process the payment. On success, save the booking to a database and send a confirmation. Use Square sandbox credentials for testing.
Copy this prompt to try it in Bolt.new
Restaurant Online Ordering
Build a digital menu and online ordering system for a restaurant already on Square. Read menu items from the Square Catalog, let customers build an order, and accept payment through Square so all transactions appear in one dashboard alongside in-person orders.
Build a restaurant online ordering page. Fetch menu categories and items from the Square Catalog API via a server-side API route. Allow customers to add items to a cart, then check out using Square Web Payments. Create an Order in the Square Orders API on payment success so the kitchen gets the order. Use my existing Square sandbox credentials.
Copy this prompt to try it in Bolt.new
Troubleshooting
Web Payments SDK fails to initialize with 'Square is not defined' or the card form doesn't appear
Cause: The Square Web Payments SDK script hasn't loaded before the React component mounts, or the sandbox vs production script URL doesn't match your environment.
Solution: Add the Square SDK script tag to your layout.tsx head section so it loads on every page. Use sandbox.web.squarecdn.com for development and web.squarecdn.com for production. The component's useEffect should check window.Square after mount.
1// In app/layout.tsx <head>:2<script src="https://sandbox.web.squarecdn.com/v1/square.js" />Payments API returns 'INVALID_VALUE' or 'BAD_REQUEST' when calling createPayment
Cause: The amountMoney.amount field expects BigInt, not a JavaScript number. Some TypeScript configurations or older SDK versions may fail with a regular number.
Solution: Wrap the amount in BigInt() explicitly when passing to the Square SDK. Also verify that currency is a valid ISO 4217 code (e.g., 'USD', 'GBP') and that locationId is set correctly.
1amountMoney: {2 amount: BigInt(amount), // Explicitly cast to BigInt3 currency: 'USD',4},The card form appears but tokenize() returns status 'ERROR' with 'VERIFY_CVV_FAILURE'
Cause: You're using a real card number instead of Square's sandbox test cards, or you entered an invalid CVV with a test card.
Solution: In sandbox mode, use Square's test card numbers: 4111 1111 1111 1111 (success), 4000 0000 0000 0002 (declined). Use any future expiration date and any 3-digit CVV. Real card numbers are rejected in sandbox mode.
'LOCATION_MISMATCH' error when processing payments after deploying
Cause: The SQUARE_LOCATION_ID in your environment variables is the sandbox location ID, but you switched to production credentials without updating the location ID.
Solution: Go to Square Developer Dashboard → Production tab → Locations. Update SQUARE_LOCATION_ID in your hosting provider's environment variables to use a production location ID, not the sandbox one.
Best practices
- Keep SQUARE_ACCESS_TOKEN strictly server-side — only NEXT_PUBLIC_SQUARE_APP_ID and location ID belong in client-accessible variables
- Always generate a fresh idempotencyKey (UUID) for each payment attempt to prevent double-charges on network retries
- Use sandbox credentials for all development and switch to production credentials only when deploying to your live domain
- Switch the Web Payments SDK script URL from sandbox.web.squarecdn.com to web.squarecdn.com when going to production — using the sandbox SDK with production credentials will fail
- Cache Square Catalog data locally rather than fetching on every page request — product catalogs change infrequently and the API has rate limits
- Store the Square payment ID returned from createPayment in your database to link transactions to orders and enable refund operations
- Handle Square's BigInt requirement for amountMoney explicitly — JavaScript number precision issues can cause incorrect charge amounts
Alternatives
Stripe has a native Bolt.new integration with automatic setup — the easiest choice for new online-only businesses that don't need Square's POS hardware ecosystem.
Stripe Connect is better for marketplace platforms where you need to route payments to multiple sellers, while Square suits single-merchant retail and restaurant businesses.
PayPal has higher consumer trust for online purchases in some markets and offers Buy Now Pay Later, but lacks Square's deep POS and inventory management ecosystem.
Braintree (owned by PayPal) offers PayPal, Venmo, and card payments in one SDK — good for apps needing PayPal as a payment option alongside traditional cards.
Frequently asked questions
How do I add Square payments to a Bolt.new app?
Install the squareup npm package, add your SQUARE_ACCESS_TOKEN to .env (server-side only), add NEXT_PUBLIC_SQUARE_APP_ID for the client-side form, load the Square Web Payments SDK via a script tag, build a React component that initializes the card form and tokenizes card data, and create a Next.js API route that calls squareClient.paymentsApi.createPayment() with the token. Test with Square's sandbox test cards.
Can I use Bolt's native Stripe integration instead of setting up Square manually?
Yes — if you don't specifically need Square, Bolt V2's native Stripe integration is much easier to set up and requires less code. Choose Square when you already use Square POS hardware, need Square's catalog and inventory management, or when Square's pricing is better for your market.
Does the Square payment form work in Bolt's WebContainer preview?
Yes. The Square Web Payments SDK loads from Square's CDN and tokenizes card data over HTTPS, which works fine in Bolt's WebContainer. You can test the complete payment flow in the preview using sandbox credentials and test card numbers. Unlike webhooks, payment processing doesn't require incoming connections.
How do I switch from Square Sandbox to production?
Update four things: change NEXT_PUBLIC_SQUARE_ENV from 'sandbox' to 'production', update SQUARE_ACCESS_TOKEN to your production access token, update SQUARE_LOCATION_ID to a production location ID, and change the Web Payments SDK script URL from sandbox.web.squarecdn.com to web.squarecdn.com in your layout. Also update NEXT_PUBLIC_SQUARE_APP_ID if your production Application ID differs from sandbox.
Can I access my existing Square POS catalog from a Bolt.new app?
Yes. Connect your Square Seller account to your Developer Application in the Square Developer Dashboard. Use production credentials in your Bolt.new app, and the Catalog API will return your actual products. For sandbox testing, create test items manually in the Developer Dashboard or via the Catalog API programmatically.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation