Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/precheck.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ jobs:
run: npm install

- name: Build Next.js app
env:
NEXT_PUBLIC_GRAPHQL_ENDPOINT: ${{ vars.GRAPHQL_ENDPOINT }}
run: npm run build

- name: Success
Expand Down
5 changes: 5 additions & 0 deletions app/(api)/_datalib/_resolvers/Order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ const resolvers = {
},
ctx: ApolloContext
) => Orders.editProductQuantity(args.id, args.productToUpdate, ctx),
processOrder: async (
_: never,
args: { input: OrderInput; products: [OrderProductInput] },
ctx: ApolloContext
) => Orders.processOrder(args.input, args.products, ctx),
},
};

Expand Down
75 changes: 75 additions & 0 deletions app/(api)/_datalib/_services/Orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import prisma from '../_prisma/client';
import { OrderInput, OrderProductInput } from '@datatypes/Order';
import { ApolloContext } from '../apolloServer';
import { Prisma } from '@prisma/client';
import Stripe from 'stripe';

export default class Orders {
//CREATE
Expand All @@ -12,6 +13,7 @@ export default class Orders {
const order = prisma.order.create({
data: {
...input, // Spread the input fields
total: 0,
status: 'pending', // Default status
created_at: new Date(), // Current timestamp
},
Expand Down Expand Up @@ -312,4 +314,77 @@ export default class Orders {
return false;
}
}

// PROCESS W/STRIPE
static async processOrder(
input: OrderInput,
products: OrderProductInput[],
ctx: ApolloContext
) {
if (!ctx.isOwner && !ctx.hasValidApiKey) return null;

try {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2025-05-28.basil', // explicitly set the API version
});

// Lookup product prices from DB
const productIds = products.map((p) => p.product_id);
const dbProducts = await prisma.product.findMany({
where: { id: { in: productIds } },
});

const productMap = Object.fromEntries(dbProducts.map((p) => [p.id, p]));

const total = products.reduce((sum, item) => {
const product = productMap[item.product_id];
return sum + (product?.price ?? 0) * item.quantity;
}, 0);

// Stripe counts payment amounts in cents
const amountInCents = Math.round(total * 100);

// Create the order
const createdOrder = await prisma.order.create({
data: {
...input,
total: total,
status: 'pending',
created_at: new Date(),
products: {
create: products.map((p) => ({
quantity: p.quantity,
product: { connect: { id: p.product_id } },
})),
},
},
include: { products: { include: { product: true } } },
});

// Create Stripe PaymentIntent
const paymentIntent = await stripe.paymentIntents.create({
amount: amountInCents,
currency: 'usd',
metadata: {
orderId: createdOrder.id,
},
});

// Save paymentIntentId to order
const updatedOrder = await prisma.order.update({
where: { id: createdOrder.id },
data: { paymentIntentId: paymentIntent.id },
include: { products: { include: { product: true } } },
});

revalidateCache(['orders', 'products']);

return {
order: updatedOrder,
clientSecret: paymentIntent.client_secret,
};
} catch (e) {
return e;
}
}
}
12 changes: 12 additions & 0 deletions app/(api)/_datalib/_typeDefs/Order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import gql from 'graphql-tag';
const typeDefs = gql`
type Order {
id: ID!
paymentIntentId: String
total: Float!
products: [OrderProduct]
customer_name: String!
customer_email: String!
Expand Down Expand Up @@ -39,6 +41,7 @@ const typeDefs = gql`
}

input OrderUpdateInput {
total: Float
customer_name: String
customer_email: String
customer_phone_num: String
Expand All @@ -65,6 +68,11 @@ const typeDefs = gql`
quantity: Int!
}

type ProcessOrderResult {
order: Order!
clientSecret: String!
}

type Query {
order(id: ID!): Order
orders(
Expand All @@ -82,6 +90,10 @@ const typeDefs = gql`
addProductToOrder(id: ID!, productToAdd: OrderProductInput!): Order
removeProductFromOrder(id: ID!, product_id: ID!): Order
editProductQuantity(id: ID!, productToUpdate: OrderProductInput!): Order
processOrder(
input: OrderInput!
products: [OrderProductInput!]!
): ProcessOrderResult
}
`;

Expand Down
1 change: 1 addition & 0 deletions app/(api)/_types/Order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Product } from './Product';

export type Order = {
id: number;
total: number;
customer_name: string;
customer_email: string;
customer_phone_num: string;
Expand Down
26 changes: 25 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-quill": "^2.0.0",
"readline": "^1.3.0",
"sass": "^1.69.5",
"stripe": "^18.2.1",
"validator": "^13.12.0",
"zod": "^3.24.2"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Order" ADD COLUMN "paymentIntentId" TEXT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Order" ADD COLUMN "paymentIntentId" TEXT;
8 changes: 8 additions & 0 deletions prisma/migrations/20250625061418_order_total/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:

- Added the required column `total` to the `Order` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "Order" ADD COLUMN "total" DOUBLE PRECISION NOT NULL;
2 changes: 2 additions & 0 deletions prisma/schema/Order.prisma
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
model Order {
id Int @id @default(autoincrement())
paymentIntentId String?
total Float
customer_name String
customer_email String
customer_phone_num String
Expand Down