11import { createId } from "@/lib/api/create-id" ;
2+ import { DubApiError } from "@/lib/api/errors" ;
23import { prisma } from "@dub/prisma" ;
3- import { Discount , Link , Partner , Project } from "@dub/prisma/client" ;
4+ import { Discount , Link , Partner , Prisma , Project } from "@dub/prisma/client" ;
45import { constructDiscountCode } from "./construct-discount-code" ;
56import { getDiscountProvider } from "./discount-provider" ;
67
@@ -19,33 +20,90 @@ export async function createDiscountCode({
1920 discount,
2021 code,
2122} : CreateDiscountCodeArgs ) {
22- let finalCode = code ;
23-
24- // Construct the discount code if no code is provided
25- if ( ! finalCode ) {
26- finalCode = constructDiscountCode ( {
23+ const finalCode =
24+ code ||
25+ constructDiscountCode ( {
2726 partner,
2827 discount,
2928 } ) ;
29+
30+ const linkWithCode = await prisma . link . findUnique ( {
31+ where : { id : link . id } ,
32+ select : { discountCode : { select : { code : true } } } ,
33+ } ) ;
34+
35+ if ( linkWithCode ?. discountCode ) {
36+ throw new DubApiError ( {
37+ code : "bad_request" ,
38+ message : `This link already has a discount code (${ linkWithCode . discountCode . code } ) assigned.` ,
39+ } ) ;
3040 }
3141
3242 const discountProvider = getDiscountProvider ( discount . provider ) ;
3343
34- const discountCode = await discountProvider . createDiscountCode ( {
35- workspace,
36- discount,
37- code : finalCode ,
38- shouldRetry : code ? false : true ,
39- } ) ;
44+ let externalDiscountCode : Awaited <
45+ ReturnType < typeof discountProvider . createDiscountCode >
46+ > ;
4047
41- return await prisma . discountCode . create ( {
42- data : {
43- id : createId ( { prefix : "dcode_" } ) ,
44- code : discountCode . code ,
45- programId : discount . programId ,
46- partnerId : partner . id ,
47- linkId : link . id ,
48- discountId : discount . id ,
49- } ,
50- } ) ;
48+ try {
49+ externalDiscountCode = await discountProvider . createDiscountCode ( {
50+ workspace,
51+ discount,
52+ code : finalCode ,
53+ shouldRetry : code ? false : true ,
54+ } ) ;
55+ } catch ( error ) {
56+ const message = error ?. raw ?. message || error ?. message || "" ;
57+ const isDuplicateCode =
58+ message . includes ( "already exists" ) ||
59+ error ?. code === "TAKEN" ||
60+ error ?. code === "DUPLICATE" ;
61+
62+ if ( isDuplicateCode ) {
63+ throw new DubApiError ( {
64+ code : "conflict" ,
65+ message : `The discount code ${ finalCode } is already in use. Please choose a different code.` ,
66+ } ) ;
67+ }
68+
69+ throw error ;
70+ }
71+
72+ try {
73+ return await prisma . discountCode . create ( {
74+ data : {
75+ id : createId ( { prefix : "dcode_" } ) ,
76+ code : externalDiscountCode . code ,
77+ programId : discount . programId ,
78+ partnerId : partner . id ,
79+ linkId : link . id ,
80+ discountId : discount . id ,
81+ } ,
82+ } ) ;
83+ } catch ( error ) {
84+ try {
85+ await discountProvider . disableDiscountCode ( {
86+ workspace,
87+ code : externalDiscountCode . code ,
88+ } ) ;
89+ } catch ( rollbackError ) {
90+ console . error ( "Failed to rollback external discount code" , {
91+ code : externalDiscountCode . code ,
92+ rollbackError,
93+ } ) ;
94+ }
95+
96+ if (
97+ error instanceof Prisma . PrismaClientKnownRequestError &&
98+ error . code === "P2002"
99+ ) {
100+ throw new DubApiError ( {
101+ code : "conflict" ,
102+ message :
103+ "This discount code is already in use, or this link already has a code. Please refresh and try again." ,
104+ } ) ;
105+ }
106+
107+ throw error ;
108+ }
51109}
0 commit comments