Initial OSS scaffold from Songs2VID (pre-strip)

This commit is contained in:
Songs2VID OSS
2026-08-03 06:15:05 +02:00
commit 7d7043b150
195 changed files with 25023 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
import type Stripe from "stripe";
import { STRIPE_PRODUCT_TAX_CODE } from "@/lib/credits";
import { prisma } from "@/lib/db";
import { getStripe } from "@/lib/stripe";
/** Enable Stripe Tax only when Dashboard registrations are active (STRIPE_AUTOMATIC_TAX=true). */
export function isStripeAutomaticTaxEnabled(): boolean {
return process.env.STRIPE_AUTOMATIC_TAX === "true";
}
/**
* Shared Checkout Session options required/recommended by Stripe for SaaS:
* - client_reference_id for linking
* - automatic_tax (opt-in) + customer_update.address when tax is on
* - tax_code already set on product_data by callers
*/
export function checkoutTaxAndReferenceOptions(userId: string): Partial<Stripe.Checkout.SessionCreateParams> {
const opts: Partial<Stripe.Checkout.SessionCreateParams> = {
client_reference_id: userId,
};
if (isStripeAutomaticTaxEnabled()) {
opts.automatic_tax = { enabled: true };
opts.customer_update = { address: "auto", name: "auto" };
// Collect billing address so tax can be calculated for new/returning customers
opts.billing_address_collection = "required";
}
return opts;
}
/** tax_behavior required on Prices when automatic tax is enabled. */
export function priceDataTaxFields(): { tax_behavior?: Stripe.Checkout.SessionCreateParams.LineItem.PriceData["tax_behavior"] } {
if (!isStripeAutomaticTaxEnabled()) return {};
// Exclusive: listed prices are pre-tax; VAT/GST added at checkout
return { tax_behavior: "exclusive" };
}
export function productDataWithTaxCode(
name: string,
description: string,
): Stripe.Checkout.SessionCreateParams.LineItem.PriceData.ProductData {
return {
name,
description,
tax_code: STRIPE_PRODUCT_TAX_CODE,
};
}
/** Ensure the app user has a Stripe Customer and return its id. */
export async function ensureStripeCustomer(userId: string, email: string): Promise<string> {
const dbUser = await prisma.user.findUniqueOrThrow({
where: { id: userId },
select: { stripeCustomerId: true },
});
if (dbUser.stripeCustomerId) return dbUser.stripeCustomerId;
const stripe = getStripe();
const customer = await stripe.customers.create({
email,
metadata: { userId },
});
await prisma.user.update({
where: { id: userId },
data: { stripeCustomerId: customer.id },
});
return customer.id;
}