fix: attaching reward to checkout and multi product attach

This commit is contained in:
John Yeo
2025-07-23 10:31:01 +01:00
parent d8d343cff7
commit 5547cc2c1f
6 changed files with 37 additions and 49 deletions

View File

@@ -7,9 +7,10 @@ MOCHA_PARALLEL=true $MOCHA_SETUP \
&& $MOCHA_CMD \
'tests/attach/basic/*.ts' \
'tests/attach/upgrade/*.ts' \
'tests/attach/downgrade/*.ts' \
'tests/attach/downgrade/*.ts'
$MOCHA_CMD \
'tests/attach/entities/*.ts' \
'tests/attach/free/*.ts'
$MOCHA_CMD 'tests/attach/entities/*.ts'
# 'tests/attach/basic/basic2.ts' \

View File

@@ -12,8 +12,8 @@ import { ErrCode } from "@/errors/errCodes.js";
import { getNextStartOfMonthUnix } from "@/internal/products/prices/billingIntervalUtils.js";
import { APIVersion } from "@autumn/shared";
import { SuccessCode } from "@autumn/shared";
import { notNullish } from "@/utils/genUtils.js";
import { getEntityInvoiceDescription } from "@/internal/entities/entityUtils/entityInvoiceUtils.js";
import { notNullish, nullish } from "@/utils/genUtils.js";
import Stripe from "stripe";
export const handleCreateCheckout = async ({
@@ -29,7 +29,7 @@ export const handleCreateCheckout = async ({
}) => {
const { db, logtail: logger } = req;
const { customer, org, freeTrial, successUrl } = attachParams;
const { customer, org, freeTrial, successUrl, reward } = attachParams;
const stripeCli = createStripeCli({
org,
@@ -67,7 +67,7 @@ export const handleCreateCheckout = async ({
if (attachParams.billingAnchor) {
billingCycleAnchorUnixSeconds = Math.floor(
attachParams.billingAnchor / 1000,
attachParams.billingAnchor / 1000
);
}
@@ -84,9 +84,17 @@ export const handleCreateCheckout = async ({
: undefined;
let checkoutParams = attachParams.checkoutSessionParams || {};
let allowPromotionCodes = notNullish(checkoutParams.discounts)
? undefined
: checkoutParams.allow_promotion_codes || true;
let allowPromotionCodes =
notNullish(checkoutParams.discounts) || notNullish(reward)
? undefined
: checkoutParams.allow_promotion_codes || true;
let rewardData = {};
if (reward) {
rewardData = {
discounts: [{ coupon: reward.id }],
};
}
const checkout = await stripeCli.checkout.sessions.create({
customer: customer.processor.id,
@@ -100,6 +108,7 @@ export const handleCreateCheckout = async ({
...(attachParams.metadata ? attachParams.metadata : {}),
},
allow_promotion_codes: allowPromotionCodes,
...rewardData,
invoice_creation: !isRecurring
? {
enabled: true,
@@ -130,7 +139,7 @@ export const handleCreateCheckout = async ({
}, product(s) ${attachParams.products.map((p) => p.name).join(", ")}`,
product_ids: attachParams.products.map((p) => p.id),
customer_id: customer.id || customer.internal_id,
}),
})
);
} else {
res.status(200).json({

View File

@@ -26,7 +26,7 @@ const getProductsForAttach = async ({
if (notNullish(product_ids)) {
let freeTrialProds = products.filter((prod) => notNullish(prod.free_trial));
console.log("freeTrialProds", freeTrialProds);
if (freeTrialProds.length > 1) {
throw new RecaseError({
message:
@@ -36,6 +36,8 @@ const getProductsForAttach = async ({
}
for (const prod of products) {
if (prod.is_add_on) continue;
let otherProd = products.find(
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id
);

View File

@@ -49,7 +49,7 @@ export default class RecaseError extends Error {
export function formatZodError(error: ZodError): string {
return error.errors
.map((err) =>
err.path.length ? `${err.path.join(".")}: ${err.message}` : err.message,
err.path.length ? `${err.path.join(".")}: ${err.message}` : err.message
)
.join(", ");
}
@@ -112,23 +112,9 @@ export const handleRequestError = ({
`RECASE WARNING (${req.org?.slug || "unknown"}): ${error.message} [${error.code}]`,
{
error: error.data,
},
}
);
// logReqUrl(logger, req, "warn");
// logger.warn(
// `Request from ${req.org?.slug || req.orgId || "unknown"} for ${action}`,
// );
// error.print(logger);
// if (req.originalUrl.includes("/webhooks/stripe")) {
// logger.warn("request body", {
// body: getJsonBody(req.body),
// });
// } else {
// logRequestBody(logger, req, "warn");
// }
// logger.warn("--------------------------------");
res.status(error.statusCode).json({
message: error.message,
code: error.code,
@@ -137,14 +123,6 @@ export const handleRequestError = ({
return;
}
// logger.error("--------------------------------");
// logger.error("ERROR");
// // logger.error(`${req.method} ${req.originalUrl}`);
// logReqUrl(logger, req, "error");
// logger.error(
// `Request from ${req.org?.slug || req.orgId || "unknown"} for ${action}`,
// );
if (error instanceof Stripe.errors.StripeError) {
let curStack;
try {
@@ -161,7 +139,7 @@ export const handleRequestError = ({
...rest,
stack: curStack,
},
},
}
);
res.status(400).json({
@@ -170,7 +148,7 @@ export const handleRequestError = ({
});
} else if (error instanceof ZodError) {
logger.error(
`ZOD ERROR (${req.org?.slug || "unknown"}): ${formatZodError(error)}`,
`ZOD ERROR (${req.org?.slug || "unknown"}): ${formatZodError(error)}`
);
res.status(400).json({
@@ -185,7 +163,7 @@ export const handleRequestError = ({
stack: error.stack,
message: error.message,
},
},
}
);
res.status(500).json({
@@ -220,7 +198,7 @@ export const handleFrontendReqError = ({
error.statusCode == StatusCodes.NOT_FOUND
) {
req.logtail.warn(
`(frontend) ${req.method} ${req.originalUrl}: not found`,
`(frontend) ${req.method} ${req.originalUrl}: not found`
);
res.status(404).json({
message: error.message,

View File

@@ -14,15 +14,11 @@ import Stripe from "stripe";
import { DrizzleCli } from "@/db/initDrizzle.js";
import { setupBefore } from "tests/before.js";
import { createProducts } from "tests/utils/productUtils.js";
import { addPrefixToProducts, runAttachTest } from "../utils.js";
import {
constructFeatureItem,
constructPrepaidItem,
} from "@/utils/scriptUtils/constructItem.js";
import { addPrefixToProducts } from "../utils.js";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
import { TestFeature } from "tests/setup/v2Features.js";
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
import { advanceTestClock } from "tests/utils/stripeUtils.js";
import { addDays, addWeeks } from "date-fns";
import { addDays } from "date-fns";
import { expect } from "chai";
import { eq } from "drizzle-orm";

View File

@@ -13,7 +13,7 @@
"author": "Recase Inc.",
"license": "Apache-2.0",
"scripts": {
"build": "bun build ./index.ts --outdir dist --target bun",
"build": "bun build ./index.ts --outdir dist --target bun --external zod",
"dev": "bunx nodemon --ext ts --ignore dist --exec \"bun run build\"",
"db:push": "cross-env NODE_OPTIONS=\"--import tsx\" pnpm exec drizzle-kit push --config drizzle.config.ts",
@@ -26,7 +26,9 @@
"dotenv": "^16.5.0",
"drizzle-kit": "^0.31.1",
"drizzle-orm": "^0.43.1",
"drizzle-zod": "^0.8.2",
"drizzle-zod": "^0.8.2"
},
"peerDependencies": {
"zod": "^3.25.23"
},
"devDependencies": {