{"record":{"id":"be9fbf68a268d5a7","repo":"TryGhost/Ghost","slug":"invalid-donation-amount-value","errorCode":null,"errorMessage":"Invalid donation amount: ${value}","messagePattern":"Invalid donation amount: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"e2e/helpers/pages/stripe/fake-checkout-page.ts","lineNumber":59,"sourceCode":"    async fillEmail(email: string): Promise<void> {\n        await this.emailInput.fill(email);\n    }\n\n    async getEmail(): Promise<string> {\n        return await this.emailInput.inputValue();\n    }\n\n    async getAmountInCents(): Promise<number> {\n        if (!await this.customAmountInput.isVisible()) {\n            await this.changeAmountButton.click();\n        }\n\n        const value = await this.customAmountInput.inputValue();\n        const normalizedValue = value.replace(/[^0-9.]/g, '');\n        const parsed = Number.parseFloat(normalizedValue);\n\n        if (!Number.isFinite(parsed)) {\n            throw new Error(`Invalid donation amount: ${value}`);\n        }\n\n        return Math.round(parsed * 100);\n    }\n\n    async submitPayment(): Promise<void> {\n        await this.submitButton.click();\n    }\n}\n","sourceCodeStart":41,"sourceCodeEnd":69,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/e2e/helpers/pages/stripe/fake-checkout-page.ts#L41-L69","documentation":"FakeCheckoutPage.getAmountInCents() reads the custom amount input, strips non-numeric characters with /[^0-9.]/g, and parses with Number.parseFloat. If the result is not finite (NaN, Infinity) it throws, embedding the raw input value. This guards against an empty or purely-symbolic amount field — the Stripe fake checkout couldn't surface a usable number.","triggerScenarios":"The customAmountInput is empty when read. The input contains only currency symbols/spaces that all get stripped, leaving ''. Value is 'Infinity' or non-numeric placeholder text. The changeAmountButton click didn't reveal the input as expected.","commonSituations":"Test navigated to checkout before an amount was set; Stripe fake page DOM changed so the selector reads the wrong element; amount formatted in a locale that the regex strips entirely.","solutions":["Ensure a donation amount is selected/typed before calling getAmountInCents().","Log the raw value to see what was read; update the selector if it grabs the wrong input.","Broaden the normalization (handle comma decimal separators) before parsing.","Assert the input is visible and populated before reading."],"exampleFix":"// before\nconst value = await this.customAmountInput.inputValue();\nconst parsed = Number.parseFloat(value.replace(/[^0-9.]/g, ''));\nif (!Number.isFinite(parsed)) throw new Error(`Invalid donation amount: ${value}`);\n\n// after\nconst value = (await this.customAmountInput.inputValue()).trim();\nconst normalized = value.replace(/[^0-9.,]/g, '').replace(',', '.');\nconst parsed = Number.parseFloat(normalized);\nif (!Number.isFinite(parsed) || parsed <= 0) {\n    throw new Error(`Invalid donation amount: ${JSON.stringify(value)}`);\n}","handlingStrategy":"validation","validationCode":"const value = (await this.customAmountInput.inputValue()).trim();\nif (!value) throw new Error('customAmountInput is empty — set an amount before reading');","typeGuard":"function isFinitePositiveAmount(raw: string): boolean {\n    const normalized = raw.replace(/[^0-9.,]/g, '').replace(',', '.');\n    const n = Number.parseFloat(normalized);\n    return Number.isFinite(n) && n > 0;\n}","tryCatchPattern":"const value = await this.customAmountInput.inputValue();\nconst normalized = value.replace(/[^0-9.,]/g, '').replace(',', '.');\nconst parsed = Number.parseFloat(normalized);\nif (!Number.isFinite(parsed) || parsed <= 0) {\n    throw new Error(`Invalid donation amount: ${JSON.stringify(value)}`);\n}\nreturn Math.round(parsed * 100);","preventionTips":["Set/select an amount before reading the field.","Handle locale-specific decimal separators (comma) in normalization.","Assert the input is visible and populated before parsing."],"tags":["stripe","e2e","parsing","validation","checkout"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}