fix: attaching reward to checkout and multi product attach
This commit is contained in:
@@ -7,9 +7,10 @@ MOCHA_PARALLEL=true $MOCHA_SETUP \
|
|||||||
&& $MOCHA_CMD \
|
&& $MOCHA_CMD \
|
||||||
'tests/attach/basic/*.ts' \
|
'tests/attach/basic/*.ts' \
|
||||||
'tests/attach/upgrade/*.ts' \
|
'tests/attach/upgrade/*.ts' \
|
||||||
'tests/attach/downgrade/*.ts' \
|
'tests/attach/downgrade/*.ts'
|
||||||
|
|
||||||
|
$MOCHA_CMD \
|
||||||
|
'tests/attach/entities/*.ts' \
|
||||||
'tests/attach/free/*.ts'
|
'tests/attach/free/*.ts'
|
||||||
|
|
||||||
$MOCHA_CMD 'tests/attach/entities/*.ts'
|
|
||||||
|
|
||||||
# 'tests/attach/basic/basic2.ts' \
|
# 'tests/attach/basic/basic2.ts' \
|
||||||
@@ -12,8 +12,8 @@ import { ErrCode } from "@/errors/errCodes.js";
|
|||||||
import { getNextStartOfMonthUnix } from "@/internal/products/prices/billingIntervalUtils.js";
|
import { getNextStartOfMonthUnix } from "@/internal/products/prices/billingIntervalUtils.js";
|
||||||
import { APIVersion } from "@autumn/shared";
|
import { APIVersion } from "@autumn/shared";
|
||||||
import { SuccessCode } from "@autumn/shared";
|
import { SuccessCode } from "@autumn/shared";
|
||||||
import { notNullish } from "@/utils/genUtils.js";
|
import { notNullish, nullish } from "@/utils/genUtils.js";
|
||||||
import { getEntityInvoiceDescription } from "@/internal/entities/entityUtils/entityInvoiceUtils.js";
|
|
||||||
import Stripe from "stripe";
|
import Stripe from "stripe";
|
||||||
|
|
||||||
export const handleCreateCheckout = async ({
|
export const handleCreateCheckout = async ({
|
||||||
@@ -29,7 +29,7 @@ export const handleCreateCheckout = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { db, logtail: logger } = req;
|
const { db, logtail: logger } = req;
|
||||||
|
|
||||||
const { customer, org, freeTrial, successUrl } = attachParams;
|
const { customer, org, freeTrial, successUrl, reward } = attachParams;
|
||||||
|
|
||||||
const stripeCli = createStripeCli({
|
const stripeCli = createStripeCli({
|
||||||
org,
|
org,
|
||||||
@@ -67,7 +67,7 @@ export const handleCreateCheckout = async ({
|
|||||||
|
|
||||||
if (attachParams.billingAnchor) {
|
if (attachParams.billingAnchor) {
|
||||||
billingCycleAnchorUnixSeconds = Math.floor(
|
billingCycleAnchorUnixSeconds = Math.floor(
|
||||||
attachParams.billingAnchor / 1000,
|
attachParams.billingAnchor / 1000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +84,18 @@ export const handleCreateCheckout = async ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
let checkoutParams = attachParams.checkoutSessionParams || {};
|
let checkoutParams = attachParams.checkoutSessionParams || {};
|
||||||
let allowPromotionCodes = notNullish(checkoutParams.discounts)
|
let allowPromotionCodes =
|
||||||
|
notNullish(checkoutParams.discounts) || notNullish(reward)
|
||||||
? undefined
|
? undefined
|
||||||
: checkoutParams.allow_promotion_codes || true;
|
: checkoutParams.allow_promotion_codes || true;
|
||||||
|
|
||||||
|
let rewardData = {};
|
||||||
|
if (reward) {
|
||||||
|
rewardData = {
|
||||||
|
discounts: [{ coupon: reward.id }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const checkout = await stripeCli.checkout.sessions.create({
|
const checkout = await stripeCli.checkout.sessions.create({
|
||||||
customer: customer.processor.id,
|
customer: customer.processor.id,
|
||||||
line_items: items,
|
line_items: items,
|
||||||
@@ -100,6 +108,7 @@ export const handleCreateCheckout = async ({
|
|||||||
...(attachParams.metadata ? attachParams.metadata : {}),
|
...(attachParams.metadata ? attachParams.metadata : {}),
|
||||||
},
|
},
|
||||||
allow_promotion_codes: allowPromotionCodes,
|
allow_promotion_codes: allowPromotionCodes,
|
||||||
|
...rewardData,
|
||||||
invoice_creation: !isRecurring
|
invoice_creation: !isRecurring
|
||||||
? {
|
? {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -130,7 +139,7 @@ export const handleCreateCheckout = async ({
|
|||||||
}, product(s) ${attachParams.products.map((p) => p.name).join(", ")}`,
|
}, product(s) ${attachParams.products.map((p) => p.name).join(", ")}`,
|
||||||
product_ids: attachParams.products.map((p) => p.id),
|
product_ids: attachParams.products.map((p) => p.id),
|
||||||
customer_id: customer.id || customer.internal_id,
|
customer_id: customer.id || customer.internal_id,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const getProductsForAttach = async ({
|
|||||||
|
|
||||||
if (notNullish(product_ids)) {
|
if (notNullish(product_ids)) {
|
||||||
let freeTrialProds = products.filter((prod) => notNullish(prod.free_trial));
|
let freeTrialProds = products.filter((prod) => notNullish(prod.free_trial));
|
||||||
console.log("freeTrialProds", freeTrialProds);
|
|
||||||
if (freeTrialProds.length > 1) {
|
if (freeTrialProds.length > 1) {
|
||||||
throw new RecaseError({
|
throw new RecaseError({
|
||||||
message:
|
message:
|
||||||
@@ -36,6 +36,8 @@ const getProductsForAttach = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const prod of products) {
|
for (const prod of products) {
|
||||||
|
if (prod.is_add_on) continue;
|
||||||
|
|
||||||
let otherProd = products.find(
|
let otherProd = products.find(
|
||||||
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id
|
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default class RecaseError extends Error {
|
|||||||
export function formatZodError(error: ZodError): string {
|
export function formatZodError(error: ZodError): string {
|
||||||
return error.errors
|
return error.errors
|
||||||
.map((err) =>
|
.map((err) =>
|
||||||
err.path.length ? `${err.path.join(".")}: ${err.message}` : err.message,
|
err.path.length ? `${err.path.join(".")}: ${err.message}` : err.message
|
||||||
)
|
)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
}
|
}
|
||||||
@@ -112,23 +112,9 @@ export const handleRequestError = ({
|
|||||||
`RECASE WARNING (${req.org?.slug || "unknown"}): ${error.message} [${error.code}]`,
|
`RECASE WARNING (${req.org?.slug || "unknown"}): ${error.message} [${error.code}]`,
|
||||||
{
|
{
|
||||||
error: error.data,
|
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({
|
res.status(error.statusCode).json({
|
||||||
message: error.message,
|
message: error.message,
|
||||||
code: error.code,
|
code: error.code,
|
||||||
@@ -137,14 +123,6 @@ export const handleRequestError = ({
|
|||||||
return;
|
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) {
|
if (error instanceof Stripe.errors.StripeError) {
|
||||||
let curStack;
|
let curStack;
|
||||||
try {
|
try {
|
||||||
@@ -161,7 +139,7 @@ export const handleRequestError = ({
|
|||||||
...rest,
|
...rest,
|
||||||
stack: curStack,
|
stack: curStack,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
@@ -170,7 +148,7 @@ export const handleRequestError = ({
|
|||||||
});
|
});
|
||||||
} else if (error instanceof ZodError) {
|
} else if (error instanceof ZodError) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`ZOD ERROR (${req.org?.slug || "unknown"}): ${formatZodError(error)}`,
|
`ZOD ERROR (${req.org?.slug || "unknown"}): ${formatZodError(error)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
@@ -185,7 +163,7 @@ export const handleRequestError = ({
|
|||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
@@ -220,7 +198,7 @@ export const handleFrontendReqError = ({
|
|||||||
error.statusCode == StatusCodes.NOT_FOUND
|
error.statusCode == StatusCodes.NOT_FOUND
|
||||||
) {
|
) {
|
||||||
req.logtail.warn(
|
req.logtail.warn(
|
||||||
`(frontend) ${req.method} ${req.originalUrl}: not found`,
|
`(frontend) ${req.method} ${req.originalUrl}: not found`
|
||||||
);
|
);
|
||||||
res.status(404).json({
|
res.status(404).json({
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
|||||||
@@ -14,15 +14,11 @@ import Stripe from "stripe";
|
|||||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "tests/before.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "tests/utils/productUtils.js";
|
||||||
import { addPrefixToProducts, runAttachTest } from "../utils.js";
|
import { addPrefixToProducts } from "../utils.js";
|
||||||
import {
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
constructFeatureItem,
|
|
||||||
constructPrepaidItem,
|
|
||||||
} from "@/utils/scriptUtils/constructItem.js";
|
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "tests/setup/v2Features.js";
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { addDays } from "date-fns";
|
||||||
import { addDays, addWeeks } from "date-fns";
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"author": "Recase Inc.",
|
"author": "Recase Inc.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"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\"",
|
"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",
|
"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",
|
"dotenv": "^16.5.0",
|
||||||
"drizzle-kit": "^0.31.1",
|
"drizzle-kit": "^0.31.1",
|
||||||
"drizzle-orm": "^0.43.1",
|
"drizzle-orm": "^0.43.1",
|
||||||
"drizzle-zod": "^0.8.2",
|
"drizzle-zod": "^0.8.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
"zod": "^3.25.23"
|
"zod": "^3.25.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user