All recipes

Get a text when a new subscription pays.

Add a short SMS alert after your Stripe webhook confirms and records a new subscription's first payment. Keep payment handling independent of notification delivery.

1. Start with your existing verified webhook

Use an existing Next.js App Router project with Next.js 15.1 or newer and a Stripe snapshot webhook handler. Your handler must already verify the signature using the raw request body, durably deduplicate events, and save the payment before calling this helper. This recipe does not replace that billing logic.

Use invoice.payment_succeeded for the payment notification. A completed checkout session can still have an unpaid or processing payment, so do not trigger this alert from the browser's success page.

Copy or create a SelfPing key on the dashboard and set SELFPING_API_KEY in your server environment. Texts go only to your verified phone. Free accounts get 10 texts a month; Hobby is $5/month for 100. Each send costs one credit.

2. Add the notification helper

Save this as lib/notify-first-payment.ts. It filters for a positive payment on a newly created subscription. Renewals and zero-amount invoices do not trigger it. A customer who begins with a trial may pay on a later subscription cycle; this deliberately narrow recipe does not count that later payment.

The alert runs after the webhook response. It is best effort and has no automatic retries. Notification failures are logged without changing your recorded payment. Use a durable notification outbox if you need guaranteed retry behavior.

lib/notify-first-payment.ts
import { after } from "next/server";

export function notifyFirstPayment(invoice: {
  billing_reason: string | null;
  amount_paid: number;
}) {
  if (invoice.billing_reason !== "subscription_create" || invoice.amount_paid <= 0) return;

  after(async () => {
    try {
      const response = await fetch("https://www.selfping.com/api/sms", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.SELFPING_API_KEY}`,
        },
        body: JSON.stringify({
          message: "A new subscription received its first payment. Check your billing dashboard.",
        }),
        signal: AbortSignal.timeout(10000),
      });
      if (!response.ok) console.error("Payment alert not accepted:", response.status);
    } catch {
      console.error("Payment alert unconfirmed. Check the SelfPing dashboard before retrying.");
    }
  });
}

3. Call it after saving a new payment

Add this inside your existing verified handler after its persistent duplicate-event check and successful payment write. Do not create a second webhook endpoint for the same billing updates. The live-mode check prevents test events from sending real texts.

Inside your existing webhook handler
import { notifyFirstPayment } from "@/lib/notify-first-payment";

if (event.livemode && event.type === "invoice.payment_succeeded") {
  notifyFirstPayment(event.data.object);
}

4. Verify without charging a customer

Mock fetch in your local tests. Check that a positive subscription_create invoice schedules one alert, subscription_cycle and zero-amount invoices schedule none, and a rejected or timed-out SMS does not undo the payment. Keep the live-mode check in production.

For a real payment already processed by your app, check your billing record first and SelfPing's final delivery status second. Avoid replaying a payment event just to test the text. Duplicate Stripe events must be stopped by your existing durable event handling before the helper runs.