# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: src/manager/tests/e2e/invoice/invoice-charge-customer.spec.ts >> rejects charging an invoice with a declined card
- Location: src/manager/tests/e2e/invoice/invoice-charge-customer.spec.ts:85:5

# Error details

```
Error: Failed to create invoice via API: 422 {"error":{"code":"internal.stripe","message":"No such price: 'e72c0b22-1a36-4d9f-9fad-4aece3ce6a27'","details":[]}}
```

# Test source

```ts
  1   | import { APIRequestContext } from '@playwright/test';
  2   | import {
  3   |   fromInvoiceCreateApiResponse,
  4   |   toInvoiceCreateApiPayload,
  5   |   toInvoiceIssueCreditNoteApiPayload,
  6   | } from '@/manager/modules/invoice/invoice-mappers';
  7   | import {
  8   |   InvoiceCreateApiResponse,
  9   |   InvoiceCreateData,
  10  |   InvoiceFetchApiResponse,
  11  |   InvoiceIssueCreditNoteData,
  12  | } from '@/manager/modules/invoice/invoice-types';
  13  | import { Invoice } from '@/manager/modules/invoice/invoice-types';
  14  | import { fetchLocationDefaultPaymentMethodIdsViaApi } from '@/manager/modules/location/location-api';
  15  | import { isModernBillingMode } from '@/shared/utils/billing-utils';
  16  | import dayjs from 'dayjs';
  17  | 
  18  | // The manager API serves invoices at /v1/invoice; the billing API (modern Kinnovis billing) serves mark-as-paid
  19  | // at /api/v1/invoice, and the manager API rejects mark-as-paid for Kinnovis-billing tenants. Only mark-as-paid
  20  | // goes through this client: create, fetch and credit notes stay on the manager API in both modes.
  21  | export interface InvoiceApiClient {
  22  |   request: APIRequestContext;
  23  |   basePath: string;
  24  | }
  25  | 
  26  | const managerInvoiceBasePath = '/v1/invoice';
  27  | const billingInvoiceBasePath = '/api/v1/invoice';
  28  | 
  29  | export function resolveInvoiceApiClient(
  30  |   managerApiRequest: APIRequestContext,
  31  |   billingApiRequest: APIRequestContext
  32  | ): InvoiceApiClient {
  33  |   return isModernBillingMode()
  34  |     ? { request: billingApiRequest, basePath: billingInvoiceBasePath }
  35  |     : { request: managerApiRequest, basePath: managerInvoiceBasePath };
  36  | }
  37  | 
  38  | export async function createInvoiceViaApi(request: APIRequestContext, data: InvoiceCreateData): Promise<Invoice> {
  39  |   const defaultPaymentMethodIds = await fetchLocationDefaultPaymentMethodIdsViaApi(request, data.location.id);
  40  | 
  41  |   const response = await request.post('/v1/invoice', {
  42  |     data: toInvoiceCreateApiPayload(data, defaultPaymentMethodIds),
  43  |   });
  44  | 
  45  |   if (!response.ok()) {
> 46  |     throw new Error(`Failed to create invoice via API: ${response.status()} ${await response.text()}`);
      |           ^ Error: Failed to create invoice via API: 422 {"error":{"code":"internal.stripe","message":"No such price: 'e72c0b22-1a36-4d9f-9fad-4aece3ce6a27'","details":[]}}
  47  |   }
  48  | 
  49  |   const body = (await response.json()) as InvoiceCreateApiResponse;
  50  | 
  51  |   return fromInvoiceCreateApiResponse(body, data);
  52  | }
  53  | 
  54  | async function fetchInvoiceViaApi(request: APIRequestContext, id: number): Promise<InvoiceFetchApiResponse> {
  55  |   const response = await request.get(`/v1/invoice/${id}`);
  56  | 
  57  |   if (!response.ok()) {
  58  |     throw new Error(`Failed to fetch invoice ${id} via API: ${response.status()} ${await response.text()}`);
  59  |   }
  60  | 
  61  |   return (await response.json()) as InvoiceFetchApiResponse;
  62  | }
  63  | 
  64  | // Whether a credit note can be issued for the invoice right now — false while the invoice's charge is still
  65  | // settling (pending payment) or the API forbids crediting it for any other reason.
  66  | export async function fetchInvoiceCanBeCreditedViaApi(request: APIRequestContext, id: number): Promise<boolean> {
  67  |   const body = await fetchInvoiceViaApi(request, id);
  68  | 
  69  |   return !body.data.has_pending_payment && body.data.actions.can_issue_credit_note;
  70  | }
  71  | 
  72  | // The credit endpoint identifies what to credit by invoice line-item ids, which only exist server-side — so
  73  | // they are fetched here rather than carried on the Invoice domain type. The response is the updated invoice,
  74  | // not the credit note — the credit note itself has no read endpoint, and its number is derivable via
  75  | // toInvoiceCreditNoteNumber, so nothing from the response is needed.
  76  | export async function issueInvoiceCreditNoteViaApi(
  77  |   request: APIRequestContext,
  78  |   id: number,
  79  |   data: InvoiceIssueCreditNoteData
  80  | ): Promise<void> {
  81  |   const body = await fetchInvoiceViaApi(request, id);
  82  | 
  83  |   const response = await request.post('/v1/credit-note', {
  84  |     data: toInvoiceIssueCreditNoteApiPayload(data, id, body.data.line_items),
  85  |   });
  86  | 
  87  |   if (!response.ok()) {
  88  |     throw new Error(
  89  |       `Failed to issue credit note for invoice ${id} via API: ${response.status()} ${await response.text()}`
  90  |     );
  91  |   }
  92  | }
  93  | 
  94  | // Marks an open invoice paid with an out-of-band cash payment — purely internal, no Stripe charge or email.
  95  | // Used to stand up the 'paid' invoices the credit-note specs need without a real card charge.
  96  | export async function markInvoiceAsPaidViaApi(client: InvoiceApiClient, id: number): Promise<void> {
  97  |   const response = await client.request.put(`${client.basePath}/${id}/status-paid`, {
  98  |     data: { payment_date: dayjs().format('YYYY-MM-DD'), payment_type: 'cash' },
  99  |   });
  100 | 
  101 |   if (!response.ok()) {
  102 |     throw new Error(`Failed to mark invoice ${id} as paid via API: ${response.status()} ${await response.text()}`);
  103 |   }
  104 | }
  105 | 
```