---
title: "Reconcile Payment Attempts Endpoint"
method: POST
path: "/internal/reconcile-payment-attempts"
tags: ["Internal", "Internal"]
---

# Reconcile Payment Attempts Endpoint

`POST /internal/reconcile-payment-attempts`

## Request

- Base URL: `https://api.call2me.app`
- URL: `https://api.call2me.app/internal/reconcile-payment-attempts`
- Auth: HTTP bearer

Ask Paddle what actually happened to checkouts we never heard back about.

Paddle only webhooks us on success. Every other ending — card declined,
dropped for fraud, 3-D Secure never completed, user closed the tab — is
silent, so our `payments` row stays 'pending' forever and all of those
endings look identical in the database.

On 24 Sep that blind spot hid the finding that mattered: of 59 stuck rows,
4 users had entered card details and been dropped with `fraud`. They were
not "users who didn't pay" — they were users we refused. Nothing in our
own data said so.

This walks recent stuck rows, asks Paddle for ground truth, and writes it
back so the funnel can be measured in SQL instead of by hand:

  - transaction completed  → mark 'completed' (webhook must have been missed)
  - a payment attempt was made and failed → 'failed' + the error code
  - still draft/ready with no attempt → leave 'pending', record last_checked

Deliberately does NOT credit wallets: crediting is the webhook's job and
doing it in two places is how double-credits happen. A completed-but-
uncredited row is surfaced as `needs_review` for a human instead.

## Headers

- `x-internal-key` string, nullable

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/call2me/apis/call2me-api.md) · [All operations](https://skmtc.dev/call2me/apis/call2me-api/llms.txt) · [OpenAPI document](https://skmtc.dev/call2me/apis/call2me-api/revisions/3951f9b7e47d?raw)
