mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
Improved payment buttons
This commit is contained in:
parent
374bef5a51
commit
054adf6869
@ -1,148 +1,106 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import { Dialog } from 'primereact/dialog'; // Import Dialog component
|
||||||
import { initializeBitcoinConnect } from './BitcoinConnect';
|
import { initializeBitcoinConnect } from './BitcoinConnect';
|
||||||
import { LightningAddress } from '@getalby/lightning-tools';
|
import { LightningAddress } from '@getalby/lightning-tools';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import axios from 'axios'; // Import axios for API calls
|
import axios from 'axios'; // Import axios for API calls
|
||||||
|
|
||||||
const PayButton = dynamic(
|
const Payment = dynamic(
|
||||||
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.PayButton),
|
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.Payment),
|
||||||
{
|
{
|
||||||
ssr: false,
|
ssr: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }) => {
|
const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }) => {
|
||||||
const [invoice, setInvoice] = useState(null);
|
const [invoice, setInvoice] = useState(null);
|
||||||
const [userId, setUserId] = useState(null);
|
const [userId, setUserId] = useState(null);
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
const [pollingInterval, setPollingInterval] = useState(null);
|
const { data: session } = useSession();
|
||||||
const { data: session } = useSession();
|
const [dialogVisible, setDialogVisible] = useState(false); // New state for dialog visibility
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
setUserId(session.user.id);
|
setUserId(session.user.id);
|
||||||
}
|
}
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeBitcoinConnect();
|
initializeBitcoinConnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchInvoice = async () => {
|
const fetchInvoice = async () => {
|
||||||
try {
|
try {
|
||||||
const ln = new LightningAddress(lnAddress);
|
const ln = new LightningAddress(lnAddress);
|
||||||
await ln.fetch();
|
await ln.fetch();
|
||||||
const invoice = await ln.requestInvoice({ satoshi: amount });
|
const invoice = await ln.requestInvoice({ satoshi: amount });
|
||||||
setInvoice(invoice);
|
setInvoice(invoice);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching invoice:', error);
|
console.error('Error fetching invoice:', error);
|
||||||
showToast('error', 'Invoice Error', 'Failed to fetch the invoice.');
|
showToast('error', 'Invoice Error', 'Failed to fetch the invoice.');
|
||||||
if (onError) onError(error);
|
if (onError) onError(error);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchInvoice();
|
||||||
|
}, [lnAddress, amount, onError, showToast]);
|
||||||
|
|
||||||
|
const handlePaymentSuccess = async (response) => {
|
||||||
|
try {
|
||||||
|
const purchaseData = {
|
||||||
|
userId: userId,
|
||||||
|
courseId: courseId,
|
||||||
|
amountPaid: parseInt(amount, 10)
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await axios.post('/api/purchase/course', purchaseData);
|
||||||
|
|
||||||
|
if (result.status === 200) {
|
||||||
|
showToast('success', 'Payment Successful', `Paid ${amount} sats and updated user purchases`);
|
||||||
|
if (onSuccess) onSuccess(response);
|
||||||
|
} else {
|
||||||
|
throw new Error('Failed to update user purchases');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating user purchases:', error);
|
||||||
|
showToast('error', 'Purchase Update Failed', 'Payment was successful, but failed to update user purchases.');
|
||||||
|
if (onError) onError(error);
|
||||||
|
}
|
||||||
|
setDialogVisible(false); // Close the dialog on successful payment
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchInvoice();
|
return (
|
||||||
}, [lnAddress, amount, onError, showToast]);
|
<>
|
||||||
|
<Button
|
||||||
const startPolling = (invoice) => {
|
label={`Pay ${amount} sats`}
|
||||||
const intervalId = setInterval(async () => {
|
onClick={() => setDialogVisible(true)}
|
||||||
try {
|
disabled={!invoice}
|
||||||
const paid = await invoice.verifyPayment();
|
severity='info'
|
||||||
console.log('Polling for payment - Paid:', paid);
|
className='text-[#f8f8ff] text-sm'
|
||||||
if (paid) {
|
/>
|
||||||
clearInterval(intervalId); // Stop polling
|
<Dialog
|
||||||
handlePaymentSuccess(invoice);
|
visible={dialogVisible}
|
||||||
}
|
onHide={() => setDialogVisible(false)}
|
||||||
} catch (error) {
|
header="Make Payment"
|
||||||
console.error('Polling error:', error);
|
style={{ width: '50vw' }}
|
||||||
clearInterval(intervalId); // Stop polling on error
|
>
|
||||||
handlePaymentError(error);
|
{invoice ? (
|
||||||
}
|
<Payment
|
||||||
}, 5000); // Poll every 5 seconds
|
invoice={invoice.paymentRequest}
|
||||||
|
onPaid={handlePaymentSuccess}
|
||||||
setPollingInterval(intervalId);
|
paymentMethods='all'
|
||||||
};
|
title={`Pay ${amount} sats`}
|
||||||
|
/>
|
||||||
const stopPolling = () => {
|
) : (
|
||||||
if (pollingInterval) {
|
<p>Loading payment details...</p>
|
||||||
clearInterval(pollingInterval);
|
)}
|
||||||
setPollingInterval(null);
|
</Dialog>
|
||||||
}
|
</>
|
||||||
};
|
);
|
||||||
|
|
||||||
const handlePaymentSuccess = async (response) => {
|
|
||||||
stopPolling();
|
|
||||||
await closeModal();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const purchaseData = {
|
|
||||||
userId: userId,
|
|
||||||
courseId: courseId,
|
|
||||||
amountPaid: parseInt(amount, 10)
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await axios.post('/api/purchase/course', purchaseData);
|
|
||||||
|
|
||||||
if (result.status === 200) {
|
|
||||||
showToast('success', 'Payment Successful', `Paid ${amount} sats and updated user purchases`);
|
|
||||||
if (onSuccess) onSuccess(response);
|
|
||||||
} else {
|
|
||||||
throw new Error('Failed to update user purchases');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating user purchases:', error);
|
|
||||||
showToast('error', 'Purchase Update Failed', 'Payment was successful, but failed to update user purchases.');
|
|
||||||
if (onError) onError(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePaymentError = (error) => {
|
|
||||||
console.error('Payment failed:', error);
|
|
||||||
showToast('error', 'Payment Failed', error.message || 'An error occurred during payment.');
|
|
||||||
if (onError) onError(error);
|
|
||||||
stopPolling(); // Stop polling on error
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalOpen = () => {
|
|
||||||
console.log('Modal opened');
|
|
||||||
if (invoice) {
|
|
||||||
startPolling(invoice); // Start polling when modal is opened
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalClose = () => {
|
|
||||||
console.log('Modal closed');
|
|
||||||
stopPolling(); // Stop polling when modal is closed
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeModal = async () => {
|
|
||||||
const { closeModal } = await import('@getalby/bitcoin-connect-react');
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center">
|
|
||||||
{invoice ? (
|
|
||||||
<PayButton
|
|
||||||
invoice={invoice.paymentRequest}
|
|
||||||
onClick={handleModalOpen}
|
|
||||||
onPaid={handlePaymentSuccess}
|
|
||||||
onModalClose={handleModalClose}
|
|
||||||
title={`Pay ${amount} sats`}
|
|
||||||
>
|
|
||||||
Pay Now
|
|
||||||
</PayButton>
|
|
||||||
) : (
|
|
||||||
<button disabled className="p-2 bg-gray-500 text-white rounded">
|
|
||||||
Loading...
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<span className="ml-2 text-white text-lg">{amount} sats</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CoursePaymentButton;
|
export default CoursePaymentButton;
|
@ -1,24 +1,24 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import { Dialog } from 'primereact/dialog';
|
||||||
import { initializeBitcoinConnect } from './BitcoinConnect';
|
import { initializeBitcoinConnect } from './BitcoinConnect';
|
||||||
import { LightningAddress } from '@getalby/lightning-tools';
|
import { LightningAddress } from '@getalby/lightning-tools';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import axios from 'axios'; // Import axios for API calls
|
import axios from 'axios';
|
||||||
|
|
||||||
const PayButton = dynamic(
|
const Payment = dynamic(
|
||||||
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.PayButton),
|
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.Payment),
|
||||||
{
|
{ ssr: false }
|
||||||
ssr: false,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const ResourcePaymentButton = ({ lnAddress, amount, onSuccess, onError, resourceId }) => {
|
const ResourcePaymentButton = ({ lnAddress, amount, onSuccess, onError, resourceId }) => {
|
||||||
const [invoice, setInvoice] = useState(null);
|
const [invoice, setInvoice] = useState(null);
|
||||||
const [userId, setUserId] = useState(null);
|
const [userId, setUserId] = useState(null);
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
const [pollingInterval, setPollingInterval] = useState(null);
|
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
const [dialogVisible, setDialogVisible] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
@ -47,45 +47,14 @@ const ResourcePaymentButton = ({ lnAddress, amount, onSuccess, onError, resource
|
|||||||
fetchInvoice();
|
fetchInvoice();
|
||||||
}, [lnAddress, amount, onError, showToast]);
|
}, [lnAddress, amount, onError, showToast]);
|
||||||
|
|
||||||
const startPolling = (invoice) => {
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
try {
|
|
||||||
const paid = await invoice.verifyPayment();
|
|
||||||
console.log('Polling for payment - Paid:', paid);
|
|
||||||
if (paid) {
|
|
||||||
clearInterval(intervalId); // Stop polling
|
|
||||||
handlePaymentSuccess(invoice);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Polling error:', error);
|
|
||||||
clearInterval(intervalId); // Stop polling on error
|
|
||||||
handlePaymentError(error);
|
|
||||||
}
|
|
||||||
}, 5000); // Poll every 5 seconds
|
|
||||||
|
|
||||||
setPollingInterval(intervalId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopPolling = () => {
|
|
||||||
if (pollingInterval) {
|
|
||||||
clearInterval(pollingInterval);
|
|
||||||
setPollingInterval(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePaymentSuccess = async (response) => {
|
const handlePaymentSuccess = async (response) => {
|
||||||
stopPolling();
|
|
||||||
await closeModal();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create a new purchase record
|
|
||||||
const purchaseData = {
|
const purchaseData = {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
resourceId: resourceId,
|
resourceId: resourceId,
|
||||||
amountPaid: parseInt(amount, 10) // Convert amount to integer
|
amountPaid: parseInt(amount, 10)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make an API call to add the purchase to the user
|
|
||||||
const result = await axios.post('/api/purchase/resource', purchaseData);
|
const result = await axios.post('/api/purchase/resource', purchaseData);
|
||||||
|
|
||||||
if (result.status === 200) {
|
if (result.status === 200) {
|
||||||
@ -99,51 +68,36 @@ const ResourcePaymentButton = ({ lnAddress, amount, onSuccess, onError, resource
|
|||||||
showToast('error', 'Purchase Update Failed', 'Payment was successful, but failed to update user purchases.');
|
showToast('error', 'Purchase Update Failed', 'Payment was successful, but failed to update user purchases.');
|
||||||
if (onError) onError(error);
|
if (onError) onError(error);
|
||||||
}
|
}
|
||||||
};
|
setDialogVisible(false);
|
||||||
|
|
||||||
const handlePaymentError = (error) => {
|
|
||||||
console.error('Payment failed:', error);
|
|
||||||
showToast('error', 'Payment Failed', error.message || 'An error occurred during payment.');
|
|
||||||
if (onError) onError(error);
|
|
||||||
stopPolling(); // Stop polling on error
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalOpen = () => {
|
|
||||||
console.log('Modal opened');
|
|
||||||
if (invoice) {
|
|
||||||
startPolling(invoice); // Start polling when modal is opened
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalClose = () => {
|
|
||||||
console.log('Modal closed');
|
|
||||||
stopPolling(); // Stop polling when modal is closed
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeModal = async () => {
|
|
||||||
const { closeModal } = await import('@getalby/bitcoin-connect-react');
|
|
||||||
closeModal();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center">
|
<>
|
||||||
{invoice ? (
|
<Button
|
||||||
<PayButton
|
label={`Pay ${amount} sats`}
|
||||||
invoice={invoice.paymentRequest}
|
icon="pi pi-wallet"
|
||||||
onClick={handleModalOpen}
|
onClick={() => setDialogVisible(true)}
|
||||||
onPaid={handlePaymentSuccess}
|
disabled={!invoice}
|
||||||
onModalClose={handleModalClose}
|
className="p-2 bg-blue-500 text-white rounded"
|
||||||
title={`Pay ${amount} sats`}
|
/>
|
||||||
>
|
<Dialog
|
||||||
Pay Now
|
visible={dialogVisible}
|
||||||
</PayButton>
|
onHide={() => setDialogVisible(false)}
|
||||||
) : (
|
header="Make Payment"
|
||||||
<button disabled className="p-2 bg-gray-500 text-white rounded">
|
style={{ width: '50vw' }}
|
||||||
Loading...
|
>
|
||||||
</button>
|
{invoice ? (
|
||||||
)}
|
<Payment
|
||||||
<span className="ml-2 text-white text-lg">{amount} sats</span>
|
invoice={invoice.paymentRequest}
|
||||||
</div>
|
onPaid={handlePaymentSuccess}
|
||||||
|
paymentMethods='all'
|
||||||
|
title={`Pay ${amount} sats`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p>Loading payment details...</p>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user