-
Notifications
You must be signed in to change notification settings - Fork 522
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Registration Discount codes (#1084)
* discount code temp * comments * BE: Proof of concept * mark/increment code as consumed * bad code * bad code * dry it up * remove test code * cleanup and fix * suspense * undo change * refactor: use internal transaction/capacities hooks for registration flow * refetch basename * fix: wrong name being used when reverseRecord is false * prefetch * ssr code instead of search params * add banner & code
- Loading branch information
Showing
17 changed files
with
570 additions
and
274 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import type { NextApiRequest, NextApiResponse } from 'next'; | ||
import { logger } from 'apps/web/src/utils/logger'; | ||
import { withTimeout } from 'apps/web/pages/api/decorators'; | ||
import { incrementDiscountCodeUsage } from 'apps/web/src/utils/proofs/discount_code_storage'; | ||
|
||
/* | ||
this endpoint will increment the discount code usage to prevent abuse | ||
*/ | ||
|
||
type DiscountCodeRequest = { | ||
code: string; | ||
}; | ||
|
||
async function handler(req: NextApiRequest, res: NextApiResponse) { | ||
if (req.method !== 'POST') { | ||
return res.status(405).json({ error: 'Method not allowed' }); | ||
} | ||
|
||
try { | ||
const { code } = req.body as DiscountCodeRequest; | ||
|
||
if (!code || typeof code !== 'string') { | ||
return res.status(500).json({ error: 'Invalid request' }); | ||
} | ||
|
||
await incrementDiscountCodeUsage(code); | ||
|
||
return res.status(200).json({ success: true }); | ||
} catch (error: unknown) { | ||
logger.error('error incrementing the discount code', error); | ||
} | ||
// If error is not an instance of Error, return a generic error message | ||
return res.status(500).json({ error: 'An unexpected error occurred' }); | ||
} | ||
|
||
export default withTimeout(handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import type { NextApiRequest, NextApiResponse } from 'next'; | ||
import { proofValidation, signDiscountMessageWithTrustedSigner } from 'apps/web/src/utils/proofs'; | ||
import { logger } from 'apps/web/src/utils/logger'; | ||
import { withTimeout } from 'apps/web/pages/api/decorators'; | ||
import { Address, Hash, stringToHex } from 'viem'; | ||
import { USERNAME_DISCOUNT_CODE_VALIDATORS } from 'apps/web/src/addresses/usernames'; | ||
import { baseSepolia } from 'viem/chains'; | ||
import { getDiscountCode } from 'apps/web/src/utils/proofs/discount_code_storage'; | ||
|
||
export type DiscountCodeResponse = { | ||
discountValidatorAddress: Address; | ||
address: Address; | ||
signedMessage: Hash; | ||
}; | ||
|
||
/* | ||
this endpoint returns whether or a discount code is valid | ||
*/ | ||
async function handler(req: NextApiRequest, res: NextApiResponse) { | ||
if (req.method !== 'GET') { | ||
return res.status(405).json({ error: 'method not allowed' }); | ||
} | ||
const { address, chain, code } = req.query; | ||
const validationErr = proofValidation(address, chain); | ||
if (validationErr) { | ||
return res.status(validationErr.status).json({ error: validationErr.error }); | ||
} | ||
|
||
if (!code || typeof code !== 'string') { | ||
return res.status(500).json({ error: 'Discount code invalid' }); | ||
} | ||
|
||
try { | ||
// 1. get the database model | ||
const discountCodes = await getDiscountCode(code); | ||
|
||
// 2. Validation: Coupon exists | ||
if (!discountCodes || discountCodes.length === 0) { | ||
return res.status(500).json({ error: 'Discount code invalid' }); | ||
} | ||
|
||
const discountCode = discountCodes[0]; | ||
|
||
// 2.1 Validation: Coupon is expired | ||
if (new Date(discountCode.expires_at) < new Date()) { | ||
return res.status(500).json({ error: 'Discount code invalid' }); | ||
} | ||
|
||
// 2.2 Validation: Coupon can be redeemed | ||
if (Number(discountCode.usage_count) >= Number(discountCode.usage_limit)) { | ||
return res.status(500).json({ error: 'Discount code invalid' }); | ||
} | ||
|
||
// 3. Sign the validationData | ||
const couponCodeUuid = stringToHex(discountCode.code, { size: 32 }); | ||
const expirationTimeUnix = Math.floor(discountCode.expires_at.getTime() / 1000); | ||
|
||
const signature = await signDiscountMessageWithTrustedSigner( | ||
address as Address, | ||
couponCodeUuid, | ||
// TODO: Set variable chain | ||
USERNAME_DISCOUNT_CODE_VALIDATORS[baseSepolia.id], | ||
expirationTimeUnix, | ||
); | ||
|
||
// 4. Return the discount data | ||
const result: DiscountCodeResponse = { | ||
discountValidatorAddress: USERNAME_DISCOUNT_CODE_VALIDATORS[baseSepolia.id], | ||
address: address as Address, | ||
signedMessage: signature, | ||
}; | ||
|
||
return res.status(200).json(result); | ||
} catch (error: unknown) { | ||
logger.error('error getting proofs for discount code', error); | ||
} | ||
// If error is not an instance of Error, return a generic error message | ||
return res.status(500).json({ error: 'An unexpected error occurred' }); | ||
} | ||
|
||
export default withTimeout(handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.