120 lines
4.0 KiB
TypeScript
120 lines
4.0 KiB
TypeScript
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<string, unknown>();
|
|
services.set("app", app);
|
|
services.set(
|
|
"openAPIRegistry",
|
|
createOpenAPIRegistry({ info: { title: "t", version: "0.1" } })
|
|
);
|
|
const plugin = createPaymentPlugin({ provider, webhookSecret: SECRET });
|
|
await plugin.onInit?.(
|
|
createPluginContext<PaymentPluginConfig>({
|
|
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);
|
|
});
|
|
});
|