EYMINT

Software licensing API for keys, activations, floating seats, and signed offline files.

support@keymint.dev

Product

  • Features
  • Pricing
  • Compare platforms
  • Customer login
  • Offline license tester

Company

  • About
  • Blog
  • Contact

Developers

  • Documentation
  • Changelog
  • GitHub
  • Status

Trust

  • Security
  • GDPR
  • Privacy
  • Terms
  • SLA

© 2026 Keymint. All rights reserved.

Skip to content
EYMINT
FeaturesPricingCompareChangelogDocs
Back to all posts

Revoking Software Licenses on Stripe Cancellation

Clifford•2 min read
stripewebhookslicense managementsubscriptionkeymint

Cancelling a customer's subscription should immediately revoke their software access. Here's how to wire Stripe's customer.subscription.deleted event to Keymint's POST /key/block endpoint so it happens automatically (full Stripe setup in our Stripe subscription licensing guide).

How It Works

  1. Customer cancels their Stripe subscription
  2. Stripe sends customer.subscription.deleted to your webhook handler
  3. Your handler looks up the license key linked to that Stripe customer
  4. Your handler calls Keymint's POST /key/block with the license key
  5. The next time the customer's app checks in via POST /key/activate, Keymint returns a 401

Step 1: Store the Stripe → License Mapping

When you create a license after a Stripe payment, save the relationship. A single SQL table is all you need: stripe_customer_id, stripe_subscription_id, license_key.

typescript
await db.stripeLicenseMap.create({
  data: {
    stripeCustomerId: session.customer,
    stripeSubscriptionId: session.subscription,
    licenseKey: newlyCreatedLicense.key,
  },
});

Step 2: Listen for Cancellation

Keymint's own Stripe integration handles customer.subscription.deleted by deactivating the org's billing plan. But it does not automatically block your customer's license keys — that's your business logic.

typescript
// app/api/webhooks/stripe-revoke/route.ts
const admin = new KeyMint(process.env.KEYMINT_ADMIN_API_KEY);

export async function POST(req: NextRequest) {
  const event = stripe.webhooks.constructEvent(
    await req.text(),
    req.headers.get('stripe-signature')!,
    process.env.STRIPE_WEBHOOK_SECRET!,
  );

  if (event.type === 'customer.subscription.deleted') {
    const subscription = event.data.object;
    const mappings = await db.stripeLicenseMap.findMany({
      where: { stripeCustomerId: subscription.customer },
    });
    for (const { licenseKey } of mappings) {
      await admin.blockKey({
        productId: process.env.KEYMINT_PRODUCT_ID!,
        licenseKey,
      });
    }
  }

  return Response.json({ received: true });
}

Step 3: Handle Reactivation

If the customer resubscribes, unblock the license when invoice.paid fires — same pattern, but call admin.unblockKey() instead.

Step 4: Test

bash
stripe listen --forward-to localhost:3000/api/webhooks/stripe-revoke
stripe trigger customer.subscription.deleted

Then verify:

bash
curl -X POST https://api.keymint.dev/key/activate \
  -H "Authorization: Bearer YOUR_CLIENT_API_KEY" \
  -d '{"productId":"YOUR_PRODUCT_ID","licenseKey":"KM-XXXX-XXXX-XXXX","hostId":"test"}'
# → { "message": "Invalid license key or insufficient permissions" }

Production Notes

  • Stripe delivers webhooks out of order — handle each event idempotently
  • Add a 3-7 day grace period before blocking to survive Stripe's dunning retries
  • Subscribe to Keymint's license.disabled outbound webhook as secondary confirmation
  • Log every block/unblock in your own database for customer support

Full API reference: keymint.dev/docs.

Related Posts

  • Create Customer Records Automatically on Purchase
  • Discord Notifications for License Activations
  • Protecting Electron Apps from Piracy with Keymint
  • Send Slack Notifications on License Revocations
  • Track Software License Activations in PostHog

Try Keymint

Simple software licensing API built for indie developers.

Get started free