Skip to content
Sad Subscription Analytics
Menu

How detection works

Not every subscription is a Stripe subscription object. When it is not, SadAnalytics looks at the charges themselves and works out whether they are really a recurring plan.

What SadAnalytics looks at

SadAnalytics' pattern detector only considers a provider's successful charges made by a real, identified customer. A charge already linked to a native Stripe subscription is left out entirely, so a confirmed subscription is never re-examined.

A refunded charge never starts a chain and is never counted toward the minimum below, but a refund landing inside an already-established chain does not break it: one refunded renewal does not turn a monthly plan into a bimonthly one.

The minimum: three charges

A customer is only examined once they have at least three unrefunded successful charges in the same currency (MIN_OCCURRENCES = 3 in the plugin's code). A chain has to reach that same minimum of three before SadAnalytics will write it up as a detected subscription.

In practice this means a new recurring buyer is only detected after their third payment, and a subscription billed annually needs a couple of years of history before detection can find it at all.

Charges are grouped by customer and currency, and a chain accepts a later charge whose amount is within 1% of the first one (compared in whole cents), so a small currency-conversion rounding difference does not split an otherwise identical plan in two.

How the interval is worked out

SadAnalytics tries a few candidate cycle lengths, one for each of the next few same-amount charges after the first, and for each candidate walks forward expecting a charge every N days. A charge is still accepted if it arrives a little early (calendar month-length variance) or noticeably late (room for a failed card and a retry), and the walk stops at the first gap wider than that. Whichever candidate produces the most regular, longest chain wins, and its interval is stored as the whole-day median gap between the charges.

  • 28 to 31 days is labeled Monthly.
  • 88 to 92 days is labeled Quarterly.
  • 178 to 184 days is labeled Half-yearly.
  • 363 to 367 days is labeled Annual.
  • Anything else shows as, for example, "every 45 days".

The Detected badge

Open a customer's drawer and every active subscription is listed with its plan name, a status pill, and its MRR contribution. A subscription SadAnalytics found this way carries a Detected badge next to its status, and underneath it a detection-context line spells out the per-cycle amount, the interval label and the start date.

Whenever a customer has at least one detected subscription, the drawer adds a note that those rows came from a recurring payment pattern, not a native subscription, and points you to the two review buttons: Confirm as subscription (covered below) and Mark as one-time (it has its own page in this section).

The customer drawer for a customer with a detected subscription: their name, email and linked WordPress user, lifetime value and time as a member, then the subscription with its Detected badge, the amount and interval SadAnalytics found, its start date and buttons to confirm it or mark it as one-time, and below it the monthly charges it was detected from.
A detected subscription in the customer drawer: the Detected badge, the interval and start date, and the two review buttons.

When a real subscription takes over

There is no automatic hand-off from a detected subscription to a real one. If the same customer later gets a real Stripe subscription, its payments are excluded from detection from that point on, so the detected row simply stops receiving new charges and lapses to cancelled once its usual interval and grace period pass, with no effect on billing.

The only way a detected row itself becomes a native subscription is clicking Confirm as subscription in the drawer. That flips its source and makes it immune to being re-detected on every later sync.