Skip to main content

Square Integration

Square POS integration enables independent bookstores to sync their physical inventory with BookWish.

Overview

BookWish integrates with Square to:

  • OAuth Authorization: Connect bookstore Square accounts securely
  • Catalog Sync: Import book items from Square catalog
  • Inventory Counts: Real-time inventory levels from Square locations
  • ISBN Extraction: Parse ISBNs from GTIN/UPC fields
  • Webhook Verification: Validate Square webhook signatures

Implementation

Location: /backend/src/integrations/square.ts

Configuration

Required environment variables:

SQUARE_APP_ID=sq0idp-...
SQUARE_APP_SECRET=sq0csp-...
SQUARE_ENVIRONMENT=sandbox # or 'production'
SQUARE_WEBHOOK_SIGNATURE_KEY=...

The integration uses the official Square Node.js SDK and supports both sandbox and production environments.

Features

1. OAuth Authorization

Bookstore owners authorize BookWish to access their Square account.

Generate Authorization URL

import { getAuthorizationUrl } from '../integrations/square';

const authUrl = getAuthorizationUrl(
'store_123', // Store ID (used as state parameter)
'https://bookwish.io/store/square/callback' // Redirect URI
);

// Redirect user to authUrl
// User authorizes on Square and returns to callback

The authorization URL requests these scopes:

  • ITEMS_READ: Read catalog items
  • INVENTORY_READ: Read inventory counts
  • MERCHANT_PROFILE_READ: Read merchant info

Exchange Code for Tokens

After user authorization, exchange the code for access/refresh tokens:

import { exchangeCodeForTokens } from '../integrations/square';

const tokens = await exchangeCodeForTokens(authorizationCode);

// Returns:
// {
// accessToken: 'EAAAl...',
// refreshToken: 'EQAAl...',
// expiresAt: Date, // Typically 30 days from now
// merchantId: 'MERCHANT_ID'
// }

// Store tokens securely in database

Refresh Expired Token

Square access tokens expire after 30 days:

import { refreshAccessToken } from '../integrations/square';

const newTokens = await refreshAccessToken(refreshToken);

// Returns new accessToken and refreshToken
// Update stored tokens in database

2. Catalog Sync

Fetch all book items from Square catalog.

Fetch Catalog Items

import { fetchCatalog } from '../integrations/square';

const items = await fetchCatalog(accessToken);

// Returns array of:
// [
// {
// id: 'CATALOG_OBJECT_ID',
// name: 'The Great Gatsby',
// sku: 'BOOK-001',
// isbn: '9780743273565', // Extracted from GTIN/UPC
// variationId: 'VARIATION_ID',
// priceCents: 1599 // $15.99
// },
// // ... more items
// ]

The integration:

  • Fetches all ITEM type objects
  • Extracts variations (price points)
  • Attempts to extract ISBN from UPC/GTIN field
  • Handles pagination automatically

ISBN Extraction

Square doesn't have dedicated ISBN fields. The integration extracts ISBNs from:

  • UPC/GTIN field
  • SKU field (if ISBN-formatted)

Supports:

  • ISBN-13: 13 digits starting with 978 or 979
  • ISBN-10: 10 digits

3. Inventory Counts

Get real-time inventory levels for catalog items.

Fetch Inventory

import { fetchInventoryCounts } from '../integrations/square';

const catalogIds = ['CATALOG_ID_1', 'CATALOG_ID_2', /* ... */];
const counts = await fetchInventoryCounts(accessToken, catalogIds);

// Returns:
// [
// {
// catalogObjectId: 'CATALOG_ID_1',
// variationId: 'VARIATION_ID_1',
// quantity: 5,
// locationId: 'LOCATION_ID'
// },
// // ... more counts
// ]

Notes:

  • Only returns items with state: 'IN_STOCK'
  • Supports batching up to 100 IDs per request
  • Automatically handles large catalogs with batching

4. Webhook Verification

Verify that webhook requests are from Square.

Verify Signature

import { verifyWebhookSignature } from '../integrations/square';

// In webhook handler
const payload = request.rawBody.toString(); // Raw string body
const signature = request.headers['x-square-signature'];

const isValid = verifyWebhookSignature(payload, signature);

if (!isValid) {
return res.status(401).send('Invalid signature');
}

// Process webhook
const event = JSON.parse(payload);

Verification uses HMAC-SHA256 with timing-safe comparison to prevent timing attacks.

Common Webhook Events

Square sends webhooks for:

  • inventory.count.updated: Inventory quantity changed
  • catalog.version.updated: Catalog items modified
  • location.updated: Store location info changed

Data Structures

SquareTokens

interface SquareTokens {
accessToken: string;
refreshToken: string;
expiresAt: Date;
merchantId: string;
}

SquareCatalogItem

interface SquareCatalogItem {
id: string; // Catalog object ID
name: string; // Item name
sku?: string; // SKU code
isbn?: string; // Extracted ISBN
variationId: string; // Variation ID (price point)
priceCents: number; // Price in cents
}

SquareInventoryCount

interface SquareInventoryCount {
catalogObjectId: string; // Catalog object ID
variationId: string; // Variation ID
quantity: number; // Stock quantity
locationId: string; // Square location ID
}

Error Handling

Common errors and how to handle them:

try {
const catalog = await fetchCatalog(accessToken);
} catch (error) {
if (error.message.includes('OAuth')) {
// Token expired or invalid - refresh token
} else if (error.message.includes('not configured')) {
// Square credentials missing
} else {
// Other Square API error
}
}

Common Error Scenarios

  1. Token Expired: Refresh using refreshAccessToken()
  2. Invalid Credentials: Re-authorize with getAuthorizationUrl()
  3. Rate Limit: Square has rate limits, implement exponential backoff
  4. Merchant Not Found: Invalid merchant ID

Sync Strategy

Recommended approach for syncing Square inventory:

Initial Sync

  1. User authorizes Square via OAuth
  2. Store access/refresh tokens in database
  3. Fetch entire catalog using fetchCatalog()
  4. Extract catalog IDs
  5. Fetch inventory counts using fetchInventoryCounts()
  6. Store in database with mapping: Square catalog ID → BookWish inventory item

Incremental Sync

  1. Set up Square webhook for inventory.count.updated
  2. On webhook, update specific item quantities
  3. Set up cron job to refresh entire catalog daily (catch any missed updates)

Token Management

  1. Check if token expires within 7 days
  2. If yes, refresh proactively using refreshAccessToken()
  3. Store new tokens in database
  4. If refresh fails, notify store owner to re-authorize

Testing

Sandbox Environment

Square provides a sandbox environment for testing:

  1. Set SQUARE_ENVIRONMENT=sandbox
  2. Use sandbox credentials from Square Developer Dashboard
  3. Test with sandbox merchant accounts
  4. Sandbox data is isolated from production

Test OAuth Flow

  1. Navigate to authorization URL
  2. Sign in with Square sandbox account
  3. Grant permissions
  4. Verify tokens are exchanged successfully

Test Webhook Locally

Use Square's webhook testing tool or ngrok:

# Expose local server
ngrok http 3000

# Configure webhook URL in Square Dashboard
https://your-ngrok-url.ngrok.io/webhooks/square

Best Practices

  1. Token Security: Store tokens encrypted in database
  2. Refresh Proactively: Refresh tokens before expiration
  3. Handle Rate Limits: Implement exponential backoff
  4. Batch Inventory Calls: Use 100-item batches for efficiency
  5. Webhook Verification: Always verify signatures
  6. Error Recovery: Gracefully handle API failures
  7. Sync Frequency: Don't sync more than once per minute per store

Limitations

  • Read-Only: Integration only reads data, doesn't write to Square
  • Books Only: Designed for book retailers, may include non-book items
  • ISBN Detection: Not all Square items have ISBNs
  • Single Location: Multi-location stores may need additional logic
  • Token Expiration: Tokens expire after 30 days
  • Rate Limits: Square enforces API rate limits

Rate Limits

Square API rate limits (per merchant):

  • Production: 50 requests per second
  • Sandbox: 50 requests per second
  • OAuth: 10 token requests per minute

Job Integration

BookWish includes a background job for automated Square syncing:

Location: /backend/src/jobs/square-sync.job.ts

This job:

  • Runs on schedule (configurable interval)
  • Fetches catalog and inventory for all connected stores
  • Updates database with latest Square data
  • Handles token refresh automatically

Additional Resources