All Docs
FeaturesCalmony PayUpdated March 15, 2026

Silent Data Loss: How a Missing Schema Field Was Swallowing Trial Periods

Silent Data Loss: How a Missing Schema Field Was Swallowing Trial Periods

Release: v1.0.107 · Severity: High · Type: Spec Drift Bug Fix

What Happened

We identified and resolved a silent data-loss bug in the Calmony Pay SDK's subscriptions.create() method. When an SDK consumer passed a trial_end Unix timestamp, that value was never actually persisted — it was stripped silently before reaching the database.

No error was thrown. No warning was logged. The subscription was created successfully, just without the trial period.

The Root Cause

The bug was caused by a mismatch between two layers of the stack:

Layertrial_end defined?
SDK CreateSubscriptionParams✅ Yes (trial_end?: number)
REST API createSubscriptionSchema (Zod)❌ No

The SDK correctly accepted trial_end as part of its parameter interface and forwarded it in the request body to the REST API. However, the Zod schema on the REST API side had no trial_end field. Zod's default behaviour is to strip unrecognised keys rather than reject them, so the value was quietly removed from the parsed body before any further processing.

The result: the subscription was created, the SDK consumer received a success response, but the trial end date was never stored.

Why This Matters

Trial periods are a billing-critical feature. A missed trial_end means:

  • Customers are charged immediately instead of after their trial
  • There is no visible error to alert the developer or the end user
  • The bug is difficult to detect without explicitly inspecting the created subscription record

The Fix

The trial_end field has been added to createSubscriptionSchema in the REST API layer. The SDK interface and the API schema are now in alignment with the pinned Calmony Pay SDK Client Interface specification.

Affected file: src/lib/calmony-pay/client.ts

What You Should Do

If your integration calls subscriptions.create() with a trial_end value:

  1. Upgrade to Calmony Pay SDK v1.0.107 or later.
  2. Audit any subscriptions created before this release that were intended to include a trial period — they will have been created without one.
  3. For affected subscriptions, update the trial_end on the subscription record directly, or cancel and recreate them as appropriate for your use case.

Example: Correct Usage After Fix

const subscription = await calmonyPay.subscriptions.create({
  customer_id: 'cust_abc123',
  plan_id: 'plan_monthly_pro',
  trial_end: Math.floor(Date.now() / 1000) + 14 * 24 * 60 * 60, // 14 days from now
});

// trial_end is now correctly persisted
console.log(subscription.trial_end); // Unix timestamp

Lessons Learned

This bug is a textbook example of schema layer drift — when a client interface and a server schema evolve independently, undocumented strips or rejections at the boundary can cause data loss with no visible feedback. We are reviewing all SDK parameter fields against their corresponding API schemas to ensure this class of issue does not recur.