Skip to content

Instantly share code, notes, and snippets.

@adamjarling
Created May 14, 2023 13:21
Show Gist options
  • Save adamjarling/3d95fe1f9d0aea5a17bebfd57f5ece29 to your computer and use it in GitHub Desktop.
Save adamjarling/3d95fe1f9d0aea5a17bebfd57f5ece29 to your computer and use it in GitHub Desktop.
Supabase Stripe handle Checkout
import Stripe from "stripe";
import { captureException } from "@sentry/nextjs";
import { getServiceSupabase } from "utils/supabase";
import { sendConfirmationEmail } from "lib/sendgrid/send-confirmation-email";
async function handleCheckoutSessionCompleted(
session: Stripe.Checkout.Session
) {
//console.log("session", session);
/**
* Using Supabase service key to bypass RLS
* since this code handles a response from a Stripe event.
* We're validating the Stripe event is legit, so
* once here it's assumed the data is legit as well
*/
const supabase = getServiceSupabase();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2022-11-15",
});
try {
const sessionInstance = await stripe.checkout.sessions.retrieve(
session.id,
{ expand: ["line_items"] }
);
const {
id,
amount_subtotal,
amount_total,
client_reference_id, // Passing profile_id
consent,
currency,
customer_details,
line_items,
livemode,
metadata,
payment_intent,
payment_method_types,
shipping_cost,
shipping_details,
status,
total_details,
} = sessionInstance;
if (status !== "complete") {
return console.log("Checkout Session not complete", sessionInstance);
}
/**
* Insert into "order" table
*/
const { data: dataOrder, error: errorOrder } = await supabase
.from("order")
.insert({
profile_id: client_reference_id!,
amount_subtotal: amount_subtotal!,
amount_total: amount_total!,
customer_details,
currency,
livemode,
payment_method_types,
shipping_cost,
shipping_details,
stripe_payment_intent_id: payment_intent as string,
stripe_checkout_session_id: id,
total_details,
})
.select()
.single();
if (errorOrder) {
captureException(errorOrder);
console.error(
"Checkout Session: Error inserting into order table",
errorOrder
);
console.error("Session instance", sessionInstance);
return;
}
/**
* Insert records into "order_line_item" table
*/
const lineItemsPayload = line_items?.data.map((lineItem) => {
return {
order_id: dataOrder.id,
product_id: lineItem.price?.product as string,
amount_subtotal: lineItem.amount_subtotal,
amount_tax: lineItem.amount_tax,
amount_total: lineItem.amount_total,
description: lineItem.description,
stripe_line_item_id: lineItem.id,
stripe_price_id: lineItem.price?.id,
quantity: lineItem.quantity,
};
});
const { data: dataOrderLineItem, error: errorOrderLineItem } =
await supabase
.from("order_line_item")
.insert(lineItemsPayload || [])
.select();
if (errorOrderLineItem) {
captureException(errorOrderLineItem);
console.error(
"Checkout Session: Error inserting order_line_item",
errorOrderLineItem
);
console.error("Session instance", sessionInstance);
return;
}
/**
* Insert into "event_order" table
*/
const orderProductIds = line_items?.data.map((lineItem) => {
console.log("lineItem.price.product", lineItem.price?.product);
return lineItem.price?.product as string;
});
/** A Product could be included in multiple Events, so grab all Events from
* products in the Order
*/
const { data: dataEventProducts, error: errorEventProducts } =
await supabase
.from("event_product")
.select("event_id")
.in("product_id", orderProductIds!);
if (errorEventProducts) {
captureException(errorEventProducts);
console.error(
"Checkout Session: Error getting event_product table data",
errorEventProducts
);
console.error("Session instance", sessionInstance);
console.error("line_items", line_items?.data);
console.error("orderProductIds", orderProductIds);
return;
}
const uniqueEventIds = [
...new Set(dataEventProducts?.map((item) => item.event_id)),
];
const { data: dataEventOrders, error: errorEventOrders } = await supabase
.from("event_order")
.insert(
uniqueEventIds.map((eventId) => ({
event_id: eventId,
order_id: dataOrder.id,
}))
)
.select();
if (errorEventOrders) {
captureException(errorEventOrders);
console.error(
"Checkout Session: Error inserting into event_order",
errorEventOrders
);
console.error("Session instance", sessionInstance);
return;
}
/**
* Update "profile" table with a customer's consent about
* receiving promotional emails
*/
const promoConsent = consent?.promotions === "opt_in";
const { data: dataConsent, error: errorConsent } = await supabase
.from("profile")
.update({ opt_in_email_list: promoConsent })
.match({ id: client_reference_id });
if (errorConsent) {
captureException(errorConsent);
console.error(
"Checkout Session: Error updating profile with email opt_in consent",
errorConsent
);
return;
}
console.log("Checkout session success", sessionInstance.id);
/**
* SendGrid confirmation email
*/
const mailResponse = await sendConfirmationEmail({
customer_details,
line_items: line_items?.data,
});
} catch (error) {
captureException(error);
console.error("Error", error);
}
}
export { handleCheckoutSessionCompleted };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment