From 53374cb6da65aa308dcfc3ab7a852f30cee4acf5 Mon Sep 17 00:00:00 2001 From: haocyan0723-code Date: Wed, 20 May 2026 12:47:02 +0800 Subject: [PATCH 1/6] Add fake payment schema --- lib/db/schema.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/db/schema.ts b/lib/db/schema.ts index 8377516..8ea7460 100644 --- a/lib/db/schema.ts +++ b/lib/db/schema.ts @@ -9,8 +9,19 @@ export const thingSchema = z.object({ }) export type Thing = z.infer +export const paymentSchema = z.object({ + payment_id: z.string(), + recipient: z.string(), + amount: z.number(), + currency: z.string(), + bounty_issue: z.number().optional(), + status: z.enum(["pending", "sent"]), +}) +export type Payment = z.infer + export const databaseSchema = z.object({ idCounter: z.number().default(0), things: z.array(thingSchema).default([]), + payments: z.array(paymentSchema).default([]), }) export type DatabaseSchema = z.infer From a8a1d4f3b45a6abe64f053b2806cbed92f7d8fa3 Mon Sep 17 00:00:00 2001 From: haocyan0723-code Date: Wed, 20 May 2026 12:47:05 +0800 Subject: [PATCH 2/6] Store fake sent payments --- lib/db/db-client.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/db/db-client.ts b/lib/db/db-client.ts index e525e65..bc83304 100644 --- a/lib/db/db-client.ts +++ b/lib/db/db-client.ts @@ -2,7 +2,12 @@ import { createStore, type StoreApi } from "zustand/vanilla" import { immer } from "zustand/middleware/immer" import { hoist, type HoistedStoreApi } from "zustand-hoist" -import { databaseSchema, type DatabaseSchema, type Thing } from "./schema.ts" +import { + databaseSchema, + type DatabaseSchema, + type Payment, + type Thing, +} from "./schema.ts" import { combine } from "zustand/middleware" export const createDatabase = () => { @@ -21,4 +26,18 @@ const initializer = combine(databaseSchema.parse({}), (set) => ({ idCounter: state.idCounter + 1, })) }, + sendPayment: (payment: Omit) => { + let payment_id = "" + set((state) => { + payment_id = `payment_${state.idCounter}` + return { + payments: [ + ...state.payments, + { ...payment, payment_id, status: "sent" as const }, + ], + idCounter: state.idCounter + 1, + } + }) + return payment_id + }, })) From cb780fbc5dacd1d5b55a9461ff8f2d3a359ac7fa Mon Sep 17 00:00:00 2001 From: haocyan0723-code Date: Wed, 20 May 2026 12:47:07 +0800 Subject: [PATCH 3/6] Add fake payment send route --- routes/payments/send.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 routes/payments/send.ts diff --git a/routes/payments/send.ts b/routes/payments/send.ts new file mode 100644 index 0000000..33c8c40 --- /dev/null +++ b/routes/payments/send.ts @@ -0,0 +1,27 @@ +import { withRouteSpec } from "lib/middleware/with-winter-spec" +import { z } from "zod" + +const paymentResponse = z.object({ + payment_id: z.string(), + recipient: z.string(), + amount: z.number(), + currency: z.string(), + bounty_issue: z.number().optional(), + status: z.enum(["pending", "sent"]), +}) + +export default withRouteSpec({ + methods: ["POST"], + jsonBody: z.object({ + recipient: z.string(), + amount: z.number().positive(), + currency: z.string().default("USD"), + bounty_issue: z.number().optional(), + }), + jsonResponse: paymentResponse, +})(async (req, ctx) => { + const body = await req.json() + const payment_id = ctx.db.sendPayment(body) + const payment = ctx.db.payments.find((p) => p.payment_id === payment_id)! + return ctx.json(payment) +}) From da4c3d086bf99d41991f83a78a5612c60132c312 Mon Sep 17 00:00:00 2001 From: haocyan0723-code Date: Wed, 20 May 2026 12:47:08 +0800 Subject: [PATCH 4/6] Add fake payment list route --- routes/payments/list.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 routes/payments/list.ts diff --git a/routes/payments/list.ts b/routes/payments/list.ts new file mode 100644 index 0000000..5610249 --- /dev/null +++ b/routes/payments/list.ts @@ -0,0 +1,20 @@ +import { withRouteSpec } from "lib/middleware/with-winter-spec" +import { z } from "zod" + +export default withRouteSpec({ + methods: ["GET"], + jsonResponse: z.object({ + payments: z.array( + z.object({ + payment_id: z.string(), + recipient: z.string(), + amount: z.number(), + currency: z.string(), + bounty_issue: z.number().optional(), + status: z.enum(["pending", "sent"]), + }), + ), + }), +})((req, ctx) => { + return ctx.json({ payments: ctx.db.payments }) +}) From 4e151d48f3f8ad328e21ec4139718c70d9b719d7 Mon Sep 17 00:00:00 2001 From: haocyan0723-code Date: Wed, 20 May 2026 12:47:10 +0800 Subject: [PATCH 5/6] Cover fake payment send route --- tests/routes/payments/send.test.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/routes/payments/send.test.ts diff --git a/tests/routes/payments/send.test.ts b/tests/routes/payments/send.test.ts new file mode 100644 index 0000000..5639ab0 --- /dev/null +++ b/tests/routes/payments/send.test.ts @@ -0,0 +1,26 @@ +import { getTestServer } from "tests/fixtures/get-test-server" +import { test, expect } from "bun:test" + +test("send and list payments", async () => { + const { axios } = await getTestServer() + + const { data: payment } = await axios.post("/payments/send", { + recipient: "maintainer@example.com", + amount: 10, + currency: "USD", + bounty_issue: 1, + }) + + expect(payment).toMatchObject({ + payment_id: "payment_0", + recipient: "maintainer@example.com", + amount: 10, + currency: "USD", + bounty_issue: 1, + status: "sent", + }) + + const { data } = await axios.get("/payments/list") + expect(data.payments).toHaveLength(1) + expect(data.payments[0]).toEqual(payment) +}) From cb9bc8187e320b1b0b5a881a127a735fc3f39f9b Mon Sep 17 00:00:00 2001 From: haocyan0723-code Date: Wed, 20 May 2026 12:47:14 +0800 Subject: [PATCH 6/6] Document fake payment endpoints --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 824427a..54da360 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,8 @@ This is a template project with best-practice modules: - Winterspec for defining the API - bun testing - Zustand store with zod definition for database state + +## Payments + +- `POST /payments/send` creates a fake payment with recipient, amount, currency, and optional bounty issue. +- `GET /payments/list` returns the fake payments stored in memory.