save for production

This commit is contained in:
Jun-te Kim 2026-01-21 01:07:39 +00:00
parent c3651b1695
commit dd92550ba7

View file

@ -83,7 +83,6 @@ export async function POST(req: NextRequest) {
.limit(1); .limit(1);
if (!stripeAccount) { if (!stripeAccount) {
console.error("❌ Stripe account not registered:", stripeAccountId);
return NextResponse.json( return NextResponse.json(
{ error: "Stripe account not registered" }, { error: "Stripe account not registered" },
{ status: 500 } { status: 500 }
@ -106,14 +105,12 @@ export async function POST(req: NextRequest) {
); );
} }
if (!xeroConn.salesAccountCode || !xeroConn.stripeClearingAccountCode) { if (!xeroConn.salesAccountCode) {
throw new Error( throw new Error("Sales account code not configured");
"Xero account codes not configured (sales / stripe clearing)"
);
} }
// -------------------------------------------------- // --------------------------------------------------
// 5⃣ Get VALID Xero access token (refresh handled centrally) // 5⃣ Get VALID Xero access token
// -------------------------------------------------- // --------------------------------------------------
const accessToken = await getValidXeroAccessToken(stripeAccount.userId); const accessToken = await getValidXeroAccessToken(stripeAccount.userId);
const xero = getXeroClient(accessToken); const xero = getXeroClient(accessToken);
@ -155,7 +152,7 @@ export async function POST(req: NextRequest) {
} }
// -------------------------------------------------- // --------------------------------------------------
// 7⃣ Create AUTHORISED invoice // 7⃣ Create AUTHORISED invoice (NO PAYMENT)
// -------------------------------------------------- // --------------------------------------------------
if (!session.amount_total || !session.currency) { if (!session.amount_total || !session.currency) {
throw new Error("Stripe session missing amount or currency"); throw new Error("Stripe session missing amount or currency");
@ -168,6 +165,8 @@ export async function POST(req: NextRequest) {
throw new Error(`Unsupported currency: ${session.currency}`); throw new Error(`Unsupported currency: ${session.currency}`);
} }
const today = new Date().toISOString().slice(0, 10);
const invoiceResponse = await xero.accountingApi.createInvoices( const invoiceResponse = await xero.accountingApi.createInvoices(
xeroConn.tenantId, xeroConn.tenantId,
{ {
@ -176,6 +175,8 @@ export async function POST(req: NextRequest) {
type: Invoice.TypeEnum.ACCREC, type: Invoice.TypeEnum.ACCREC,
status: Invoice.StatusEnum.AUTHORISED, status: Invoice.StatusEnum.AUTHORISED,
contact: { contactID: contact.contactID }, contact: { contactID: contact.contactID },
date: today,
dueDate: today,
lineItems: [ lineItems: [
{ {
description: `Stripe payment (${session.id})`, description: `Stripe payment (${session.id})`,
@ -197,34 +198,14 @@ export async function POST(req: NextRequest) {
} }
// -------------------------------------------------- // --------------------------------------------------
// 8⃣ Mark invoice as PAID → Stripe Clearing // 8⃣ Record idempotency (LAST STEP)
// --------------------------------------------------
const paymentReference =
typeof session.payment_intent === "string"
? session.payment_intent
: session.id;
await xero.accountingApi.createPayments(xeroConn.tenantId, {
payments: [
{
invoice: { invoiceID: invoice.invoiceID },
amount,
date: new Date().toISOString().slice(0, 10),
reference: paymentReference,
account: { code: xeroConn.stripeClearingAccountCode },
},
],
});
// --------------------------------------------------
// 9⃣ Record idempotency (LAST STEP)
// -------------------------------------------------- // --------------------------------------------------
await db.insert(processedStripeEvents).values({ await db.insert(processedStripeEvents).values({
stripeEventId: event.id, stripeEventId: event.id,
stripeAccountId, stripeAccountId,
}); });
console.log("✅ Stripe → Xero sync complete", { console.log("✅ Stripe → Xero invoice created", {
eventId: event.id, eventId: event.id,
invoiceId: invoice.invoiceID, invoiceId: invoice.invoiceID,
stripeAccountId, stripeAccountId,