Skip to content

Commit 16332f0

Browse files
feat: implement plans
1 parent 3b5d5c6 commit 16332f0

9 files changed

Lines changed: 162 additions & 3 deletions

File tree

.env.sample

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
DB_FILE_NAME=file:local.db
2-
PAYSTACK_SECRET_KEY=sk_domain_xxxx
2+
PAYSTACK_SECRET_KEY=sk_domain_xxxx
3+
PAYSTACK_BASE_URL="https://api.paystack.co"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ lerna-debug.log*
2626

2727
# misc
2828
.DS_Store
29+
30+
# db
31+
local.db

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
},
1010
"dependencies": {
1111
"@hono/node-server": "^1.19.5",
12+
"@hono/zod-validator": "^0.7.3",
1213
"@libsql/client": "^0.15.15",
1314
"dotenv": "^17.2.3",
1415
"drizzle-orm": "^0.44.6",
15-
"hono": "^4.9.9"
16+
"hono": "^4.9.9",
17+
"zod": "^4.1.11"
1618
},
1719
"devDependencies": {
1820
"@types/node": "^20.11.17",

pnpm-lock.yaml

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/db.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import 'dotenv/config';
2+
import { drizzle } from 'drizzle-orm/libsql';
3+
4+
export const db = drizzle(process.env.DB_FILE_NAME!);

src/db/schema/plans.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
2+
3+
export const plansTable = sqliteTable("plans", {
4+
id: int().primaryKey({ autoIncrement: true }),
5+
name: text().notNull(),
6+
amount: int().notNull().default(0),
7+
interval: text().notNull(),
8+
code: text().notNull().unique(),
9+
description: text()
10+
});

src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { serve } from '@hono/node-server'
22
import { Hono } from 'hono'
3+
import plansRouter from './routes/plans.js'
4+
import { showRoutes } from 'hono/dev'
35

46
const app = new Hono()
57

68
app.get('/', (c) => {
7-
return c.text('Hello Hono!')
9+
return c.text('Server running fine')
810
})
911

12+
app.route("/plan", plansRouter)
13+
14+
// To list all routes
15+
showRoutes(app)
16+
1017
serve({
1118
fetch: app.fetch,
1219
port: 3000

src/routes/plans.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import 'dotenv/config';
2+
import { Hono } from "hono";
3+
import { db } from '../db.js';
4+
import { plansTable } from '../db/schema/plans.js';
5+
import { zValidator } from '@hono/zod-validator';
6+
import { PlanCreate, PlanUpdate } from '../schema/index.js';
7+
import { eq } from 'drizzle-orm';
8+
9+
const plansRouter = new Hono()
10+
11+
plansRouter.get("/", async (c) => {
12+
const plans = await db.select().from(plansTable)
13+
14+
return c.json({
15+
status: true,
16+
data: plans,
17+
}, 200)
18+
})
19+
20+
plansRouter.post("/",
21+
zValidator('json', PlanCreate),
22+
async (c) => {
23+
const validateReq = c.req.valid('json')
24+
const api = await fetch(`${process.env.PAYSTACK_BASE_URL}/plan`, {
25+
method: "POST",
26+
headers: {
27+
"Authorization": `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
28+
'Content-Type': "application/json"
29+
},
30+
body: JSON.stringify(validateReq)
31+
})
32+
33+
const data = await api.json()
34+
35+
if (!data["status"]) {
36+
return c.json({
37+
status: false,
38+
data: {}
39+
}, 500)
40+
}
41+
42+
const plan = await db.insert(plansTable)
43+
.values({
44+
name: data["data"]["name"],
45+
amount: data["data"]["amount"],
46+
interval: data["data"]["interval"],
47+
code: data["data"]["plan_code"],
48+
})
49+
.returning()
50+
51+
return c.json({
52+
status: true,
53+
data: plan[0]
54+
}, 201)
55+
})
56+
57+
plansRouter.put("/:id",
58+
zValidator('json', PlanUpdate),
59+
async (c) => {
60+
const validatedReq = c.req.valid('json')
61+
const id = c.req.param('id')
62+
const result = await db.select({ code: plansTable.code }).from(plansTable)
63+
const { code } = result[0]
64+
65+
const api = await fetch(`${process.env.PAYSTACK_BASE_URL}/plan/${code}`, {
66+
method: "PUT",
67+
headers: {
68+
"Authorization": `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
69+
'Content-Type': "application/json"
70+
},
71+
body: JSON.stringify(validatedReq)
72+
})
73+
74+
const data = await api.json()
75+
console.log("Data: ", data)
76+
77+
if (!data["status"]) {
78+
return c.json({
79+
status: false,
80+
data: {}
81+
}, 500)
82+
}
83+
84+
const plan = await db.update(plansTable)
85+
.set({ ...validatedReq })
86+
.where(eq(plansTable.id, parseInt(id)))
87+
.returning();
88+
89+
return c.json({
90+
status: true,
91+
data: plan[0]
92+
}, 200)
93+
})
94+
95+
export default plansRouter

src/schema/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as z from "zod";
2+
3+
export const PlanCreate = z.object({
4+
name: z.string(),
5+
amount: z.number(),
6+
interval: z.string(),
7+
description: z.boolean().optional()
8+
});
9+
10+
export const PlanUpdate = z.object({
11+
name: z.string().optional(),
12+
amount: z.number().optional(),
13+
interval: z.string().optional(),
14+
update_existing_subscriptions: z.boolean().optional()
15+
});

0 commit comments

Comments
 (0)