lnurl-pay.me/src/App.svelte

233 lines
6.0 KiB
Svelte
Raw Normal View History

2021-07-17 13:44:07 +03:00
<script>
export let name;
import { Styles,
Container,
Row,
Col,
Card, CardBody, CardHeader, CardTitle,
Button,
Tooltip,
Icon,
InputGroup,
Nav, NavItem, NavLink,
} from 'sveltestrap';
import SiteHead from './SiteHead.svelte';
import SiteCard from './SiteCard.svelte';
import SiteDeck from './SiteDeck.svelte';
import InputMask from './InputMask.svelte';
import payways from './payways.js';
import QR from './QR.svelte';
import CTC from './CTC.svelte';
import Tipped from './Tipped.svelte';
import { bech32 } from 'bech32'
import UTF8 from 'utf-8'
import PayFlow from './PayFlow.svelte';
let payway = payways[0];
setTimeout(()=>{
payway = payways[0];
accounts[inputId]=null;
},0);
let inputId;
let amountMask;
let realAmount;
$: inputId = payway.iid||payway.id;
$: amountMask = {
mask: Number, scale:2,
min: payway.min, max:payway.max,
radix:".",mapToRadix:[","],
padFractionalZeros: true,
normalizeZeros: true}
$: realAmount = amounts[payway.id] && (
Math.max(Math.min(amounts[payway.id], payway.max),payway.min))
function genAutoMemo(payway,account,amount) {
if (!account)
return "...automatic";
if (amount)
amount = parseFloat(amount)
let sum = amount?
amount.toFixed(amount===Math.round(amount)?0:2)
+ " " + payway.currency.toLowerCase() : "some sats";
return sum + " to " + payway.id + " " + account;
}
let autoMemo;
$: autoMemo = genAutoMemo(payway,
accountComplete && accounts[inputId],
realAmount)
function lnurlEncode(url) {
return bech32.encode("LNURL",
bech32.toWords(UTF8.setBytesFromString(url)),2048)
}
function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
}
function genLNURL(payway,account,amount,memo) {
let params = new URLSearchParams();
params.set("mtg","pay");
params.set("p",payway.id);
params.set("acc",toHexString(UTF8.setBytesFromString(account)))
params.set("v","1")
if (amount) {
params.set(payway.currency.toLowerCase(),amount)
}
if (memo) {
params.set("m",toHexString(UTF8.setBytesFromString(memo)))
}
return lnurlEncode("https://lnurl-pay.me/pay?"+params.toString()).toUpperCase()
}
let accounts = {};
let amounts = {};
let memo;
let lnurl;
$: lnurl = accountComplete ?
genLNURL(payway, accounts[inputId], realAmount, memo) : "";
const curr = {
"UAH":"₴",
"RUB":"₽",
"USD":"$",
"KZT":"₸",
}
let accountComplete = false;
const canShare = !!navigator.share;
let getInvoiceFor;
</script>
<SiteHead/>
<SiteDeck>
<SiteCard>
<p class="form-text">Fill in everything to get your lnurl-pay link</p>
<label class="form-label mb-3">Payment direction
<select class="form-select" autofocus bind:value="{payway}">
{#each payways as payway}
<option value="{payway}">{payway.name}</option>
{/each}
</select>
</label>
{#key payway}
<label class="form-label mb-3">{payway.acc}
<InputMask
unmask
bind:value={accounts[inputId]}
bind:isComplete={accountComplete}
imask={payway.imask||{mask:/.*/}}
autocomplete={payway.autocomplete}
inputmode={payway.inputmode||"numeric"}
placeholder={payway.placeholder}
class="form-control"/>
</label>
<label class="form-label mb-3">
Amount: {payway.currency} {payway.min} {payway.max}
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">{curr[payway.currency]}</span>
</div>
<InputMask bind:value={amounts[payway.id]}
unmask
imask={amountMask}
placeholder="determined by payer"
inputmode={"numeric"}
class="form-control"/>
<button class="btn btn-outline-secondary"
type="button"
on:click={()=> amounts[payway.id]=payway.min.toString()}>Min
</button>
<button class="btn btn-outline-secondary"
type="button"
on:click={()=> amounts[payway.id]=payway.max.toString()}>Max
</button>
</div>
</label>
{/key}
<label class="form-label mb-3">Lnurl MEMO
<input bind:value={memo} placeholder="{autoMemo}"
class="form-control"/>
</label>
</SiteCard>
<SiteCard>
{#if lnurl}
<CTC let:id let:action force text={lnurl}>
<div class="form-text user-select-all mb-2 mt-2"
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
on:click={action} id={id}>{lnurl}
</div>
</CTC>
{:else}
<div class="form-text mb-2 mt-2">Expecting: {payway.acc.toLowerCase()}</div>
{/if}
<div class="d-flex justify-content-center align-items-stretch m-2"
class:invisible={!lnurl}>
<!-- {#key lnurl} -->
<a href="lightning:{lnurl}">
<QR value={lnurl} size="{230}" />
</a>
<!-- {/key} -->
</div>
<div class="d-flex justify-content-center align-items-stretch"
class:invisible={!lnurl}>
<p class="form-text">You might want to save image and share it
as you like. All form data are present in lnurl: don't share what you wanted to hide.</p>
</div>
<div class="btn-group flex-wrap mt-auto mb-2"
role="group" class:invisible={!lnurl}>
<CTC let:id let:action text={lnurl}>
<button type="button" class="btn btn-outline-secondary"
id={id} on:click={action}>Copy</button>
</CTC>
{#if canShare}
<button type="button" class="btn btn-outline-secondary"
on:click={()=>{ navigator.share({text:lnurl})}}>Share
</button>
{/if}
<Tipped let:id>
<a slot="thing" role="button" class="btn btn-outline-secondary"
href="lightning:{lnurl}" {id}>Pay</a>
<div slot="tip">open in your wallet</div>
</Tipped>
<Tipped let:id>
<button slot="thing" {id} type="button"
class="btn btn-outline-secondary"
on:click="{()=>getInvoiceFor=lnurl}">
Invoice</button>
<div slot="tip">get an invoice for wallets with no lnurl-pay</div>
</Tipped>
</div>
</SiteCard>
</SiteDeck>
<PayFlow bind:lnurl={getInvoiceFor}/>
<!-- <Styles/> -->