allow purchasing time with lightning directly not just cashu (fix #9)

This commit is contained in:
Danny Morabito 2024-12-21 22:21:28 +01:00
parent 77a9a6842b
commit 88d9515ba6
Signed by: dannym
GPG key ID: 7CC8056A5A04557E
3 changed files with 67 additions and 1 deletions

View file

@ -26,6 +26,7 @@
}, },
"dependencies": { "dependencies": {
"@arx/utils": "git+ssh://git@git.arx-ccn.com:222/Arx/ts-utils#03163e9f4a07b011a28a8f97c90852ecfc806ddd", "@arx/utils": "git+ssh://git@git.arx-ccn.com:222/Arx/ts-utils#03163e9f4a07b011a28a8f97c90852ecfc806ddd",
"@cashu/cashu-ts": "^2.1.0",
"@gandlaf21/bc-ur": "^1.1.12", "@gandlaf21/bc-ur": "^1.1.12",
"@nostr-dev-kit/ndk": "^2.10.7", "@nostr-dev-kit/ndk": "^2.10.7",
"@nostr-dev-kit/ndk-cache-dexie": "^2.5.8", "@nostr-dev-kit/ndk-cache-dexie": "^2.5.8",

View file

@ -37,7 +37,7 @@ button, .button {
} }
input[type="text"], input[type="password"] { input[type="text"], input[type="number"], input[type="password"] {
background: var(--editor-bg); background: var(--editor-bg);
border: 1px solid var(--input-border); border: 1px solid var(--input-border);
color: var(--text-primary); color: var(--text-primary);

View file

@ -3,6 +3,10 @@
import { TokenInfoWithMailSubscriptionDuration } from '@arx/utils'; import { TokenInfoWithMailSubscriptionDuration } from '@arx/utils';
import TimeCountdown from '../../components/TimeCountdown.svelte'; import TimeCountdown from '../../components/TimeCountdown.svelte';
import { ndk } from '$lib/stores.svelte'; import { ndk } from '$lib/stores.svelte';
import { CashuMint, CashuWallet, getEncodedTokenV4, MintQuoteState } from '@cashu/cashu-ts';
import type { MintQuoteResponse } from '@cashu/cashu-ts';
import Dialog from '../../components/Dialog.svelte';
import * as qr from 'qrcode';
let { onReloadNeeded } = $props<{ let { onReloadNeeded } = $props<{
onReloadNeeded: () => void; onReloadNeeded: () => void;
@ -35,6 +39,44 @@
} }
} }
let buyingWithLightning = $state(false);
let lightningAmount = $state(21);
let invoice = $state('');
let invoiceQR = $state('');
let mintQuote = $state<MintQuoteResponse | null>(null);
const mint = new CashuMint('https://mint.minibits.cash/Bitcoin');
const wallet = new CashuWallet(mint);
async function buyWithLightning() {
if (tokenInfo) return;
if(!mintQuote) return;
const mintQuoteChecked = await wallet.checkMintQuote(mintQuote.quote);
if (mintQuoteChecked.state === MintQuoteState.PAID) {
const proofs = await wallet.mintProofs(lightningAmount, mintQuote.quote);
cashuTokenForBuy = getEncodedTokenV4({ mint: mint.mintUrl, proofs: proofs });
buyingWithLightning = false;
invoice = '';
invoiceQR = '';
mintQuote = null;
try {
tokenInfo = new TokenInfoWithMailSubscriptionDuration(cashuTokenForBuy);
tokenInfoError = '';
} catch (e) {
tokenInfoError = (e as Error).message;
}
buyTime();
} else
alert('Invoice not paid yet')
}
async function generateInvoice() {
await wallet.loadMint();
mintQuote = await wallet.createMintQuote(lightningAmount);
invoice = mintQuote.request;
invoiceQR = await qr.toDataURL(invoice);
}
$effect(() => { $effect(() => {
try { try {
tokenInfo = new TokenInfoWithMailSubscriptionDuration(cashuTokenForBuy); tokenInfo = new TokenInfoWithMailSubscriptionDuration(cashuTokenForBuy);
@ -45,6 +87,28 @@
}); });
</script> </script>
<Dialog bind:open={buyingWithLightning}>
<div class="section-title">
<h3>Buy time with lightning</h3>
<button class="close-button" onclick={() => (buyingWithLightning = false)}>Close</button>
</div>
{#if !invoice}
<p>Enter the amount of time you want to buy (min 21 sats, 210 sats = 1 day):</p>
<input bind:value={lightningAmount} min="21" placeholder="Enter amount" type="number" />
<button onclick={generateInvoice}>Generate Invoice</button>
{:else}
<div style="align-items: center; display: flex; flex-direction: column;">
<img src={invoiceQR} alt="Lightning Invoice" />
<input disabled value={invoice} type="text" />
<p>After payment, click the button below to add the time to your account.</p>
<button onclick={buyWithLightning}>Add time</button>
</div>
{/if}
</Dialog>
{#if cashuTokenForBuy !== ''} {#if cashuTokenForBuy !== ''}
{#if tokenInfoError} {#if tokenInfoError}
<p class="error">{tokenInfoError}</p> <p class="error">{tokenInfoError}</p>
@ -56,6 +120,7 @@
{/if} {/if}
<input bind:value={cashuTokenForBuy} placeholder="Enter cashu token" type="text" /> <input bind:value={cashuTokenForBuy} placeholder="Enter cashu token" type="text" />
<button onclick={() => buyTimeForNpub.trim() ? buyingWithLightning = true : alert('Please enter a valid npub first')}>Or click here for lightning</button>
<h4>Buy for:</h4> <h4>Buy for:</h4>
<input bind:value={buyTimeForNpub} placeholder="Enter npub" type="text" /> <input bind:value={buyTimeForNpub} placeholder="Enter npub" type="text" />
<button onclick={() => (buyTimeForNpub = $ndk.activeUser.npub)}>Set to your own</button> <button onclick={() => (buyTimeForNpub = $ndk.activeUser.npub)}>Set to your own</button>