Start on discount code impl, keep it single and hardcoded and simple at first

This commit is contained in:
austinkelsay 2024-11-18 13:34:46 -06:00
parent 1392f50261
commit 9e8e355ae8
No known key found for this signature in database
GPG Key ID: 44CB4EC6D9F2FA02
4 changed files with 71 additions and 9 deletions

View File

@ -10,12 +10,15 @@ import axios from 'axios';
import GenericButton from '@/components/buttons/GenericButton';
import { useRouter } from 'next/router';
import useWindowWidth from '@/hooks/useWindowWidth';
import { InputText } from 'primereact/inputtext';
const Payment = dynamic(
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.Payment),
{ ssr: false }
);
const DISCOUNT_CODE = process.env.NEXT_PUBLIC_DISCOUNT_CODE;
const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }) => {
const [invoice, setInvoice] = useState(null);
const [isLoading, setIsLoading] = useState(false);
@ -25,6 +28,9 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
const router = useRouter();
const windowWidth = useWindowWidth();
const isMobile = windowWidth < 768;
const [discountCode, setDiscountCode] = useState('');
const [discountApplied, setDiscountApplied] = useState(false);
const [showDiscountInput, setShowDiscountInput] = useState(false);
useEffect(() => {
let intervalId;
@ -49,12 +55,21 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
};
}, [invoice]);
const calculateDiscount = (originalAmount) => {
if (discountCode === DISCOUNT_CODE) {
const discountedAmount = 21000;
const savedPercentage = Math.round(((originalAmount - discountedAmount) / originalAmount) * 100);
return { discountedAmount, savedPercentage };
}
return { discountedAmount: originalAmount, savedPercentage: 0 };
};
const fetchInvoice = async () => {
setIsLoading(true);
try {
const ln = new LightningAddress(lnAddress);
await ln.fetch();
const invoice = await ln.requestInvoice({ satoshi: amount });
const invoice = await ln.requestInvoice({ satoshi: discountApplied ? calculateDiscount(amount).discountedAmount : amount });
setInvoice(invoice);
setDialogVisible(true);
} catch (error) {
@ -70,7 +85,7 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
const purchaseData = {
userId: session.user.id,
courseId: courseId,
amountPaid: parseInt(amount, 10)
amountPaid: discountApplied ? calculateDiscount(amount).discountedAmount : parseInt(amount, 10)
};
const result = await axios.post('/api/purchase/course', purchaseData);
@ -90,9 +105,56 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
};
return (
<>
<div className="flex flex-col gap-2">
{!showDiscountInput ? (
<button
onClick={() => setShowDiscountInput(true)}
className="text-sm text-blue-500 hover:text-blue-700 underline self-start flex items-center gap-1"
>
<i className="pi pi-tag text-xs"></i>
Have a discount code?
</button>
) : (
<div className="flex flex-col gap-2 w-full">
<div className="flex gap-2 items-center">
<div className="relative flex-1">
<InputText
value={discountCode}
onChange={(e) => {
setDiscountCode(e.target.value);
setDiscountApplied(e.target.value === DISCOUNT_CODE);
}}
placeholder="Enter discount code"
className="text-sm w-full p-2"
/>
<button
onClick={() => {
setShowDiscountInput(false);
setDiscountCode('');
setDiscountApplied(false);
}}
className="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
>
<i className="pi pi-times text-xs"></i>
</button>
</div>
{discountApplied && (
<span className="text-green-500 text-sm whitespace-nowrap flex items-center gap-1">
<i className="pi pi-check-circle"></i>
{calculateDiscount(amount).savedPercentage}% off!
</span>
)}
</div>
{discountApplied && (
<div className="text-xs text-gray-500 flex items-center gap-1">
<span className="line-through">{amount} sats</span>
<span className="text-green-500 font-semibold"> {calculateDiscount(amount).discountedAmount} sats</span>
</div>
)}
</div>
)}
<GenericButton
label={`${amount} sats`}
label={`${discountApplied ? calculateDiscount(amount).discountedAmount : amount} sats`}
icon="pi pi-wallet"
onClick={() => {
if (status === 'unauthenticated') {
@ -133,7 +195,7 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
<p>Loading payment details...</p>
)}
</Dialog>
</>
</div>
);
};

View File

@ -91,7 +91,7 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec
}
if (paidCourse && decryptionPerformed && author && processedEvent?.pubkey !== session?.user?.pubkey && !session?.user?.role?.subscribed) {
return <GenericButton 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" />
return <GenericButton icon="pi pi-check" label={`Paid`} severity="success" outlined size="small" tooltip={`You paid ${processedEvent.price} sats to access this course (or potentially less if a discount was applied)`} tooltipOptions={{ position: 'top' }} className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
if (paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey) {

View File

@ -77,11 +77,11 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,
// if the user paid for the course that this lesson is in, show a message that says you have this lesson through the course and show how much you paid for the course
if (isLesson && course && session?.user?.purchased?.some(purchase => purchase.courseId === course)) {
return <GenericButton tooltipOptions={{ position: 'top' }} tooltip={`You have this lesson through purchasing the course it belongs to. You paid ${session?.user?.purchased?.find(purchase => purchase.courseId === course)?.course?.price} sats for the course.`} icon="pi pi-check" label={`Paid ${session?.user?.purchased?.find(purchase => purchase.courseId === course)?.course?.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
return <GenericButton tooltipOptions={{ position: 'top' }} tooltip={`You have this lesson through purchasing the course it belongs to. You paid ${session?.user?.purchased?.find(purchase => purchase.courseId === course)?.course?.price} sats for the course.`} icon="pi pi-check" label={`Paid`} 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 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" />
return <GenericButton icon="pi pi-check" label={`Paid`} severity="success" outlined size="small" tooltip={`You paid ${processedEvent.price} sats to access this content (or potentially less if a discount was applied)`} tooltipOptions={{ position: 'top' }} className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" />
}
if (paidResource && author && processedEvent?.pubkey === session?.user?.pubkey) {

View File

@ -11,7 +11,7 @@ const appConfig = {
"wss://purplerelay.com/",
"wss://relay.devs.tools/"
],
authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345"],
authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345", "468f729dd409053dac5e7470622c3996aad88db6ed1de9165cb1921b5ab4fd5e"],
customLightningAddresses: [
{
// todo remove need for lowercase