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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/.idea

/tooling

# dependencies
/node_modules
/.pnp
Expand Down
2 changes: 1 addition & 1 deletion lib/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const updatePaymentStatus = async (orderId: string) => {
auth: auth,
});

const updatedValues = values.map((row) => {
const updatedValues = values?.map((row) => {
if (row.includes(orderId)) {
row[10] = "yes";
}
Expand Down
8 changes: 7 additions & 1 deletion lib/nodemailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ export const sendEmailStripePayment = async (
name: string,
orderId: string,
price: string,
items: string[] = [],
): Promise<boolean> => {
const html = pug.renderFile(creditCardTemplatePath, { name, orderId, price });
const html = pug.renderFile(creditCardTemplatePath, {
name,
orderId,
price,
items: items.join(", "),
});
const mailOptions = {
from: process.env.GMAIL_USER,
to: email,
Expand Down
32 changes: 27 additions & 5 deletions lib/stripe.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import Stripe from "stripe";
import type { StripeBackendItem } from "../types/types";
import type {
CustomerFriendlyStripeItem,
StripeBackendItem,
} from "../types/types";
import {
getItemsWithBulkDiscount,
getMinimumItemsForDiscount,
getPercentOff,
} from "./utils";

let stripeCached: any = null;
let stripeCached: Stripe;

export const CUSTOMER_FRIENDLY_STRIPE_ITEMS_KEY =
"customerFriendlyStripeItems" as const;

// caches the connection or starts a new one
const makeStripeConnection = async () => {
Expand Down Expand Up @@ -75,7 +81,7 @@ export const getPriceForPages = async (pages: number, isColor: boolean) => {
}`,
});
const priceId = products.data[0].default_price?.toString();
const price = await findPrice(priceId);
const price = await findPrice(priceId || "");
const productId = products.data[0].id;
return {
price: price,
Expand Down Expand Up @@ -157,11 +163,24 @@ export const createSession = async (
customer_email: email,
cancel_url: `${process.env
.STRIPE_CANCEL_URL!}?session_id={CHECKOUT_SESSION_ID}`,
metadata: { orderId: orderId },
metadata: {
orderId: orderId,
[CUSTOMER_FRIENDLY_STRIPE_ITEMS_KEY]: JSON.stringify(
generateCustomerFriendlyStripeItems(items),
),
},
});
return session;
};

const generateCustomerFriendlyStripeItems = (
items: StripeBackendItem[],
): CustomerFriendlyStripeItem[] => {
return items.map(
(item) => `${item.name} x ${item.quantity}` as CustomerFriendlyStripeItem,
);
};

export const checkSession = async (sessionId: string) => {
const stripe: Stripe = await makeStripeConnection();
const session = await stripe.checkout.sessions.retrieve(sessionId);
Expand All @@ -170,6 +189,9 @@ export const checkSession = async (sessionId: string) => {
price: session.amount_total,
customer: session.customer_details,
paid: session.status === "complete",
orderId: session.metadata.orderId,
orderId: session.metadata?.orderId,
// This is the RAW JSON string from the metadata
[CUSTOMER_FRIENDLY_STRIPE_ITEMS_KEY]:
session.metadata?.[CUSTOMER_FRIENDLY_STRIPE_ITEMS_KEY],
};
};
6 changes: 5 additions & 1 deletion lib/templates/creditCard.pug
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ p Thank you for your payment (Order: #{orderId})

p Your total was $#{price}.

p for the items

p #{items}

p The next step is to pick a collection time for your order using this link:
a(href="https://calendly.com/ryanguo02/printing-pickup") https://calendly.com/ryanguo02/printing-pickup

p If you have any issues, email us at
a(href="mailto:primalprintingnz@gmail.com") primalprintingnz@gmail.com
| or text us on 02108678038
p or text us on 02108678038

p Thank you again for your support 🙂

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"react-dom": "18.2.0",
"react-messenger-chat-plugin": "^3.0.5",
"stripe": "^12.1.1",
"tsx": "^4.20.3",
"zod": "^4.0.5"
},
"devDependencies": {
Expand Down
25 changes: 17 additions & 8 deletions pages/api/checkpaymentstatus.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { updatePaymentStatus } from "../../lib/google";
import { checkSession } from "../../lib/stripe";
import {
CUSTOMER_FRIENDLY_STRIPE_ITEMS_KEY,
checkSession,
} from "../../lib/stripe";

const CheckPaymentStatusSchema = z.object({
session_id: z.string(),
Expand All @@ -14,10 +17,10 @@ export default async function handler(
try {
const parsedQuery = CheckPaymentStatusSchema.parse(req.query);

const success = await checkSession(parsedQuery.session_id);
const orderId = success.orderId;
const customer = success.customer;
const price = success.price;
const sessionData = await checkSession(parsedQuery.session_id);
const orderId = sessionData.orderId;
const customer = sessionData.customer;
const price = sessionData.price;

if (!process.env.BASE_URL) {
return res.status(500).json({
Expand All @@ -26,15 +29,21 @@ export default async function handler(
});
}

if (success.paid) {
updatePaymentStatus(orderId);
if (sessionData.paid) {
await updatePaymentStatus(
orderId || "Unknown Order ID - please contact us",
);
console.log(sessionData);
await fetch(`${process.env.BASE_URL}/api/sendemailcreditcard`, {
method: "POST",
body: JSON.stringify({
name: customer?.name,
email: customer?.email,
orderId: orderId,
price: (price || NaN) / 100,
price: String((price || NaN) / 100),
items: JSON.parse(
sessionData[CUSTOMER_FRIENDLY_STRIPE_ITEMS_KEY] || "[]",
),
}),
});
return res.redirect(307, `/success?orderId=${orderId}`);
Expand Down
23 changes: 22 additions & 1 deletion pages/api/sendemailbank.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { sendEmailBankTransfer } from "../../lib/nodemailer";

const schema = z.object({
email: z.email(),
name: z.string().min(1),
orderId: z.string().min(1),
items: z.any(),
price: z.string().min(1),
});

export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const body = JSON.parse(req.body);
let body: z.infer<typeof schema>;
try {
body = typeof req.body === "string" ? JSON.parse(req.body) : req.body;
} catch {
return res.status(400).json({ error: "Invalid JSON" });
}
const parseResult = schema.safeParse(body);
if (!parseResult.success) {
return res.status(400).json({
error: "Invalid request body",
details: parseResult.error,
});
}
const success = await sendEmailBankTransfer(
body.email,
body.name,
Expand Down
24 changes: 23 additions & 1 deletion pages/api/sendemailcreditcard.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { sendEmailStripePayment } from "../../lib/nodemailer";

const schema = z.object({
email: z.email(),
name: z.string().min(1),
orderId: z.string().min(1),
price: z.string().min(1),
items: z.array(z.string()),
});

export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const body = JSON.parse(req.body);
let body: z.infer<typeof schema>;
try {
body = typeof req.body === "string" ? JSON.parse(req.body) : req.body;
} catch {
return res.status(400).json({ error: "Invalid JSON" });
}
const parseResult = schema.safeParse(body);
if (!parseResult.success) {
return res.status(400).json({
error: "Invalid request body",
details: parseResult.error,
});
}
const success = await sendEmailStripePayment(
body.email,
body.name,
body.orderId,
body.price,
body.items,
);
res.status(200).json({ success: success });
}
Loading