feat: billing change
This commit is contained in:
parent
6ab8a2471b
commit
9afeb1e0f8
|
|
@ -9,6 +9,7 @@ import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.req
|
|||
import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
|
||||
import { Request } from 'express';
|
||||
import { Nowpayments } from '@gitroom/nestjs-libraries/crypto/nowpayments';
|
||||
import { AuthService } from '@gitroom/helpers/auth/auth.service';
|
||||
|
||||
@ApiTags('Billing')
|
||||
@Controller('/billing')
|
||||
|
|
@ -30,6 +31,20 @@ export class BillingController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get('/check-discount')
|
||||
async checkDiscount(@GetOrgFromRequest() org: Organization) {
|
||||
return {
|
||||
offerCoupon: !(await this._stripeService.checkDiscount(org.paymentId))
|
||||
? false
|
||||
: AuthService.signJWT({ discount: true }),
|
||||
};
|
||||
}
|
||||
|
||||
@Post('/apply-discount')
|
||||
async applyDiscount(@GetOrgFromRequest() org: Organization) {
|
||||
await this._stripeService.applyDiscount(org.paymentId);
|
||||
}
|
||||
|
||||
@Post('/finish-trial')
|
||||
async finishTrial(@GetOrgFromRequest() org: Organization) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -135,6 +135,38 @@ export const Features: FC<{
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Accept: FC<{ resolve: (res: boolean) => void }> = ({ resolve }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const fetch = useFetch();
|
||||
const toaster = useToaster();
|
||||
|
||||
const apply = useCallback(async () => {
|
||||
setLoading(true);
|
||||
await fetch('/billing/apply-discount', {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
resolve(true);
|
||||
toaster.show('50% discount applied successfully');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-[20px]">
|
||||
Would you accept 50% discount for 3 months instead? 🙏🏻
|
||||
</div>
|
||||
<div className="flex gap-[10px]">
|
||||
<Button loading={loading} onClick={apply}>
|
||||
Apply 50% discount for 3 months
|
||||
</Button>
|
||||
<Button onClick={() => resolve(false)} className="!bg-red-800">
|
||||
Cancel my subscription
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const Info: FC<{
|
||||
proceed: (feedback: string) => void;
|
||||
}> = (props) => {
|
||||
|
|
@ -277,13 +309,34 @@ export const MainBillingComponent: FC<{
|
|||
if (
|
||||
subscription?.cancelAt ||
|
||||
(await deleteDialog(
|
||||
`Are you sure you want to cancel your subscription? ${messages.join(
|
||||
', '
|
||||
)}`,
|
||||
`Are you sure you want to cancel your subscription?
|
||||
${messages.join(', ')}`,
|
||||
'Yes, cancel',
|
||||
'Cancel Subscription'
|
||||
))
|
||||
) {
|
||||
const checkDiscount = await (
|
||||
await fetch('/billing/check-discount')
|
||||
).json();
|
||||
if (checkDiscount.offerCoupon) {
|
||||
const info = await new Promise((res) => {
|
||||
modal.openModal({
|
||||
title: 'Before you cancel',
|
||||
withCloseButton: true,
|
||||
classNames: {
|
||||
modal: 'bg-transparent text-textColor',
|
||||
},
|
||||
children: <Accept resolve={res} />,
|
||||
});
|
||||
});
|
||||
|
||||
modal.closeAll();
|
||||
|
||||
if (info) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const info = await new Promise((res) => {
|
||||
modal.openModal({
|
||||
title: t(
|
||||
|
|
@ -297,6 +350,7 @@ export const MainBillingComponent: FC<{
|
|||
children: <Info proceed={(e) => res(e)} />,
|
||||
});
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
const { cancel_at } = await (
|
||||
await fetch('/billing/cancel', {
|
||||
|
|
|
|||
|
|
@ -475,6 +475,72 @@ export class StripeService {
|
|||
});
|
||||
}
|
||||
|
||||
async checkDiscount(customer: string) {
|
||||
if (!process.env.STRIPE_DISCOUNT_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const list = await stripe.charges.list({
|
||||
customer,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
if (!list.data.filter(f => f.amount > 1000).length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentUserSubscription = {
|
||||
data: (
|
||||
await stripe.subscriptions.list({
|
||||
customer,
|
||||
status: 'all',
|
||||
expand: ['data.discounts'],
|
||||
})
|
||||
).data.find((f) => f.status === 'active' || f.status === 'trialing'),
|
||||
};
|
||||
|
||||
if (!currentUserSubscription) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
currentUserSubscription.data?.items.data[0]?.price.recurring?.interval ===
|
||||
'year' ||
|
||||
currentUserSubscription.data?.discounts.length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async applyDiscount(customer: string) {
|
||||
const check = this.checkDiscount(customer);
|
||||
if (!check) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentUserSubscription = {
|
||||
data: (
|
||||
await stripe.subscriptions.list({
|
||||
customer,
|
||||
status: 'all',
|
||||
expand: ['data.discounts'],
|
||||
})
|
||||
).data.find((f) => f.status === 'active' || f.status === 'trialing'),
|
||||
};
|
||||
|
||||
await stripe.subscriptions.update(currentUserSubscription.data.id, {
|
||||
discounts: [
|
||||
{
|
||||
coupon: process.env.STRIPE_DISCOUNT_ID!,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async checkSubscription(organizationId: string, subscriptionId: string) {
|
||||
const orgValue = await this._subscriptionService.checkSubscription(
|
||||
organizationId,
|
||||
|
|
|
|||
Loading…
Reference in New Issue