Next Wave Agentic
Back to Blog
How a Leaked Stripe API Key Turned My Account Into a Phishing Machine
SecurityStripeAPI KeysPhishingDeveloper Lessons

How a Leaked Stripe API Key Turned My Account Into a Phishing Machine

5 min read

Today I opened one of my Stripe accounts and found a surprise nobody wants before coffee:

About 150 invoices for $699.99 each, all addressed to people I had never heard of.

My first thought was: Well, this is bad.

My second thought was: Wait. If someone pays one of these, doesn't the money land in my Stripe balance? That is a remarkably inefficient robbery.

That question led to the real answer. The attacker was not trying to steal money from my Stripe account.

They were using my Stripe account to send phishing emails.

The World's Least Subtle Automation Loop

The Stripe API logs quickly gave the game away. Every few minutes, the same four-step routine appeared:

POST /v1/customers
POST /v1/invoices
POST /v1/invoiceitems
POST /v1/invoices/{invoice_id}/send

Create a customer. Create an invoice. Add a $699.99 item. Send it. Repeat.

A playful phishing bot running an automated invoice loop

The requests came from the Stripe Python SDK and were authenticated with one of my live secret keys. Someone had obtained an sk_live_... key, wrapped a script around it, and pointed it at a list of names and email addresses.

By the time I found it, the little invoice factory had produced roughly 150 invoices—more than $100,000 in fake charges.

But the total was theater. Nobody was supposed to pay the invoice.

Then I Read the Fine Print

The invoice description contained the missing piece. It was essentially:

Dear customer, your auto-renewal is confirmed. Call this number to cancel.

Ah. There it was.

This was callback phishing.

The recipient sees a large, unexpected invoice, panics, and calls the “cancellation” number. That phone call—not the Stripe payment page—is where the scam begins.

A legitimate-looking invoice used as bait in a callback phishing attempt

Stripe Wasn't the Cash Register. It Was the Costume.

This is the part that initially confused me. Why compromise somebody else's Stripe account if invoice payments would go to that account?

Because the attacker was not borrowing my payment processing. They were borrowing Stripe's credibility.

The recipient got:

  • A real transactional email sent through Stripe's infrastructure
  • Links to legitimate Stripe domains
  • A genuine Stripe invoice number
  • A Stripe-hosted invoice page
  • A PDF generated by Stripe
  • A real merchant account behind the message

From the recipient's perspective, almost everything looked legitimate.

Almost.

The phone number was the trap door.

The $699.99 Was Pure Panic Fuel

The invoice data confirmed that no automatic card charge was being attempted:

collection_method: send_invoice
amount_due: 69999
amount_paid: 0
attempt_count: 0
attempted: false
default_payment_method: null
default_source: null

There was no payment method attached to the customer. The $699.99 existed to create urgency—not revenue.

The psychology is simple:

Auto-renewal confirmed. Amount: $699.99. Call immediately to cancel.

Most people will not calmly inspect invoice metadata. They will think, What the heck is this? I need to cancel it.

And then they call.

A Little Unicode Camouflage

The message had one more trick. Text that visually resembled “Auto-Renewal Confirmed” was assembled from look-alike characters across several Unicode alphabets. The phone number used unusual full-width characters too.

To a person, the text looked normal. To a basic keyword filter or pattern matcher, it could look quite different.

It was a small detail, but a revealing one: this was not random dashboard mischief. The workflow had been built deliberately to make automated detection harder.

API Keys Are Also Reputation Keys

Developers usually model a leaked payment key as a direct financial threat:

  • Can someone create charges?
  • Issue refunds?
  • Initiate payouts or transfers?
  • Reach customer data?

Those questions still matter. This incident exposed a second category of risk.

Your SaaS accounts are reputation and infrastructure assets.

A compromised payment account can give an attacker a trusted merchant identity, legitimate transactional email delivery, hosted pages, generated documents, and years of domain reputation they did not have to earn.

The attacker did not need my money. They needed a respected platform to vouch for their message long enough to get someone on the phone.

What I Did Next

The first move was easy: rotate the compromised secret key immediately. Once a key has escaped, it does not get a redemption arc.

Then I:

  1. Reviewed API activity beyond invoice creation, especially payment intents, payouts, transfers, refunds, and external accounts.
  2. Checked payout destinations and account settings for unauthorized changes.
  3. Preserved evidence, including request IDs, timestamps, IP addresses, invoice and customer IDs, user agents, and the callback number.
  4. Contacted Stripe so its team could investigate the abuse from its side.
  5. Voided the fraudulent invoices after preserving the evidence.

A Practical Developer Checklist

Treat a Stripe secret key with the same seriousness as a database password:

  • Never put it in frontend JavaScript.
  • Never commit it to Git.
  • Never bundle it into a mobile or desktop application.
  • Be careful when logging configuration and environment variables.
  • Audit CI/CD systems, preview deployments, and hosting environments.
  • Use restricted keys when the integration does not need full account access.
  • Rotate keys that may have been exposed, even if exploitation is not yet confirmed.
  • Monitor unusual API activity—even when no money appears to be moving.

The Bigger Lesson

The attacker did not compromise my Stripe account because they wanted Stripe to send them $699.99.

They compromised it because they wanted Stripe to tell somebody else that they owed $699.99.

That distinction explains the entire operation.

My Stripe account was not the bank they were trying to rob.

It was the phishing machine they wanted to borrow.