Login issue fixed + final improvements before launch
This commit is contained in:
parent
99c596d658
commit
e4b210b2cc
6
.env
6
.env
@ -6,7 +6,9 @@ REVALIDATION_TOKEN="FTLCmhMVGiudVXgVMg4XFfSpR50c5s57KvSwdElNwPw="
|
||||
NEXT_PUBLIC_ADMIN_LIVE_STATIC_TOKEN="VIHsGh1MmQN7bWEcAANYWQs3YrHWas7k"
|
||||
NEXT_PUBLIC_LOCAL_ADMIN_STATIC_TOKEN="VIHsGh1MmQN7bWEcAANYWQs3YrHWas7k"
|
||||
# https://dashboard.stripe.com/apikeys
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_51Op4JZF1S9Vi166eEQ0GblJy3BY0HluYuVkffQNYC3xFIby6edEkxk8V1rS3podXvtxCqZKS9TrTeTKyLSkVBwYa00TPjffkFs"
|
||||
STRIPE_SECRET_KEY="sk_test_51Op4JZF1S9Vi166eMw1Oa2wkfEUdI6eKFZcRzC7kOvXClDQu6139gVvOop2ukEqkUPpsPLovZoffEE715XSXnbAk00BUvrMT51"
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_live_51Op4JZF1S9Vi166ey3TtkAgSjUbDGDKKXxus9MZuiiiVtUqtDpGMAQVy0ArQfIXvcLmYA3FuyWCnFy4adBbL6BW500T7gXCKQe"
|
||||
NEXT_PUBLIC_STRIPE_TEST_PUBLISHABLE_KEY="pk_test_51Op4JZF1S9Vi166eEQ0GblJy3BY0HluYuVkffQNYC3xFIby6edEkxk8V1rS3podXvtxCqZKS9TrTeTKyLSkVBwYa00TPjffkFs"
|
||||
STRIPE_SECRET_KEY="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"
|
||||
STRIPE_TEST_SECRET_KEY="sk_test_51Op4JZF1S9Vi166eMw1Oa2wkfEUdI6eKFZcRzC7kOvXClDQu6139gVvOop2ukEqkUPpsPLovZoffEE715XSXnbAk00BUvrMT51"
|
||||
# Set this environment variable to support webhooks — https://stripe.com/docs/webhooks#verify-events
|
||||
STRIPE_WEBHOOK_SECRET="whsec_d94f4fae7a3c577ff28e4f59bf780356bb7b5000b1d5758471f95ca2dc21cd7c"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
|
||||
37
src/common/services/general/stripe.ts
Normal file
37
src/common/services/general/stripe.ts
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
// get stripe publishable key based on environment
|
||||
export const getStripePublishableKey = () => {
|
||||
const secretKey = process.env.NODE_ENV === 'production' ? (process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as any) : (process.env.NEXT_PUBLIC_STRIPE_TEST_PUBLISHABLE_KEY as any);
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
// get stripe secret key based on environment
|
||||
export const getStripeSecretKey = () => {
|
||||
const secretKey = process.env.NODE_ENV === 'production' ? (process.env.STRIPE_SECRET_KEY as any) : (process.env.STRIPE_TEST_SECRET_KEY as any);
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
// get stripe secret key based on environment
|
||||
export const getStripeWalletProductId = (amount: number) => {
|
||||
const priceIds = [
|
||||
{ amount: 10, price_id: "price_1P0jWMF1S9Vi166enI8ALC13", test_price_id: "price_1P1PXhF1S9Vi166ebIz6k0pm" },
|
||||
{ amount: 20, price_id: "price_1P0jWiF1S9Vi166e9nK2P7Tu", test_price_id: "price_1P1PXhF1S9Vi166ebIz6k0pm" },
|
||||
{ amount: 50, price_id: "price_1P0jXAF1S9Vi166e3XtjMW6R", test_price_id: "price_1P1PXhF1S9Vi166ebIz6k0pm" },
|
||||
{ amount: 100, price_id: "price_1P0jXPF1S9Vi166e6ddQilu8", test_price_id: "price_1P1PXhF1S9Vi166ebIz6k0pm" },
|
||||
{ amount: 200, price_id: "price_1P0jXeF1S9Vi166eZNORDZfC", test_price_id: "price_1P1PXhF1S9Vi166ebIz6k0pm" }
|
||||
]
|
||||
const productId = priceIds.filter(x => x.amount === amount)[0][process.env.NODE_ENV === 'production' ? "price_id" : "test_price_id"];
|
||||
return productId;
|
||||
}
|
||||
|
||||
// get stripe secret key based on environment
|
||||
export const getStripeAdsProductId = (cost: number) => {
|
||||
const priceIds = [
|
||||
{ cost: 20, price_id: "price_1OwTxWF1S9Vi166eQk5MLA6I", test_price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ cost: 50, price_id: "price_1OwU0IF1S9Vi166e8dn2pEpF", test_price_id: "price_1OwlnxF1S9Vi166eA2ZlEV5W" },
|
||||
{ cost: 100, price_id: "price_1OwU2oF1S9Vi166edFUgabk2", test_price_id: "price_1OwlnxF1S9Vi166eA2ZlEV5W" },
|
||||
{ cost: 200, price_id: "price_1OwU5MF1S9Vi166e6tKQY2UU", test_price_id: "price_1OwlnxF1S9Vi166eA2ZlEV5W" }
|
||||
]
|
||||
const productId = priceIds.filter(x => x.cost === cost)[0][process.env.NODE_ENV === 'production' ? "price_id" : "test_price_id"];
|
||||
return productId;
|
||||
}
|
||||
42
src/common/templates/authentication/autoSessionRefetch.tsx
Normal file
42
src/common/templates/authentication/autoSessionRefetch.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface RefreshTokenHandler {
|
||||
|
||||
}
|
||||
|
||||
const RefreshTokenHandler: React.FunctionComponent<RefreshTokenHandler> = ({}) => {
|
||||
|
||||
// states
|
||||
const [isFirstLoad, setIsFirstLoad] = useState(true);
|
||||
|
||||
const { data: session, update } = useSession();
|
||||
|
||||
const handleUpdate = () => {
|
||||
setIsFirstLoad(false);
|
||||
const timeRemaining = Math.round((Number(session?.expires) - Date.now()) / 1000);
|
||||
// If remaining time to token expiration is less than 60 sec (1 min), refresh the token
|
||||
timeRemaining < 70 && update();
|
||||
}
|
||||
|
||||
session && isFirstLoad && handleUpdate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!!session) {
|
||||
const intervalId = setInterval(() => {
|
||||
// Your code to execute every 30 seconds goes here
|
||||
// console.log('Executing every 30 seconds');
|
||||
handleUpdate();
|
||||
}, 30000); // 30 seconds in milliseconds
|
||||
|
||||
// Cleanup function to clear the interval when the component unmounts or when the effect is re-run
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default RefreshTokenHandler;
|
||||
@ -15,10 +15,10 @@ import { ArrowsRotateSolid, CaretDownSolid, ChevronDownSolid, CreditCardSolid, M
|
||||
import RadioButton from "components/radio-button/radio-button";
|
||||
import { useAppSelector } from "common/redux/hooks";
|
||||
import { userData } from "common/redux/slices/user";
|
||||
import { loadStripe } from "@stripe/stripe-js";
|
||||
import { updateUserData } from "../account/payloads";
|
||||
import { newPaymentQuery, updatePaidAdQuery, updateUserWalletQuery } from "services/queries/data-queries";
|
||||
import { addNewPayment } from "services/queries/cms";
|
||||
import { getStripeAdsProductId } from "services/general/stripe";
|
||||
|
||||
interface Item {
|
||||
id: string;
|
||||
@ -72,10 +72,6 @@ interface TableItem {
|
||||
onOpenPay?: (id: string, title: string, pic: any) => void;
|
||||
}
|
||||
|
||||
// const stripePromise = loadStripe(
|
||||
// process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as any
|
||||
// );
|
||||
|
||||
const itemTitle = (item:any, locale:string | undefined) => item.translations.filter((x:any) => x.languages_code.code.slice(0, 2) === locale)[0]?.title ?? item.translations[0].title;
|
||||
|
||||
const TableItem: React.FunctionComponent<TableItem> = ({ item, onDelete, onOpenPay, className }) => {
|
||||
@ -259,10 +255,10 @@ const AdsTable: React.FunctionComponent<AdsTable> = ({ items, onDelete, classNam
|
||||
const loadMoreText = translate("load-more");
|
||||
const hasMore = filterItemsBySearch(items).length > pageIndex * pageSize;
|
||||
const paymentOptions = [
|
||||
{ id: 1, title: translate("ads-table-payment-popup-one"), cost: 20, period: 1, price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ id: 2, title: translate("ads-table-payment-popup-two"), cost: 50, period: 3, price_id: "price_1OwlnxF1S9Vi166eA2ZlEV5W" },
|
||||
{ id: 3, title: translate("ads-table-payment-popup-three"), cost: 100, period: 6, price_id: "" },
|
||||
{ id: 4, title: translate("ads-table-payment-popup-four"), cost: 200, period: 12, price_id: "" }
|
||||
{ id: 1, title: translate("ads-table-payment-popup-one"), cost: 20, period: 1 },
|
||||
{ id: 2, title: translate("ads-table-payment-popup-two"), cost: 50, period: 3 },
|
||||
{ id: 3, title: translate("ads-table-payment-popup-three"), cost: 100, period: 6 },
|
||||
{ id: 4, title: translate("ads-table-payment-popup-four"), cost: 200, period: 12 }
|
||||
];
|
||||
const selectedPaymentData = paymentOptions.filter(x => x.period === selectedPayment)[0];
|
||||
const today = new Date();
|
||||
@ -330,7 +326,7 @@ const AdsTable: React.FunctionComponent<AdsTable> = ({ items, onDelete, classNam
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
price_id: selectedPaymentData.price_id,
|
||||
price_id: getStripeAdsProductId(selectedPaymentData.cost),
|
||||
service_type: "flierland-ad",
|
||||
service_id: unpaidItem.id,
|
||||
stripe_id: user.generalDetails.stripe_id,
|
||||
|
||||
@ -34,6 +34,7 @@ export interface GeneralData {
|
||||
translation: string[],
|
||||
original: string[],
|
||||
},
|
||||
expiration_date: any,
|
||||
photos: Gallery[],
|
||||
owner_pic: Gallery[],
|
||||
advertiser: string,
|
||||
|
||||
@ -22,6 +22,7 @@ export const defaultFormData: FormDataType = {
|
||||
en: undefined
|
||||
},
|
||||
photos: [],
|
||||
expiration_date: null,
|
||||
owner_pic: [],
|
||||
ad_status: {
|
||||
translation: [],
|
||||
@ -233,7 +234,7 @@ export const defaultFormData: FormDataType = {
|
||||
|
||||
export const editFormData = (ad: any, translate:any, locale: any) => {
|
||||
|
||||
// console.log(ad.ad_owner_pic);
|
||||
// console.log(ad.expiration_date);
|
||||
|
||||
|
||||
const data = {
|
||||
@ -255,6 +256,7 @@ export const editFormData = (ad: any, translate:any, locale: any) => {
|
||||
body: { fa: ad.translations.filter((x: any) => x.languages_code.code.slice(0, 2) === "fa")[0]?.body ?? '', en: ad.translations.filter((x: any) => x.languages_code.code.slice(0, 2) === "en")[0]?.body ?? '' },
|
||||
photos: ad.gallery.length > 0 ? ad.gallery.map((x: any) => ({ name: x.directus_files_id.filename_download, type: x.directus_files_id.type, size: x.directus_files_id.filesize, src: `${serverAddress() + x.directus_files_id.id}` })) : [],
|
||||
owner_pic: hasValue(ad.ad_owner_pic) ? [ad.ad_owner_pic].map((x: any) => ({ name: x.filename_download, type: x.type, size: x.filesize, src: `${serverAddress() + x.id}` })) : [],
|
||||
expiration_date: ad.expiration_date,
|
||||
advertiser: hasValue(ad.advertiser_name) ? ad.advertiser_name : "",
|
||||
email: hasValue(ad.email) ? ad.email : "",
|
||||
phoneNumber: hasValue(ad.phone_number) ? ad.phone_number[0].phone_number : "",
|
||||
|
||||
@ -82,7 +82,7 @@ const FormParent: React.FunctionComponent<FormParent> = ({ adData, uploadedPics,
|
||||
<StepsMap steps={steps} currentStep={currentStep} onStepClick={setCurrentStep} formErrors={formErrors.current} />
|
||||
{currentStep === 1 && <GeneralDetailsForm fieldsData={fieldsData.data} finalData={formData.generalData} onCatUpdate={handleCatChange} handleInputChange={handleInputChange} onNextStep={handleNextStep} />}
|
||||
{currentStep === 2 && <MainDetails handleInputChange={handleInputChange} onNextStep={handleNextStep} onPrevStep={handlePrevStep} cat={cat} formData={formData} />}
|
||||
{currentStep === 3 && <ReviewDetails onPrevStep={handlePrevStep} uploadedPics={uploadedPics} prevOwnerPic={prevOwnerPic} formData={formData} cat={cat} />}
|
||||
{currentStep === 3 && <ReviewDetails onPrevStep={handlePrevStep} uploadedPics={uploadedPics} prevOwnerPic={prevOwnerPic} formData={formData} cat={cat} expDate={adData.generalData.expiration_date} />}
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
@ -63,10 +63,10 @@ export const jobDetails = (formData: FormDataType, photos: any, ownerPhotos:any,
|
||||
languages: ${dataRepeater(formData.job.languages.ids, "languages")}
|
||||
}`
|
||||
}
|
||||
export const generalDetails = (formData: FormDataType, photos: any, ownerPhotos: any, locale: any) => {
|
||||
export const generalDetails = (formData: FormDataType, photos: any, ownerPhotos: any, locale: any, expirationDate: any) => {
|
||||
|
||||
return `{
|
||||
status: "${formData.generalData.mainCat.ids[0] === '27' ? 'draft' : 'published'}",
|
||||
status: "${formData.generalData.mainCat.ids[0] === '27' ? (hasValue(expirationDate) ? 'published' : 'draft') : 'published'}",
|
||||
ad_status: "${formData.generalData.ad_status.original}",
|
||||
translations: [
|
||||
${hasValue(formData.generalData.title.fa) ?
|
||||
@ -290,21 +290,21 @@ export const updateJob = (formData: FormDataType, photos: any, ownerPhotos: any,
|
||||
`
|
||||
|
||||
// General ads
|
||||
export const addNewGeneralAd = (formData: FormDataType, photos: any, ownerPhotos:any, locale: any) => gql`
|
||||
export const addNewGeneralAd = (formData: FormDataType, photos: any, ownerPhotos:any, locale: any, expirationDate: any) => gql`
|
||||
mutation {
|
||||
create_Ads_item (
|
||||
data: ${generalDetails(formData, photos, ownerPhotos, locale)}
|
||||
data: ${generalDetails(formData, photos, ownerPhotos, locale, expirationDate)}
|
||||
)
|
||||
{
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
export const updateGeneralAd = (formData: FormDataType, photos: any, ownerPhotos: any, id: string | string[] | undefined, locale: any) => gql`
|
||||
export const updateGeneralAd = (formData: FormDataType, photos: any, ownerPhotos: any, id: string | string[] | undefined, locale: any, expirationDate: any) => gql`
|
||||
mutation {
|
||||
update_Ads_item (
|
||||
id: ${id},
|
||||
data: ${generalDetails(formData, photos, ownerPhotos, locale)}
|
||||
data: ${generalDetails(formData, photos, ownerPhotos, locale, expirationDate)}
|
||||
)
|
||||
{
|
||||
id
|
||||
|
||||
@ -19,6 +19,7 @@ interface ReviewDetails {
|
||||
prevOwnerPic: any,
|
||||
formData: FormDataType,
|
||||
cat: string,
|
||||
expDate: any;
|
||||
onPrevStep: () => void,
|
||||
}
|
||||
interface PublishError {
|
||||
@ -180,7 +181,7 @@ const AdPublished: React.FunctionComponent<AdPublished> = ({ }) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
const ReviewDetails: React.FunctionComponent<ReviewDetails> = ({ className, uploadedPics, prevOwnerPic, formData, cat, onPrevStep }) => {
|
||||
const ReviewDetails: React.FunctionComponent<ReviewDetails> = ({ className, uploadedPics, prevOwnerPic, formData, cat, expDate, onPrevStep }) => {
|
||||
|
||||
// states
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
@ -298,7 +299,7 @@ const ReviewDetails: React.FunctionComponent<ReviewDetails> = ({ className, uplo
|
||||
if (cat === "استخدام" || cat === "employment") paylaod = hasValue(router.query.id) ? updateJob(formData, uploadedPhotos, ownerPicUploadedPhotos, router.query.id, router.locale) : addNewJob(formData, uploadedPhotos, ownerPicUploadedPhotos, router.locale)
|
||||
else if (cat === "وسایل نقلیه" || cat === "vehicles") paylaod = hasValue(router.query.id) ? updateVehicle(formData, uploadedPhotos, ownerPicUploadedPhotos, router.query.id, router.locale) : addNewVehicle(formData, uploadedPhotos, ownerPicUploadedPhotos, router.locale)
|
||||
else if (cat === "املاک" || cat === "real estate") paylaod = hasValue(router.query.id) ? updateRealEstate(formData, uploadedPhotos, ownerPicUploadedPhotos, router.query.id, router.locale) : addNewRealEstate(formData, uploadedPhotos, ownerPicUploadedPhotos, router.locale)
|
||||
else paylaod = hasValue(router.query.id) ? updateGeneralAd(formData, uploadedPhotos, ownerPicUploadedPhotos, router.query.id, router.locale) : addNewGeneralAd(formData, uploadedPhotos, ownerPicUploadedPhotos, router.locale)
|
||||
else paylaod = hasValue(router.query.id) ? updateGeneralAd(formData, uploadedPhotos, ownerPicUploadedPhotos, router.query.id, router.locale, expDate) : addNewGeneralAd(formData, uploadedPhotos, ownerPicUploadedPhotos, router.locale, expDate)
|
||||
|
||||
return paylaod;
|
||||
};
|
||||
@ -313,10 +314,6 @@ const ReviewDetails: React.FunctionComponent<ReviewDetails> = ({ className, uplo
|
||||
|
||||
// useEffects
|
||||
useEffect(() => {
|
||||
console.log('uploaded photos: ', uploadedPhotos);
|
||||
console.log('is owner pic uploaded: ', isOwnerPicUploaded);
|
||||
console.log('is submitted: ', isSubmitted);
|
||||
|
||||
if (isGalleryPicUploaded && !isSubmitted && (formData.generalData.owner_pic.length > 0 ? (isNewOwnerPic ? isOwnerPicUploaded : true) : true)) {
|
||||
submitForm();
|
||||
}
|
||||
|
||||
@ -6,22 +6,15 @@ import { CreditCardSolid, PlusSolid } from 'components/icons';
|
||||
import { basePath, numberFormatterFixed, useGetRouter } from 'services/general/general';
|
||||
import Modal from 'components/modal/modal';
|
||||
import RadioButton from 'components/radio-button/radio-button';
|
||||
import DataList from 'components/select/data-list';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
import { gql, useMutation } from '@apollo/client';
|
||||
import { privateDataFetch } from 'common/data/apollo-client';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { userData } from 'common/redux/slices/user';
|
||||
import { getStripeWalletProductId } from 'services/general/stripe';
|
||||
|
||||
interface Balance {
|
||||
balance: number;
|
||||
}
|
||||
|
||||
const stripePromise = loadStripe(
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as any
|
||||
);
|
||||
|
||||
const Balance: React.FunctionComponent<Balance> = ({ balance }) => {
|
||||
|
||||
// states
|
||||
@ -35,11 +28,11 @@ const Balance: React.FunctionComponent<Balance> = ({ balance }) => {
|
||||
const { data: session, status, update }: any = useSession();
|
||||
const accessToken = session && session.accessToken;
|
||||
const topupAmounts = [
|
||||
{ id: 1, amount: 10, text: `${translate("ads-wallet-topup-amount-option-title")} 10 ${translate("cad")}`, price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ id: 2, amount: 20, text: `${translate("ads-wallet-topup-amount-option-title")} 20 ${translate("cad")}`, price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ id: 3, amount: 50, text: `${translate("ads-wallet-topup-amount-option-title")} 50 ${translate("cad")}`, price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ id: 4, amount: 100, text: `${translate("ads-wallet-topup-amount-option-title")} 100 ${translate("cad")}`, price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ id: 5, amount: 200, text: `${translate("ads-wallet-topup-amount-option-title")} 200 ${translate("cad")}`, price_id: "price_1OwQlOF1S9Vi166eodLWGN3Z" },
|
||||
{ id: 1, amount: 10, text: `${translate("ads-wallet-topup-amount-option-title")} 10 ${translate("cad")}` },
|
||||
{ id: 2, amount: 20, text: `${translate("ads-wallet-topup-amount-option-title")} 20 ${translate("cad")}` },
|
||||
{ id: 3, amount: 50, text: `${translate("ads-wallet-topup-amount-option-title")} 50 ${translate("cad")}` },
|
||||
{ id: 4, amount: 100, text: `${translate("ads-wallet-topup-amount-option-title")} 100 ${translate("cad")}` },
|
||||
{ id: 5, amount: 200, text: `${translate("ads-wallet-topup-amount-option-title")} 200 ${translate("cad")}` }
|
||||
];
|
||||
|
||||
// methods
|
||||
@ -51,7 +44,7 @@ const Balance: React.FunctionComponent<Balance> = ({ balance }) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
price_id: topupAmounts.filter(x => x.amount === paymentAmount)[0].price_id,
|
||||
price_id: getStripeWalletProductId(topupAmounts.filter(x => x.amount === paymentAmount)[0].amount),
|
||||
service_type: "flierland-wallet-top-up",
|
||||
stripe_id: user.generalDetails.stripe_id,
|
||||
user_flierland_id: user.generalDetails.id,
|
||||
|
||||
@ -2,14 +2,13 @@ import SmartTable from 'components/tables/smart-table';
|
||||
import { useAppSelector, useAppDispatch } from '../../../redux/hooks';
|
||||
import Widget from '../navigation/widget';
|
||||
import useTranslate from 'services/translation/translation';
|
||||
import { adminAccessToken, basePath, fetchPrivateData, fetchPublicData, fetchSearchData, hasValue, numberFormatterFixed, url, useGetRouter } from 'services/general/general';
|
||||
import { fetchPrivateData, hasValue, numberFormatterFixed, useGetRouter } from 'services/general/general';
|
||||
import { userData } from 'common/redux/slices/user';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useSWRImmutable from 'swr/immutable';
|
||||
import { privateDataFetch } from 'common/data/apollo-client';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import InnerLoading from 'components/loading/inner-loading';
|
||||
import { ArrowLeftRegular, CheckSolid, CircleExclamationSolid } from 'components/icons';
|
||||
import { CircleExclamationSolid } from 'components/icons';
|
||||
import Modal from 'components/modal/modal';
|
||||
import Button from 'components/button/button';
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { useEffect } from 'react';
|
||||
import Script from 'next/script';
|
||||
import { pageView } from 'services/analytics/analytics';
|
||||
import { useGetRouter } from 'services/general/general';
|
||||
import RefreshTokenHandler from 'common/templates/authentication/autoSessionRefetch';
|
||||
|
||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: any) {
|
||||
|
||||
@ -27,13 +28,15 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: any) {
|
||||
}
|
||||
}, [router.events]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.dir = dir;
|
||||
}, [dir]);
|
||||
|
||||
// return
|
||||
return (
|
||||
<SessionProvider session={session} refetchInterval={10 * 60} refetchOnWindowFocus={false} refetchWhenOffline={false}>
|
||||
<SessionProvider session={session} refetchInterval={0} refetchOnWindowFocus={false} refetchWhenOffline={false}>
|
||||
<RefreshTokenHandler />
|
||||
<Provider store={store}>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
|
||||
@ -103,8 +103,8 @@ export default NextAuth({
|
||||
}
|
||||
|
||||
// Return previous token if the access token has not expired yet
|
||||
// if (Date.now() < token.expires_at - (120 secs = 2 mins)) {
|
||||
if (Date.now() < token.expires_at - 60000) {
|
||||
// if (Date.now() < token.expires_at - (90 secs = 1 mins & half)) {
|
||||
if (Date.now() < token.expires_at - (90 * 1000)) {
|
||||
return token
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { basePath } from 'services/general/general';
|
||||
import { getStripeSecretKey } from 'services/general/stripe';
|
||||
|
||||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
const stripe = require('stripe')(getStripeSecretKey());
|
||||
|
||||
const checkoutHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { basePath } from 'services/general/general';
|
||||
import { getStripeSecretKey } from 'services/general/stripe';
|
||||
|
||||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
const stripe = require('stripe')(getStripeSecretKey());
|
||||
|
||||
const createCustomer = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { basePath } from 'services/general/general';
|
||||
import { getStripeSecretKey } from 'services/general/stripe';
|
||||
|
||||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
const stripe = require('stripe')(getStripeSecretKey());
|
||||
|
||||
const getCustomerData = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { basePath } from 'services/general/general';
|
||||
import { getStripeSecretKey } from 'services/general/stripe';
|
||||
|
||||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
const stripe = require('stripe')(getStripeSecretKey());
|
||||
|
||||
const getPaymentIntents = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
|
||||
@ -8,8 +8,9 @@ import { updateAd } from 'common/templates/dashboard/ads/forms/payloads';
|
||||
import { updateUserData, updateUserDetails } from 'common/templates/dashboard/account/payloads';
|
||||
import { newPaymentQuery } from 'services/queries/data-queries';
|
||||
import { addNewPayment } from 'services/queries/cms';
|
||||
import { getStripeSecretKey } from 'services/general/stripe';
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as any);
|
||||
const stripe = new Stripe(getStripeSecretKey());
|
||||
|
||||
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
||||
|
||||
|
||||
168
src/pages/api/stripe/stripe_webhooks_test.ts
Normal file
168
src/pages/api/stripe/stripe_webhooks_test.ts
Normal file
@ -0,0 +1,168 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import Stripe from 'stripe';
|
||||
import { buffer } from 'micro';
|
||||
import Cors from 'micro-cors';
|
||||
import { adminAccessToken, hasValue, mtd } from 'services/general/general';
|
||||
import { privateDataFetch } from 'common/data/apollo-client';
|
||||
import { updateAd } from 'common/templates/dashboard/ads/forms/payloads';
|
||||
import { updateUserData, updateUserDetails } from 'common/templates/dashboard/account/payloads';
|
||||
import { newPaymentQuery } from 'services/queries/data-queries';
|
||||
import { addNewPayment } from 'services/queries/cms';
|
||||
import { getStripeSecretKey } from 'services/general/stripe';
|
||||
|
||||
const stripe = new Stripe(getStripeSecretKey());
|
||||
|
||||
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
||||
|
||||
const updatePayedAdData = (date: string) => {
|
||||
return `{
|
||||
status: "published",
|
||||
expiration_date: "${date}"
|
||||
}`
|
||||
};
|
||||
const updateUserDataQuery = (id: string) => {
|
||||
return `{
|
||||
stripe_customer_id: "${id}"
|
||||
}`
|
||||
};
|
||||
const updateUserWalletBalanceQuery = (newBalance: string) => {
|
||||
return `{
|
||||
wallet_balance: "${newBalance}"
|
||||
}`
|
||||
};
|
||||
|
||||
// Stripe requires the raw body to construct the event.
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false,
|
||||
},
|
||||
};
|
||||
|
||||
const cors = Cors({
|
||||
allowMethods: ['POST', 'HEAD'],
|
||||
});
|
||||
|
||||
const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
if (req.method === 'POST') {
|
||||
const buf = await buffer(req);
|
||||
const signature = req.headers['stripe-signature'];
|
||||
|
||||
let event;
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(
|
||||
buf.toString(),
|
||||
signature as any,
|
||||
webhookSecret as any
|
||||
);
|
||||
} catch (err:any) {
|
||||
// On error, log and return the error message.
|
||||
console.log(`❌ Error message: ${err.message}`);
|
||||
res.status(400).send(`Webhook Error: ${err.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Successfully constructed event.
|
||||
console.log('✅ Success:', event.id);
|
||||
|
||||
switch (event.type) {
|
||||
|
||||
case 'payment_intent.succeeded': {
|
||||
const paymentIntent = event.data.object;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'payment_intent.payment_failed': {
|
||||
const paymentIntent = event.data.object;
|
||||
console.log(
|
||||
`❌ Payment failed: ${paymentIntent.last_payment_error?.message}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'customer.created': {
|
||||
const customer = event.data.object;
|
||||
console.log(`Customer id: ${customer.id}`);
|
||||
|
||||
// update user's ad to the "paid" status & set exp date.
|
||||
try {
|
||||
const { data } = await privateDataFetch("system", adminAccessToken).mutate({
|
||||
mutation: updateUserData(customer.metadata.flierland_user_id, updateUserDataQuery(customer.id)),
|
||||
});
|
||||
console.log('Updated user:', data);
|
||||
} catch (error) {
|
||||
console.error('Error updating user:', error);
|
||||
// Handle error appropriately
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'checkout.session.completed': {
|
||||
const session = event.data.object;
|
||||
const today = new Date();
|
||||
const payedAdExpirationDate = mtd(today.setMonth(new Date().getMonth() + Number(session.metadata?.service_period)));
|
||||
|
||||
// add new payment to cms.
|
||||
try {
|
||||
const { data } = await privateDataFetch("", adminAccessToken).mutate({
|
||||
mutation: addNewPayment(newPaymentQuery({
|
||||
status: "published",
|
||||
payment_status: "succeeded",
|
||||
stripe_payment_id: session.payment_intent,
|
||||
stripe_customer_id: session.customer,
|
||||
owner_user_id: session.metadata?.user_flierland_id ?? "",
|
||||
order_amount: String(session.amount_subtotal),
|
||||
payed_amount: String(session.amount_total),
|
||||
currency: session.currency,
|
||||
payment_method: 'payment_gateway',
|
||||
discount_used: 'no',
|
||||
service_type: session.metadata?.service_type,
|
||||
service_id: session.metadata?.service_id ?? "",
|
||||
service_duration: session.metadata?.service_period ? Number(session.metadata?.service_period) : 0,
|
||||
service_duration_term: "month"
|
||||
})),
|
||||
});
|
||||
console.log('added payment:', data);
|
||||
} catch (error) {
|
||||
console.error('Error adding payment:', error);
|
||||
}
|
||||
|
||||
// update user's ad to the "paid" status & set exp date.
|
||||
if (session.metadata?.service_type !== "flierland-wallet-top-up") {
|
||||
try {
|
||||
const { data } = await privateDataFetch("", adminAccessToken).mutate({
|
||||
mutation: updateAd(session.metadata?.service_id, updatePayedAdData(payedAdExpirationDate)),
|
||||
});
|
||||
console.log('Updated ad:', data);
|
||||
} catch (error) {
|
||||
console.error('Error updating ad:', error);
|
||||
}
|
||||
}
|
||||
// update user's wallet (top-up)
|
||||
if (session.metadata?.service_type === "flierland-wallet-top-up") {
|
||||
try {
|
||||
const { data } = await privateDataFetch("system", adminAccessToken).mutate({
|
||||
mutation: updateUserData(session.metadata?.user_flierland_id, updateUserWalletBalanceQuery(String(Number(session.metadata?.user_wallet_balance) + Number((session.amount_subtotal ? (session.amount_subtotal / 100) : 0))))),
|
||||
});
|
||||
console.log('Updated user wallet:', data);
|
||||
} catch (error) {
|
||||
console.error('Error updating user walle:', error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
console.warn(`Unhandled event type: ${event.type}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a response to acknowledge receipt of the event.
|
||||
res.json({ received: true });
|
||||
} else {
|
||||
res.setHeader('Allow', 'POST');
|
||||
res.status(405).end('Method Not Allowed');
|
||||
}
|
||||
};
|
||||
|
||||
export default cors(webhookHandler as any);
|
||||
@ -74,6 +74,7 @@ const NewAdFrom: React.FunctionComponent<any> = ({ }) => {
|
||||
height
|
||||
type
|
||||
}
|
||||
expiration_date
|
||||
advertiser_name
|
||||
address
|
||||
postal_code
|
||||
|
||||
@ -3,7 +3,6 @@ import { userData } from "common/redux/slices/user";
|
||||
import GeneralLayout from "common/templates/dashboard/navigation/general-layout";
|
||||
import Balance from "common/templates/dashboard/wallet/balance";
|
||||
import PaymentHistory from "common/templates/dashboard/wallet/payment-history";
|
||||
import { hasValue } from "services/general/general";
|
||||
|
||||
interface Wallet {
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user