Skip to content

Add promo codes for first payment - #679

Open
alisawavezen12 wants to merge 47 commits into
masterfrom
feat/promo-code-v2
Open

Add promo codes for first payment#679
alisawavezen12 wants to merge 47 commits into
masterfrom
feat/promo-code-v2

Conversation

@alisawavezen12

Copy link
Copy Markdown

Summary

  • Add promo code verification, pricing, and usage tracking (percent_discount, fixed_price; other benefit types are rejected).
  • composePayment returns the amount to charge now (chargeAmount) and signs only promo.id in the checksum; CloudPayments /check and /pay recompute the price from DB.
  • First widget charge can be discounted; recurrent renewals stay on the full plan price. UTM is stored with promo usage.

Dobrunia Kostrigin and others added 30 commits June 11, 2026 19:37
…ne promo code application and workspace unblocking
…c to improve clarity and remove deprecated methods
…r improved readability and maintainability
…ocks and utility functions for improved clarity and functionality
…e subscription renewal handling in CloudPayments
…ture setup and utility functions for clarity

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-payment promo discounts with server-side validation, CloudPayments integration, usage tracking, and UTM attribution.

Changes:

  • Adds promo models, pricing, validation, usage limits, and indexes.
  • Integrates discounted charges into GraphQL and CloudPayments.
  • Adds extensive unit and integration coverage.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
package.json Bumps version and shared types.
yarn.lock Locks updated shared types.
migrations/20260615140000-add-promo-code-indexes.js Adds promo indexes.
src/index.ts Registers promo factories.
src/types/graphql.ts Extends context factories.
src/models/promoCode.ts Defines promo model.
src/models/promoCodesFactory.ts Adds promo lookup factory.
src/models/promoCodeUsage.ts Defines usage model.
src/models/promoCodeUsagesFactory.ts Adds usage persistence.
src/models/user.ts Uses shared UTM type.
src/models/usersFactory.ts Updates UTM typing.
src/services/promoCodeService.ts Implements validation and usage.
src/utils/promoCodePricing.ts Calculates discounted prices.
src/utils/utm/utm.ts Adds UTM sanitization.
src/utils/checksumService.ts Signs promo references.
src/billing/types/paymentData.ts Adds promo payment metadata.
src/billing/cloudpayments.ts Integrates promos into webhooks.
src/resolvers/billingNew.ts Adds promo billing resolvers.
src/resolvers/user.ts Updates signup UTM typing.
src/directives/requireAdmin.ts Supports nested identifiers.
src/typeDefs/billing.ts Exposes promo GraphQL API.
test/services/promoCodeService.test.ts Tests promo service behavior.
test/utils/utm.test.ts Tests UTM sanitization.
test/billing/cloudpayments.test.ts Tests webhook billing flows.
test/integration/cases/billing/promo.test.ts Tests persisted promo payments.
test/resolvers/billingNew.test.ts Tests resolver promo flows.
test/directives/requireAdmin.test.ts Tests nested authorization.
test/resolvers/project.test.ts Updates factory fixtures.
test/sso/saml/controller.test.ts Updates factory fixture.
test/integrations/github-routes.test.ts Updates route fixtures.
Suppressed comments (1)

src/services/promoCodeService.ts:176

  • NaN and Infinity both pass this fixed-price validation and are returned through GraphQL's Int field, causing serialization errors rather than the documented promo validation error. Add the same finite-number check already used for minFinalPrice.
      if (typeof benefit.amount !== 'number' || benefit.amount < DEFAULT_MIN_FINAL_PRICE) {

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

*/
const isRightAmount = +body.Amount === plan.monthlyCharge || recurrentPaymentSettings?.startDate;
const expectedAmount = promoPricing?.finalAmount ?? plan.monthlyCharge;
const isRightAmount = +body.Amount === expectedAmount || (!data.promo && recurrentPaymentSettings?.startDate);
Comment on lines +360 to +362
} catch (error) {
console.error('[Billing / Pay] Failed to record promo usage after plan change', error);
}
Comment on lines +436 to +440
const [totalUses, userUsage, workspaceUsage] = await Promise.all([
this.factories.promoCodeUsagesFactory.countByPromoCodeId(promoCode._id),
this.factories.promoCodeUsagesFactory.findByPromoCodeAndUser(promoCode._id, userId),
this.factories.promoCodeUsagesFactory.findByPromoCodeAndWorkspace(promoCode._id, workspaceId),
]);
function validateBenefitStructure(benefit: PromoCodeModel['benefit']): void {
switch (benefit.type) {
case 'percent_discount':
if (typeof benefit.percent !== 'number' || benefit.percent <= 0 || benefit.percent > 100) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants