feat: 🎸 watch a vibe code cleanup specialist in action

cleaned up a bunch of crappy logs
This commit is contained in:
amianthus
2025-11-11 19:25:51 +00:00
parent 4273781a78
commit 583e28a7f4
19 changed files with 62 additions and 363 deletions

View File

@@ -13,6 +13,7 @@ import {
submitBillingDataToVercel,
submitInvoiceToVercel,
} from "@/external/vercel/misc/vercelInvoicing.js";
import { logVercelWebhook } from "@/external/vercel/misc/vercelMiddleware.js";
import { CusService } from "@/internal/customers/CusService.js";
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
import { FeatureService } from "@/internal/features/FeatureService.js";
@@ -59,9 +60,6 @@ export const handleInvoiceFinalized = async ({
const subId = invoiceToSubId({ invoice });
console.log("subId", subId);
console.log("will Invoice Vercel", subId, invoice.amount_due > 0);
if (subId) {
const stripeCli = createStripeCli({ org, env });
// Handle Vercel custom payment method invoices
@@ -72,9 +70,6 @@ export const handleInvoiceFinalized = async ({
subscription.metadata?.vercel_installation_id;
const vercelBillingPlanId = subscription.metadata?.vercel_billing_plan_id;
console.log("vercelInstallationId", vercelInstallationId);
console.log("vercelBillingPlanId", vercelBillingPlanId);
if (
vercelInstallationId &&
vercelBillingPlanId &&
@@ -86,15 +81,14 @@ export const handleInvoiceFinalized = async ({
// Only process if it's a custom payment method (Vercel)
if (paymentMethod.type === "custom") {
console.info(
"🔵 Vercel invoice finalized, submitting to Vercel marketplace",
{
invoiceId: invoice.id,
subscriptionId: subscription.id,
vercelInstallationId,
amountDue: invoice.amount_due / 100,
logVercelWebhook({
logger,
org,
event: {
type: "marketplace.invoice.finalized",
id: invoice.id,
},
);
});
try {
// Get customer and product
@@ -132,10 +126,8 @@ export const handleInvoiceFinalized = async ({
product,
});
console.info("✅ Vercel billing data submitted");
// Submit invoice to Vercel
const result = await submitInvoiceToVercel({
await submitInvoiceToVercel({
installationId: vercelInstallationId,
invoice,
customer,
@@ -144,14 +136,6 @@ export const handleInvoiceFinalized = async ({
features,
});
console.info(
"✅ Vercel invoice submitted, waiting for payment confirmation webhook",
{
vercelInvoiceId: result.invoiceId,
stripeInvoiceId: invoice.id,
},
);
// Do NOT report payment to Stripe here - we've only submitted the invoice to Vercel
// Vercel will process payment asynchronously and send marketplace.invoice.paid webhook
// handleMarketplaceInvoicePaid will then:
@@ -159,9 +143,11 @@ export const handleInvoiceFinalized = async ({
// 2. Report payment as "guaranteed" to Stripe
// 3. Attach payment record to invoice (marks it as paid)
} catch (error: any) {
console.error("Failed to process Vercel invoice", {
error: error.message,
invoiceId: invoice.id,
logger.error("Failed to process Vercel invoice", {
data: {
error: error.message,
invoiceId: invoice.id,
},
});
}
}

View File

@@ -233,11 +233,6 @@ export const handleInvoicePaid = async ({
),
);
console.log(
"Invoice paid, filtered cus products:",
cusProducts.map((cp) => `${cp.product.name} - ${cp.product.id}`),
);
if (cusProducts.length === 0) {
cusProducts = activeCusProducts;
}

View File

@@ -61,15 +61,6 @@ function calculatePrepaidCosts({
// Calculate total cost for this feature
const featureCost = subscriptionItemQuantity * unitAmount;
totalPrepaidCost += featureCost;
console.info("Calculated prepaid feature cost for billing plan", {
featureId: options.feature_id,
quantity: options.quantity,
billingUnits,
subscriptionItemQuantity,
unitAmount,
featureCost,
});
}
}
@@ -139,8 +130,8 @@ export const handleListBillingPlansPerInstall = createRoute({
metadata: z.string().optional(),
}),
handler: async (c) => {
const { orgId, env, integrationConfigurationId } = c.req.param();
const { db, org, features, logger } = c.get("ctx");
const { orgId, env } = c.req.param();
const { db, org, logger } = c.get("ctx");
// Parse metadata from query params
let metadata: Record<string, any> = {};

View File

@@ -9,11 +9,6 @@ export const handleDeleteInstallation = createRoute({
const { db, org, logger } = ctx;
try {
// 1. Delete all resources for this installation in parallel
logger.info("Deleting resources for installation", {
integrationConfigurationId,
});
const resources = await VercelResourceService.listByInstallation({
db,
installationId: integrationConfigurationId,
@@ -32,19 +27,6 @@ export const handleDeleteInstallation = createRoute({
),
);
const successCount = deleteResults.filter(
(r) => r.status === "fulfilled",
).length;
const failCount = deleteResults.filter(
(r) => r.status === "rejected",
).length;
logger.info("Resources deletion complete", {
total: resources.length,
success: successCount,
failed: failCount,
});
// 2. Delete the customer/installation
await deleteCusById({
db: ctx.db,
@@ -59,11 +41,6 @@ export const handleDeleteInstallation = createRoute({
error,
integrationConfigurationId,
});
console.log(
"ERROR: Error deleting installation: --------------------------------",
);
console.log(error);
console.log("--------------------------------");
}
return c.json(
{

View File

@@ -17,17 +17,6 @@ export const handleGetInstallation = createRoute({
env: ctx.env,
});
// const billingPlan = customer?.customer_products?.find((x) =>
// isMainProduct({
// product: {
// ...x.product,
// prices: x.customer_prices.map((y) => y.price) ?? {},
// entitlements: x.customer_entitlements.map((y) => y.entitlement) ?? [],
// },
// prices: x.customer_prices.map((y) => y.price) ?? [],
// }),
// );
if (!customer) {
return c.json(
{
@@ -41,6 +30,7 @@ export const handleGetInstallation = createRoute({
{
notification: null,
billingPlan:
// edge case: [0] = add-on [1] = main
customer.customer_products?.[0] !== undefined
? (productToBillingPlan({
product: cusProductToProduct({

View File

@@ -24,17 +24,8 @@ import { productToBillingPlan } from "../handleListBillingPlans.js";
export const handleUpsertInstallation = createRoute({
handler: async (c) => {
console.log("Vercel Webhook Router: PUT /installations");
console.log("Vercel Webhook Router: req.params", c.req.param());
const body = await c.req.json<VercelUpsertInstallation>();
console.log("Vercel Webhook Router: req.body", body);
const ctx = c.get("ctx");
console.log("Vercel Webhook Router: ctx.org", ctx.org);
console.log("Vercel Webhook Router: ctx.env", ctx.env);
console.log("Vercel Webhook Router: ctx.features", ctx.features);
const { integrationConfigurationId } = c.req.param();
let createdCustomer: Customer | null = null;
@@ -50,7 +41,6 @@ export const handleUpsertInstallation = createRoute({
c.req.header("Authorization") as string,
);
const claims = await verifyToken({ token, org: ctx.org, env: ctx.env });
console.log("Vercel Webhook Router: claims", claims);
if (
!verifyClaims({
@@ -81,13 +71,6 @@ export const handleUpsertInstallation = createRoute({
frozen_time: Math.floor(Date.now() / 1000),
});
testClockId = testClock.id;
ctx.logger.info(
"Created test clock for sandbox Vercel installation",
{
testClockId: testClock.id,
},
);
}
const stripeCustomer = await createStripeCustomer({
@@ -140,26 +123,15 @@ export const handleUpsertInstallation = createRoute({
},
},
});
if (customPaymentMethod) {
ctx.logger.info("✅ Created custom payment method for Vercel", {
paymentMethodId: customPaymentMethod.id,
customerId: stripeCustomer.id,
});
} else {
ctx.logger.warn(
"⚠️ No custom payment method created - check org config",
);
}
}
}
} catch (_) {
console.log(
ctx.logger.error(
"ERROR: Error creating customer: --------------------------------",
);
console.log(_);
console.log(ctx.org);
console.log("--------------------------------");
ctx.logger.error(_);
ctx.logger.error(ctx.org);
ctx.logger.error("--------------------------------");
}
if (createdCustomer) {

View File

@@ -45,13 +45,6 @@ export const handleMarketplaceInvoicePaid = async ({
invoiceDate,
} = payload;
logger.info("💰 marketplace.invoice.paid webhook received", {
vercelInvoiceId: invoiceId,
stripeInvoiceId: externalInvoiceId,
amount: invoiceTotal,
installationId,
});
const stripeCli = createStripeCli({ org, env });
// 1. Get the invoice
@@ -59,23 +52,12 @@ export const handleMarketplaceInvoicePaid = async ({
expand: ["subscription"],
});
logger.info("Retrieved Stripe invoice", {
invoiceId: invoice.id,
status: invoice.status,
amountDue: invoice.amount_due / 100,
});
// 2. Check if already paid
if (invoice.status === "paid") {
logger.info("Invoice already marked as paid, skipping");
return;
}
console.log(
"invoice.lines.data",
JSON.stringify(invoice.lines.data, null, 4),
);
// 3. Get subscription and payment method
const subscription = await stripeCli.subscriptions.retrieve(
invoice.lines.data.find(
@@ -85,22 +67,10 @@ export const handleMarketplaceInvoicePaid = async ({
)?.parent?.subscription_item_details?.subscription as string,
);
console.log("subscription", JSON.stringify(subscription, null, 4));
const customPaymentMethod = await stripeCli.paymentMethods.retrieve(
subscription.default_payment_method as string,
);
logger.info("Found custom payment method", {
paymentMethodId: customPaymentMethod.id,
type: customPaymentMethod.type,
});
// 4. Create cus_product BEFORE reporting payment
// This ensures the user gets access to the product when payment is confirmed
// and the invoice.paid webhook can find the cus_product
logger.info("Creating customer product before reporting payment");
try {
const partialCustomer = await CusService.getByStripeId({
db,
@@ -108,9 +78,6 @@ export const handleMarketplaceInvoicePaid = async ({
});
if (!partialCustomer) {
logger.error("Customer not found for payment", {
stripeCustomerId: invoice.customer,
});
throw new Error("Customer not found");
}
@@ -122,9 +89,6 @@ export const handleMarketplaceInvoicePaid = async ({
});
if (!customer) {
logger.error("Customer not found", {
internalCustomerId: partialCustomer.internal_id,
});
throw new Error("Customer not found");
}
@@ -142,9 +106,6 @@ export const handleMarketplaceInvoicePaid = async ({
});
if (!product) {
logger.error("Product not found", {
billingPlanId: vercelBillingPlanId,
});
throw new Error("Product not found");
}
@@ -158,12 +119,6 @@ export const handleMarketplaceInvoicePaid = async ({
const isRenewal = existingCusProducts.length > 0;
logger.info("Detected subscription type", {
isRenewal,
subscriptionId: subscription.id,
existingCusProductCount: existingCusProducts.length,
});
// Fetch Vercel resource by ID from subscription metadata
let optionsList: FeatureOptions[] = [];
const vercelResourceId = subscription.metadata?.vercel_resource_id;
@@ -178,11 +133,6 @@ export const handleMarketplaceInvoicePaid = async ({
});
if (resource?.metadata && Object.keys(resource.metadata).length > 0) {
logger.info("Parsing prepaid quantities from resource metadata", {
resourceId: resource.id,
metadata: resource.metadata,
});
optionsList = parseVercelPrepaidQuantities({
metadata: resource.metadata,
product,
@@ -196,21 +146,9 @@ export const handleMarketplaceInvoicePaid = async ({
});
// Continue with empty optionsList
}
} else {
logger.info(
"No resource ID in subscription metadata (legacy or installation-level billing)",
{
vercelResourceId,
},
);
}
if (isRenewal) {
// Process renewal - reset balances after payment confirmation
logger.info(
"Processing renewal - resetting balances after payment confirmation",
);
// Call sendUsageAndReset which handles all balance resets
const activeProduct = existingCusProducts[0];
@@ -223,14 +161,8 @@ export const handleMarketplaceInvoicePaid = async ({
stripeSubs: [subscription],
logger,
});
logger.info(
"✅ Renewal balance resets completed after payment confirmation",
);
} else {
// New subscription - create cus_product
logger.info("New subscription - creating cus_product");
await createFullCusProduct({
db,
attachParams: attachToInsertParams(
@@ -260,11 +192,6 @@ export const handleMarketplaceInvoicePaid = async ({
scenario: AttachScenario.New,
logger,
});
logger.info("✅ Customer product created", {
productId: product.id,
customerId: customer.id,
});
}
} catch (error: any) {
logger.error("❌ Failed to create customer product", {
@@ -275,8 +202,6 @@ export const handleMarketplaceInvoicePaid = async ({
// 5. Report successful payment to Stripe via Payment Records API
// This marks the payment as "guaranteed" and allows Stripe to mark the invoice as paid
logger.info("Reporting guaranteed payment to Stripe");
const paymentRecord = await stripeCli.paymentRecords.reportPayment({
amount_requested: {
value: invoice.amount_due,
@@ -302,20 +227,11 @@ export const handleMarketplaceInvoicePaid = async ({
},
});
logger.info("✅ Payment reported to Stripe", {
paymentRecordId: paymentRecord.id,
});
// 6. Attach payment record to invoice
try {
await stripeCli.invoices.attachPayment(externalInvoiceId, {
payment_record: paymentRecord.id,
});
logger.info("✅ Payment record attached to invoice", {
invoiceId: externalInvoiceId,
paymentRecordId: paymentRecord.id,
});
} catch (error: any) {
// Might already be attached from handleInvoicePaymentAttemptRequired
if (error.code === "resource_already_exists") {
@@ -324,8 +240,4 @@ export const handleMarketplaceInvoicePaid = async ({
throw error;
}
}
logger.info(
"🎉 Vercel payment confirmed - invoice paid, subscription active",
);
};

View File

@@ -33,13 +33,6 @@ export const handleMarketplaceInvoiceNotPaid = async ({
invoiceDate,
} = payload;
logger.info("❌ marketplace.invoice.notpaid webhook received", {
vercelInvoiceId: invoiceId,
stripeInvoiceId: externalInvoiceId,
amount: invoiceTotal,
installationId,
});
const stripeCli = createStripeCli({ org, env });
// 1. Get the invoice
@@ -47,23 +40,12 @@ export const handleMarketplaceInvoiceNotPaid = async ({
expand: ["subscription"],
});
logger.info("Retrieved Stripe invoice", {
invoiceId: invoice.id,
status: invoice.status,
amountDue: invoice.amount_due / 100,
});
// 2. Check if already paid
if (invoice.status === "paid") {
logger.info("Invoice already marked as not paid, skipping");
return;
}
console.log(
"invoice.lines.data",
JSON.stringify(invoice.lines.data, null, 4),
);
// 3. Get subscription and payment method
const subscription = await stripeCli.subscriptions.retrieve(
invoice.lines.data.find(
@@ -73,17 +55,10 @@ export const handleMarketplaceInvoiceNotPaid = async ({
)?.parent?.subscription_item_details?.subscription as string,
);
console.log("subscription", JSON.stringify(subscription, null, 4));
const customPaymentMethod = await stripeCli.paymentMethods.retrieve(
subscription.default_payment_method as string,
);
logger.info("Found custom payment method", {
paymentMethodId: customPaymentMethod.id,
type: customPaymentMethod.type,
});
try {
const partialCustomer = await CusService.getByStripeId({
db,
@@ -139,8 +114,6 @@ export const handleMarketplaceInvoiceNotPaid = async ({
// 5. Report failed payment to Stripe via Payment Records API
// This marks the payment as "failed" and allows Stripe to mark the invoice as not paid
logger.info("Reporting failed payment to Stripe");
const paymentRecord = await stripeCli.paymentRecords.reportPayment({
amount_requested: {
value: invoice.amount_due,
@@ -166,36 +139,18 @@ export const handleMarketplaceInvoiceNotPaid = async ({
},
});
logger.info("✅ Payment reported to Stripe", {
paymentRecordId: paymentRecord.id,
});
// 6. Attach payment record to invoice
try {
await stripeCli.invoices.attachPayment(externalInvoiceId, {
payment_record: paymentRecord.id,
});
logger.info("✅ Payment record attached to invoice", {
invoiceId: externalInvoiceId,
paymentRecordId: paymentRecord.id,
});
} catch (error: any) {
// Might already be attached from handleMarketplaceInvoicePaid
if (error.code === "resource_already_exists") {
logger.info("Payment record already attached to invoice");
} else {
throw error;
}
}
await stripeCli.subscriptions.cancel(subscription.id);
logger.info("Cancelled subscription", {
subscriptionId: subscription.id,
});
logger.info(
"❌ Vercel payment failed - invoice not paid, subscription cancelled",
);
};

View File

@@ -32,13 +32,6 @@ export const handleCreateResource = createRoute({
const { db, org, features, logger } = c.get("ctx");
const { productId, name, metadata, billingPlanId } = c.req.valid("json");
logger.info("Creating Vercel resource", {
productId,
name,
billingPlanId,
integrationConfigurationId,
});
// 1. Get customer
const customer = await CusService.getFull({
db,
@@ -75,7 +68,7 @@ export const handleCreateResource = createRoute({
// 2. Create resource in database (enforces 1-resource limit)
const resourceId = generateId("vre");
const resource = await VercelResourceService.create({
await VercelResourceService.create({
db,
resource: {
id: resourceId,
@@ -88,10 +81,6 @@ export const handleCreateResource = createRoute({
},
});
logger.info("Resource created in database", {
resourceId: resource.id,
});
// 3. Create subscription (installation-level billing)
const { product } = await createVercelSubscription({
db,

View File

@@ -10,12 +10,7 @@ export const handleDeleteResource = createRoute({
handler: async (c) => {
const { orgId, env, integrationConfigurationId, resourceId } =
c.req.param();
const { db, logger } = c.get("ctx");
logger.info("Deleting Vercel resource", {
integrationConfigurationId,
resourceId,
});
const { db } = c.get("ctx");
await VercelResourceService.delete({
db,

View File

@@ -10,12 +10,7 @@ export const handleGetResource = createRoute({
handler: async (c) => {
const { orgId, env, integrationConfigurationId, resourceId } =
c.req.param();
const { db, logger } = c.get("ctx");
logger.info("Getting Vercel resource", {
integrationConfigurationId,
resourceId,
});
const { db } = c.get("ctx");
const resource = await VercelResourceService.getByIdAndInstallation({
db,

View File

@@ -46,7 +46,6 @@ export async function verifyToken({
throw new AuthError("Invalid issuer");
}
console.log("Vercel auth claims", JSON.stringify(claims, null, 4));
return claims;
} catch (err) {
if (err instanceof JWTExpired) {
@@ -117,26 +116,23 @@ export class AuthError extends Error {}
* 5. Store validated claims in context
*/
export const vercelOidcAuthMiddleware = async (c: any, next: any) => {
const { org, env, logger } = c.get("ctx");
const { org, env } = c.get("ctx");
const authHeader = c.req.header("authorization");
const authType = c.req.header("x-vercel-auth");
// Validate required headers
if (!authHeader) {
logger.warn("Missing Authorization header");
return c.json({ error: "Unauthorized", code: "missing_auth_header" }, 401);
}
if (!authType) {
logger.warn("Missing X-Vercel-Auth header");
return c.json(
{ error: "Unauthorized", code: "missing_auth_type_header" },
401,
);
}
if (authType !== "user" && authType !== "system") {
logger.warn("Invalid X-Vercel-Auth value", { authType });
if (!["user", "system"].includes(authType)) {
return c.json({ error: "Unauthorized", code: "invalid_auth_type" }, 401);
}
@@ -145,7 +141,6 @@ export const vercelOidcAuthMiddleware = async (c: any, next: any) => {
try {
token = getAuthorizationToken(authHeader);
} catch (error) {
logger.warn("Invalid Authorization header format");
return c.json(
{ error: "Unauthorized", code: "invalid_auth_header_format" },
401,
@@ -157,10 +152,6 @@ export const vercelOidcAuthMiddleware = async (c: any, next: any) => {
try {
claims = await verifyToken({ token, org, env });
} catch (error: any) {
logger.warn("JWT verification failed", {
error: error.message,
authType,
});
return c.json(
{
error: "Unauthorized",
@@ -186,12 +177,6 @@ export const vercelOidcAuthMiddleware = async (c: any, next: any) => {
if (authType === "user") {
// User auth: always validate installation_id matches URL param
if (claims.installation_id !== integrationConfigurationId) {
logger.warn("Installation ID mismatch for user auth", {
claim_installation_id: claims.installation_id,
url_installation_id: integrationConfigurationId,
path,
all_params: c.req.param(),
});
return c.json(
{ error: "Forbidden", code: "installation_id_mismatch" },
403,
@@ -201,12 +186,6 @@ export const vercelOidcAuthMiddleware = async (c: any, next: any) => {
// System auth: validate installation_id only if not null
if (claims.installation_id !== null) {
if (claims.installation_id !== integrationConfigurationId) {
logger.warn("Installation ID mismatch for system auth", {
claim_installation_id: claims.installation_id,
url_installation_id: integrationConfigurationId,
path,
all_params: c.req.param(),
});
return c.json(
{ error: "Forbidden", code: "installation_id_mismatch" },
403,
@@ -216,13 +195,6 @@ export const vercelOidcAuthMiddleware = async (c: any, next: any) => {
// If installation_id is null, we only validate JWKS (already done above)
}
}
logger.debug("OIDC auth successful", {
authType,
installation_id: claims.installation_id,
user_id: claims.user_id,
});
// Store claims in context for downstream handlers
c.set("vercelClaims", claims);

View File

@@ -166,8 +166,8 @@ export const submitInvoiceToVercel = async ({
? {
test: {
validate: true,
// result: "paid",
result: "notpaid",
result: "paid",
// result: "notpaid",
},
}
: {}),
@@ -213,11 +213,6 @@ export const parseVercelPrepaidQuantities = ({
);
if (!entitlement) {
console.warn("Feature not found in product, skipping prepaid quantity", {
featureId,
productId: product.id,
quantity,
});
continue;
}
@@ -228,16 +223,6 @@ export const parseVercelPrepaidQuantities = ({
});
if (!prepaidPrice) {
console.warn(
"Feature is not prepaid or has no price configured, skipping",
{
featureId,
featureName: entitlement.feature.name,
internalFeatureId: entitlement.internal_feature_id,
productId: product.id,
quantity,
},
);
continue;
}
@@ -247,13 +232,6 @@ export const parseVercelPrepaidQuantities = ({
internal_feature_id: entitlement.internal_feature_id,
quantity,
});
console.info("Parsed prepaid quantity from Vercel metadata", {
featureId,
featureName: entitlement.feature.name,
quantity,
priceId: prepaidPrice.id,
});
}
return optionsList;

View File

@@ -1,5 +1,7 @@
import { AppEnv } from "@autumn/shared";
import { AppEnv, type Organization } from "@autumn/shared";
import chalk from "chalk";
import type { Context, Next } from "hono";
import type { Logger } from "@/external/logtail/logtailUtils.js";
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
import { FeatureService } from "@/internal/features/FeatureService.js";
import { OrgService } from "@/internal/orgs/OrgService.js";
@@ -27,3 +29,26 @@ export const vercelSeederMiddleware = async (
await next();
};
export const logVercelWebhook = ({
logger,
org,
event,
}: {
logger: Logger;
org: Organization;
event: any;
}) => {
logger.info(
`${chalk.magenta("VERCEL").padEnd(18)} ${event.type.padEnd(30)} ${org.slug} | ${event.id}`,
);
};
export const vercelLogMiddleware = async (c: Context<HonoEnv>, next: Next) => {
const { db, logger, org } = c.get("ctx");
const body = await c.req.json();
logVercelWebhook({ logger, org, event: body });
await next();
};

View File

@@ -72,7 +72,5 @@ export const vercelSignatureMiddleware = async (c: any, next: any) => {
return c.json({ error: "Unauthorized", code: "invalid_signature" }, 401);
}
logger.debug("Webhook signature validated", { env });
await next();
};

View File

@@ -65,11 +65,6 @@ export const createVercelSubscription = async ({
metadata?: Record<string, any>;
resourceId?: string;
}): Promise<{ subscription: Stripe.Subscription; product: FullProduct }> => {
logger.info("Creating Vercel subscription", {
billingPlanId,
integrationConfigurationId,
});
// 1. Check for existing subscription (only allow one per installation)
const existingSubscription = stripeCustomer.subscriptions?.data.find(
(s) => s.metadata.vercel_installation_id === integrationConfigurationId,
@@ -121,23 +116,13 @@ export const createVercelSubscription = async ({
});
}
logger.info("Found custom payment method", {
paymentMethodId: customPaymentMethod.id,
type: customPaymentMethod.type,
});
// 4. Parse prepaid quantities from metadata if provided
let optionsList;
if (metadata && Object.keys(metadata).length > 0) {
logger.info("Parsing prepaid quantities from metadata", {
metadata,
productId: product.id,
});
optionsList = parseVercelPrepaidQuantities({
metadata,
product,
prices: product.prices,
logger,
});
}
@@ -176,15 +161,6 @@ export const createVercelSubscription = async ({
logger,
});
logger.info("Subscription created with custom payment method", {
subscriptionId: subscription.id,
status: subscription.status,
latestInvoiceId:
typeof subscription.latest_invoice === "string"
? subscription.latest_invoice
: subscription.latest_invoice?.id,
});
// Subscription will be 'incomplete' initially with an 'open' invoice
// Payment flow:
// 1. invoice.finalized webhook → handleInvoiceFinalized submits invoice to Vercel

View File

@@ -28,7 +28,7 @@ export class VercelResourceService {
db,
installationId: resource.installation_id,
orgId: resource.org_id,
env: resource.env,
env: resource.env as AppEnv,
});
if (existing) {
@@ -136,7 +136,7 @@ export class VercelResourceService {
if (!resource) {
throw new RecaseError({
message: `Resource ${resourceId} not found for installation ${installationId}`,
code: ErrCode.NotFound,
code: ErrCode.VercelResourceNotFound,
statusCode: StatusCodes.NOT_FOUND,
});
}

View File

@@ -14,7 +14,10 @@ import { handleGetResource } from "./handlers/resources/handleGetResource.js";
import { handleUpdateResource } from "./handlers/resources/handleUpdateResource.js";
import { captureRawBody } from "./misc/rawBodyMiddleware.js";
import { vercelOidcAuthMiddleware } from "./misc/vercelAuth.js";
import { vercelSeederMiddleware } from "./misc/vercelMiddleware.js";
import {
vercelLogMiddleware,
vercelSeederMiddleware,
} from "./misc/vercelMiddleware.js";
import { vercelSignatureMiddleware } from "./misc/vercelSignatureMiddleware.js";
export const vercelWebhookRouter = new Hono<HonoEnv>();
@@ -95,10 +98,9 @@ vercelWebhookRouter.post(
vercelSeederMiddleware,
captureRawBody,
vercelSignatureMiddleware,
vercelLogMiddleware,
async (c) => {
const { db, org, env, logger } = c.get("ctx");
const params = c.req.param();
const headers = c.req.header();
let body: any;
try {
body = await c.req.json();
@@ -106,16 +108,6 @@ vercelWebhookRouter.post(
body = {};
}
logger.info("Vercel webhook received", {
method: "POST",
eventType: body.type,
params,
headers,
body,
});
console.log("Vercel webhook headers", JSON.stringify(headers, null, 4));
console.log("Vercel webhook received", "POST", params, body);
const eventType = body.type;
try {

View File

@@ -166,4 +166,5 @@ export const ErrCode = {
// Vercel
VercelSubscriptionAlreadyExists: "vercel_subscription_already_exists",
VercelSubscriptionNotFound: "vercel_subscription_not_found",
VercelResourceNotFound: "vercel_resource_not_found",
};