Fixing the Silent confirm: true Bug in Payment Intent Creation
Fixing the Silent confirm: true Bug in Payment Intent Creation
Release: v1.0.100
Background
Calmony Pay's SDK is designed to be familiar to developers who have used Stripe. As part of that design, the original CreatePaymentIntentParams TypeScript interface included two Stripe-compatible fields:
confirm?: boolean
return_url?: string
In Stripe's API, passing confirm: true when creating a payment intent triggers automatic confirmation in a single request — the intent is created and immediately charged without a separate confirm call.
The Problem
While these fields were present in the SDK's TypeScript interface, the Calmony Pay server (POST /v1/payment_intents) did not implement the corresponding logic. The createPaymentIntentSchema on the server silently discarded both fields.
The result: a caller passing confirm: true would receive a 200 OK response and a valid payment intent object — but the intent would be in requires_confirmation status, not confirmed. No error was raised. No warning was returned. The flag was simply ignored.
This is a particularly dangerous class of bug. The SDK's type signature advertised a capability the server did not provide, and the failure mode was invisible at call time.
Example of affected code
// Before v1.0.100 — this looked valid but confirm was silently ignored
const intent = await calmonyPay.createPaymentIntent({
amount: 4999,
currency: 'gbp',
confirm: true, // ⚠️ silently stripped by server
return_url: 'https://example.com/complete', // ⚠️ silently stripped
});
// intent.status === 'requires_confirmation'
// — NOT confirmed, despite confirm: true being passed
The Fix
Rather than implement partial Stripe-compatibility that could mask further edge cases, the confirm and return_url fields have been removed from CreatePaymentIntentParams. The SDK interface now precisely matches what the server accepts and processes.
This is a deliberate contract-first correction: the SDK's type surface is the source of truth for what the API supports. Removing these fields makes the mismatch a compile-time error for any integration relying on them, rather than a silent runtime failure.
// After v1.0.100 — CreatePaymentIntentParams no longer includes confirm or return_url
const intent = await calmonyPay.createPaymentIntent({
amount: 4999,
currency: 'gbp',
// confirm and return_url are not valid fields — TypeScript will surface this
});
// To confirm, use the dedicated confirm endpoint:
await calmonyPay.confirmPaymentIntent(intent.id);
Migration Guide
If your integration passed confirm: true or return_url during intent creation, update your code as follows:
Before
const intent = await calmonyPay.createPaymentIntent({
amount: 4999,
currency: 'gbp',
confirm: true,
return_url: 'https://example.com/complete',
});
After
// Step 1: Create the intent
const intent = await calmonyPay.createPaymentIntent({
amount: 4999,
currency: 'gbp',
});
// Step 2: Confirm it explicitly
const confirmed = await calmonyPay.confirmPaymentIntent(intent.id);
The two-step flow gives you the same result — a confirmed, charged intent — with explicit control at each stage.
Affected Files
| File | Change |
|---|---|
src/lib/calmony-pay/client.ts | Removed confirm and return_url from CreatePaymentIntentParams |
src/app/api/v1/payment_intents/route.ts | No functional change; server already did not process these fields |
Why This Matters
Silent parameter stripping is one of the hardest categories of API bug to detect in production. An integration test that checks for a 200 OK would pass; only a test asserting on the returned status field would catch it. By aligning the SDK interface with actual server behaviour, integrations that relied on confirm: true will now fail loudly at the TypeScript compilation stage rather than silently at runtime.