# Troubleshooting

Common issues and solutions for AuxCORE API integration.

---

## 🔐 Authentication Issues

### `TOKEN_EXPIRED` / `INVALID_TOKEN` (401)

**Cause:** JWT expired or malformed on an Admin API route
**Solution:**
```bash
# Get a new token (or exchange your refresh token)
curl -X POST https://uat.auxcore.net/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -H "X-Tenant-ID: your-tenant-id" \
  -d '{"email": "...", "password": "..."}'
```

### "API key required" / "API key validation failed" (401)

**Cause:** No key, or a revoked/unknown/deactivated key, on `/api/v1/public/*`
**Solution:** Send the key in the `x-api-key` header. On these routes `error` is a string, e.g. `{ "success": false, "error": "API key validation failed", "message": "..." }`.

### "Origin required" / "Origin not allowed" (403)

**Cause:** An SDK public key (`lpk_live_…`) with allowed origins configured, called without an `Origin` / `X-Merchant-Origin` header (e.g. from a server) or from an unlisted site
**Solution:** Use a secret key server-side, or add the origin to the SDK key at `/settings/developer/api-keys`.

### `TENANT_REQUIRED` (400)

**Cause:** Missing tenant ID on an **Admin API** route (`/api/v1/*`)
**Solution:**
```bash
# Admin routes require JWT + tenant ID
curl -H "Authorization: Bearer YOUR_JWT" \
     -H "X-Tenant-ID: your-tenant-id" ...
```

Public API keys resolve the tenant from the key — this error should not appear on `/api/v1/public/*`. Tenant-wide ERP keys (`lv_erp_…`) must send `X-Merchant-ID` instead, or merchant-specific calls fail with `VALIDATION_ERROR`.

---

## 💳 Transaction Issues

### `PCI_SCOPE_VIOLATION` (400)

**Cause:** A raw `CardNumber` was sent to a payment endpoint
**Solution:** Tokenize with the PCI SDK (or `POST /tokenize-card` server-side) and send `pciToken`.

### "Transaction declined" (402)

The public `/transaction` endpoint answers `402` with `status: "declined"`, a readable `message`, and `data.ResponseCode` (NMI-style, e.g. `202` insufficient funds, `223` expired card, `225` invalid CVV).

**Causes:**
- Insufficient funds
- Card expired
- Invalid card details
- AVS/CVV mismatch

**Solutions:**
- Ask customer for different card
- Verify card details
- Check AVS/CVV responses

### "This transaction has already been settled and cannot be voided"

**Cause:** Transaction already settled (only `approved`, unsettled transactions can be voided)
**Solution:** Use refund instead of void

### "Only approved or settled transactions can be refunded"

**Cause:** Refund attempted on a declined, voided or pending transaction
**Solution:** Void a pending/approved one instead, or nothing is owed

---

## 🔄 Subscription Issues

### Subscription payment failed

**Cause:** Card declined, expired, or insufficient funds
**Solution:**
- Retry the failed charge with `POST /api/v1/recurring/:id/retry-failed`
- Send the customer a card-update link with `POST /api/v1/recurring/:id/send-method-update-link`
- Watch for `transaction.declined` webhooks on the recurring charges

---

## 📧 Webhook Issues

### Webhooks not received

**Checks:**
1. URL publicly accessible?
2. HTTPS enabled?
3. Returns a 2xx status?
4. Not timing out?
5. Endpoint subscribed to the event (e.g. `transaction.approved`) and active?

**Solution:**
```bash
# Send a webhook.test event to the endpoint
curl -X POST https://uat.auxcore.net/api/v1/webhooks/endpoints/4e3d2c1b-0a9f-4e8d-7c6b-5a4f3e2d1c0b/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

# Inspect delivery attempts
curl https://uat.auxcore.net/api/v1/webhooks/endpoints/4e3d2c1b-0a9f-4e8d-7c6b-5a4f3e2d1c0b/deliveries \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"
```

### "Invalid signature"

**Cause:** Signature verification failing — usually because the body was parsed and re-serialized before hashing, the wrong secret is used, or the timestamp is outside your tolerance
**Solution:** Hash the **raw** body: `HMAC-SHA256(secret, "<t>.<rawBody>")` and compare with `v1` from `X-Webhook-Signature: t=<unix>,v1=<hex>`. The secret is shown only once at creation; if lost, call `POST /api/v1/webhooks/endpoints/:id/regenerate-secret`.

Use the [Node.js verifier](../examples/nodejs.md#webhook-handler-express) to reject absent or malformed headers with `401` instead of a buffer-length exception. Mount the raw-body route before `express.json()`. Repeated deliveries with the same `event.id` should return `2xx` after confirming that the durable inbox already holds the event; do not repeat its business effects.

---

## 🐛 Common Errors

### Rate limit exceeded (429)

**Solution:** Implement exponential backoff using the `RateLimit-*` headers. Public token/checkout endpoints return `{ "success": false, "error": { "code": "RATE_LIMITED", ... } }` (30 requests / 15 min per IP).

### Gateway timeout

**Cause:** The response was lost or the gateway timed out. The processor may still have accepted the payment.

**Solution:**

- On `POST /api/v1/public/transaction`, do not automatically submit another charge. This endpoint does not forward `Idempotency-Key`, and `OrderId` does not enforce uniqueness.
- Query `GET /api/v1/public/transaction-list?orderId=…` and match the results to your payment attempt. Wait for the webhook or confirm with the processor if the outcome is unknown; an empty lookup immediately after a timeout is not proof that no charge occurred.
- For an endpoint that supports idempotency, retry with the same persisted key and exact payload. Wait before retrying `409 IDEMPOTENCY_REQUEST_IN_PROGRESS`; investigate `422 IDEMPOTENCY_KEY_REUSED` without generating a replacement key to bypass it.
- For voids and refunds, look up the current transaction/refund status before submitting another reversal. A refund may already be queued for a server-side retry.

See [Use Idempotency Keys](./best-practices.md#use-idempotency-keys).

### Invalid JSON

**Solution:** Verify JSON syntax

---

**Need help?** Contact support@auxcore.net
