Inspiration

A customer opens their card app and sees PAYPAL * Brightleaf St ₩13,900. They don't remember it. The amount is in won, the merchant name is truncated to 22 characters, and the card app offers one obvious button: Dispute.

Often the charge is legitimate. A free trial turned into the first paid month, or one order was paid in two parts. But once the dispute is filed, the merchant is already paying for it: the funds are held, a dispute fee is charged whether or not the merchant wins, and the account's dispute rate goes up.

The merchant had the answer all along, in its PayPal records. The customer just had no way to ask.

What it does

Charge Desk is a help page a PayPal merchant links from its site and receipt emails.

  1. The customer asks in their own words. "I was charged ₩13,900 and I don't know why." They can give the amount, the statement text, a rough date, or any mix.
  2. Is this even ours? Before asking for anything personal, the agent checks the statement text against how this merchant's charges actually print. "PAYPAL *QUICKWINPRIZE" gets a straight "that isn't us, here is how to ask your card issuer", without asking for an email.
  3. Verification first. The customer verifies the email they paid with using a one-time code. Nothing about any payment is shown before that, and the reply is the same whether or not that email has ever paid here, so nobody can probe who shops at this store.
  4. Find the charge. The agent searches only that customer's own payments and matches them to what they said:
    • rebuilds the 22-character statement line PayPal prints,
    • converts the merchant's USD price into the card currency, allowing for card FX fees,
    • groups split captures into one order (₩89,280 + ₩50,220 is one USD 100 order shipped in two parts),
    • spots subscriptions from PayPal's subscription reference or a regular billing interval.
  5. Explain with evidence. The answer comes with cards that show the statement line next to the PayPal record: order, amount and FX, date, subscription, statement name.
  6. If they still want out. The agent never suggests a refund. If the customer asks for one, it first offers what the merchant set up (pause the membership, switch to a cheaper plan). If they still want to cancel or get a refund, it sends a request to the merchant.
  7. The merchant decides. The dashboard shows the request with what was bought, when, and what the customer said. One click approves it, and only then does the app call PayPal to refund the capture or cancel the subscription. The dashboard shows the resulting PayPal status.

The dashboard also counts what Charge Desk did: questions answered, charges settled by an explanation, sales kept, and dispute fees at stake.

How we built it

  • Agent: Claude (Opus 5.5) with tool use, in a manual tool loop on the server. Four tools: check_descriptor, request_verification, find_charge, request_change.
  • PayPal, with the merchant's own credentials:
    • Transaction Search (/v1/reporting/transactions) for the customer's payments,
    • Orders and Captures (/v2/checkout/orders, /v2/payments/captures) for items, soft descriptor and split-capture grouping,
    • Subscriptions and Plans (/v1/billing/...) for trial and billing cycle facts, and cancellation,
    • Refunds, made idempotent with PayPal-Request-Id.
  • Matching core (src/core/): plain TypeScript with no dependencies, covered by unit tests that need no keys.
  • Web app: Next.js 16 and React 19. A receipt email, the customer help page and the merchant dashboard.
  • Hosting: Render (free web service from a render.yaml blueprint, with a Deploy to Render button in the README).

The live demo runs on a built-in demo store, so judges can try every step without a PayPal account, including approving a cancellation, and start over with one click. The same code runs against a real PayPal sandbox merchant account with PAYPAL_MODE=sandbox. We ran it on our sandbox account, where Transaction Search returned the real sandbox payments and the desk explained a subscription renewal, a two-part order and a charge that wasn't ours. The end of the video shows that run.

Safety by design

  • The model never handles money. It can only send a request; refunds and cancellations happen only after the merchant clicks Approve.
  • The model never sees the verification token. The server holds it and scopes every search to the verified customer.
  • Retention before refunds is enforced in code, not only in the prompt: the first request_change call for a case returns the merchant's alternatives instead of sending the request.
  • Spend cap: the demo stops calling the AI once it reaches its daily budget.

Challenges we ran into

  • The 22-character statement line. PayPal prints PAYPAL * plus the merchant's descriptor, cut to 22 characters, and customers retype it loosely. We rebuild the exact line and score fuzzy matches against it.
  • Amounts the merchant never charged. The card shows ₩13,900 for a USD 9.99 charge. Matching has to convert and allow for FX fees without matching the wrong payment.
  • One order, two statement lines. Split captures looked like two separate questions and double-counted the kept revenue until we grouped them by order.
  • An agent that was too helpful. Early versions offered a refund before the customer asked. That costs the merchant money, so we moved the rule into the tool itself.
  • Sandbox timing. Transaction Search data lags behind new sandbox payments by hours, so the matching core was built and tested against a demo store first.

Accomplishments that we're proud of

  • The whole loop works end to end: a vague question, verification, an answer with evidence, a retention offer, merchant approval, and a PayPal state change.
  • A charge that isn't the merchant's is answered without collecting any personal data.
  • Revenue protection is a product rule, not a prompt suggestion.

What we learned

  • The hard part of "AI customer support" here is matching, not chatting. Most of the work is in deterministic code the agent calls.
  • Leaving the money decision to a person made the agent simpler and easier to trust.

What's next for Charge Desk

  • Webhooks to keep a fresh payment cache, instead of searching on demand.
  • Sending the verification code by real email (the demo shows it on screen).
  • A dispute evidence pack for the cases that still reach the card issuer.
  • Shopify and other storefront embeds for PayPal merchants.

Built With

  • anthropic-api
  • claude
  • next.js
  • node.js
  • paypal-orders-api
  • paypal-rest-api
  • paypal-subscriptions-api
  • paypal-transaction-search
  • react
  • render
  • typescript
Share this project:

Updates

Submission history