RevClamp Enterprise B2B REVENUE ASSURANCE Start Free
ENGINEERING DEEP-DIVE · STRIPE BASIL & VISA CE 3.0
← Back to Engineering Blog • STRIPE 2025-03-31.BASIL • 4 min read

Stripe's basil API broke my grace-period logic, and CE 3.0 turned out to be a routing problem

When we pinned our webhook endpoint to 2025-03-31.basil, entitlement checks started failing quietly. Nothing threw an error. current_period_end on the top-level Subscription object was undefined, and in JavaScript undefined < Date.now() evaluates to false. Every subscription looked like it was still inside its paid period, so unpaid accounts kept burning API credits without ever being revoked.

I'll cover the entitlement trap first, then the Visa 10.4 dispute routing problem that showed up right behind it.

Stripe 2025-03-31.basil Billing Period Normalization and Visa CE 3.0 Evidence Pipeline Flowchart

1. The silent undefined in 2025-03-31.basil

Most older Stripe webhook handlers check whether a past_due subscription is still within a grace window by reading the top-level period timestamp:

// Legacy pattern — silently evaluates to false on 2025-03-31.basil
const isExpired = sub.current_period_end * 1000 + GRACE_MS < Date.now();

In 2025-03-31.basil, billing period fields moved off the subscription root and onto items.data[]. That sounds like a one-line change until you hit the edge cases in real B2B accounts:

2. Why Visa 10.4 (CE 3.0) is a two-tier routing problem

When a card-absent fraud dispute (fraudulent / Visa 10.4) lands, most teams either paste generic logs manually or pay a 25%–30% contingency fee to a dispute agency.

Under Visa Compelling Evidence 3.0 (CE 3.0), qualifying requires at least two prior succeeded, undisputed, unrefunded transactions settled 120 to 365 days before the disputed charge that share at least two consistent core identifiers (user_id, email, ip_subnet, device_id, shipping_address) across both prior charges and the disputed charge, including at least one network or device identifier (ip_subnet or device_id).

That means routing between Tier 1 and Tier 2 depends on whether qualifying historical transactions exist, not solely on account age:

Scenario Legacy Webhook / DIY Behavior Failure Mode RevClamp Pipeline Action
2025-03-31.basil Multi-Item Drift Reads sub.current_period_end (undefined) Infinite unpaid API/LLM token bleed Aggregates items.data[] periods (has_more aware, fail-closed)
Multi-Subscription Cancellation Revokes access on first subscription.deleted False lockout of active annual plan Reconciles full customer subscription set before revoking
Out-of-Order Dunning Webhook Overwrites state on late invoice.payment_failed Paid renewal reverted to past_due Enforces 72h event deduplication, transition ordering & 0–14d grace
Visa 10.4 (<2 Qualifying Priors) Submits incomplete CE 3.0 claim or generic text Automatic issuer rejection Routes to Tier 1 90-day first-party usage log (submit: false)
Visa 10.4 (≥2 Priors 120d–365d) Overwrites manual support notes on update Wipes finance context & truncates UTF-16 code units Summarizes CE 3.0 priors + usage log non-destructively (<= 18,000 chars)

3. Why dispute automation should stage (submit: false), never auto-submit

In the Stripe Disputes API, submitting evidence (submit: true) is irreversible—you get one submission to the card network. Even worse, a naive stripe.disputes.update() call overwrites uncategorized_text and access_activity_log, wiping out any context your support or finance team already typed into the Stripe Dashboard.

A safe pipeline evaluates CE 3.0 eligibility, summarizes the matched prior transaction IDs (120–365 days old), shared identifiers, and 90-day first-party usage log inside evidence[access_activity_log] with submit: false, and merges non-destructively around existing delimiter blocks within the 18,000 UTF-16 code-unit ceiling. Your team reviews the staged draft in the Stripe Dashboard and clicks Submit when ready.

RevClamp Enterprise — B2B Revenue & Compliance Guardrails
Open Free Stripe basil Entitlement Auditor → Open Free Visa CE 3.0 Readiness Checker → Start Free Workspace (5,000 events/mo)