# Payment Page WP-CLI Contract

This document is the **authoritative output contract** for the `wp payment-page` CLI namespace. Third-party tooling (monitoring scripts, cron jobs, support tooling) that depends on PP CLI output should target the schemas below. PP commits to not breaking these schemas without a major version bump.

**Status:** Stable since 1.5.1.

**Locked by:** `tests/Unit/CLI/PaymentPageCliContractTest.php`

---

## `wp payment-page verify-integrity`

Verify cross-table database integrity. Same invariants as `tests/Unit/Db/DatabaseIntegrityTest.php`, run live.

### Synopsis

```
wp payment-page verify-integrity [--repair] [--format=<human|json|yaml>]
```

### JSON output schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["passed", "issues_found", "issues"],
  "properties": {
    "passed": { "type": "boolean", "description": "true if all 3 invariants pass" },
    "issues_found": { "type": "integer", "minimum": 0 },
    "issues": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["check", "count", "severity", "description"],
        "properties": {
          "check": { "type": "string", "enum": ["orphan_log_rows", "paid_no_gateway", "stuck_ach_pi"] },
          "count": { "type": "integer", "minimum": 1 },
          "severity": { "type": "string", "enum": ["info", "warning", "error"] },
          "description": { "type": "string" },
          "repaired": { "type": "boolean", "description": "true if --repair was passed AND this check was safely repairable" }
        }
      }
    }
  }
}
```

### Exit codes

| Code | Meaning |
|------|---------|
| 0 | All invariants satisfied |
| 1 | Integrity violations found (use `--repair` to attempt fix) |
| 2 | Unexpected error (DB unreachable, etc.) |

### Idempotency

Read-only by default. With `--repair`, idempotent — running twice does not delete more than running once (orphan-deletion queries match the same rows).

---

## `wp payment-page retry-stuck-pi`

Reconcile ACH PaymentIntents stuck in `processing` state. Queries Stripe for the current PI status and updates the local row if Stripe says succeeded.

### Synopsis

```
wp payment-page retry-stuck-pi (--id=<id>|--all) [--format=<human|json>]
```

### JSON output schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["processed", "reconciled", "still_processing", "errors"],
  "properties": {
    "processed": { "type": "integer", "minimum": 0, "description": "total rows attempted" },
    "reconciled": { "type": "integer", "minimum": 0, "description": "rows where Stripe says succeeded → flipped is_paid=1" },
    "still_processing": { "type": "integer", "minimum": 0, "description": "rows where Stripe still says processing → left as-is" },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["row_id", "message"],
        "properties": {
          "row_id": { "type": "integer" },
          "message": { "type": "string" }
        }
      }
    }
  }
}
```

### Exit codes

| Code | Meaning |
|------|---------|
| 0 | All attempted rows reconciled (or no stuck rows found) |
| 1 | At least one row could not be reconciled (Stripe API error) |
| 2 | Stripe credentials not configured |

### Idempotency

Idempotent — running twice on the same `--id` re-queries Stripe and either matches the current state (no-op) or warns. Never double-charges.

---

## `wp payment-page dump-payments`

Operational snapshot of recent payments for support triage. Read-only.

### Synopsis

```
wp payment-page dump-payments [--format=<json|csv>] [--limit=<n>]
```

### JSON output schema (one object per row)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "array",
  "items": {
    "type": "object",
    "required": ["id", "post_id", "email_address", "amount", "currency", "is_paid", "created_at"],
    "properties": {
      "id": { "type": "integer" },
      "post_id": { "type": "integer", "description": "pp_payment_form CPT post id" },
      "user_id": { "type": "integer" },
      "first_name": { "type": "string" },
      "last_name": { "type": "string" },
      "email_address": { "type": "string", "format": "email" },
      "amount": { "type": "string", "description": "decimal as string (avoid float precision loss)" },
      "amount_received": { "type": "string" },
      "currency": { "type": "string", "minLength": 3, "maxLength": 3 },
      "payment_gateway": { "type": "string", "enum": ["stripe", "paypal"] },
      "payment_method": { "type": "string" },
      "is_paid": { "type": "integer", "enum": [0, 1] },
      "is_live": { "type": "integer", "enum": [0, 1] },
      "stripe_payment_intent_id": { "type": ["string", "null"] },
      "paypal_capture_id": { "type": ["string", "null"] },
      "created_at": { "type": "integer", "description": "Unix timestamp" }
    }
  }
}
```

### CSV output

Headers row = the JSON property names above (column order from `wp_payment_page_payments` table definition).

### Exit codes

| Code | Meaning |
|------|---------|
| 0 | Dump completed (or no rows found) |
| 1 | Invalid `--limit` (must be 1-1000) |
| 2 | DB unreachable |

### Idempotency

Read-only. Idempotent by construction.

---

## Versioning policy

The schemas above are **stable** — adding new fields is allowed (additive, backward-compatible). Removing fields, renaming fields, or changing types is a **breaking change** and requires a PP major-version bump (1.x → 2.x). The locked contract test enforces this:

```bash
vendor/bin/phpunit --filter=PaymentPageCliContract
```

A failure means the schema drifted. If the drift is intentional (additive), update the fixture in `tests/fixtures/cli/`. If the drift removes/renames a field, abort the change — it breaks downstream consumers.

---

*Last updated 2026-05-25 for PP 1.5.1.*
