UNPKG

@coursebuilder/core

Version:

Core package for Course Builder

130 lines (118 loc) 3.74 kB
import { NodemailerConfig } from '@auth/core/providers/nodemailer' import { FULL_PRICE_COUPON_REDEEMED_EVENT } from 'src/inngest/commerce/event-full-price-coupon-redeemed' import { z } from 'zod' import { InternalOptions, RequestInternal, ResponseInternal } from '../../types' import { sendServerEmail } from '../send-server-email' import { Cookie } from '../utils/cookie' export async function redeem( request: RequestInternal, cookies: Cookie[], options: InternalOptions, ): Promise<ResponseInternal> { if (!options.adapter) throw new Error('Adapter not found') const { email: baseEmail, couponId, sendEmail = true, productIds, } = z .object({ email: z.string().email(), couponId: z.string().optional(), sendEmail: z.boolean().optional().default(true), productIds: z.array(z.string()).optional(), }) .parse(request.body) if (!baseEmail) throw new Error(`invalid-email-${baseEmail}`) const currentUser = await options.adapter.getUserByEmail(baseEmail) const createdPurchase = await options.adapter.redeemFullPriceCoupon({ email: baseEmail, couponId, productIds, currentUserId: currentUser?.id, redeemingProductId: productIds?.[0], }) if (createdPurchase) { const { purchase, redeemingForCurrentUser } = createdPurchase // if it's redeemed for the current user we don't need to send a login email if (redeemingForCurrentUser) { // send an appropriate email - but still need to trigger the workflow for entitlements if (purchase) { const product = await options.adapter.getProduct(purchase.productId) if (product) { try { await options.inngest.send({ name: FULL_PRICE_COUPON_REDEEMED_EVENT, data: { purchaseId: purchase.id, productType: product.type || 'self-paced', }, user: { id: purchase.userId, email: baseEmail, }, }) } catch (e) { console.log( 'error sending inngest event for current user redemption', e, ) throw new Error('unable-to-send-inngest-event') } } } } else if (purchase?.userId && sendEmail) { const user = await options.adapter.getUserById(purchase.userId) if (!user) throw new Error(`unable-to-find-user-with-id-${purchase.id}`) const emailProvider = options.providers.find((p) => p.type === 'email') const product = await options.adapter.getProduct(purchase.productId) if (product) { try { await options.inngest.send({ name: FULL_PRICE_COUPON_REDEEMED_EVENT, data: { purchaseId: purchase.id, productType: product.type || 'self-paced', }, user, }) } catch (e) { console.log('error', e) throw new Error('unable-to-send-inngest-event') } } try { await sendServerEmail({ email: user.email as string, baseUrl: options.baseUrl, callbackUrl: `${options.baseUrl}/welcome?purchaseId=${purchase.id}`, adapter: options.adapter, authOptions: options.authConfig, emailProvider: emailProvider ? (emailProvider as NodemailerConfig) : undefined, }) } catch (e) { console.log('error', e) throw new Error('unable-to-send-email') } } // TODO: create Slack Provider // Post to Slack to notify the team when a special-purpose coupon is // redeemed. Ignore redemption of bulk coupon. // if (params.options.slack?.redeem && !bulkCouponRedemption) { // await postRedemptionToSlack( // user.email, // purchase.productId, // params.options.slack, // ) // } return { body: { purchase, redeemingForCurrentUser }, headers: { 'Content-Type': 'application/json' }, cookies, } } else { throw new Error(`coupon-is-invalid-${couponId}`) } }