Standardized and improved payment messages on paid courses and resources

This commit is contained in:
austinkelsay 2024-09-12 09:17:22 -05:00
parent edcbb5fa52
commit aaedada2ca
5 changed files with 65 additions and 56 deletions

View File

@ -58,6 +58,8 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
amountPaid: parseInt(amount, 10)
};
console.log('purchaseData', purchaseData);
const result = await axios.post('/api/purchase/course', purchaseData);
if (result.status === 200) {

View File

@ -4,8 +4,9 @@ import { useImageProxy } from '@/hooks/useImageProxy';
import ZapDisplay from '@/components/zaps/ZapDisplay';
import { getTotalFromZaps } from '@/utils/lightning';
import { Tag } from 'primereact/tag';
import { nip19, nip04 } from 'nostr-tools';
import { nip19 } from 'nostr-tools';
import { useSession } from 'next-auth/react';
import GenericButton from '@/components/buttons/GenericButton';
import Image from 'next/image';
import dynamic from 'next/dynamic';
import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
@ -36,26 +37,6 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
useEffect(() => {
console.log("processedEvent", processedEvent);
}, [processedEvent]);
useEffect(() => {
console.log("lessons", lessons);
}, [lessons]);
useEffect(() => {
console.log("zaps", zaps);
}, [zaps]);
useEffect(() => {
console.log("paidCourse", paidCourse);
}, [paidCourse]);
useEffect(() => {
console.log("decryptionPerformed", decryptionPerformed);
}, [decryptionPerformed]);
useEffect(() => {
if (session) {
setUser(session.user);
@ -96,6 +77,38 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec
}
}, [zaps, processedEvent]);
const renderPaymentMessage = () => {
if (paidCourse && !decryptionPerformed) {
return (
<CoursePaymentButton
lnAddress={lnAddress}
amount={processedEvent.price}
onSuccess={handlePaymentSuccess}
onError={handlePaymentError}
courseId={processedEvent.d}
/>
);
}
const coursePurchased = session?.user?.purchased?.some(purchase =>
purchase?.courseId === processedEvent.d
);
if (paidCourse && decryptionPerformed && author && session?.user?.role?.subscribed && processedEvent?.pubkey !== session?.user?.pubkey) {
return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You are subscribed so you can access all paid content`} icon="pi pi-check" label="Subscribed" severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
if (paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey) {
return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You created this paid course, users must pay ${processedEvent.price} sats to access it`} icon="pi pi-check" label={`Price ${processedEvent.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
if (paidCourse && decryptionPerformed && author && coursePurchased) {
return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You have purchased this course`} icon="pi pi-check" label="Purchased" severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
return null;
};
if (!processedEvent || !author) {
return (
<div className="flex justify-center items-center h-screen">
@ -146,21 +159,7 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec
className="w-[344px] h-[194px] object-cover object-top rounded-lg"
/>
<div className='w-full flex justify-between items-center'>
{paidCourse && !decryptionPerformed && (
<CoursePaymentButton
lnAddress={lnAddress}
amount={processedEvent.price}
onSuccess={handlePaymentSuccess}
onError={handlePaymentError}
resourceId={processedEvent.d}
/>
)}
{paidCourse && decryptionPerformed && author && processedEvent?.pubkey !== session?.user?.pubkey && (
<p className='text-green-500'>Paid {processedEvent.price} sats</p>
)}
{paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey && (
<p className='text-green-500'>Price {processedEvent.price} sats</p>
)}
{renderPaymentMessage()}
<ZapDisplay
zapAmount={zapAmount}
event={processedEvent}

View File

@ -14,19 +14,11 @@ const MDDisplay = dynamic(
}
);
const CourseLesson = ({ lesson, course }) => {
const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid }) => {
const [zapAmount, setZapAmount] = useState(0);
const [paidResource, setPaidResource] = useState(false);
const [decryptedContent, setDecryptedContent] = useState(false);
const { zaps, zapsLoading, zapsError } = useZapsQuery({ event: lesson, type: "lesson" });
const { returnImageProxy } = useImageProxy();
useEffect(() => {
if (course.price) {
setPaidResource(true);
}
}, [course]);
useEffect(() => {
if (!zaps || zapsLoading || zapsError) return;
@ -36,10 +28,10 @@ const CourseLesson = ({ lesson, course }) => {
}, [zaps, zapsLoading, zapsError, lesson]);
const renderContent = () => {
if (decryptedContent) {
return <MDDisplay className='p-4 rounded-lg w-full' source={decryptedContent} />;
if (isPaid && decryptionPerformed) {
return <MDDisplay className='p-4 rounded-lg w-full' source={lesson.content} />;
}
if (paidResource && !decryptedContent) {
if (isPaid && !decryptionPerformed) {
return <p className="text-center text-xl text-red-500">This content is paid and needs to be purchased before viewing.</p>;
}
if (lesson?.content) {

View File

@ -4,6 +4,7 @@ import Image from "next/image";
import { useRouter } from "next/router";
import ResourcePaymentButton from "@/components/bitcoinConnect/ResourcePaymentButton";
import ZapDisplay from "@/components/zaps/ZapDisplay";
import GenericButton from "@/components/buttons/GenericButton";
import { useImageProxy } from "@/hooks/useImageProxy";
import { useZapsSubscription } from "@/hooks/nostrQueries/zaps/useZapsSubscription";
import { getTotalFromZaps } from "@/utils/lightning";
@ -26,6 +27,22 @@ const ResourceDetails = ({processedEvent, topics, title, summary, image, price,
}
}, [zaps, processedEvent]);
const renderPaymentMessage = () => {
if (session?.user && session.user?.role?.subscribed && decryptedContent) {
return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You are subscribed so you can access all paid content`} icon="pi pi-check" label="Subscribed" severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
if (paidResource && decryptedContent && author && processedEvent?.pubkey !== session?.user?.pubkey && !session?.user?.role?.subscribed) {
return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`Pay ${processedEvent.price} sats to access this content or subscribe to get access to all content`} icon="pi pi-check" label={`Paid ${processedEvent.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
if (paidResource && author && processedEvent?.pubkey === session?.user?.pubkey) {
return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You created this paid content, users must pay ${processedEvent.price} sats to access it`} icon="pi pi-check" label={`Price ${processedEvent.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
return null;
};
return (
<div className='w-full flex flex-row justify-between max-tab:flex-col max-mob:flex-col'>
<i className='pi pi-arrow-left pr-8 cursor-pointer hover:opacity-75 max-tab:pl-2 max-tab:my-4' onClick={() => router.push('/')} />
@ -76,11 +93,7 @@ const ResourceDetails = ({processedEvent, topics, title, summary, image, price,
resourceId={processedEvent.d}
/>}
{/* if the resource has been paid for show a green paid x sats text */}
{paidResource && decryptedContent && author && !processedEvent?.pubkey === session?.user?.pubkey && <p className='text-green-500'>Paid {processedEvent.price} sats</p>}
{/* if this is the author of the resource show a zap button */}
{paidResource && author && processedEvent?.pubkey === session?.user?.pubkey && <p className='text-green-500'>Price {processedEvent.price} sats</p>}
{renderPaymentMessage()}
<ZapDisplay
zapAmount={zapAmount}

View File

@ -129,6 +129,8 @@ const Course = () => {
session.user?.role?.subscribed ||
session.user?.pubkey === course?.pubkey;
console.log('canAccess', canAccess);
if (canAccess && lessons.length > 0) {
try {
const decryptedLessons = await Promise.all(lessons.map(async (lesson) => {
@ -154,11 +156,12 @@ const Course = () => {
}
}, [course, lessons, paidCourse, decryptionPerformed]);
const handlePaymentSuccess = async (response, newCourse) => {
const handlePaymentSuccess = async (response) => {
if (response && response?.preimage) {
console.log("newCourse", newCourse);
const updated = await update();
console.log("session after update", updated);
showToast('success', 'Payment Success', 'You have successfully purchased this course');
router.reload();
} else {
showToast('error', 'Error', 'Failed to purchase course. Please try again.');
}
@ -187,7 +190,7 @@ const Course = () => {
handlePaymentError={handlePaymentError}
/>
{lessons.length > 0 && lessons.map((lesson, index) => (
<CourseLesson key={index} lesson={lesson} course={course} />
<CourseLesson key={index} lesson={lesson} course={course} decryptionPerformed={decryptionPerformed} isPaid={paidCourse} />
))}
<div className="mx-auto my-6">
{course?.content && <MDDisplay className='p-4 rounded-lg' source={course.content} />}