feat(payment): expose secure APIs and themed pages

This commit is contained in:
Clintchiz committed 2026-08-28 09:59:07 +05:30
1 parent dd8d6ecc3b
commit 2d77f5457c
15 files changed
+654 -13

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
import { expect, test } from "bun:test";
import { configurePaymentFromConfig } from "../src/config.ts";
import { configurePaymentApi } from "../src/http.ts";
import { GET, POST } from "../src/runtime-api.ts";
import { memoryPaymentStore } from "../src/store.ts";
function request(path: string, body?: unknown) {
return new Request(`https://app.test${path}`, {
method: body ? "POST" : "GET",
headers: body ? { "content-type": "application/json" } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
}
test("package APIs use server subjects and never expose gateway secrets", async () => {
configurePaymentFromConfig(
{
default: "testGateway",
environment: "test",
gateways: { testGateway: { provider: "sandbox", secret: "server-only" } },
},
memoryPaymentStore(),
);
configurePaymentApi({ subject: (ctx) => ctx.user?.id });
const providers = await GET({ req: request("/api/payments/providers") });
const providerBody = await providers.text();
expect(providers.status).toBe(200);
expect(providerBody).toContain("testGateway");
expect(providerBody).not.toContain("server-only");
const denied = await POST({
req: request("/api/payments/intents", {
subject: "attacker-selected",
amount: 500,
currency: "USD",
idempotencyKey: "anonymous",
}),
});
expect(denied.status).toBe(403);
const created = await POST({
req: request("/api/payments/intents", {
subject: "attacker-selected",
amount: 500,
currency: "USD",
idempotencyKey: "owned",
}),
user: { id: "user-1" },
});
expect(created.status).toBe(201);
const payment = await created.json();
expect(payment.id).toStartWith("pay_");
expect(JSON.stringify(payment)).not.toContain("server-only");
const listed = await GET({
req: request("/api/payments/intents"),
user: { id: "user-1" },
});
expect((await listed.json()).payments).toHaveLength(1);
});
test("administrative payment APIs deny by default", async () => {
configurePaymentApi({ subject: () => "user-1" });
const response = await POST({
req: request("/api/payments/reconcile", {}),
user: { id: "user-1" },
});
expect(response.status).toBe(403);
});