Appearance
Square Integration Setup
Looking for guided setup? Start Emily in the backoffice for step-by-step guided setup.
For a merchant-facing overview, see the Square Integration feature doc.
Overview
Square is a merchant-scoped full POS integration that handles both order management and payment processing. Unlike other integrations, Square manages payments directly -- no separate payment provider is needed. Menu, locations, and inventory sync between Square and Upvendo, with the direction decided automatically per entity.
Integration Type: Full POS integration (merchant-scoped, not location-scoped) Sync Direction: Per-entity automatic (push / pull / skip) for locations, menu (categories, modifier groups, items, variant groups), and inventory Payment Handling: Square handles all payments -- kiosk (Square Terminal) and online (Square Web Payments SDK) Authentication: OAuth2 authorization code flow with a base64-encoded JSON state parameter (vendor_id + timestamp + random nonce) for CSRF protection
Menu Flexibility: You can either sync your existing menu from Square OR create items in Upvendo and push them to Square.
Key Benefits:
- No Payment Profile needed -- Square handles kiosk payments (Square Terminal) and online payments (Square Web Payments SDK)
- Locations are imported automatically with rich data (address, business hours, coordinates, timezone, language, currency, phone, email, social links)
- Automatic per-entity sync for menu items, categories, modifier groups, variant groups, and inventory
- Webhooks for location changes, catalog updates, inventory changes, device pairing, and payment confirmations
Purpose
The Square integration serves merchants who use Square as their POS system and want to:
- Use Upvendo's kiosk and online ordering channels powered by Square payments
- Keep their menu, locations, and inventory synchronized between Square and Upvendo
- Process payments through Square Terminal (kiosk) or the Square Web Payments SDK (online)
- Manage everything from a single back-office while keeping Square as the payment processor
Key Concepts
| Concept | Description |
|---|---|
| Merchant-Scoped | Unlike location-scoped integrations, the Square integration covers the entire merchant account. All locations under the Square merchant are synced. |
| SquareIntegration Record | A specialized integration record (extends ThirdPartyIntegration, app/RawModels/SquareIntegration.php) storing OAuth credentials, merchant ID, sync status, logs, and settings. Provider is square. |
| External IDs | Upvendo entities store their Square counterpart IDs in external_ids.square (and external_ids.square_item_id for the parent Square item). Square entity snapshots and sync metadata are stored in external_data.square. |
| Sync Direction | For each entity, the system determines whether to push (Upvendo to Square), pull (Square to Upvendo), or skip, based on timestamps and a content hash. |
| Sync Hash | An MD5 hash of the Square entity's JSON representation (after stripping server-managed fields like version, updated_at, and the present/absent_at_location arrays), stored on the Upvendo entity to detect changes in Square. |
| Last Synced At | Timestamp of the last reconcile with Square for that entity (bumped on both push and pull). Used with the sync hash to determine sync direction. |
| Daily Auto-Sync | A configurable daily sync (locations, menu, inventory) driven by the InHouseAutoSyncCommand (in-house:auto-sync) scheduler. |
| Terminal Checkout | The flow for processing kiosk payments through a paired Square Terminal device. |
Prerequisites
Square integration is simpler than other POS systems because it handles payments:
1. Branding Profile Setup (Required)
- Go to Settings -> Brand (
/settings/brand) - Configure your store's branding (logo, colors, fonts)
- Square imports social links (Facebook, X/Twitter, Instagram) automatically, but does NOT provide colors, fonts, or logos
2. Billing Profile Setup (Optional)
- Go to Settings -> Billing (
/settings/billing) - Complete your billing information for Upvendo platform invoicing
- This is for Upvendo billing only -- NOT related to customer payments (Square handles those). The connect dialog warns if no default billing profile exists.
Note: Payment Profile is NOT required -- Square handles all customer payments directly.
Setup Steps
1. Connect Square Account (OAuth)
- Go to Square (
/square) -> Settings tab - Click Connect with Square
- You are redirected to Square's OAuth authorization page
- Log in to your Square account
- Authorize Upvendo to access your Square data
- After authorization you are redirected to
/square/successand a full sync starts automatically (locations, menu, inventory)
OAuth Technical Details:
The authorization URL is generated by SquareIntegrationController::initiateOAuth and requests these scopes:
MERCHANT_PROFILE_READ,MERCHANT_PROFILE_WRITE-- Merchant and location infoITEMS_READ,ITEMS_WRITE-- Catalog management (items, categories, modifiers, pricing)ORDERS_READ,ORDERS_WRITE-- Order managementPAYMENTS_READ,PAYMENTS_WRITE-- Payment processingINVENTORY_READ,INVENTORY_WRITE-- Stock level managementCUSTOMERS_READ,CUSTOMERS_WRITE-- Customer info for orders/paymentsDEVICES_READ,DEVICE_CREDENTIAL_MANAGEMENT-- Terminal device management
The state parameter is a base64-encoded JSON payload with vendor_id, timestamp, and a random nonce for CSRF protection. The authorize URL also sets session=false.
Token exchange (GET /square-callback, route name square.callback):
- The callback decodes
stateto recover thevendor_id, thenSquareIntegrationService::handleSquareCallbackexchanges the authorization code atPOST https://connect.squareup.com/oauth2/token(orconnect.squareupsandbox.comin sandbox) - The response is stored under the integration record's
credentials:access_token,refresh_token,expires_at,refresh_token_expires_at,merchant_id(Square's merchant ID), plustoken_typeandshort_lived - The merchant's
provider_integratedis set tosquare - On success the user is redirected to
{backoffice_url}/square/success; on failure to{backoffice_url}/square/error?message=...
2. Locations Imported Automatically
Once connected, the location sync runs in two phases (SquareLocationSync::syncLocations):
- Phase 1: push existing Upvendo locations to Square (or pull, per the direction algorithm)
- Phase 2: import any Square locations not yet linked to an Upvendo location
For each imported Square location, Upvendo:
- Imports location data -- address (via
SquareAddressConverter), business hours (viaSquareBusinessHoursConverter), coordinates, timezone, language, currency, phone, email, description, status - Creates a branding profile per location (with defaults + social links from Square)
- Links billing using the default billing profile's Stripe customer ID (if one exists)
After import, go to Settings -> Locations (/settings/locations) to:
- Verify imported data is correct
- Customize branding (logo, colors, fonts -- Square doesn't provide these)
- Add delivery zones (if doing delivery)
3. Configure Sync Settings
| Setting | Description |
|---|---|
Daily Sync Time (sync_time) | Time of day the automatic sync runs (menu, locations, inventory). Format: HH:00 or HH:30. |
Auto-sync enabled (enable_auto_sync) | Internal boolean that controls whether the daily auto-sync runs. Accepted by the update endpoint; the back-office UI surfaces only the sync time field. |
The back-office Settings tab exposes the daily sync time. There are no per-data-type toggles (items/orders/inventory) and no sync-direction picker -- all sync features run together and direction is decided automatically. You can also trigger a manual full sync at any time from the Status tab.
4. Test the Integration
- Go to the Square page -> Status tab
- The page shows a connection status; use Sync Now to run a manual sync (the backend
GET /square/testendpoint fetches merchant info to verify connectivity) - For kiosk: pair a Square Terminal and process a test payment
- For online ordering: place a test order and verify the payment completes
Data Sync Details
Sync Direction Algorithm
For every entity (location, category, modifier group, item, variant group), determineSyncDirection() (in SquareSyncDirectionTrait) picks a direction:
- Get
last_synced_at-- last reconcile timestamp (bumped on push and pull) - Get
last_sync_hash-- MD5 hash of the Square entity at last sync (server-managed fields stripped first) - Compare Upvendo
updated_atagainstlast_synced_atto detect Upvendo changes - Compare current Square hash against
last_sync_hashto detect Square changes
| Upvendo Changed | Square Changed | Webhook Context | Result |
|---|---|---|---|
| No | No | Any | Skip (no sync needed) |
| Yes | No | Any | Push Upvendo to Square |
| No | Yes | Any | Pull Square to Upvendo |
| Yes | Yes | Yes (webhook) | Square wins (webhook just fired) |
| Yes | Yes | No (manual) | Most recent wins (compare updated_at vs last_synced_at) |
First sync (no last_synced_at or no Square entity): defaults to pushing from Upvendo to Square.
Location Sync
Phase 1: Sync Upvendo locations to Square
- For each Upvendo location, determine sync direction
- Push to Square: create or update the Square location with name, address, timezone, business hours, phone, email, website, description, status, coordinates, social links
- Pull from Square: update the Upvendo location with Square data
Phase 2: Import unsynced Square locations
- Any Square locations not already linked to an Upvendo location are imported, creating Upvendo locations with branding profiles
Data mapped:
| Square Field | Upvendo Field |
|---|---|
| name | name (unique-name generation if conflict) |
| address | address (via SquareAddressConverter) |
| timezone | timezone |
| business_hours | business_hours (via SquareBusinessHoursConverter) |
| phone_number | contact_information.phone |
| business_email | contact_information.email |
| language_code | preferred_language |
| coordinates | pin_point_latitude, pin_point_longitude |
| facebook_url | branding_profile.social_links.facebook |
| twitter_username | branding_profile.social_links.x |
| instagram_username | branding_profile.social_links.instagram |
| website_url | online_ordering_url |
| description | description |
| status | status (push to Square only; not mapped back on pull) |
| currency | currency (import/create only; not pushed back to Square) |
Menu Sync (Categories, Modifier Groups, Items)
Menu sync runs three entity types in order (SquareIntegrationService::syncMenus):
- Categories -- between Upvendo categories and Square catalog categories
- Modifier Groups -- between Upvendo modifier groups and Square modifier lists
- Items -- between Upvendo items and Square catalog items, including variant groups
Direction for each entity is decided by the sync direction algorithm above.
Incremental sync: When triggered by a specific entity change (via SyncSquareMenuJob), only the changed entity is synced:
category->syncSingleCategory()modifier_group->syncSingleModifierGroup()item->syncSingleItem()variant_group->syncSingleVariantGroup()
Full sync: When triggered by a catalog webhook (catalog.*) or daily/manual full sync, all categories, modifier groups, and items are synced.
SyncSquareMenuJob configuration:
- Max retries (
$tries): 3 - Backoff: 60 seconds between retries
Inventory Sync
Inventory sync is per item variation per location (SquareInventorySync):
Push (Upvendo to Square): Sends physical-count changes via the Square Inventory API (batchCreateChanges). Pull (Square to Upvendo): Reads counts via the Inventory API (batchGetCounts) and updates Upvendo. Conflict between sides is resolved by comparing Square's calculated_at against the Upvendo update timestamp.
Inventory sync runs as part of the full sync (daily auto-sync or manual). Webhook-triggered inventory syncs (SyncSquareInventoryJob) target a specific catalog object + location.
Propagation Across Locations
When a modifier group is pushed to Square, SquarePropagation::propagateUpvendoModifierGroupToAllLocations copies the source data to all other Upvendo modifier groups that share the same Square modifier list ID (external_ids.square) at different locations, keeping multi-location setups consistent. Items and variant groups have analogous propagation.
Not fully verified here: field-by-field round-trip fidelity of the
external_data.squaresnapshot blobs, variant-group option-combination reconstruction, and the cross-location distribution helper internals were not exhaustively traced.
Webhook Events
Square sends webhooks to POST /webhook/square (full path /api/webhook/square), verified by the verify.square-webhook middleware (VerifySquareWebhook). The middleware checks the X-Square-HmacSha256-Signature header against config('square.webhook_signature_key') using Square's WebhooksHelper::verifySignature. The verified payload is dispatched to ProcessSquareWebhookJob (async, $tries=3, $backoff=30, $timeout=120), which calls SquareWebhookService::processWebhook.
Event Deduplication
Square may retry failed webhooks. The system deduplicates by caching processed event_id values for 24 hours (86400s).
Anti-Recursion Protection
When Upvendo pushes changes to Square, a cache timestamp is set. If a webhook arrives within the threshold of a push, it is skipped to prevent recursive update loops:
- Location: 10 seconds
- Catalog: 10 seconds
- Inventory: 15 seconds
Event Types
Routing is by event-type prefix (SquareWebhookService::processWebhook):
| Event Prefix | Handler | Action |
|---|---|---|
location.* (created/updated) | handleLocationEvent() | Dispatches SyncSquareLocationJob |
catalog.* (e.g. catalog.version.updated) | handleCatalogEvent() | Dispatches SyncSquareMenuJob (full sync) |
inventory.* (e.g. inventory.count.updated) | handleInventoryEvent() | Dispatches SyncSquareInventoryJob |
device.code.* (acts only on device.code.paired) | handleDeviceCodeEvent() | Direct device update (no job) |
payment.* (acts only on payment.updated) | handlePaymentEvent() | Direct processing (no job) |
order.* | handleOrderEvent() | Log-only (no ingestion) |
Device Code Pairing (device.code.paired)
When a Square Terminal is paired:
- The webhook payload provides
device_code_id(data.id) anddevice_id - The system finds the Upvendo device with a pending device code matching
device_code_id(external_data.square.device_code.idwithis_pending == true) - Updates the device record under
external_data.square.device_code: setsterminal_device_id,is_pending = false,is_connected = true, andpaired_at
Payment Processing (payment.updated)
When a payment completes:
- Only
payment.updatedwith statusCOMPLETEDis processed - The
reference_id(format{merchant_id}:{order_no}, decoded bySquareReferenceId) identifies the tenant and transaction; the transaction is matched on the decodedorder_no. (Legacy bareorder_noreferences fall back to mappinglocation_idto an Upvendo location via external ID.) - If tip changed, the transaction's tip and total are updated
- Online ordering payments: run
OnlineOrderingOrchestrator::verifyPayment()under a 30-second database lock - Kiosk payments: run
PaymentCaptureService::capturePayment()under a 30-second database lock
Actions
| Action | Method | Endpoint | Description |
|---|---|---|---|
| Get Status | GET | /back-office/square/status | Returns integration status, sync info, settings |
| Initiate OAuth | POST | /back-office/square/oauth | Generates the Square OAuth authorization URL |
| Update Sync Time | POST | /back-office/square/sync-time | Set the daily auto-sync time (HH:00/HH:30) and optional enable_auto_sync |
| Disable | POST | /back-office/square/disable | Revoke token, delete the integration, clear payment provider |
| Start Sync | POST | /back-office/square/start-sync | Trigger a manual full sync (dispatches SyncSquareIntegrationJob) |
| Test Connection | GET | /back-office/square/test | Test API connectivity by fetching merchant info |
| OAuth Callback | GET | /square-callback | Handles OAuth redirect from Square (guest route, name square.callback) |
| Webhook | POST | /webhook/square | Receives Square webhooks (guest route, verify.square-webhook middleware) |
| Online Payment | POST | /online-ordering/payment/process | Processes an online-ordering Square payment (OnlineOrderingController::processSquarePayment) |
The merchant-facing /square/* routes are under the /back-office API prefix and the admin-vendor-override middleware.
Fields
Status Response (SquareIntegrationService::getStatus)
| Field | Type | Description |
|---|---|---|
is_connected | boolean | Whether the integration is active |
provider | string | square |
vendor_id | string | The Upvendo merchant (tenant) this integration belongs to |
is_sandbox | boolean | Whether using the sandbox environment |
merchant_id | string | Square merchant ID (from credentials.merchant_id) |
access_token_expires_at | string | When the access token expires |
is_token_expired | boolean | Whether the token needs refresh |
settings | object | Integration settings |
sync_status | object | Current sync status, logs, connection status |
last_sync_at | string | Timestamp of last successful sync |
sync_time | string | Configured daily auto-sync time |
enable_auto_sync | boolean | Whether daily auto-sync is enabled |
created_at / updated_at | string | Record timestamps |
Sync Time Request (UpdateSyncTimeRequest)
| Field | Type | Required | Validation |
|---|---|---|---|
sync_time | string | Yes | Must match HH:00 or HH:30 (regex: `^([01]?[0-9] |
enable_auto_sync | boolean | No (sometimes) | boolean |
Payment Flow
Kiosk Payments (Square Terminal)
Customer Order -> Upvendo Kiosk -> Create Square Order -> Create Terminal Checkout -> Payment on Terminal -> payment.updated Webhook -> PaymentCaptureService::capturePaymentTerminal Checkout Details (SquareTerminalAndPayments::createTerminalCheckout):
- Requires the device's
getSquareTerminalId(); throws if missing - A Square Order is created first (if not already existing) composed WITHOUT tip (
includeTip: false), since the tip is collected on-device - A Terminal Checkout is created linking the order to the paired device
- Device options:
skipReceiptScreen = false,collectSignature = false, andtipSettingsbuilt from the location'scollect_tipsconfig (on-device tip prompt is configurable, not hard-disabled) - The checkout amount is the transaction total in the smallest currency unit
- The
referenceIdencodes the tenant + order_no (SquareReferenceId) for deterministic webhook routing - The checkout ID and status are stored on the transaction's
external_data.square - Payment confirmation arrives via the
payment.updatedwebhook
Retry handling (PaymentService::processSquarePayment): if an existing checkout is PENDING, the transaction is returned without creating a duplicate; if COMPLETED, the transaction is returned as-is; if canceled/failed, a new checkout is created.
Terminal Checkout Statuses (SquareConstants):
PENDING-- Checkout created, waiting for terminalIN_PROGRESS-- Customer interacting with terminalCANCEL_REQUESTED-- Cancellation requestedCANCELED-- Checkout cancelledCOMPLETED-- Payment successful
Cancellation: Terminal checkouts can be cancelled via cancelTerminalCheckout() (wrapped by PaymentService::cancelSquareTerminalCheckout). Cancellation failures are logged but do not throw.
Online Ordering Payments (Square Web Payments SDK)
Customer Order -> Online Ordering -> Square Web Payments SDK (card tokenization) -> POST /online-ordering/payment/process -> OnlineOrderingOrchestrator::processSquarePayment -> SquareIntegrationService::processOnlinePayment -> payment.updated Webhook -> OnlineOrderingOrchestrator::verifyPayment()The backend processSquarePayment takes the storefront-tokenized source_id and optional verification_token. Square's Web Payments SDK replaces Stripe for online payments -- no Stripe or Viva Wallet configuration is needed.
Not fully verified here: the storefront / proxy code that loads the Square Web Payments SDK and tokenizes the card lives outside these repos; only the backend
processSquarePayment/processOnlinePaymentpath was verified.
Business Rules
- Merchant-scoped, not location-scoped: One Square integration covers all locations under the merchant. All locations sync together.
- Provider exclusivity: When Square is connected, the merchant's
provider_integratedis set tosquare, routing payments to Square instead of other providers. - Token management: Access tokens have an expiration. The integration stores
expires_atandrefresh_token_expires_at(undercredentials) for token lifecycle management. - Sandbox vs Production: Determined by
config('square.sandbox'). Sandbox usesconnect.squareupsandbox.com; production usesconnect.squareup.com. - Sync is non-destructive: The sync direction algorithm avoids overwriting newer changes. Conflicts are resolved by timestamps and webhook context.
- Webhook deduplication: Event IDs are cached for 24 hours to prevent duplicate processing.
- Anti-recursion: Cache-based timestamps skip webhook processing within 10 seconds (location/catalog) or 15 seconds (inventory) of a push to Square.
- Sync logs: The integration keeps the last 1000 log entries. Consecutive duplicate logs update the timestamp instead of creating new entries.
- Full sync order: Test connection -> Locations -> Menus -> Inventory -> update sync status (then fires
ReloadMenuper location). - SyncSquareIntegrationJob: Runs with 3 retries, a 600-second (10-minute) timeout, and
afterCommit. - No app fee / commission: There is no
app_fee_money/ commission logic in the current Square payment code.
Square-Specific Features
Terminal Integration
- Go to Device Management -> Devices (
/device-management/devices) - Select a device and open the Square Terminal section
- Click Generate Device Code -- a pairing code is created (
POST /devices/.../square-device-code) - Enter the code on the Square Terminal
- Once paired, the
device.code.pairedwebhook confirms the connection and the terminal is ready
Catalog Sync
- Items, categories, modifier groups, variant groups sync per-entity
- Changes in Upvendo push to Square (via
SyncSquareMenuJobor full sync) - Changes in Square pull to Upvendo (via catalog webhooks / full sync)
- Conflict resolution: timestamp + hash comparison; webhook-triggered changes from Square win in conflict
Inventory Sync
- Stock levels sync per item variation per location
- Webhook-triggered for targeted updates (
SyncSquareInventoryJob); also part of the full sync
Daily Auto-Sync
- Configurable sync time on the Square page -> Settings tab
- Runs a full sync: connection test -> locations -> menus -> inventory
- Driven by the
InHouseAutoSyncCommand(in-house:auto-sync) scheduler, which callsstartSync()directly. Auto-sync is skipped after 3 consecutive failures.
FAQs
Do I need to set up a payment profile for Square?
No. Square handles all payments directly. You do not need to configure Stripe, Viva Wallet, or any other payment provider when Square is connected.
What happens when I connect Square for the first time?
After OAuth authorization, a full sync runs automatically: Square locations are imported with their data, the menu catalog is synced, and inventory levels are reconciled. This may take a few minutes depending on the size of your catalog.
Can I create items in Upvendo and push them to Square?
Yes. Items created in Upvendo are pushed to Square on the next sync (manual or daily); items created in Square are pulled to Upvendo. The direction is decided automatically per entity.
How does conflict resolution work?
When both sides changed the same entity since the last sync: in webhook context (Square just changed), Square wins; in manual sync context, the most recently modified side wins based on timestamps.
What data does Square NOT provide?
Square does not provide branding data like logos, colors, or fonts. Configure these manually in the Upvendo Branding Profile after locations are imported.
Can I use Square for only kiosk OR only online ordering?
Yes. Square handles both, and you configure which channels are active per location. Payment routing adapts based on the order channel.
How often does the daily sync run?
Once per day at the time configured on the Settings tab. The sync time must be HH:00 or HH:30.
What happens if I disconnect Square?
Disconnecting revokes the Square token, deletes the integration record, and clears the merchant's provider_integrated. No more payment processing until an alternative (Stripe, Viva Wallet) is configured. Sync also stops.
Troubleshooting
"OAuth connection failed"
- Verify the Square account is active and in good standing
- Ensure you have owner/admin permissions on the Square account
- Try disconnecting and reconnecting from the Square page (
/square) - Check that the Square Application ID and Secret are configured in the backend (
config/square.php) - For sandbox: have a sandbox seller dashboard open in another browser tab
"Locations not appearing after connecting"
- The first sync runs automatically -- check the sync status on the Square page -> Status tab
- If sync shows "in progress" for too long, check the logs for errors
- Click Sync Now to trigger a manual sync
SyncSquareIntegrationJobhas a 10-minute timeout -- check if it timed out
"Menu items not syncing"
- Trigger a manual sync: Square page -> Status tab -> Sync Now
- Items created in either system sync per the direction algorithm
- Check the daily sync time on the Settings tab
- Review sync logs for specific item errors
- For single-entity sync issues, check the
SyncSquareMenuJoblogs (3 retries, 60-second backoff)
"Payments not processing"
- Verify the location is synced with Square (it should have a Square external ID)
- Check the Square Dashboard for account holds or verification requirements
- For kiosk: ensure the Square Terminal is paired and online
- Check that the
payment.updatedwebhook is being received and processed
"Square Terminal not connecting"
- Ensure the terminal is powered on and connected to the internet
- Generate a new device code if the previous one expired
- The terminal must be signed into the same Square account connected to Upvendo
- Pairing is confirmed via the
device.code.pairedwebhook -- the device record should show a pending device code before pairing
"Inventory counts are wrong"
- Trigger a manual sync to reconcile counts with Square
- Check that the item variation has a Square external ID (unsynced items won't have inventory)
- Verify the correct Square location is mapped to the Upvendo location
- Note the anti-recursion cooldown -- inventory webhooks are skipped within 15 seconds of a push
"Sync seems stuck or running too long"
SyncSquareIntegrationJobhas a 10-minute timeout- Check sync logs on the Status tab for errors
- If the job failed, it retries up to 3 times
- Click Sync Now to start a fresh sync
- Check the queue worker is running and processing jobs
"Changes in Square are not reflected in Upvendo"
- Verify the
verify.square-webhookmiddleware is not rejecting Square webhooks (signature key mismatch) - Check that the webhook URL
POST /webhook/squareis registered in the Square Developer Dashboard - The anti-recursion cache may be blocking the webhook (10s catalog/locations, 15s inventory)
- Trigger a manual sync to force a reconcile from Square
Disconnecting Square
If you need to disconnect Square:
- Go to Square (
/square) -> Settings tab - Click Delete and confirm
- This revokes the Square token, deletes the integration, and removes Square as the payment provider
Warning: Disconnecting Square means:
- No more payment processing (kiosk and online) until an alternative is configured
- Sync stops
- Set up Stripe (online) and/or Viva Wallet (kiosk) as alternatives before disconnecting
Note:
disable()aborts if a sync is currentlyin_progress.
Assistant Guidance
When helping users with Square integration:
- Square is merchant-scoped -- one connection covers all locations. Do not suggest per-location setup.
- The first sync after OAuth runs automatically. If locations or items are missing, suggest a manual Sync Now.
- For payment issues, check that the location has a Square external ID and (for kiosk) the terminal is paired.
- Square handles all payments -- do not suggest configuring Stripe or Viva Wallet while Square is active.
- There are no per-data-type sync toggles, no location picker, and no sync-direction setting -- do not tell users to change these.
- Conflict resolution favors Square in webhook context and most-recent-change in manual sync.
- Branding (logos, colors, fonts) must be set manually -- Square does not provide these.