diff --git a/package-lock.json b/package-lock.json index 8eca486..a12a9ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@getalby/bitcoin-connect-react": "^3.5.3", "@getalby/lightning-tools": "^5.0.3", + "@getalby/sdk": "^3.6.1", "@nostr-dev-kit/ndk": "^2.10.0", "@nostr-dev-kit/ndk-cache-dexie": "^2.5.1", "@prisma/client": "^5.17.0", @@ -1357,28 +1358,6 @@ "@types/ms": "*" } }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -4358,9 +4337,9 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -11757,13 +11736,12 @@ } }, "node_modules/webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "license": "MIT", "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -11772,7 +11750,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/package.json b/package.json index ae1698a..08fca98 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@getalby/bitcoin-connect-react": "^3.5.3", "@getalby/lightning-tools": "^5.0.3", + "@getalby/sdk": "^3.6.1", "@nostr-dev-kit/ndk": "^2.10.0", "@nostr-dev-kit/ndk-cache-dexie": "^2.5.1", "@prisma/client": "^5.17.0", diff --git a/src/components/bitcoinConnect/SubscriptionPaymentButton.js b/src/components/bitcoinConnect/SubscriptionPaymentButton.js index 6a84d0a..d22b8aa 100644 --- a/src/components/bitcoinConnect/SubscriptionPaymentButton.js +++ b/src/components/bitcoinConnect/SubscriptionPaymentButton.js @@ -1,21 +1,24 @@ import React, { useState, useEffect } from 'react'; import { Button } from 'primereact/button'; +import { ProgressSpinner } from 'primereact/progressspinner'; import { initializeBitcoinConnect } from './BitcoinConnect'; import { LightningAddress } from '@getalby/lightning-tools'; import { useToast } from '@/hooks/useToast'; import { useSession } from 'next-auth/react'; -import { useLocalStorageWithEffect } from '@/hooks/useLocalStroage'; +import { webln, nwc } from '@getalby/sdk'; +import { useRouter } from 'next/router'; import dynamic from 'next/dynamic'; - +import AlbyButton from '@/components/buttons/AlbyButton'; +import axios from 'axios'; const PaymentModal = dynamic( () => import('@getalby/bitcoin-connect-react').then((mod) => mod.Payment), { ssr: false } ); -const SubscriptionPaymentButtons = ({ onSuccess, onError }) => { +const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptionSuccess, setIsProcessing }) => { const [invoice, setInvoice] = useState(null); - const [paid, setPaid] = useState(null); - const [nwcUrl, setNwcUrl] = useState(null); + const [showRecurringOptions, setShowRecurringOptions] = useState(false); + const [nwcInput, setNwcInput] = useState(''); const { showToast } = useToast(); const { data: session } = useSession(); @@ -32,12 +35,7 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError }) => { intervalId = setInterval(async () => { const paid = await invoice.verifyPayment(); - console.log('paid', paid); - if (paid && invoice.preimage) { - setPaid({ - preimage: invoice.preimage, - }); clearInterval(intervalId); // handle success onSuccess(); @@ -60,11 +58,10 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError }) => { await ln.fetch(); const newInvoice = await ln.requestInvoice({ satoshi: amount }); console.log('newInvoice', newInvoice); - setInvoice(newInvoice); return newInvoice; } catch (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: ${error.message}`); if (onError) onError(error); return null; } @@ -73,84 +70,168 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError }) => { const handlePaymentSuccess = async (response) => { console.log('Payment successful', response); clearInterval(checkPaymentInterval); + showToast('success', 'Payment Successful', 'Your payment has been processed successfully.'); + if (onSuccess) onSuccess(response); }; const handlePaymentError = async (error) => { console.error('Payment error', error); clearInterval(checkPaymentInterval); + showToast('error', 'Payment Failed', `An error occurred during payment: ${error.message}`); + if (onError) onError(error); }; const handleRecurringSubscription = async () => { - const { init, launchModal, onConnected } = await import('@getalby/bitcoin-connect-react'); - - init({ - appName: 'plebdevs.com', - filters: ['nwc'], - onConnected: async (connector) => { - console.log('connector', connector); - if (connector.type === 'nwc') { - console.log('connector inside nwc', connector); - const nwcConnector = connector; - const url = await nwcConnector.getNWCUrl(); - setNwcUrl(url); - console.log('NWC URL:', url); - // Here you can handle the NWC URL, e.g., send it to your backend + setIsProcessing(true); + const newNwc = nwc.NWCClient.withNewSecret(); + const yearFromNow = new Date(); + yearFromNow.setFullYear(yearFromNow.getFullYear() + 1); + + try { + const initNwcOptions = { + name: "plebdevs.com", + requestMethods: ['pay_invoice'], + maxAmount: 25, + editable: false, + budgetRenewal: 'monthly', + expiresAt: yearFromNow, + }; + await newNwc.initNWC(initNwcOptions); + showToast('info', 'Alby', 'Alby connection window opened.'); + const newNWCUrl = newNwc.getNostrWalletConnectUrl(); + + if (newNWCUrl) { + const subscriptionResponse = await axios.put('/api/users/subscription', { + userId: session.user.id, + isSubscribed: true, + nwc: newNWCUrl, + }); + + if (subscriptionResponse.status === 200) { + showToast('success', 'Subscription Setup', 'Recurring subscription setup successful!'); + if (onRecurringSubscriptionSuccess) onRecurringSubscriptionSuccess(); + } else { + throw new Error(`Unexpected response status: ${subscriptionResponse.status}`); } - }, - }); + } else { + throw new Error('Failed to generate NWC URL'); + } + } catch (error) { + console.error('Error initializing NWC:', error); + showToast('error', 'Subscription Setup Failed', `Error: ${error.message}`); + if (onError) onError(error); + } finally { + setIsProcessing(false); + } + }; - launchModal(); + const handleManualNwcSubmit = async () => { + if (!nwcInput) { + showToast('error', 'NWC', 'Please enter a valid NWC URL'); + return; + } - // Set up a listener for the connection event - const unsubscribe = onConnected((provider) => { - console.log('Connected provider:', provider); - const nwc = provider?.client?.options?.nostrWalletConnectUrl; - // try to make payment - // if successful, encrypt and send to db with subscription object on the user - }); + setIsProcessing(true); + try { + const nwc = new webln.NostrWebLNProvider({ + nostrWalletConnectUrl: nwcInput, + }); - // Clean up the listener when the component unmounts - return () => { - unsubscribe(); - }; + await nwc.enable(); + + const invoice = await fetchInvoice(); + if (!invoice || !invoice.paymentRequest) { + showToast('error', 'NWC', `Failed to fetch invoice from ${lnAddress}`); + return; + } + + const payResponse = await nwc.sendPayment(invoice.paymentRequest); + if (!payResponse || !payResponse.preimage) { + showToast('error', 'NWC', 'Payment failed'); + return; + } + + showToast('success', 'NWC', 'Payment successful!'); + + try { + const subscriptionResponse = await axios.put('/api/users/subscription', { + userId: session.user.id, + isSubscribed: true, + nwc: nwcInput, + }); + + if (subscriptionResponse.status === 200) { + showToast('success', 'NWC', 'Subscription setup successful!'); + if (onRecurringSubscriptionSuccess) onRecurringSubscriptionSuccess(); + } else { + throw new Error('Unexpected response status'); + } + } catch (error) { + console.error('Subscription setup error:', error); + showToast('error', 'NWC', 'Subscription setup failed. Please contact support.'); + if (onError) onError(error); + } + } catch (error) { + console.error('NWC error:', error); + showToast('error', 'NWC', `An error occurred: ${error.message}`); + if (onError) onError(error); + } finally { + setIsProcessing(false); + } }; return ( <> - { - !invoice && ( -
Manually enter NWC URL
+ *make sure you set a budget of at least 25000 sats and set budget renewal to monthly + setNwcInput(e.target.value)} + placeholder="Enter NWC URL" + className="w-full p-2 mb-4 border rounded" + /> +- Subscribe to PlebDevs and get access to: -
-- ALSO -
-+ Subscribe to PlebDevs and get access to: +
++ ALSO +
+