feat: responsive
This commit is contained in:
parent
12022a9d85
commit
72523ed2c1
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Param, Post, Req } from '@nestjs/common';
|
||||
import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service';
|
||||
import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service';
|
||||
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
|
||||
|
|
@ -7,6 +7,7 @@ import { BillingSubscribeDto } from '@gitroom/nestjs-libraries/dtos/billing/bill
|
|||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
|
||||
import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
|
||||
import { Request } from 'express';
|
||||
|
||||
@ApiTags('Billing')
|
||||
@Controller('/billing')
|
||||
|
|
@ -33,9 +34,12 @@ export class BillingController {
|
|||
@Post('/subscribe')
|
||||
subscribe(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Body() body: BillingSubscribeDto
|
||||
@GetUserFromRequest() user: User,
|
||||
@Body() body: BillingSubscribeDto,
|
||||
@Req() req: Request
|
||||
) {
|
||||
return this._stripeService.subscribe(org.id, body);
|
||||
const uniqueId = req?.cookies?.track;
|
||||
return this._stripeService.subscribe(uniqueId, org.id, user.id, body);
|
||||
}
|
||||
|
||||
@Get('/portal')
|
||||
|
|
|
|||
|
|
@ -48,21 +48,14 @@ export class PublicController {
|
|||
body: { fbclid?: string; tt: TrackEnum; additional: Record<string, any> }
|
||||
) {
|
||||
const uniqueId = req?.cookies?.track || makeId(10);
|
||||
console.log(
|
||||
req?.cookies?.track,
|
||||
ip,
|
||||
userAgent,
|
||||
body.tt,
|
||||
body.additional,
|
||||
body.fbclid
|
||||
);
|
||||
const fbclid = req?.cookies?.fbclid || body.fbclid;
|
||||
await this._trackService.track(
|
||||
req?.cookies?.track,
|
||||
uniqueId,
|
||||
ip,
|
||||
userAgent,
|
||||
body.tt,
|
||||
body.additional,
|
||||
body.fbclid
|
||||
fbclid
|
||||
);
|
||||
if (!req.cookies.track) {
|
||||
res.cookie('track', uniqueId, {
|
||||
|
|
|
|||
|
|
@ -54,11 +54,13 @@ export class StripeController {
|
|||
// Maybe it comes from another stripe webhook
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (event?.data?.object?.metadata?.service !== 'gitroom') {
|
||||
if (event?.data?.object?.metadata?.service !== 'gitroom' && event.type !== 'invoice.payment_succeeded') {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case 'invoice.payment_succeeded':
|
||||
return this._stripeService.paymentSucceeded(event);
|
||||
case 'checkout.session.completed':
|
||||
return this._stripeService.updateOrder(event);
|
||||
case 'account.updated':
|
||||
|
|
|
|||
|
|
@ -222,10 +222,11 @@ export class UsersController {
|
|||
@GetUserFromRequest() user: User,
|
||||
@RealIP() ip: string,
|
||||
@UserAgent() userAgent: string,
|
||||
@Body() body: { tt: TrackEnum; additional: Record<string, any> }
|
||||
@Body() body: { tt: TrackEnum; fbclid: string, additional: Record<string, any> }
|
||||
) {
|
||||
const uniqueId = req?.cookies?.track || makeId(10);
|
||||
await this._trackService.track(req?.cookies?.track, ip, userAgent, body.tt, body.additional, null, user);
|
||||
const fbclid = req?.cookies?.fbclid || body.fbclid;
|
||||
await this._trackService.track(uniqueId, ip, userAgent, body.tt, body.additional, fbclid, user);
|
||||
if (!req.cookies.track) {
|
||||
res.cookie('track', uniqueId, {
|
||||
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
|
||||
|
|
@ -236,6 +237,7 @@ export class UsersController {
|
|||
});
|
||||
}
|
||||
|
||||
console.log('hello');
|
||||
res.status(200).send();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -14,6 +14,7 @@ import { Fragment } from 'react';
|
|||
import { PHProvider } from '@gitroom/react/helpers/posthog';
|
||||
import UtmSaver from '@gitroom/helpers/utils/utm.saver';
|
||||
import { ToltScript } from '@gitroom/frontend/components/layout/tolt.script';
|
||||
import { FacebookComponent } from '@gitroom/frontend/components/layout/facebook.component';
|
||||
|
||||
const chakra = Chakra_Petch({ weight: '400', subsets: ['latin'] });
|
||||
|
||||
|
|
@ -25,11 +26,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
|
|||
return (
|
||||
<html className={interClass}>
|
||||
<head>
|
||||
<link
|
||||
rel="icon"
|
||||
href="/favicon.ico"
|
||||
sizes="any"
|
||||
/>
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
</head>
|
||||
<body className={clsx(chakra.className, 'text-primary dark')}>
|
||||
<VariableContextComponent
|
||||
|
|
@ -46,6 +43,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
|
|||
tolt={process.env.NEXT_PUBLIC_TOLT!}
|
||||
>
|
||||
<ToltScript />
|
||||
<FacebookComponent />
|
||||
<Plausible
|
||||
domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
|
||||
>
|
||||
|
|
@ -53,8 +51,10 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
|
|||
phkey={process.env.NEXT_PUBLIC_POSTHOG_KEY}
|
||||
host={process.env.NEXT_PUBLIC_POSTHOG_HOST}
|
||||
>
|
||||
<UtmSaver />
|
||||
<LayoutContext>{children}</LayoutContext>
|
||||
<LayoutContext>
|
||||
<UtmSaver />
|
||||
{children}
|
||||
</LayoutContext>
|
||||
</PHProvider>
|
||||
</Plausible>
|
||||
</VariableContextComponent>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useForm, SubmitHandler, FormProvider } from 'react-hook-form';
|
||||
import { FormProvider, SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@gitroom/react/form/button';
|
||||
|
|
@ -16,6 +16,8 @@ import clsx from 'clsx';
|
|||
import { GoogleProvider } from '@gitroom/frontend/components/auth/providers/google.provider';
|
||||
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
|
||||
import { useVariables } from '@gitroom/react/helpers/variable.context';
|
||||
import { useTrack } from '@gitroom/react/helpers/use.track';
|
||||
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
|
||||
|
||||
type Inputs = {
|
||||
email: string;
|
||||
|
|
@ -83,10 +85,11 @@ export function RegisterAfter({
|
|||
token: string;
|
||||
provider: string;
|
||||
}) {
|
||||
const {isGeneral} = useVariables();
|
||||
const { isGeneral } = useVariables();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
const fireEvents = useFireEvents();
|
||||
const track = useTrack();
|
||||
|
||||
const isAfterProvider = useMemo(() => {
|
||||
return !!token && !!provider;
|
||||
|
|
@ -112,27 +115,33 @@ export function RegisterAfter({
|
|||
await fetchData('/auth/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ...data }),
|
||||
}).then((response) => {
|
||||
setLoading(false);
|
||||
|
||||
if (response.status === 200) {
|
||||
fireEvents('register')
|
||||
|
||||
if (response.headers.get('activate') === "true") {
|
||||
router.push('/auth/activate');
|
||||
} else {
|
||||
router.push('/auth/login');
|
||||
}
|
||||
} else {
|
||||
form.setError('email', {
|
||||
message: getHelpfulReasonForRegistrationFailure(response.status),
|
||||
});
|
||||
}
|
||||
}).catch(e => {
|
||||
form.setError("email", {
|
||||
message: 'General error: ' + e.toString() + '. Please check your browser console.',
|
||||
});
|
||||
})
|
||||
.then((response) => {
|
||||
setLoading(false);
|
||||
|
||||
if (response.status === 200) {
|
||||
fireEvents('register');
|
||||
return track(TrackEnum.CompleteRegistration).then(() => {
|
||||
if (response.headers.get('activate') === 'true') {
|
||||
router.push('/auth/activate');
|
||||
} else {
|
||||
router.push('/auth/login');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
form.setError('email', {
|
||||
message: getHelpfulReasonForRegistrationFailure(response.status),
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
form.setError('email', {
|
||||
message:
|
||||
'General error: ' +
|
||||
e.toString() +
|
||||
'. Please check your browser console.',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -143,7 +152,8 @@ export function RegisterAfter({
|
|||
Sign Up
|
||||
</h1>
|
||||
</div>
|
||||
{!isAfterProvider && (!isGeneral ? <GithubProvider /> : <GoogleProvider />)}
|
||||
{!isAfterProvider &&
|
||||
(!isGeneral ? <GithubProvider /> : <GoogleProvider />)}
|
||||
{!isAfterProvider && (
|
||||
<div className="h-[20px] mb-[24px] mt-[24px] relative">
|
||||
<div className="absolute w-full h-[1px] bg-fifth top-[50%] -translate-y-[50%]" />
|
||||
|
|
@ -198,7 +208,11 @@ export function RegisterAfter({
|
|||
</div>
|
||||
<div className="text-center mt-6">
|
||||
<div className="w-full flex">
|
||||
<Button type="submit" className="flex-1 rounded-[4px]" loading={loading}>
|
||||
<Button
|
||||
type="submit"
|
||||
className="flex-1 rounded-[4px]"
|
||||
loading={loading}
|
||||
>
|
||||
Create Account
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,19 +5,9 @@ import useSWR from 'swr';
|
|||
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
|
||||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
import { MainBillingComponent } from './main.billing.component';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
|
||||
|
||||
export const BillingComponent = () => {
|
||||
const fetch = useFetch();
|
||||
const searchParams = useSearchParams();
|
||||
const fireEvents = useFireEvents();
|
||||
|
||||
useEffect(() => {
|
||||
if (searchParams.get('check')) {
|
||||
fireEvents('purchase');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const load = useCallback(async (path: string) => {
|
||||
return await (await fetch(path)).json();
|
||||
|
|
@ -36,7 +26,5 @@ export const BillingComponent = () => {
|
|||
return <LoadingComponent />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MainBillingComponent sub={subscription?.subscription} />
|
||||
);
|
||||
return <MainBillingComponent sub={subscription?.subscription} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import { Textarea } from '@gitroom/react/form/textarea';
|
|||
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
|
||||
import { useUtmUrl } from '@gitroom/helpers/utils/utm.saver';
|
||||
import { useTolt } from '@gitroom/frontend/components/layout/tolt.script';
|
||||
import { useTrack } from '@gitroom/react/helpers/use.track';
|
||||
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
|
||||
|
||||
export interface Tiers {
|
||||
month: Array<{
|
||||
|
|
@ -223,6 +225,7 @@ export const MainBillingComponent: FC<{
|
|||
const router = useRouter();
|
||||
const utm = useUtmUrl();
|
||||
const tolt = useTolt();
|
||||
const track = useTrack();
|
||||
|
||||
const [subscription, setSubscription] = useState<Subscription | undefined>(
|
||||
sub
|
||||
|
|
@ -350,12 +353,18 @@ export const MainBillingComponent: FC<{
|
|||
period: monthlyOrYearly === 'on' ? 'YEARLY' : 'MONTHLY',
|
||||
utm,
|
||||
billing,
|
||||
tolt: tolt()
|
||||
tolt: tolt(),
|
||||
}),
|
||||
})
|
||||
).json();
|
||||
|
||||
if (url) {
|
||||
await track(TrackEnum.InitiateCheckout, {
|
||||
value:
|
||||
pricing[billing][
|
||||
monthlyOrYearly === 'on' ? 'year_price' : 'month_price'
|
||||
],
|
||||
});
|
||||
window.location.href = url;
|
||||
return;
|
||||
}
|
||||
|
|
@ -412,13 +421,13 @@ export const MainBillingComponent: FC<{
|
|||
<div>YEARLY</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-[16px]">
|
||||
<div className="flex gap-[16px] [@media(max-width:1024px)]:flex-col [@media(max-width:1024px)]:text-center">
|
||||
{Object.entries(pricing)
|
||||
.filter((f) => !isGeneral || f[0] !== 'FREE')
|
||||
.map(([name, values]) => (
|
||||
<div
|
||||
key={name}
|
||||
className="flex-1 bg-sixth border border-customColor6 rounded-[4px] p-[24px] gap-[16px] flex flex-col"
|
||||
className="flex-1 bg-sixth border border-customColor6 rounded-[4px] p-[24px] gap-[16px] flex flex-col [@media(max-width:1024px)]:items-center"
|
||||
>
|
||||
<div className="text-[18px]">{name}</div>
|
||||
<div className="text-[38px] flex gap-[2px] items-center">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
'use client';
|
||||
|
||||
import Script from "next/script";
|
||||
|
||||
export const FacebookComponent = () => {
|
||||
if (!process.env.NEXT_PUBLIC_FACEBOOK_PIXEL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Script strategy="afterInteractive" id="fb-pixel">
|
||||
{`!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'/f.js');
|
||||
fbq('init', '${process.env.NEXT_PUBLIC_FACEBOOK_PIXEL}');
|
||||
`}
|
||||
</Script>
|
||||
);
|
||||
};
|
||||
|
|
@ -78,9 +78,9 @@ export const LayoutSettings = ({ children }: { children: ReactNode }) => {
|
|||
{user.tier !== 'FREE' && <Onboarding />}
|
||||
<Support />
|
||||
<ContinueProvider />
|
||||
<div className="min-h-[100vh] w-full max-w-[1440px] mx-auto bg-primary sm:px-6 px-0 text-textColor flex flex-col">
|
||||
<div className="min-h-[100vh] w-full max-w-[1440px] mx-auto bg-primary px-6 text-textColor flex flex-col">
|
||||
{user?.admin && <Impersonate />}
|
||||
<nav className="px-0 md:px-[23px] gap-2 grid grid-rows-[repeat(2,_auto)] grid-cols-2 md:grid-rows-1 md:grid-cols-[repeat(3,_auto)] items-center justify-between z-[200] sticky top-0 bg-primary">
|
||||
<nav className="flex items-center justify-between">
|
||||
<Link
|
||||
href="/"
|
||||
className="text-2xl flex items-center gap-[10px] text-textColor order-1"
|
||||
|
|
@ -130,7 +130,7 @@ export const LayoutSettings = ({ children }: { children: ReactNode }) => {
|
|||
(user.tier !== 'FREE' || !isGeneral || !billingEnabled) ? (
|
||||
<TopMenu />
|
||||
) : (
|
||||
<div />
|
||||
<></>
|
||||
)}
|
||||
<div id = "systray-buttons" className="flex items-center justify-self-end gap-[8px] order-2 md:order-3">
|
||||
<ModeComponent />
|
||||
|
|
@ -140,11 +140,11 @@ export const LayoutSettings = ({ children }: { children: ReactNode }) => {
|
|||
</div>
|
||||
</nav>
|
||||
<div className="flex-1 flex">
|
||||
<div className="flex-1 rounded-3xl px-0 md:px-[23px] py-[17px] flex flex-col">
|
||||
<div className="flex-1 rounded-3xl px-0 py-[17px] flex flex-col">
|
||||
{user.tier === 'FREE' && isGeneral && billingEnabled ? (
|
||||
<>
|
||||
<div className="text-center mb-[20px] text-xl">
|
||||
<h1 className="text-3xl">
|
||||
<div className="text-center mb-[20px] text-xl [@media(max-width:1024px)]:text-xl">
|
||||
<h1 className="text-3xl [@media(max-width:1024px)]:text-xl">
|
||||
Join 1000+ Entrepreneurs Who Use Postiz
|
||||
<br />
|
||||
To Manage All Your Social Media Channels
|
||||
|
|
|
|||
|
|
@ -3,10 +3,23 @@
|
|||
import { FC, useCallback, useEffect } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
|
||||
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
|
||||
import { useTrack } from '@gitroom/react/helpers/use.track';
|
||||
|
||||
const UtmSaver: FC = () => {
|
||||
const query = useSearchParams();
|
||||
const [value, setValue] = useLocalStorage({ key: 'utm', defaultValue: '' });
|
||||
const searchParams = useSearchParams();
|
||||
const fireEvents = useFireEvents();
|
||||
const track = useTrack();
|
||||
|
||||
useEffect(() => {
|
||||
if (searchParams.get('check')) {
|
||||
fireEvents('purchase');
|
||||
track(TrackEnum.StartTrial);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const landingUrl = localStorage.getItem('landingUrl');
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { ExtractContentService } from '@gitroom/nestjs-libraries/openai/extract.
|
|||
import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service';
|
||||
import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service';
|
||||
import { AgenciesRepository } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.repository';
|
||||
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
|
|
@ -66,6 +67,7 @@ import { AgenciesRepository } from '@gitroom/nestjs-libraries/database/prisma/ag
|
|||
ExtractContentService,
|
||||
OpenaiService,
|
||||
EmailService,
|
||||
TrackService,
|
||||
],
|
||||
get exports() {
|
||||
return this.providers;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import { capitalize, groupBy } from 'lodash';
|
|||
import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service';
|
||||
import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing';
|
||||
import { AuthService } from '@gitroom/helpers/auth/auth.service';
|
||||
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
|
||||
import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service';
|
||||
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
||||
apiVersion: '2024-04-10',
|
||||
|
|
@ -19,7 +22,9 @@ export class StripeService {
|
|||
constructor(
|
||||
private _subscriptionService: SubscriptionService,
|
||||
private _organizationService: OrganizationService,
|
||||
private _messagesService: MessagesService
|
||||
private _userService: UsersService,
|
||||
private _messagesService: MessagesService,
|
||||
private _trackService: TrackService
|
||||
) {}
|
||||
validateRequest(rawBody: Buffer, signature: string, endpointSecret: string) {
|
||||
return stripe.webhooks.constructEvent(rawBody, signature, endpointSecret);
|
||||
|
|
@ -265,10 +270,12 @@ export class StripeService {
|
|||
}
|
||||
|
||||
private async createCheckoutSession(
|
||||
ud: string,
|
||||
uniqueId: string,
|
||||
customer: string,
|
||||
body: BillingSubscribeDto,
|
||||
price: string
|
||||
price: string,
|
||||
userId: string
|
||||
) {
|
||||
const isUtm = body.utm ? `&utm_source=${body.utm}` : '';
|
||||
const { url } = await stripe.checkout.sessions.create({
|
||||
|
|
@ -283,14 +290,18 @@ export class StripeService {
|
|||
metadata: {
|
||||
service: 'gitroom',
|
||||
...body,
|
||||
userId,
|
||||
uniqueId,
|
||||
ud,
|
||||
},
|
||||
},
|
||||
...body.tolt ? {
|
||||
metadata: {
|
||||
tolt_referral: body.tolt,
|
||||
}
|
||||
} : {},
|
||||
...(body.tolt
|
||||
? {
|
||||
metadata: {
|
||||
tolt_referral: body.tolt,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
allow_promotion_codes: true,
|
||||
line_items: [
|
||||
{
|
||||
|
|
@ -416,7 +427,12 @@ export class StripeService {
|
|||
return { url };
|
||||
}
|
||||
|
||||
async subscribe(organizationId: string, body: BillingSubscribeDto) {
|
||||
async subscribe(
|
||||
uniqueId: string,
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
body: BillingSubscribeDto
|
||||
) {
|
||||
const id = makeId(10);
|
||||
const priceData = pricing[body.billing];
|
||||
const org = await this._organizationService.getOrgById(organizationId);
|
||||
|
|
@ -475,7 +491,14 @@ export class StripeService {
|
|||
};
|
||||
|
||||
if (!currentUserSubscription.data.length) {
|
||||
return this.createCheckoutSession(id, customer, body, findPrice!.id);
|
||||
return this.createCheckoutSession(
|
||||
uniqueId,
|
||||
id,
|
||||
customer,
|
||||
body,
|
||||
findPrice!.id,
|
||||
userId
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -484,7 +507,9 @@ export class StripeService {
|
|||
metadata: {
|
||||
service: 'gitroom',
|
||||
...body,
|
||||
userId,
|
||||
id,
|
||||
ud: uniqueId,
|
||||
},
|
||||
proration_behavior: 'always_invoice',
|
||||
items: [
|
||||
|
|
@ -505,6 +530,23 @@ export class StripeService {
|
|||
}
|
||||
}
|
||||
|
||||
async paymentSucceeded(event: Stripe.InvoicePaymentSucceededEvent) {
|
||||
// get subscription from payment
|
||||
const subscription = await stripe.subscriptions.retrieve(
|
||||
event.data.object.subscription as string
|
||||
);
|
||||
|
||||
const { userId, ud } = subscription.metadata;
|
||||
const user = await this._userService.getUserById(userId);
|
||||
if (user && user.ip && user.agent) {
|
||||
this._trackService.track(ud, user.ip, user.agent, TrackEnum.Purchase, {
|
||||
value: event.data.object.amount_paid / 100,
|
||||
});
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async updateOrder(event: Stripe.CheckoutSessionCompletedEvent) {
|
||||
if (event?.data?.object?.metadata?.type !== 'marketplace') {
|
||||
return { ok: true };
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export class TrackService {
|
|||
}
|
||||
|
||||
if (user && user.email) {
|
||||
userData.setEmails([this.hashValue(user.email)]);
|
||||
userData.setEmail(this.hashValue(user.email));
|
||||
}
|
||||
|
||||
let customData = null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum';
|
||||
import { useUser } from '@gitroom/frontend/components/layout/user.context';
|
||||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useTrack = () => {
|
||||
const user = useUser();
|
||||
const fetch = useFetch();
|
||||
|
||||
return useCallback(
|
||||
async (track: TrackEnum, additional?: Record<string, any>) => {
|
||||
if (!process.env.NEXT_PUBLIC_FACEBOOK_PIXEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (window.fbq) {
|
||||
// @ts-ignore
|
||||
window.fbq('track', TrackEnum[track], additional);
|
||||
}
|
||||
|
||||
await fetch(user ? `/user/t` : `/public/t`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tt: track,
|
||||
...(additional ? { additional } : {}),
|
||||
}),
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
},
|
||||
[user]
|
||||
);
|
||||
};
|
||||
Loading…
Reference in New Issue