mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 19:01:19 +00:00
Payment flow for resources works
This commit is contained in:
parent
573f560f28
commit
7808a88258
@ -3,6 +3,7 @@ import dynamic from 'next/dynamic';
|
||||
import { initializeBitcoinConnect } from './BitcoinConnect';
|
||||
import { LightningAddress } from '@getalby/lightning-tools';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import axios from 'axios'; // Import axios for API calls
|
||||
|
||||
const PayButton = dynamic(
|
||||
@ -12,10 +13,18 @@ const PayButton = dynamic(
|
||||
}
|
||||
);
|
||||
|
||||
const PaymentButton = ({ lnAddress, amount, onSuccess, onError, userId, resourceId }) => {
|
||||
const ResourcePaymentButton = ({ lnAddress, amount, onSuccess, onError, resourceId }) => {
|
||||
const [invoice, setInvoice] = useState(null);
|
||||
const [userId, setUserId] = useState(null);
|
||||
const { showToast } = useToast();
|
||||
const [pollingInterval, setPollingInterval] = useState(null);
|
||||
const { data: session } = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.user) {
|
||||
setUserId(session.user.id);
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
useEffect(() => {
|
||||
initializeBitcoinConnect();
|
||||
@ -77,7 +86,7 @@ const PaymentButton = ({ lnAddress, amount, onSuccess, onError, userId, resource
|
||||
};
|
||||
|
||||
// Make an API call to add the purchase to the user
|
||||
const result = await axios.post('/api/purchases', purchaseData);
|
||||
const result = await axios.post('/api/purchase/resource', purchaseData);
|
||||
|
||||
if (result.status === 200) {
|
||||
showToast('success', 'Payment Successful', `Paid ${amount} sats and updated user purchases`);
|
||||
@ -138,4 +147,4 @@ const PaymentButton = ({ lnAddress, amount, onSuccess, onError, userId, resource
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentButton;
|
||||
export default ResourcePaymentButton;
|
||||
|
@ -44,17 +44,24 @@ export const getUserByPubkey = async (pubkey) => {
|
||||
});
|
||||
}
|
||||
|
||||
export const addPurchaseToUser = async (userId, purchaseData) => {
|
||||
export const addResourcePurchaseToUser = async (userId, purchaseData) => {
|
||||
return await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
purchased: {
|
||||
create: purchaseData
|
||||
}
|
||||
create: {
|
||||
resourceId: purchaseData.resourceId,
|
||||
amountPaid: purchaseData.amountPaid,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
purchased: true
|
||||
}
|
||||
purchased: {
|
||||
include: {
|
||||
resource: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,43 @@ const ndk = new NDK({
|
||||
explicitRelayUrls: relayUrls,
|
||||
});
|
||||
|
||||
const authorize = async (pubkey) => {
|
||||
await ndk.connect();
|
||||
const user = ndk.getUser({ pubkey });
|
||||
|
||||
try {
|
||||
const profile = await user.fetchProfile();
|
||||
|
||||
// Check if user exists, create if not
|
||||
const response = await axios.get(`${BASE_URL}/api/users/${pubkey}`);
|
||||
if (response.status === 200 && response.data) {
|
||||
const fields = await findKind0Fields(profile);
|
||||
|
||||
// Combine user object with kind0Fields, giving priority to kind0Fields
|
||||
const combinedUser = { ...fields, ...response.data };
|
||||
|
||||
// Update the user on the backend if necessary
|
||||
// await axios.put(`${BASE_URL}/api/users/${combinedUser.id}`, combinedUser);
|
||||
|
||||
return combinedUser;
|
||||
} else if (response.status === 204) {
|
||||
// Create user
|
||||
if (profile) {
|
||||
const fields = await findKind0Fields(profile);
|
||||
console.log('FEEEEELDS', fields);
|
||||
const payload = { pubkey, ...fields };
|
||||
|
||||
const createUserResponse = await axios.post(`${BASE_URL}/api/users`, payload);
|
||||
return createUserResponse.data;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Nostr login error:", error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
@ -30,46 +67,19 @@ export default NextAuth({
|
||||
},
|
||||
authorize: async (credentials) => {
|
||||
if (credentials?.pubkey) {
|
||||
await ndk.connect();
|
||||
|
||||
const user = ndk.getUser({ pubkey: credentials.pubkey });
|
||||
|
||||
try {
|
||||
const profile = await user.fetchProfile();
|
||||
|
||||
// Check if user exists, create if not
|
||||
const response = await axios.get(`${BASE_URL}/api/users/${credentials.pubkey}`);
|
||||
if (response.status === 200 && response.data) {
|
||||
const fields = await findKind0Fields(profile);
|
||||
|
||||
// Combine user object with kind0Fields, giving priority to kind0Fields
|
||||
const combinedUser = { ...fields, ...response.data };
|
||||
|
||||
// Update the user on the backend if necessary
|
||||
// await axios.put(`${BASE_URL}/api/users/${combinedUser.id}`, combinedUser);
|
||||
|
||||
return combinedUser;
|
||||
} else if (response.status === 204) {
|
||||
// Create user
|
||||
if (profile) {
|
||||
const fields = await findKind0Fields(profile);
|
||||
console.log('FEEEEELDS', fields);
|
||||
const payload = { pubkey: credentials.pubkey, ...fields };
|
||||
|
||||
const createUserResponse = await axios.post(`${BASE_URL}/api/users`, payload);
|
||||
return createUserResponse.data;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Nostr login error:", error);
|
||||
}
|
||||
return await authorize(credentials.pubkey);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token, user }) {
|
||||
async jwt({ token, trigger, user }) {
|
||||
if (trigger === "update") {
|
||||
// if we trigger an update call the authorize function again
|
||||
const newUser = await authorize(token.user.pubkey);
|
||||
token.user = newUser;
|
||||
}
|
||||
// Add combined user object to the token
|
||||
if (user) {
|
||||
token.user = user;
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { addPurchaseToUser } from "@/db/models/userModels";
|
||||
import { addResourcePurchaseToUser } from "@/db/models/userModels";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
if (req.method === 'POST') {
|
||||
try {
|
||||
const { userId, resourceId, amountPaid } = req.body;
|
||||
|
||||
const updatedUser = await addPurchaseToUser(userId, {
|
||||
const updatedUser = await addResourcePurchaseToUser(userId, {
|
||||
resourceId,
|
||||
amountPaid: parseInt(amountPaid, 10) // Ensure amountPaid is an integer
|
||||
});
|
@ -48,7 +48,7 @@ export default function Details() {
|
||||
const [authorView, setAuthorView] = useState(false);
|
||||
|
||||
const ndk = useNDKContext();
|
||||
const { data: session, status } = useSession();
|
||||
const { data: session, update } = useSession();
|
||||
const [user, setUser] = useState(null);
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const { showToast } = useToast();
|
||||
@ -81,12 +81,20 @@ export default function Details() {
|
||||
useEffect(() => {
|
||||
const decryptContent = async () => {
|
||||
if (user && paidResource) {
|
||||
if (user?.purchased?.includes(processedEvent.id) || (user?.role && user?.role.subscribed)) {
|
||||
if (user?.purchased?.length > 0) {
|
||||
const purchasedResource = user?.purchased.find(purchase => purchase.resourceId === processedEvent.d);
|
||||
if (purchasedResource) {
|
||||
console.log("purchasedResource", purchasedResource)
|
||||
const decryptedContent = await nip04.decrypt(privkey, pubkey, processedEvent.content);
|
||||
setDecryptedContent(decryptedContent);
|
||||
}
|
||||
} else if (user?.role && user?.role.subscribed) {
|
||||
// decrypt the content
|
||||
const decryptedContent = await nip04.decrypt(privkey, pubkey, processedEvent.content);
|
||||
setDecryptedContent(decryptedContent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
decryptContent();
|
||||
}, [user, paidResource, processedEvent]);
|
||||
@ -211,12 +219,20 @@ export default function Details() {
|
||||
return null;
|
||||
};
|
||||
|
||||
const handlePaymentSuccess = (response) => {
|
||||
console.log("response in higher level", response)
|
||||
const handlePaymentSuccess = async (response, newResource) => {
|
||||
if (response && response?.preimage) {
|
||||
console.log("newResource", newResource)
|
||||
// Refetch session to get the latest user data
|
||||
const updated = await update();
|
||||
console.log("session after update", updated)
|
||||
// router.reload(); // Optionally, reload the page if necessary
|
||||
} else {
|
||||
showToast('error', 'Error', 'Failed to purchase resource. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
const handlePaymentError = (error) => {
|
||||
console.log("error in higher level", error)
|
||||
showToast('error', 'Payment Error', `Failed to purchase resource. Please try again. Error: ${error}`);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -267,10 +283,12 @@ export default function Details() {
|
||||
amount={processedEvent.price}
|
||||
onSuccess={handlePaymentSuccess}
|
||||
onError={handlePaymentError}
|
||||
userId={user.id} // Pass the user ID
|
||||
resourceId={processedEvent.id} // Pass the course/resource ID
|
||||
resourceId={processedEvent.d}
|
||||
/>}
|
||||
|
||||
{/* if the resource has been paid for show a green paid x sats text */}
|
||||
{paidResource && decryptedContent && <p className='text-green-500'>Paid {processedEvent.price} sats</p>}
|
||||
|
||||
<ZapDisplay zapAmount={zapAmount} event={processedEvent} zapsLoading={zapsLoading} />
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user