feat(billing): TD-13 webhook idempotency + Stripe Customer Portal + doc cleanup
- Table stripe_webhook_events + helpers isEventProcessed/markEventProcessed - POST /stripe/customer-portal (auth + stripe_customer_id check) - ARCHITECTURE-backend.md: suppression POST /plans/upgrade (duplication doc) - TD-13 fermé dans TECH_DEBT-backend.md - Tests: 261 → 278 verts (+17)
This commit is contained in:
parent
ec0598d122
commit
6671bac347
10 changed files with 891 additions and 324 deletions
69
src/lib/__tests__/createBillingPortalSession.test.ts
Normal file
69
src/lib/__tests__/createBillingPortalSession.test.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const portalCreateMock = vi.fn();
|
||||
|
||||
vi.mock("stripe", () => ({
|
||||
default: vi.fn(() => ({
|
||||
billingPortal: {
|
||||
sessions: {
|
||||
create: portalCreateMock,
|
||||
},
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
import { createBillingPortalSession } from "../stripe";
|
||||
|
||||
describe("createBillingPortalSession", () => {
|
||||
beforeEach(() => {
|
||||
portalCreateMock.mockReset();
|
||||
});
|
||||
|
||||
it("retourne l'URL de la billing portal session", async () => {
|
||||
portalCreateMock.mockResolvedValue({
|
||||
url: "https://billing.stripe.com/p/session/abc123",
|
||||
});
|
||||
|
||||
const result = await createBillingPortalSession({
|
||||
customerId: "cus_abc",
|
||||
returnUrl: "https://expria.app/dashboard",
|
||||
});
|
||||
|
||||
expect(result.url).toBe("https://billing.stripe.com/p/session/abc123");
|
||||
expect(portalCreateMock).toHaveBeenCalledWith({
|
||||
customer: "cus_abc",
|
||||
return_url: "https://expria.app/dashboard",
|
||||
});
|
||||
});
|
||||
|
||||
it("throw si customerId vide", async () => {
|
||||
await expect(
|
||||
createBillingPortalSession({
|
||||
customerId: "",
|
||||
returnUrl: "https://expria.app/dashboard",
|
||||
}),
|
||||
).rejects.toThrow("customerId requis");
|
||||
expect(portalCreateMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("throw si returnUrl vide", async () => {
|
||||
await expect(
|
||||
createBillingPortalSession({
|
||||
customerId: "cus_abc",
|
||||
returnUrl: "",
|
||||
}),
|
||||
).rejects.toThrow("returnUrl requis");
|
||||
expect(portalCreateMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("throw si Stripe ne retourne pas d'URL", async () => {
|
||||
portalCreateMock.mockResolvedValue({ url: null });
|
||||
|
||||
await expect(
|
||||
createBillingPortalSession({
|
||||
customerId: "cus_abc",
|
||||
returnUrl: "https://expria.app/dashboard",
|
||||
}),
|
||||
).rejects.toThrow("URL de billing portal");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue