Fixing the SDK's CreatePaymentMethodParams.card type mismatch
Fixing the SDK's CreatePaymentMethodParams.card type mismatch
Release: v1.0.69
What happened
A type definition in the Calmony Pay TypeScript SDK caused every call to paymentMethods.create() made via the typed SDK to fail with a 400 invalid_request_error at runtime.
The card field on CreatePaymentMethodParams was defined as:
card: {
/** Cardstream hosted payment page token */
token: string;
}
But POST /v1/payment_methods validates the request body expecting four raw card fields:
card: {
number: string; // PAN
exp_month: number; // 1–12
exp_year: number; // e.g. 2027
cvc: string; // 3–4 digits
}
Because TypeScript types are erased at runtime, any developer who typed their payload as { card: { token: '...' } } and passed it through the SDK would compile without errors but hit a 400 immediately on the first real API call.
The fix
The CreatePaymentMethodParams interface in src/lib/calmony-pay/client.ts has been updated to exactly mirror the REST API validation schema:
export interface CreatePaymentMethodParams {
type: "card";
/**
* Raw card details. The API tokenises the card via Cardstream and stores
* only the last-4 digits and the resulting cross-reference token —
* raw PAN is never persisted.
*/
card: {
/** Full card number (PAN), e.g. "4111111111111111" */
number: string;
/** Two-digit expiry month, e.g. 12 */
exp_month: number;
/** Four-digit expiry year, e.g. 2027 */
exp_year: number;
/** Card security code (3–4 digits) */
cvc: string;
};
billing_details?: {
name?: string;
// ...
};
}
The REST API route itself was already correct — only the SDK type needed updating.
Migration
If you were using the SDK and supplying { card: { token: '...' } }, update your code to pass the four raw card fields:
const pm = await pay.paymentMethods.create({
type: "card",
card: {
number: "4111111111111111",
exp_month: 12,
exp_year: 2027,
cvc: "123",
},
});
No changes are required if you were calling POST /v1/payment_methods directly via HTTP — the API contract is unchanged.
Security note
Raw card details supplied to paymentMethods.create() are tokenised immediately via Cardstream. Only the last four digits of the PAN and the resulting Cardstream cross-reference token are persisted — the raw PAN, expiry, and CVC are never stored.
Also in this release: PII encryption test hardening
The tamper-detection test for PII encryption was updated to more reliably exercise the GCM authentication tag check. The previous test flipped the last character of the full encrypted string, which included the enc: prefix and could corrupt the prefix rather than the ciphertext. The updated test:
- Strips the
enc:prefix. - Corrupts a byte at the midpoint of the base64url payload (the GCM auth tag region).
- Re-attaches the prefix before asserting that
decryptPii()throws.
This is an internal test-quality improvement and has no effect on the behaviour of the encryption utility itself.