To integrate Square payments with V0 by Vercel, use V0 to generate your checkout UI, then create a Next.js API route that processes payments server-side using the Square Node.js SDK. Store your SQUARE_ACCESS_TOKEN as a Vercel environment variable — never expose it to the browser. Square's sandbox environment lets you test end-to-end without real charges.
Accept Square Payments in Your V0-Generated Next.js App
Square is the go-to payment processor for businesses that also operate in person — it bridges online and offline sales in a unified dashboard. If you are building a V0-powered app for a restaurant, retail store, service business, or any merchant already using Square hardware, integrating Square payments in your Next.js app is the right move. It keeps all transactions, inventory, and customer data in one ecosystem.
Square's integration model requires two layers: a client-side layer that tokenizes card data using the Square Web Payments SDK, and a server-side layer that uses the token to create actual payments. This separation is a PCI compliance requirement — your server never sees raw card numbers, only the tokenized reference (called a nonce). Your Next.js API route handles everything server-side using the official square npm package, and your SQUARE_ACCESS_TOKEN stays safely in Vercel's encrypted environment variable storage.
V0 makes the UI generation fast. You describe your checkout form, product display, or order summary and V0 produces the Tailwind-styled React components. You then wire those components to Square's payment flow. This guide walks you through the complete integration from V0 UI generation to first successful sandbox payment.
Integration method
V0 generates your checkout and payment UI components using Square's Web Payments SDK for the frontend card form. A Next.js API route running on Vercel handles the server-side payment processing using the Square Node.js SDK, keeping your access token secure. Payments are created by sending the card nonce (a tokenized card reference) from the frontend to your API route, which then creates the payment with Square.
Prerequisites
- A Square developer account at developer.squareup.com — free to create, provides sandbox credentials immediately
- Your Square Application ID and Sandbox Access Token from the Square Developer Dashboard
- Your Square Location ID from the Square Developer Dashboard (found under Locations in your app settings)
- A V0 project with Next.js App Router structure
- Node.js package management access to install the square npm package in your project
Step-by-step guide
Get Your Square API Credentials and Install the SDK
Get Your Square API Credentials and Install the SDK
Log in to the Square Developer Dashboard at developer.squareup.com and either create a new application or select an existing one. You need three values: your Application ID (starts with sq0idp-), your Sandbox Access Token (starts with EAAAl or similar — found under Credentials tab → Sandbox), and your Location ID (found under Locations in the left sidebar — click the location to see its ID). Square provides completely separate sandbox and production credentials, which is excellent for testing. The sandbox is a full simulation of the Square API with test card numbers that trigger different responses. Save these three values securely — you will put them in Vercel environment variables, never in code. Next, install the Square Node.js SDK in your project. Open your project in your local editor or use V0's built-in GitHub integration to work with the code. In your project's package.json, you need to add the square package. The official package name is simply square and it is published by Square Inc on npm. Also note the Square Web Payments SDK script URL: https://sandbox.web.squarecdn.com/v1/square.js for sandbox and https://web.squarecdn.com/v1/square.js for production — you will load this via a script tag in your page to initialize the card payment form on the frontend.
1// Install the Square SDK2// Run in your project terminal:3// npm install square45// package.json dependency (add to dependencies):6// "square": "^40.0.0"Pro tip: Square releases SDK updates frequently. The package name is simply 'square' on npm. Check npmjs.com/package/square for the latest version before installing.
Expected result: The square package is listed in your package.json dependencies and installed in node_modules. You have your Application ID, Sandbox Access Token, and Location ID ready.
Generate the Checkout UI with V0
Generate the Checkout UI with V0
Open V0 and describe your checkout page in detail. Square's Web Payments SDK requires a div element with a specific ID (conventionally 'card-container') where it will inject the hosted card input fields. Ask V0 to create this container in the appropriate place in the form. The Square SDK injects an iframe into this container, so you cannot style the inner card fields directly — but you can style the container div and surrounding layout. Ask V0to create the order summary section alongside the payment form so users can see what they are paying for. V0 should generate a React component for the checkout page. After generating, you will add the Square SDK initialization logic to this component — V0 will not know the Square-specific JavaScript API calls, so you will need to add those manually or in a follow-up V0 prompt. The key pattern is: on component mount, load the Square Web Payments SDK script, call window.Square.payments(appId, locationId) to initialize, then call payments.card() to create a card payment instance, then card.attach('#card-container') to render the input fields into your div. When the user submits the form, call card.tokenize() to get the payment nonce, then send that nonce to your Next.js API route. Keep this flow in mind as you review V0's generated component code.
Create a Next.js checkout page at app/checkout/page.tsx. Show an order summary section on the left with item name, quantity selector, item price, subtotal, tax, and total. On the right, show a payment form with a cardholder name text input, a div with id='card-container' and className='min-h-[89px]' for the Square card element, and a 'Pay Now' button styled in black. Add a success state showing an order confirmation with order ID. Use Tailwind CSS with a clean two-column grid layout on desktop, single column on mobile.
Paste this in V0 chat
Pro tip: Add a min-height to your card-container div so the layout does not jump when the Square SDK injects the card fields. The Square hosted card input is about 89px tall by default.
Expected result: V0 generates a checkout page with an order summary section, a cardholder name input, a div#card-container placeholder, and a payment button with a success state component.
Create the Square Payment API Route
Create the Square Payment API Route
Create the server-side API route that processes Square payments using the Square Node.js SDK. This route receives a payment nonce (sourceId) from the frontend after the card is tokenized, along with the payment amount in cents. It creates a Square Payment using your access token and location ID, which are stored as server-only environment variables. The Square SDK uses the Client class from the square package to initialize a client with your access token and the environment (SANDBOX or PRODUCTION). The PaymentsApi on the client has a createPayment method that accepts the source ID, amount (in cents as a BigInt), currency, idempotency key, and location ID. The idempotency key is crucial — it prevents duplicate charges if the request is retried. Use a UUID or a combination of the nonce and timestamp. The route returns the payment ID on success so the frontend can display a confirmation. Always validate the request body on the server side before calling Square — check that the amount is a positive number and the nonce is a non-empty string. Return appropriate HTTP status codes so the frontend can handle errors gracefully.
Create a Next.js API route at app/api/square/payment/route.ts that accepts a POST request with sourceId (the Square card nonce) and amountCents (integer). Use the Square SDK Client to create a payment. The Client should be initialized with process.env.SQUARE_ACCESS_TOKEN and Environment.Sandbox. Call paymentsApi.createPayment with the sourceId, amountMoney in cents, USD currency, a unique idempotency key using crypto.randomUUID(), and process.env.SQUARE_LOCATION_ID. Return the payment id on success, or a descriptive error message on failure.
Paste this in V0 chat
1import { NextRequest, NextResponse } from 'next/server';2import { Client, Environment } from 'square';34const client = new Client({5 accessToken: process.env.SQUARE_ACCESS_TOKEN,6 environment:7 process.env.SQUARE_ENVIRONMENT === 'production'8 ? Environment.Production9 : Environment.Sandbox,10});1112export async function POST(request: NextRequest) {13 try {14 const body = await request.json();15 const { sourceId, amountCents, currency = 'USD', note = '' } = body;1617 if (!sourceId || typeof sourceId !== 'string') {18 return NextResponse.json(19 { error: 'Invalid payment source' },20 { status: 400 }21 );22 }2324 if (!amountCents || amountCents < 100) {25 return NextResponse.json(26 { error: 'Amount must be at least $1.00' },27 { status: 400 }28 );29 }3031 const { result, statusCode } = await client.paymentsApi.createPayment({32 sourceId,33 idempotencyKey: crypto.randomUUID(),34 amountMoney: {35 amount: BigInt(amountCents),36 currency,37 },38 locationId: process.env.SQUARE_LOCATION_ID,39 note,40 });4142 if (statusCode !== 200 || !result.payment) {43 return NextResponse.json(44 { error: 'Payment creation failed' },45 { status: 500 }46 );47 }4849 return NextResponse.json({50 paymentId: result.payment.id,51 status: result.payment.status,52 receiptUrl: result.payment.receiptUrl,53 });54 } catch (error: unknown) {55 console.error('Square payment error:', error);56 const message =57 error instanceof Error ? error.message : 'Payment processing failed';58 return NextResponse.json({ error: message }, { status: 500 });59 }60}Pro tip: Square uses BigInt for monetary amounts to avoid floating-point precision issues. Always pass amountCents as a whole integer (e.g., $15.00 = 1500) and convert to BigInt in the API route.
Expected result: Posting to /api/square/payment with a valid sourceId and amountCents returns a JSON object with paymentId, status 'COMPLETED', and a receipt URL.
Add Frontend Square SDK Integration
Add Frontend Square SDK Integration
Now add the Square Web Payments SDK initialization to your checkout page component. The Square Web Payments SDK is loaded via a script tag and provides the window.Square global. You need to load this script before the component tries to initialize Square. In Next.js, use the next/script component with strategy='beforeInteractive' or load it via a useEffect. The initialization sequence is critical: first call Square.payments(applicationId, locationId) with your public-facing Application ID (this is safe to expose in the browser — it is not a secret key) and Location ID. Then call payments.card() to create a card payment form object. Then call card.attach('#card-container') to render the hosted card fields into your div. Wrap this in a useEffect that runs once on mount. The card.attach() call is asynchronous, so await it. In the payment submit handler, call card.tokenize() to get back a result object — if result.status is 'OK', extract result.token (the nonce/sourceId) and send it to your API route via fetch POST. The NEXT_PUBLIC_ prefix variables (Application ID and Location ID) are safe to use on the client side because they are public-facing identifiers, not secrets. Only the access token must stay server-side.
Update the checkout page at app/checkout/page.tsx to initialize the Square Web Payments SDK. In a useEffect, load the Square SDK from the sandbox script URL, call Square.payments() with process.env.NEXT_PUBLIC_SQUARE_APP_ID and process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID, attach a card element to '#card-container', and in the handlePayment function call card.tokenize() to get the nonce then POST it along with the amount to /api/square/payment. Show a loading state while processing and display the receipt URL on success or an error message on failure.
Paste this in V0 chat
1'use client';23import { 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}1718export default function CheckoutPage() {19 const cardRef = useRef<Awaited<ReturnType<Awaited<ReturnType<typeof window.Square.payments>>['card']>> | null>(null);20 const [loading, setLoading] = useState(false);21 const [error, setError] = useState('');22 const [success, setSuccess] = useState<{ paymentId: string; receiptUrl: string } | null>(null);2324 useEffect(() => {25 const initSquare = async () => {26 if (!window.Square) return;27 try {28 const payments = await window.Square.payments(29 process.env.NEXT_PUBLIC_SQUARE_APP_ID!,30 process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID!31 );32 const card = await payments.card();33 await card.attach('#card-container');34 cardRef.current = card;35 } catch (err) {36 console.error('Square SDK init error:', err);37 setError('Failed to load payment form. Please refresh.');38 }39 };4041 if (window.Square) {42 initSquare();43 } else {44 const script = document.createElement('script');45 script.src = 'https://sandbox.web.squarecdn.com/v1/square.js';46 script.onload = initSquare;47 document.head.appendChild(script);48 }49 }, []);5051 const handlePayment = async () => {52 if (!cardRef.current) return;53 setLoading(true);54 setError('');5556 try {57 const result = await cardRef.current.tokenize();5859 if (result.status !== 'OK' || !result.token) {60 const msg = result.errors?.map((e) => e.message).join(', ') || 'Card tokenization failed';61 setError(msg);62 return;63 }6465 const res = await fetch('/api/square/payment', {66 method: 'POST',67 headers: { 'Content-Type': 'application/json' },68 body: JSON.stringify({ sourceId: result.token, amountCents: 2999 }),69 });7071 const data = await res.json();7273 if (!res.ok) {74 setError(data.error || 'Payment failed');75 return;76 }7778 setSuccess({ paymentId: data.paymentId, receiptUrl: data.receiptUrl });79 } catch (err) {80 setError('An unexpected error occurred. Please try again.');81 } finally {82 setLoading(false);83 }84 };8586 if (success) {87 return (88 <div className="max-w-md mx-auto p-8 text-center">89 <h2 className="text-2xl font-bold text-green-600 mb-2">Payment Successful!</h2>90 <p className="text-gray-600 mb-4">Order ID: {success.paymentId}</p>91 {success.receiptUrl && (92 <a href={success.receiptUrl} target="_blank" rel="noopener noreferrer"93 className="text-blue-600 underline">View Receipt</a>94 )}95 </div>96 );97 }9899 return (100 <div className="max-w-md mx-auto p-8">101 <h1 className="text-2xl font-bold mb-6">Checkout</h1>102 <div id="card-container" className="min-h-[89px] mb-4 border rounded-lg p-2" />103 {error && <p className="text-red-600 text-sm mb-4">{error}</p>}104 <button105 onClick={handlePayment}106 disabled={loading}107 className="w-full bg-black text-white py-3 rounded-lg font-medium disabled:opacity-50"108 >109 {loading ? 'Processing...' : 'Pay Now'}110 </button>111 </div>112 );113}Pro tip: For production, change the Square SDK script URL from sandbox.web.squarecdn.com to web.squarecdn.com and set SQUARE_ENVIRONMENT=production in your Vercel environment variables.
Expected result: The checkout page loads the Square card input fields inside the card-container div, accepts the test card number 4111 1111 1111 1111, and on submit displays a payment success confirmation.
Configure Vercel Environment Variables and Deploy
Configure Vercel Environment Variables and Deploy
Add all five Square environment variables to your Vercel project. Go to vercel.com, open your project, click Settings in the top navigation, and select Environment Variables from the left sidebar. Add the following variables: SQUARE_ACCESS_TOKEN set to your Sandbox Access Token (starts with EAAAl — server-only, no NEXT_PUBLIC_ prefix), SQUARE_LOCATION_ID set to your Location ID (server-only, no NEXT_PUBLIC_ prefix), SQUARE_ENVIRONMENT set to sandbox (server-only), NEXT_PUBLIC_SQUARE_APP_ID set to your Application ID (safe for client-side, uses NEXT_PUBLIC_ prefix), and NEXT_PUBLIC_SQUARE_LOCATION_ID set to your Location ID again (this one is public-facing and needed by the browser SDK). Yes, you need the Location ID in both a server-only and a public variable — the server route uses it via process.env.SQUARE_LOCATION_ID, and the browser SDK initialization needs it via process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID. After adding all variables, redeploy your project from the Deployments tab. Test the integration using Square's sandbox test card numbers: 4111 1111 1111 1111 with any future expiry date, any 3-digit CVV, and any zip code. This test card always succeeds. For a declined payment test, use 4000 0000 0000 0002. Check your Square Developer Dashboard under Sandbox → Transactions to verify payments are being recorded.
1// Square test card numbers for sandbox testing:2// Success: 4111 1111 1111 11113// Declined: 4000 0000 0000 00024// CVV Failure: 4000 0000 0000 00105// Expiry: any future date (MM/YY)6// CVV: any 3 digits7// Zip: any 5 digitsPro tip: When switching from sandbox to production, you must update SQUARE_ACCESS_TOKEN to your production token, SQUARE_ENVIRONMENT to 'production', and update the frontend SDK script URL from sandbox.web.squarecdn.com to web.squarecdn.com.
Expected result: Your deployed V0 app processes test payments through Square's sandbox. Test transactions appear in your Square Developer Dashboard under Sandbox Transactions.
Common use cases
One-Time Product Checkout
A small business sells physical products online and already uses Square POS in their store. Building a checkout page in V0 that processes payments through Square keeps all sales data in the Square dashboard for unified reporting across online and in-store channels.
Create a Next.js checkout page with a product summary on the left showing item name, quantity, and price total, and a payment form on the right with fields for cardholder name, a Square card element container div with id 'card-container', and a pay button. Use Tailwind CSS with a clean two-column layout. The pay button should call a handlePayment async function and show a loading spinner while processing.
Copy this prompt to try it in V0
Service Booking Payment
A service provider (salon, consultant, personal trainer) wants clients to pay a deposit when booking appointments. The V0 app shows the service details and price, collects payment via Square, and confirms the booking.
Build a service booking confirmation page that shows the service name, date, time, provider name, and total price. Include a Square payment section with a card input area and a 'Confirm Booking and Pay' button. Add a success state that shows a confirmation message with a booking reference number after payment. Style it with Tailwind in a professional blue and white color scheme.
Copy this prompt to try it in V0
Donation or Tip Collection
A nonprofit or creator wants to accept custom-amount donations through their V0 site. Users enter a custom amount, pay via Square, and receive a confirmation. The Square dashboard tracks all donations for reporting.
Create a donation page with preset amount buttons ($10, $25, $50, $100) and a custom amount input field. Show a brief message about where donations go. Below the amount selector, add a Square card payment form and a 'Donate Now' button. Include a success banner with a thank you message and the donated amount after payment completes.
Copy this prompt to try it in V0
Troubleshooting
TypeError: Cannot read properties of undefined (reading 'payments') — Square SDK not loading
Cause: The Square Web Payments SDK script has not finished loading before your useEffect tries to call window.Square.payments(). This happens if the script is loaded asynchronously and the component initializes before the script is ready.
Solution: Add an onload callback to the script element before appending it, as shown in the checkout component code. Check that the script URL matches your environment (sandbox vs production). Also verify the Application ID format is correct — it should start with 'sq0idp-'.
1const script = document.createElement('script');2script.src = 'https://sandbox.web.squarecdn.com/v1/square.js';3script.onload = () => initSquare(); // Only call initSquare after script loads4document.head.appendChild(script);Square API returns UNAUTHORIZED or 401 error
Cause: The SQUARE_ACCESS_TOKEN environment variable is missing, incorrect, or you are using a production token in sandbox mode (or vice versa).
Solution: Verify the SQUARE_ACCESS_TOKEN in your Vercel environment variables matches the access token shown in the Square Developer Dashboard under Credentials → Sandbox Access Token. Make sure SQUARE_ENVIRONMENT matches the credentials you are using. After updating environment variables in Vercel, redeploy for the changes to take effect.
Payment fails with 'Amount must be greater than 0' or 'INVALID_VALUE' error from Square
Cause: The amountCents value is being passed as a string, a float, or is zero. Square's API requires a positive integer in the smallest currency unit (cents for USD).
Solution: Ensure amountCents is always converted to an integer before sending. BigInt(amountCents) will throw if the value is a float like 29.99 — you must pass 2999. Parse and round before calling BigInt.
1// Convert price to cents correctly2const priceInDollars = 29.99;3const amountCents = Math.round(priceInDollars * 100); // 29994// In the API route:5amountMoney: {6 amount: BigInt(amountCents), // BigInt(2999)7 currency: 'USD',8}Card tokenization succeeds but the API route returns a 500 error
Cause: The SQUARE_LOCATION_ID environment variable is missing or incorrect, or the Square Client is being initialized incorrectly with the wrong environment setting.
Solution: Double-check that SQUARE_LOCATION_ID in your Vercel environment variables matches the Location ID in Square Developer Dashboard → Locations. Verify that the Client environment is set to Environment.Sandbox (import from the square package) for testing. Check Vercel function logs under your project → Functions → Logs for the specific Square error message.
Best practices
- Never expose SQUARE_ACCESS_TOKEN to the browser — always use it exclusively in server-side API routes or server components with no NEXT_PUBLIC_ prefix
- Always use idempotency keys when creating Square payments to prevent duplicate charges if users click the pay button multiple times or if a network retry occurs
- Test all payment scenarios in sandbox mode before switching to production — Square provides test card numbers for success, decline, CVV failure, and expired card scenarios
- Store the Square payment ID returned from the API in your database immediately so you have a record linking orders to Square transactions for reconciliation
- Use environment-specific SDK script URLs — sandbox.web.squarecdn.com for testing and web.squarecdn.com for production — and make this configurable via an environment variable
- Implement proper error handling that distinguishes between card errors (show to user) and server errors (show generic message, log details) so you do not expose internal error details
- Consider adding Square webhooks to handle asynchronous payment status updates — some payment methods do not complete synchronously and require webhook confirmation
Alternatives
Stripe has a larger developer ecosystem, more extensive Vercel Marketplace support, and is often preferred for pure online businesses — choose Square if you also need unified in-person point-of-sale payment processing.
Spocket is a dropshipping product sourcing platform rather than a payment processor — use it alongside Square or Stripe to source products, not as a payment alternative.
Frequently asked questions
Can I use Square with V0 without a physical Square card reader?
Yes. Square's online payment APIs work entirely without hardware. The Web Payments SDK handles card-not-present transactions over the internet. You only need Square hardware if you want to also accept in-person payments at a physical location.
Is Square available internationally for V0 apps?
Square is available in the United States, Canada, United Kingdom, Australia, Japan, Ireland, France, and Spain. If your target market is primarily outside these countries, consider Stripe, which has broader international coverage. Check Square's current country list at squareup.com/global.
What is the difference between Square Sandbox and production credentials?
Square provides completely separate API credentials for sandbox (testing) and production (real payments). Sandbox transactions never charge real cards and do not transfer money. You switch between them by changing the access token, environment setting, and Web Payments SDK script URL. Always develop and test using sandbox credentials before going live.
Can V0 generate a Square payment component I can reuse across pages?
Yes. Prompt V0 to create a reusable SquarePayment component that accepts an amountCents prop and an onSuccess callback. The component handles Square SDK initialization, card tokenization, and API route calls internally, exposing only the amount and success callback to parent pages. This pattern keeps payment logic encapsulated and reusable.
How do I handle Square webhooks in my Next.js app?
Create a webhook endpoint at app/api/square/webhook/route.ts. Square sends webhook events for payment updates, subscription changes, and more. Verify the webhook signature using the x-square-hmacsha256-signature header and your webhook signature key from the Square Developer Dashboard. Process the event type from request.body and return a 200 status quickly — do any heavy processing asynchronously.
Does Square support subscriptions and recurring payments?
Yes, Square has a Subscriptions API that handles recurring billing with automatic card charges. You create a plan (billing frequency and amount), then subscribe a customer to that plan. The subscription API is separate from the one-time payment API and requires storing a customer card on file using Square's SavedCard feature.
What currencies does Square support in the API?
The currencies supported depend on your Square account country. US accounts support USD, Canadian accounts support CAD, UK accounts support GBP, and so on. Square does not support multi-currency accounts in the same way Stripe does — your Square account is tied to a single country and currency. Always set the currency field in createPayment to match your account's currency.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation