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.
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.
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:
- Receives the dispute from the queue (
rm_queuewithjob_type = 'process_payarc_dispute') - Extracts the Merchant ID (MID) and dispute details from the job payload
- Resolves the MID-specific NMI security key from the admin-configured Merchant Accounts
- Searches the NMI query API for the original transaction by authorization code (±2-day window)
- Disambiguates multiple matches by card first-6 + last-4 digits
- Stores a
rm_payarc_dispute_linkagesrecord (PayArc ↔ NMI audit trail) - Submits a chargeback notification to Signifyd using site-specific credentials from
rm_site - Inserts a full chargeback record into
rm_chargebackswith action log - 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:
- External cron —
cron/process-queue.phpprocesses all pending queue jobs (recommended) - Admin UI — Click "Process" on individual jobs in the Queue Page
- REST API —
POST /queue/{id}/processto 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.
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:
| Column | Description |
|---|---|
signifyd_team_id | Signifyd team identifier |
signifyd_api_key | Signifyd 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.
| Column | Type | Description |
|---|---|---|
id | INT UNSIGNED | Auto-increment primary key |
case_id | VARCHAR(64) | PayArc case ID (unique) |
case_number | VARCHAR(64) | PayArc case number |
mid | VARCHAR(64) | PayArc Merchant ID |
nmi_transaction_id | VARCHAR(64) | NMI transaction ID |
nmi_order_id | VARCHAR(128) | NMI order ID |
order_number | VARCHAR(20) | Order number from rm_order |
match_strategy | VARCHAR(64) | Match method (authcode_card_match) |
amount | DECIMAL(10,2) | Dispute amount |
auth_code | VARCHAR(32) | Authorization code |
arn | VARCHAR(128) | Acquirer Reference Number |
transaction_date | DATE | Original transaction date |
reason_code | VARCHAR(32) | Dispute reason code |
reason_desc | VARCHAR(255) | Dispute reason description |
dispute_status | VARCHAR(64) | Current dispute status |
payarc_data | LONGTEXT | Full PayArc dispute JSON |
nmi_data | LONGTEXT | Full NMI transaction JSON |
linked_at | DATETIME | When the linkage was created |
created_at | TIMESTAMP | Row 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.
| Column | Type | Description |
|---|---|---|
id | INT UNSIGNED | Auto-increment primary key |
case_id | VARCHAR(64) | PayArc case ID |
mid | VARCHAR(64) | PayArc Merchant ID |
nmi_transaction_id | VARCHAR(64) | Matched NMI transaction ID |
order_number | VARCHAR(20) | Matched order number |
amount | DECIMAL(10,2) | Dispute amount |
reason_code | VARCHAR(32) | Dispute reason code |
reason_desc | VARCHAR(255) | Dispute reason description |
signifyd_response | LONGTEXT | Full Signifyd API response JSON |
payarc_data | LONGTEXT | Full PayArc webhook payload |
action_log | LONGTEXT | Chronological processing action log |
created_at | TIMESTAMP | Row 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):
wp-content/uploads/gsm-middleware-logs/STDERR(CLI/cron) orerror_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
| File | Purpose |
|---|---|
src/Disputes/Dispute_Processor.php | Core processing class |
src/API/Disputes_API.php | REST endpoint registration (replay) |
src/API/PayArc_Webhook_API.php | Webhook receiver and queue enqueue |
src/Queue/Queue_System.php | Queue management (enqueue, process, requeue) |
src/Database/migrations/005_create_dispute_linkages_table.php | DB migration for rm_payarc_dispute_linkages |
src/Database/migrations/014_drop_rm_webhooks.php | Drops legacy rm_webhooks table |
Related Documentation
- PayArc Webhook Receiver — Phase 1: Webhook ingestion
- Queue Page — Monitor and manage queue jobs
- Chargebacks Page — View processed disputes
- Webhook Logs — Audit incoming webhooks
- Merchant Accounts — NMI credential management