feat(payment): expose secure APIs and themed pages
This commit is contained in:
1 parent
dd8d6ecc3b
commit
2d77f5457c
15 files changed
+654
-13
No files matched your search
@@ -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);
|
||||
});
|
||||
Reference in new issue
Block a user