Skip to content

About

Square payment processor extension for CiviCRM

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

org.civicrm.square

Square payment processor extension for CiviCRM.

  • Extension key: org.civicrm.square
  • Version: 1.2.0 (beta)
  • CiviCRM compatibility: 6.10+
  • License: AGPL-3.0-or-later
  • Square API version: 2025-10-16 (sent by the bundled square/square 43.2 SDK)
  • Requires: mjwshared, civi_contribute (see info.xml)

Installation

This extension ships its vendor/ directory (and composer.lock) committed to the repository, with only its runtime dependencies, so no composer install step is required after cloning/downloading — just enable the extension as usual (Administer → System Settings → Extensions).


Features

  • One-time card payments via Square Payments API (/v2/payments)
  • Recurring contributions via Square Subscriptions API (/v2/subscriptions)
  • Refunds via Square Refunds API (/v2/refunds), recorded in CiviCRM once Square completes them
  • Subscription cancellation and amount changes synced to Square, and cancellations made in Square synced back to CiviCRM
  • Donors can replace the card a recurring contribution is charged to (CiviCRM's Update billing details)
  • Square Web Payments SDK for browser-side card tokenization — card details never pass through CiviCRM
  • Card form ZIP/postal code kept in step with the billing address's, so Square never sees two different ones
  • Card-on-file support through CiviCRM PaymentToken
  • Square customer creation, reusing an existing Square customer only when both its reference_id and its email are the contact's (and never one already mapped to another contact)
  • Buyer verification (Strong Customer Authentication) during card tokenization
  • Webhook event handling with deduplication and delivery logging
  • Supports sandbox (test) and production environments
  • Currency-aware amounts: Square amounts are in each currency's smallest unit, so JPY (which has no cents) is never multiplied by 100

Payment Processor Configuration

Navigate to Administer → System Settings → Payment Processors and create a new processor of type Square.

Field Description
Square Application ID Found under Developer Dashboard → Your Application → Credentials
Square Access Token Live or sandbox access token
Square Location ID Found under Locations in your Square Dashboard
Square Webhook Signature Key Used to validate incoming webhook events (HMAC-SHA256)

Separate sandbox credentials are supported for test mode. The processor automatically sets billing_mode = 1 (on-site) on enable to ensure compatibility with Drupal Webform CiviCRM.


Recurring Payments — Supported Cadences

Cadence Interval
DAILY Every day
WEEKLY Every week
EVERY_TWO_WEEKS Every 2 weeks
THIRTY_DAYS Every 30 days
SIXTY_DAYS Every 60 days
NINETY_DAYS Every 90 days
MONTHLY Every month
EVERY_TWO_MONTHS Every 2 months
QUARTERLY Every 3 months
EVERY_FOUR_MONTHS Every 4 months
EVERY_SIX_MONTHS Every 6 months
ANNUAL Every year
EVERY_TWO_YEARS Every 2 years

A CiviCRM frequency with no Square equivalent (for example every 3 weeks) can't be billed by Square, and is refused at checkout before any card is saved.

Recurring payments use the Square Catalog API to create subscription plans and plan variations on demand, then create a Square Subscription linked to a card-on-file. The subscription starts immediately and Square itself charges every installment, including the first — no separate charge is made at checkout. The checkout's contribution stays Pending until Square confirms that first charge by webhook, which then completes that same contribution; each later installment becomes a new contribution (via Contribution.repeattransaction) completed the same way. All payments are recorded with CiviCRM's Payment.create, and the Square payment ID becomes the contribution's and the payment's transaction ID.

A donor can replace the card a recurring contribution is charged to, e.g. one that has expired, from CiviCRM's Update billing details link (civicrm/contribute/updatebilling, which calls updateSubscriptionBillingInfo()). The new card is saved to the subscription's Square customer, Square charges it from the next installment, and it becomes the recurring contribution's payment token. The old card stays on file at Square.


Webhook Events

Configure the Square webhook endpoint as https://your-site.org/civicrm/payment/ipn/{processor_id}. It validates the X-Square-Hmacsha256-Signature header before the event is queued. The queue record is processed immediately after it is accepted. A record that fails transiently (Square unavailable, or a related CiviCRM record not saved yet because webhooks arrived out of order) is left in status new for the Process Payment Processor Webhooks scheduled job to retry, for up to 72 hours; any other failure is marked error. See docs/WEBHOOKS.md.

Handled events

Event Action
subscription.created Syncs subscription status to ContributionRecur
subscription.updated Syncs subscription status/amount to ContributionRecur. Square has no subscription.canceled event: a cancellation arrives here with a canceled_date (the status stays ACTIVE until that date), and marks the ContributionRecur Cancelled
invoice.created Nothing is recorded: the invoice is still a draft Square has not charged
invoice.payment_made Records the installment: completes the checkout's Pending contribution for the first invoice, or creates and completes the next contribution of the series
invoice.scheduled_charge_failed Square could not charge the card on file: marks that installment's contribution Failed (the first installment's stays Pending, since Square keeps the invoice open). Ignored if Square reports the invoice paid by the time it is processed, since webhooks can arrive out of order
payment.updated Completes a one-time contribution; for a subscription payment, looks up its invoice at Square and records the installment exactly as invoice.payment_made does. A payment taken outside CiviCRM (Square Dashboard, Square Online, point of sale) is ignored unless Import Square payments made outside CiviCRM is enabled in Square Settings — and even then only at the processor's own Square location
refund.created, refund.updated Records a completed refund as a negative payment against the refunded payment, including a refund made from CiviCRM that was still PENDING at Square. A refund that arrives before its payment is recorded is retried

Webhook deduplication uses the civicrm_paymentprocessor_webhook queue (provided by the mjwshared extension) and Square's globally unique event ID. Do not log webhook signatures, access tokens, card nonces, or unredacted Square responses.


Card & Customer Storage

Square identifiers are not stored as Contact custom fields. Both are scoped per payment processor, since live and sandbox (or multiple Square merchant accounts) are entirely separate environments with their own customer/card records:

  • Cards are stored as standard CiviCRM PaymentToken records (civicrm_payment_token), one per card, linked from civicrm_contribution_recur.payment_token_id. This lets a contact have more than one card on file across different recurring contributions, and lets other CiviCRM UI/tooling that understands PaymentToken work with them normally.
  • Square customer IDs are stored in square_customer_map, an extension-owned table keyed by (contact_id, payment_processor_id) — see Database Tables below.

When a contact has no Square customer on a processor yet, an existing Square customer is reused only if its reference_id is the contact's ID and its email is the contact's primary email (compared ignoring case), and no other contact is mapped to it; otherwise a new one is created. Neither value alone is trusted: other Square products and integrations set reference_id to their own values, and family members often share one email address. Updating the customer's name and email at checkout is best-effort: if Square refuses, that is logged and the payment goes ahead.

When contacts are merged, the removed contact's mappings move to the contact kept (square_civicrm_merge()); where both had a Square customer on the same processor, the kept contact's is kept. Deleting a payment processor deletes its mappings.



JavaScript Integration

js/square.js provides full browser-side integration with the Square Web Payments SDK. It supports:

  • CiviCRM native contribution pages and event registration forms
  • Drupal Webform (webform_civicrm module) billing blocks, including AJAX reloads

Back-office (staff-entered) card payments are not supported.

Key globals:

  • CRM.squarePayment — Square's namespaced integration state
  • CRM.vars.orgCivicrmSquare — processor settings (Application ID, Location ID, sandbox flag)
  • window.civicrmSquareHandleReload — reinitializes the card element when the billing block is replaced via AJAX

The card element mounts into #square-card-container. Tokenization happens on form submit; the resulting nonce is written to a hidden square_payment_token field for PHP to read. The billing details on the form (name, email, address) are passed to card.tokenize() as Square's verification details, so Square performs buyer verification (Strong Customer Authentication) as part of tokenizing — with intent CHARGE, plus the amount and currency, for a one-time payment, and STORE for a recurring one, whose card Square's subscription charges, or a card update (Update billing details). Square refuses an amount or currency with STORE.

The card form's ZIP/postal code and the billing address's (billing_postal_code-N) are kept in step: the card form starts with the billing postal code and is updated (via card.configure()) whenever the donor changes it, an empty billing postal code is filled in as the donor types one in the card form, and submit is stopped with an error if the two still differ.



CiviCRM Hooks

Hook Purpose
hook_civicrm_config Standard civix bootstrap; loads the bundled Square SDK (vendor/autoload.php), after the site's own autoloaders
hook_civicrm_install Standard civix install (schema/table creation is handled separately by CRM_Square_Upgrader, not this hook)
hook_civicrm_enable Enforces billing_mode = 1 on all Square payment processor instances
hook_civicrm_managed Wires up managed/PaymentProcessorType.mgd.php (registers the Square payment processor type)
hook_civicrm_navigationMenu Adds "Square Settings" under Administer → System Settings

Database Tables

Table Owner Purpose
square_customer_map This extension (CRM_Square_Upgrader) Maps (contact_id, payment_processor_id) → Square customer ID; each customer maps to one contact per processor. Created on install; dropped on uninstall.
square_catalog_map This extension (CRM_Square_Upgrader) The Square subscription plans and plan variations created for each processor, so each is created once. Created on install; dropped on uninstall. Deleting a payment processor deletes its rows.
civicrm_payment_token CiviCRM core Stores Square card-on-file references (one row per card), linked from civicrm_contribution_recur.payment_token_id. Not owned by this extension — never dropped on uninstall.
civicrm_paymentprocessor_webhook mjwshared extension Webhook queue; retains deduplication, status and retry information. Not owned by this extension.

File Structure

CRM/
  Core/Payment/
    Square.php               Payment processor adapter (CiviCRM's CRM_Core_Payment contract)
    SquareIPN.php            Webhook event router and queue processing
    SquareRetryableException.php  Marks transient failures for webhook retry
    SquareOutcomeUnknownException.php  A checkout charge Square may have made without confirming it
    SquareDebugLogger.php    Opt-in verbose debug logging, gated by the square_ipn_debug_logging setting
  Square/
    Gateway.php              Square API access (client, errors, idempotency keys)
    Customers.php            Square customers and cards on file
    Subscriptions.php        Catalog plans/variations; subscription changes and cancellation
    Reconciler.php           Webhook-driven sync into CiviCRM's ledger
    Status.php               CiviCRM option values and Square status mappings
    Currency.php             Amounts in Square's smallest currency units
    Compat.php               Calls into CiviCRM core that differ between supported versions
    Form/Settings.php        Administer > System Settings > Square Settings (debug logging, external payment import)
    Upgrader.php             Creates/drops the square_customer_map and square_catalog_map tables (see Database Tables)
js/
  square.js                  Browser-side Square Web Payments SDK integration
managed/
  PaymentProcessorType.mgd.php  Registers the Square payment processor type
settings/
  Square.setting.php         Declares the square_ipn_debug_logging and square_import_external_payments settings
templates/
  CRM/Core/Payment/Square/Card.tpl  Card container HTML injected into billing block
  CRM/Square/Form/Settings.tpl      Settings form markup
xml/Menu/square.xml          Route: civicrm/admin/setting/square -> CRM_Square_Form_Settings
square.php                   Extension bootstrap and hooks (config/install/enable/managed/navigationMenu)
tools/composer.json          Development tools (PHPUnit, PHPCS); never shipped (see docs/DEVELOPMENT.md)

Deployment checklist

Use sandbox credentials first. Before accepting live payments, confirm each of the following:

  1. Enable the required mjwshared and civi_contribute extensions, then enable this extension.
  2. Create a Square payment processor in CiviCRM and enter the sandbox Application ID, Access Token, Location ID, and Webhook Signature Key.
  3. In the Square Developer Dashboard, create a webhook subscription for that same sandbox application and set its notification URL to https://your-site.org/civicrm/payment/ipn/{processor_id}. Replace {processor_id} with the CiviCRM payment processor ID.
  4. Subscribe to the events in Webhook Events. Events not listed there are acknowledged but intentionally ignored.
  5. Verify that mjwshared's Process Payment Processor Webhooks scheduled job is enabled. It retries queued records left in status new by a transient failure.
  6. Complete a test one-time contribution and, if recurring payments are in scope, a test recurring contribution. Confirm the contribution, payment token, recurring contribution, and webhook records in CiviCRM.
  7. Repeat the configuration with production credentials and the production Square application only after sandbox verification succeeds.

The notification URL configured in Square must be the same URL CiviCRM uses for the payment processor. Square's HMAC signature covers the full URL and raw request body; a different scheme, hostname, path, or proxy rewrite causes signature validation to fail.

Operations and troubleshooting

The extension records webhook delivery and processing state in civicrm_paymentprocessor_webhook. Review records with status = error and the CiviCRM log when reconciliation does not occur.

Symptom Check
Webhook returns HTTP 401 Verify the processor's Webhook Signature Key and the exact notification URL in Square. Do not trim or transform the raw request body.
Webhook returns HTTP 400 Verify Square is posting valid JSON with an event_id.
No change after a webhook Confirm the event type is supported, the processor ID in the URL is correct, and the queue record is not in an error state.
Repeated webhook delivery Square retries deliveries that did not succeed: check for HTTP 401 or 400 above. An event that is recorded is acknowledged even if processing it fails; resolve the queue record's logged error instead.
Card field does not appear Confirm the selected processor is Square, on-site payment collection is enabled, and browser JavaScript is loading without Content Security Policy errors.
Sandbox data appears missing in live mode Sandbox and production use different Square customers, cards, credentials, and processor configuration.

Enable verbose webhook diagnostics at Administer → System Settings → Square Settings only while investigating an issue. Disable it after the investigation and never place access tokens, signature keys, card tokens, or unredacted API responses in tickets or logs.

Development

Requirements: PHP 8.2 or later and Composer. The development tools are a separate Composer project in tools/, so the committed vendor/ keeps only the runtime dependencies. Run these checks from the extension root before submitting a change:

composer install --working-dir=tools
tools/vendor/bin/phpcs --standard=phpcs.xml.dist CRM square.php settings managed tests
tools/vendor/bin/phpunit --configuration tests/phpunit/phpunit.xml.dist

The codebase follows CiviCRM's two-space, Drupal-derived PHP style. The local .editorconfig enforces whitespace basics, while phpcs.xml.dist records the necessary CiviCRM exceptions for legacy CRM_* class names and extension hook functions. Do not run automatic formatters across vendor/.

About

Square payment processor extension for CiviCRM

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages