problem: zap handling is messy

This commit is contained in:
gsovereignty
2024-07-12 15:29:22 +08:00
parent 5a4bd46ef3
commit 73a5ba10f7
4 changed files with 143 additions and 157 deletions

View File

@@ -14,7 +14,7 @@
function zap() { function zap() {
let z = new NDKZap({ndk:$ndk, zappedEvent:rocket, zappedUser: rocket.author}) let z = new NDKZap({ndk:$ndk, zappedEvent:rocket, zappedUser: rocket.author})
z.createZapRequest(1000, `Purchase of ${product.getMatchingTags("name")[0][1]} from ${rocket.dTag}`, [["e", product.id]]).then(invoice=>{ z.createZapRequest(1000, `Purchase of ${product.getMatchingTags("name")[0][1]} from ${rocket.dTag}`, [["product", product.id]]).then(invoice=>{
if (invoice) { if (invoice) {
requestProvider().then((webln)=>{ requestProvider().then((webln)=>{
webln.sendPayment(invoice).then((response)=>{ webln.sendPayment(invoice).then((response)=>{

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import * as Card from '@/components/ui/card'; import * as Card from '@/components/ui/card';
import * as Table from '@/components/ui/table'; import * as Table from '@/components/ui/table';
import type { RocketProduct } from '@/event_helpers/rockets'; import { ZapPurchase, type RocketProduct } from '@/event_helpers/rockets';
import { unixToRelativeTime } from '@/helpers'; import { unixToRelativeTime } from '@/helpers';
import { ndk } from '@/ndk'; import { ndk } from '@/ndk';
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
@@ -33,59 +33,18 @@
}); });
}); });
let newZaps = derived(zaps, ($zaps) => { let purchases = derived(zaps, ($zaps) => {
let zapMap = new Map<string, NDKEvent>(); let zapMap = new Map<string, ZapPurchase>();
for (let z of $zaps) { for (let z of $zaps) {
if (!product.Purchases.get(z.id)) { let zapPurchase = new ZapPurchase(z);
let zapRequestEvent = getZapRequest(z); if (zapPurchase.Valid(rocket)) {
if (zapRequestEvent) { zapMap.set(zapPurchase.ZapReceipt.id, zapPurchase);
for (let zapEtag of zapRequestEvent.getMatchingTags('e')) {
if (zapEtag && zapEtag.length > 1 && zapEtag[1].length == 64) {
if (product.ID == zapEtag[1]) {
//todo: validate zapper pubkey is from a LSP specified in rocket
//todo: validate amount is same as product amount in rocket
zapMap.set(z.id, z);
}
}
}
}
} }
} }
return zapMap; return zapMap;
}); });
function getZapRequest(zapReceipt: NDKEvent): NDKEvent | undefined { //todo: update rocket event with confirmed zaps if we have votepower
let zapRequestEvent: NDKEvent | undefined = undefined;
let zapRequest = zapReceipt.getMatchingTags('description');
if (zapRequest.length == 1) {
let zapRequestJSON = JSON.parse(zapRequest[0][1]);
if (zapRequestJSON) {
zapRequestEvent = new NDKEvent($ndk, zapRequestJSON);
}
}
return zapRequestEvent;
}
function getPayerPubkey(zapReceipt:NDKEvent):string|undefined {
let pubkey = undefined
let zreq = getZapRequest(zapReceipt)
if (zreq && zreq.author.pubkey.length == 64) {
pubkey = zreq.author.pubkey
}
return pubkey
}
function getZapAmount(zapRequest?: NDKEvent): number {
let amount = 0;
let amountTag = zapRequest?.getMatchingTags('amount');
if (amountTag?.length == 1) {
amount = parseInt(amountTag[0][1], 10);
}
return amount;
}
//fetch payments from rocket::product and live zaps and make a store Map<productID, []payments>
//todo: validate zaps against product, publish store of all successful payments including those already in rocket. Publish another store with successful payments that are not yet included in rocket state so we can update the state and republish.
</script> </script>
<Card.Root> <Card.Root>
@@ -105,29 +64,31 @@
</Table.Row> </Table.Row>
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
{#each $newZaps as [id, zapReceipt], _ (id)} {#each $purchases as [id, purchase], _ (id)}
<Table.Row on:click={()=>{console.log(getZapRequest(zapReceipt)?.rawEvent())}} class="bg-accent"> <Table.Row
on:click={() => {
console.log(purchase.ZapReceipt.rawEvent());
}}
class="bg-accent"
>
<Table.Cell> <Table.Cell>
<div class="flex flex-nowrap"> <div class="flex flex-nowrap">
<div class=" hidden">{getZapRequest(zapReceipt)?.author.pubkey}</div>
<Avatar <Avatar
ndk={$ndk} ndk={$ndk}
pubkey={getPayerPubkey(zapReceipt)} pubkey={purchase.BuyerPubkey}
class="h-10 w-10 flex-none rounded-full object-cover" class="h-10 w-10 flex-none rounded-full object-cover"
/> />
<Name <Name
ndk={$ndk} ndk={$ndk}
pubkey={getZapRequest(zapReceipt)?.author.pubkey} pubkey={purchase.BuyerPubkey}
class="inline-block truncate p-2 max-w-32" class="inline-block max-w-32 truncate p-2"
/> />
</div> </div>
</Table.Cell> </Table.Cell>
<Table.Cell class="hidden md:table-cell" <Table.Cell class="hidden md:table-cell">{purchase.Amount / 1000}</Table.Cell>
>{getZapAmount(getZapRequest(zapReceipt)) / 1000}</Table.Cell <Table.Cell class="text-right"
>{unixToRelativeTime(purchase.ZapReceipt.created_at * 1000)}</Table.Cell
> >
<Table.Cell class="text-right">{unixToRelativeTime(zapReceipt.created_at*1000)}</Table.Cell>
</Table.Row> </Table.Row>
{/each} {/each}
</Table.Body> </Table.Body>

View File

@@ -1,59 +1,4 @@
import { NDKEvent, type NDKTag } from "@nostr-dev-kit/ndk"; import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
import type NDKSvelte from "@nostr-dev-kit/ndk-svelte";
export function getZapData(ndk:NDKSvelte, zap: NDKEvent, rocket:NDKEvent) {
let productPrice = 0;
let zapAmount = 0;
let productID: string | undefined = undefined;
let buyerPubkey: string | undefined = undefined;
let zapRequest: NDKEvent | undefined = undefined;
let desc = zap.getMatchingTags('description');
if (desc && desc.length == 1 && rocket) {
zapRequest = new NDKEvent(ndk, JSON.parse(desc[0][1]));
let zapRequestETags = zapRequest.getMatchingTags('e');
if (zapRequestETags && zapRequestETags.length > 0) {
for (let productIDfromZapRequest of zapRequestETags) {
if (productIDfromZapRequest.length > 1) {
let productsInRocket = getMapOfProductsFromRocket(rocket);
if (productsInRocket.size > 0) {
productID = productIDfromZapRequest[1];
if (productID.length == 64) {
let productDataFromRocket = productsInRocket.get(productID);
if (productDataFromRocket) {
productPrice = productDataFromRocket.Price;
}
}
}
}
}
}
let amount = zapRequest.getMatchingTags('amount');
if (amount && amount.length == 1) {
if (amount[0].length == 2) {
zapAmount = parseInt(amount[0][1], 10);
}
}
buyerPubkey = zapRequest.author.pubkey;
}
let success = false;
if (zapRequest && productID && buyerPubkey && productPrice && zapAmount) {
if (zapAmount >= productPrice && productID.length == 64 && buyerPubkey.length == 64) {
success = true;
return {
productPrice: productPrice,
zapAmount: zapAmount,
productID: productID,
buyerPubkey: buyerPubkey,
zapReceipt: zap.id
};
}
}
if (!success) {
console.log('invalid product payment zap found:', zapRequest?.rawEvent());
}
}
export class RocketProduct { export class RocketProduct {
ID: string; ID: string;
@@ -76,6 +21,7 @@ export class RocketProduct {
} }
} }
//ProductPayment takes the payment string from a product tag on a rocket event
export class ProductPayment { export class ProductPayment {
ZapID: string; ZapID: string;
BuyerPubkey: string; BuyerPubkey: string;
@@ -96,3 +42,88 @@ export function getMapOfProductsFromRocket(rocket: NDKEvent): Map<string, Rocket
} }
return productIDs; return productIDs;
} }
export class ZapPurchase {
Amount: number;
ProductID: string;
BuyerPubkey: string;
ZapReceipt: NDKEvent;
ZapRequest(): NDKEvent | undefined {
return getZapRequest(this.ZapReceipt);
}
IncludedInRocketState(rocket: NDKEvent): boolean {
let thisProduct = this.ProductFromRocket(rocket);
if (thisProduct) {
return thisProduct.Purchases.get(this.ZapReceipt.id) ? true : false;
} else {
return false;
}
}
ProductFromRocket(rocket: NDKEvent): RocketProduct | undefined {
let productsInRocket = getMapOfProductsFromRocket(rocket);
return productsInRocket.get(this.ProductID);
}
ValidAmount(rocket: NDKEvent): boolean {
if (this.Amount < 1) {
return false;
}
if (this.IncludedInRocketState(rocket)) {
return true;
}
let product = this.ProductFromRocket(rocket);
if (product && this.Amount >= product.Price) {
return true;
}
return false;
}
Valid(rocket: NDKEvent): boolean {
//todo: validate zapper pubkey is from a LSP specified in rocket
let valid = true;
if (!this.ValidAmount(rocket)) {
valid = false;
}
if (!this.ProductID) {
valid = false;
}
if (this.ProductID && this.ProductID.length != 64) {
valid = false;
}
if (this.BuyerPubkey.length != 64) {
valid = false;
}
return valid;
}
constructor(zapReceipt: NDKEvent) {
this.ZapReceipt = zapReceipt;
this.Amount = getZapAmount(this.ZapRequest());
let zapRequest = this.ZapRequest();
if (zapRequest) {
this.BuyerPubkey = zapRequest.pubkey;
let products = zapRequest.getMatchingTags('product');
if (products.length == 1 && products[0] && products[0][1] && products[0][1].length == 64) {
this.ProductID = products[0][1];
}
}
}
}
function getZapRequest(zapReceipt: NDKEvent): NDKEvent | undefined {
let zapRequestEvent: NDKEvent | undefined = undefined;
let zapRequest = zapReceipt.getMatchingTags('description');
if (zapRequest.length == 1) {
let zapRequestJSON = JSON.parse(zapRequest[0][1]);
if (zapRequestJSON) {
zapRequestEvent = new NDKEvent(zapReceipt.ndk, zapRequestJSON);
}
}
return zapRequestEvent;
}
function getZapAmount(zapRequest?: NDKEvent): number {
let amount = 0;
let amountTag = zapRequest?.getMatchingTags('amount');
if (amountTag?.length == 1) {
amount = parseInt(amountTag[0][1], 10);
}
return amount;
}

View File

@@ -74,13 +74,7 @@
} }
} }
class ZapPurchase {
Amount: number;
ProductID: string;
Buyer: string;
ZapReceiptID: string;
constructor(zapReceipt: NDKEvent) {}
}
//todo: check that this zap is not already included in the payment JSON for the product //todo: check that this zap is not already included in the payment JSON for the product
//todo: list purchases on the rocket page (from product tags, as well as zap receipts that aren't yet included). Deduct total products available if not 0. //todo: list purchases on the rocket page (from product tags, as well as zap receipts that aren't yet included). Deduct total products available if not 0.