Files
cfw-autumn/server/src/internal/api/rewards/handlers/referrals/handleRedeemReferral.ts

377 lines
10 KiB
TypeScript

import {
CustomerNotFoundError,
ErrCode,
InternalError,
RecaseError,
RewardCategory,
type RewardRedemption,
RewardTriggerEvent,
} from "@autumn/shared";
import { z } from "zod/v4";
import { parseCtxForAction } from "@/internal/analytics/actionUtils.js";
import { CusService } from "@/internal/customers/CusService.js";
import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js";
import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js";
import { RewardService } from "@/internal/rewards/RewardService.js";
import { triggerFreeProduct } from "@/internal/rewards/referralUtils/triggerFreeProduct.js";
import { triggerRedemption } from "@/internal/rewards/referralUtils.js";
import { getRewardCat } from "@/internal/rewards/rewardUtils.js";
import { generateId, notNullish } from "@/utils/genUtils.js";
import type { ExtendedRequest } from "@/utils/models/Request.js";
import { createRoute } from "../../../../../honoMiddlewares/routeHandler";
export const handleRedeemReferral = createRoute({
body: z.object({
code: z.string(),
customer_id: z.string(),
}),
handler: async (c) => {
const ctx = c.get("ctx");
const { db, org, env } = ctx;
const { code, customer_id: customerId } = c.req.valid("json");
// 1. Get redeemed by customer, and referral code
const [customer, referralCode] = await Promise.all([
CusService.get({
db,
orgId: org.id,
env,
idOrInternalId: customerId,
}),
RewardProgramService.getReferralCode({
db,
orgId: org.id,
env,
code,
withRewardProgram: true,
}),
]);
if (!customer) throw new CustomerNotFoundError({ customerId });
// 2. Check that code has not reached max redemptions
const redemptionCount = await RewardProgramService.getCodeRedemptionCount({
db,
referralCodeId: referralCode.id,
});
if (
referralCode.reward_program.max_redemptions &&
redemptionCount >= referralCode.reward_program.max_redemptions
) {
throw new RecaseError({
message: "Referral code has reached max redemptions",
statusCode: 400,
code: ErrCode.ReferralCodeMaxRedemptionsReached,
});
}
// 3. Check that customer has not already redeemed a code in this referral program
const existingRedemptions = await RewardRedemptionService.getByCustomer({
db,
internalCustomerId: customer.internal_id,
internalRewardProgramId: referralCode.internal_reward_program_id,
});
if (existingRedemptions.length > 0) {
throw new RecaseError({
message: `Customer ${customer.id} has already redeemed a code in this referral program`,
statusCode: 400,
code: ErrCode.CustomerAlreadyRedeemedReferralCode,
});
}
// Don't let customer redeem their own code
const codeCustomer = await CusService.getByInternalId({
db,
internalId: referralCode.internal_customer_id,
});
if (!codeCustomer) {
throw new InternalError({
message: `Referral code customer not found, internal ID: ${referralCode.internal_customer_id}`,
});
}
if (
codeCustomer.id === customer.id ||
(notNullish(codeCustomer.fingerprint) &&
codeCustomer.fingerprint === customer.fingerprint)
) {
throw new RecaseError({
message: "Customer cannot redeem their own code",
statusCode: 400,
code: ErrCode.CustomerCannotRedeemOwnCode,
});
}
// 4. Insert redemption into db
let redemption: RewardRedemption = {
id: generateId("rr"),
referral_code_id: referralCode.id,
internal_customer_id: customer.internal_id, // redeemed by customer
internal_reward_program_id: referralCode.internal_reward_program_id,
created_at: Date.now(),
triggered:
referralCode.reward_program.when ===
RewardTriggerEvent.CustomerCreation,
applied: false,
updated_at: Date.now(),
redeemer_applied: false,
};
redemption = await RewardRedemptionService.insert({
db,
rewardRedemption: redemption,
});
// 5. If reward trigger when is immediate:
const { reward_program } = referralCode;
const redeemRewardNow =
referralCode.reward_program.when === RewardTriggerEvent.CustomerCreation;
if (redeemRewardNow) {
const reward = await RewardService.get({
db,
orgId: org.id,
env,
idOrInternalId: reward_program.internal_reward_id,
});
if (!reward) {
throw new RecaseError({
message: `Reward ${reward_program.internal_reward_id} not found`,
statusCode: 404,
code: ErrCode.RewardNotFound,
});
}
const rewardCat = getRewardCat(reward);
if (rewardCat === RewardCategory.FreeProduct) {
await triggerFreeProduct({
req: parseCtxForAction({ ctx }) as ExtendedRequest,
db,
referralCode,
redeemer: customer,
rewardProgram: reward_program,
org,
env,
logger: ctx.logger,
redemption,
});
} else {
await triggerRedemption({
db,
referralCode,
org,
env,
logger: ctx.logger,
reward,
redemption,
});
}
}
return c.json({
id: redemption.id,
customer_id: customer.id,
reward_id: reward_program.reward.id,
referrer: {
id: codeCustomer.id,
name: codeCustomer.name,
email: codeCustomer.email,
created_at: codeCustomer.created_at,
},
redeemer: {
id: customer.id,
name: customer.name,
email: customer.email,
created_at: customer.created_at,
},
});
},
});
// export default async (req: any, res: any) =>
// routeHandler({
// req,
// res,
// action: "redeem referral code",
// handler: async (req, res) => {
// const { orgId, env, logger, db } = req;
// const { code, customer_id: customerId } = req.body;
// // 1. Get redeemed by customer, and referral code
// const [customer, referralCode, org] = await Promise.all([
// CusService.get({
// db,
// orgId,
// env,
// idOrInternalId: customerId,
// }),
// RewardProgramService.getReferralCode({
// db,
// orgId,
// env,
// code,
// withRewardProgram: true,
// }),
// OrgService.getFromReq(req),
// ]);
// if (!customer) {
// throw new RecaseError({
// message: "Customer not found",
// statusCode: 404,
// code: ErrCode.CustomerNotFound,
// });
// }
// // 2. Check that code has not reached max redemptions
// const redemptionCount = await RewardProgramService.getCodeRedemptionCount(
// {
// db,
// referralCodeId: referralCode.id,
// },
// );
// if (
// referralCode.reward_program.max_redemptions &&
// redemptionCount >= referralCode.reward_program.max_redemptions
// ) {
// throw new RecaseError({
// message: "Referral code has reached max redemptions",
// statusCode: 400,
// code: ErrCode.ReferralCodeMaxRedemptionsReached,
// });
// }
// // 3. Check that customer has not already redeemed a code in this referral program
// const existingRedemptions = await RewardRedemptionService.getByCustomer({
// db,
// internalCustomerId: customer.internal_id,
// internalRewardProgramId: referralCode.internal_reward_program_id,
// });
// if (existingRedemptions.length > 0) {
// throw new RecaseError({
// message: `Customer ${customer.id} has already redeemed a code in this referral program`,
// statusCode: 400,
// code: ErrCode.CustomerAlreadyRedeemedReferralCode,
// });
// }
// // Don't let customer redeem their own code
// const codeCustomer = await CusService.getByInternalId({
// db: req.db,
// internalId: referralCode.internal_customer_id,
// });
// if (!codeCustomer) {
// throw new RecaseError({
// message: "Referral code customer not found",
// statusCode: 404,
// code: ErrCode.CustomerNotFound,
// });
// }
// if (
// codeCustomer.id === customer.id ||
// (notNullish(codeCustomer.fingerprint) &&
// codeCustomer.fingerprint === customer.fingerprint)
// ) {
// throw new RecaseError({
// message: "Customer cannot redeem their own code",
// statusCode: 400,
// code: ErrCode.CustomerCannotRedeemOwnCode,
// });
// }
// // 4. Insert redemption into db
// let redemption: RewardRedemption = {
// id: generateId("rr"),
// referral_code_id: referralCode.id,
// internal_customer_id: customer.internal_id, // redeemed by customer
// internal_reward_program_id: referralCode.internal_reward_program_id,
// created_at: Date.now(),
// triggered:
// referralCode.reward_program.when ===
// RewardTriggerEvent.CustomerCreation,
// applied: false,
// updated_at: Date.now(),
// redeemer_applied: false,
// };
// redemption = await RewardRedemptionService.insert({
// db,
// rewardRedemption: redemption,
// });
// // 5. If reward trigger when is immediate:
// const { reward_program } = referralCode;
// const redeemRewardNow =
// referralCode.reward_program.when ===
// RewardTriggerEvent.CustomerCreation;
// if (redeemRewardNow) {
// const reward = await RewardService.get({
// db,
// orgId,
// env,
// idOrInternalId: reward_program.internal_reward_id,
// });
// if (!reward) {
// throw new RecaseError({
// message: `Reward ${reward_program.internal_reward_id} not found`,
// statusCode: 404,
// code: ErrCode.RewardNotFound,
// });
// }
// const rewardCat = getRewardCat(reward);
// if (rewardCat === RewardCategory.FreeProduct) {
// await triggerFreeProduct({
// req: parseReqForAction(req) as ExtendedRequest,
// db,
// referralCode,
// redeemer: customer,
// rewardProgram: reward_program,
// org,
// env,
// logger,
// redemption,
// });
// } else {
// await triggerRedemption({
// db,
// referralCode,
// org,
// env,
// logger,
// reward,
// redemption,
// });
// }
// }
// return res.status(200).json({
// id: redemption.id,
// customer_id: customer.id,
// reward_id: reward_program.reward.id,
// referrer: {
// id: codeCustomer.id,
// name: codeCustomer.name,
// email: codeCustomer.email,
// created_at: codeCustomer.created_at,
// },
// redeemer: {
// id: customer.id,
// name: customer.name,
// email: customer.email,
// created_at: customer.created_at,
// },
// });
// },
// });