import { OpenAPIHono } from "@hono/zod-openapi"; import { createOpenAPIRegistry } from "@suaveplan/api-openapi"; import { createPluginContext } from "@suaveplan/api-plugin"; import { EventBus } from "@suaveplan/events"; import { beforeEach, describe, expect, it } from "@suaveplan/testing/runner"; import { createPaymentPlugin, resetPaymentInstruments, } from "../../src/index.js"; import type { PaymentPluginConfig } from "../../src/types/types.js"; import { FakePaymentProvider } from "../setup/fake-provider.js"; const noopLogger = { debug: () => undefined, info: () => undefined, warn: () => undefined, error: () => undefined, }; const SECRET = "shared-webhook-secret-32-chars-or-more"; async function boot(setPrincipalId: string | null = "u1") { resetPaymentInstruments(); const app = new OpenAPIHono(); app.use("*", async (c, next) => { c.set( "user", setPrincipalId === null ? null : { id: setPrincipalId, emailVerified: true, roles: [] } ); await next(); }); const provider = new FakePaymentProvider(); const services = new Map(); services.set("app", app); services.set( "openAPIRegistry", createOpenAPIRegistry({ info: { title: "t", version: "0.1" } }) ); const plugin = createPaymentPlugin({ provider, webhookSecret: SECRET }); await plugin.onInit?.( createPluginContext({ logger: noopLogger, eventBus: new EventBus(), signal: new AbortController().signal, config: plugin.defaults as PaymentPluginConfig, services, }) ); return { app, provider }; } beforeEach(() => { resetPaymentInstruments(); }); describe("payment plugin — error paths", () => { it("currency mismatch on refund returns 422", { timeout: 60_000, }, async () => { const { app } = await boot(); const created = await app.request("/payments/intents", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ amount: { amount: "100", currency: "USD" }, }), }); const { intent } = (await created.json()) as { intent: { id: string }; }; await app.request(`/payments/intents/${intent.id}/capture`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}), }); const refundRes = await app.request( `/payments/intents/${intent.id}/refund`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ amount: { amount: "10", currency: "EUR" }, }), } ); expect(refundRes.status).toBe(422); const refundBody = (await refundRes.json()) as { error: { code: string }; }; expect(refundBody.error.code).toBe("PAYMENT_PLUGIN.CURRENCY_MISMATCH"); }); it("foreign intent capture returns 404", { timeout: 60_000 }, async () => { const { app } = await boot("u1"); const created = await app.request("/payments/intents", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ amount: { amount: "100", currency: "USD" }, }), }); const { intent } = (await created.json()) as { intent: { id: string }; }; const otherApp = await boot("u2"); const captureRes = await otherApp.app.request( `/payments/intents/${intent.id}/capture`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}), } ); expect(captureRes.status).toBe(404); }); });