Skip to main content

PayArc Dispute Processing

GSM Middleware includes a self-contained PayArc dispute processing pipeline. It automatically links PayArc chargeback webhooks to their originating NMI transactions, submits chargebacks to Signifyd, and stores full chargeback records.

Queue-based pipeline

Dispute processing uses a two-phase queue architecture. Phase 1 — the PayArc Webhook Receiver accepts the incoming POST from PayArc, logs it to rm_webhook_log, and enqueues a process_payarc_dispute job in rm_queue. Phase 2 (documented on this page) processes queue jobs through the full matching and Signifyd submission pipeline.

Legacy rm_webhooks table removed

As of v1.15.0, the legacy rm_webhooks table has been dropped (migration 014). All dispute processing now flows through the rm_queue system with rm_webhook_log for audit logging. The POST /disputes/process batch endpoint has been removed.

Overview

When a customer disputes a charge, PayArc sends a webhook to the site. The dispute processor:

  1. Receives the dispute from the queue (rm_queue with job_type = 'process_payarc_dispute')
  2. Extracts the Merchant ID (MID) and dispute details from the job payload
  3. Resolves the MID-specific NMI security key from the admin-configured Merchant Accounts
  4. Searches the NMI query API for the original transaction by authorization code (±2-day window)
  5. Disambiguates multiple matches by card first-6 + last-4 digits
  6. Stores a rm_payarc_dispute_linkages record (PayArc ↔ NMI audit trail)
  7. Submits a chargeback notification to Signifyd using site-specific credentials from rm_site
  8. Inserts a full chargeback record into rm_chargebacks with action log
  9. Marks the queue job as completed or failed

Triggering Processing

Queue-Based Processing (Primary)

Disputes are automatically enqueued when PayArc webhooks arrive. They are processed by:

  1. External croncron/process-queue.php processes all pending queue jobs (recommended)
  2. Admin UI — Click "Process" on individual jobs in the Queue Page
  3. REST APIPOST /queue/{id}/process to process a specific job

Replay Endpoint

POST /wp-json/gsm-middleware/v1/disputes/replay

Authentication: Requires the manage_gsm_middleware capability (Administrator role).

Re-enqueues a previously processed dispute for reprocessing.

Configuration

NMI Credentials

NMI security keys are managed through the Merchant Accounts admin page (GSM Middleware → Merchant Accounts). Each PayArc merchant account (MID) must have a corresponding NMI security key configured. Keys are encrypted at rest using the plugin's Security_Manager.

See Merchant Accounts for configuration details.

tip

PayArc transmits MIDs without leading zeros. For example, account 0567000000025411 appears as 567000000025411 in webhook payloads.

Error Notification Email (.env)

When a dispute cannot be definitively matched to a single NMI transaction (ambiguous match), an alert email is sent:

Signifyd Credentials (Database)

Signifyd credentials are stored per-site in the rm_site table:

ColumnDescription
signifyd_team_idSignifyd team identifier
signifyd_api_keySignifyd API key

These are resolved at runtime by looking up the order's site column in rm_order.

Database Tables

rm_payarc_dispute_linkages

Stores the PayArc ↔ NMI linkage for every processed dispute. Created automatically by migration 005_create_dispute_linkages_table.php.

ColumnTypeDescription
idINT UNSIGNEDAuto-increment primary key
case_idVARCHAR(64)PayArc case ID (unique)
case_numberVARCHAR(64)PayArc case number
midVARCHAR(64)PayArc Merchant ID
nmi_transaction_idVARCHAR(64)NMI transaction ID
nmi_order_idVARCHAR(128)NMI order ID
order_numberVARCHAR(20)Order number from rm_order
match_strategyVARCHAR(64)Match method (authcode_card_match)
amountDECIMAL(10,2)Dispute amount
auth_codeVARCHAR(32)Authorization code
arnVARCHAR(128)Acquirer Reference Number
transaction_dateDATEOriginal transaction date
reason_codeVARCHAR(32)Dispute reason code
reason_descVARCHAR(255)Dispute reason description
dispute_statusVARCHAR(64)Current dispute status
payarc_dataLONGTEXTFull PayArc dispute JSON
nmi_dataLONGTEXTFull NMI transaction JSON
linked_atDATETIMEWhen the linkage was created
created_atTIMESTAMPRow creation timestamp

rm_chargebacks

Stores the full chargeback record for each processed dispute. Each record includes all dispute data, processing results, and a chronological action log.

ColumnTypeDescription
idINT UNSIGNEDAuto-increment primary key
case_idVARCHAR(64)PayArc case ID
midVARCHAR(64)PayArc Merchant ID
nmi_transaction_idVARCHAR(64)Matched NMI transaction ID
order_numberVARCHAR(20)Matched order number
amountDECIMAL(10,2)Dispute amount
reason_codeVARCHAR(32)Dispute reason code
reason_descVARCHAR(255)Dispute reason description
signifyd_responseLONGTEXTFull Signifyd API response JSON
payarc_dataLONGTEXTFull PayArc webhook payload
action_logLONGTEXTChronological processing action log
created_atTIMESTAMPRow creation timestamp

Logging

Logs are written to:

wp-content/plugins/gsm-middleware/logs/payarc-dispute-processor.log

Fallback locations (if primary directory is not writable):

  1. wp-content/uploads/gsm-middleware-logs/
  2. STDERR (CLI/cron) or error_log() (web)

Logs are never silently lost — if no file location is writable, output goes to the system error log.

Each run produces detailed per-webhook step logs at INFO level, with errors at ERROR level.

Sample log output:

[2026-03-26 14:32:01 UTC] [INFO] Starting PayArc dispute processing (limit: 10)...
[2026-03-26 14:32:01 UTC] [INFO] Found 1 unprocessed PayArc webhooks
[2026-03-26 14:32:01 UTC] [INFO] --- Begin webhook ID 42 (received: 2026-03-26 12:00:00) ---
[2026-03-26 14:32:01 UTC] [INFO] [Webhook 42] Step 1 OK: case=CASE-12345, mid=567000000025411...
[2026-03-26 14:32:01 UTC] [INFO] [Webhook 42] Step 2 OK: NMI transaction_id=8765432109...
[2026-03-26 14:32:01 UTC] [INFO] [Webhook 42] Step 3 OK: Order found — number=BGM-10001...
[2026-03-26 14:32:01 UTC] [INFO] [Webhook 42] Step 4 OK: Linkage stored successfully
[2026-03-26 14:32:01 UTC] [INFO] [Webhook 42] Step 5 OK: Signifyd chargeback submitted...
[2026-03-26 14:32:02 UTC] [INFO] --- End webhook ID 42: success=true, linked=yes, signifyd=yes ---
[2026-03-26 14:32:02 UTC] [INFO] Processing complete. Processed: 1, Linked: 1, Signifyd Submitted: 1, Errors: 0

Matching Algorithm

The processor uses a two-stage match:

Stage 1: Auth Code + Date Window
→ Query NMI with authorization_code + date_start/date_end (±2 days)
→ If 1 result → use it ✓
→ If 0 results → no match, mark processed without linkage

Stage 2: Card Disambiguation (when Stage 1 returns multiple)
→ Filter by first-6 + last-4 card digits
→ If 1 result → use it ✓
→ If 0 results → no match
→ If multiple → send alert email, use first as fallback

Troubleshooting

"No NMI account found for MID"

The MID from the webhook does not have a matching entry in the Merchant Accounts admin page. Navigate to GSM Middleware → Merchant Accounts and add the MID with its NMI security key.

"No NMI match found"

No NMI transaction was found for the authorization code within ±2 days of the transaction date. The queue job is marked completed without a linkage. Possible causes:

  • The transaction was processed through a different payment gateway
  • The authorization code in the PayArc payload is incorrect
  • The transaction date is outside the ±2-day window

"AMBIGUOUS MATCH" alert email

Multiple NMI transactions share the same authorization code and card digits. The first match is used as a fallback. Review the contents of the alert email and manually verify the correct transaction.

"No Signifyd credentials configured for site X"

The rm_site row for the order's site does not have signifyd_team_id / signifyd_api_key populated. The dispute is still linked to NMI; only the Signifyd submission is skipped.

Source Files

FilePurpose
src/Disputes/Dispute_Processor.phpCore processing class
src/API/Disputes_API.phpREST endpoint registration (replay)
src/API/PayArc_Webhook_API.phpWebhook receiver and queue enqueue
src/Queue/Queue_System.phpQueue management (enqueue, process, requeue)
src/Database/migrations/005_create_dispute_linkages_table.phpDB migration for rm_payarc_dispute_linkages
src/Database/migrations/014_drop_rm_webhooks.phpDrops legacy rm_webhooks table