joule-labs / webln

Spec and client library for WebLN apps and providers
244 stars 28 forks source link

Spec-Proposal: Extend Webln SendPayment method to pass metadata as a optional parameter. #47

Open pavanjoshi914 opened 2 years ago

pavanjoshi914 commented 2 years ago

Abstract

Specifications such as Webln bought better solutions to the UX for lightning networks. On top of this, many broad applications such as instant payments via a web browser using Bitcoin Sats (eg. Alby), tippings for content writers, podcasts and streamers (Medium tipping via Alby) and many more came into working. Along with every transaction we can also store additional information in form of metadata which can make specifications such as Webln more broad and applicative in terms of interactivity.

Transaction lists as we know them from our private bank accounts are often a simple list of transactions sorted by date. Each transaction has data like Sender, Receiver, amount, reason and date.

The aim of this proposal is to extend existing standards such as Webln to enrich transactions with additional information as structured metadata so that transactions containing static data contain more meaningful information giving more interactivity to the transactions.

webln.sendPayment method

Webln SendPayment which takes paymentRequest parameter holding bolt11 invoice, we can extend this function to add an extra "optional" parameter named metadata which will hold metadata as a string which can be passed to the Wallets.

sendPayment(paymentRequest: string, metadata?: string): Promise<SendPaymentResponse>;

WeblnProvider attachted by wallets currently image

WeblnProvider attached by wallets after implementation of spec image

Working/architecture of this spec

  1. Make a Request to Wallet for WebLN Provider Via Triggering SendPayment()

    sendPayment(paymentRequest: string, metadata?: string): Promise<SendPaymentResponse>;

  2. Structure Metadata and pass it to the wallet in Callback

    Use Schema.org specifications to structure metadata in form of JSON-LD.

    eg.

    var Metadata = {};
    Metadata = {
     "[context](http://twitter.com/context)": "[https://schema.org](https://schema.org)",
     "[type](http://twitter.com/type)": "AudioObject",
     "name": "title",
     "creator": "artist",
     "image": "image" 
     }
     export var Metadata;

    Learn more about how to structure metadata for Bitcoin Transactions Visit — Here.

  3. Validate Metadata

    Metadata is user-generated, such type of metadata should be cross-checked for its correctness and before performing any further action we have to do validation of metadata.

    What is JSON Schema? 🧐

    JSON Schema is a JSON media type for defining the structure of JSON data. JSON Schema provides a contract for what JSON data is required for a given application and how to interact with it.

    Schemas can be created to validate received data against a predefined data model. To do this many validator plugins are available too

    • Generate Schema

    • Compile Schema

    • Validate received data for that particular schema

    Zod plugin can be used to validate JSON schemas as well as Object schemas. The best thing about this plugin is that it has zero external dependencies involved.

    Validator function which validates schemas using zod plugin

    • Schema Created using Zod plugin
    
    import { z } from "zod";
    
    export const audioObjectSchema = z.object({
      type: z.string(),
      name: z.string().optional(),
      creator: z.string().optional(),
      image: z.string().optional(),
    });
    • Validator function to validate metadata
    
    import { audioObjectSchema } from "./audioObjectSchema";
    
    export function isBase64(str: string) {
      if (str === "" || str.trim() === "") {
        return false;
      }
      try {
        return btoa(atob(str)) == str;
      } catch (err) {
        return false;
      }
    }
    
    export function MetadataValidator(metadata: { [key: string]: string }) {
      let hasValidType = false;
      let hasValidImage = true;
      for (const key in metadata) {
        if (key == "type") {
          if (metadata[key] == "AudioObject") {
            const parsed = audioObjectSchema.safeParse(metadata);
            if (parsed.success) {
              hasValidType = true;
            }
          }
        }
        if (key == "image") {
          hasValidImage = isBase64(metadata[key]);
        }
      }
    
    return hasValidType && hasValidImage;
    }
    • To Validate Metadata Just call the function by passing metadata.
    const isMetadataValid = MetadataValidator(metadata);
  4. Render Metadata in Confirmation Dialogue (If Valid) 🚦

    If metadata is successfully validated, we can show the user the amount the user will be paying along with the content used will be buying in form of metadata.

    Here users can see the purpose of the transaction, looking over the content, the user will be buying through this transaction and will have a clear idea of everything before making the payment.

  5. Store, Interact and do after actions with Metadata 💾

    Once the user confirms his/her payment and the transaction gets successful -

    • In Bitcoin Lightning Wallet

    We can store and render the metadata in the transaction database of the wallet allowing users to interact with the metadata.

    • In Lightning Application

    Once the payment is successful , we can allow the user to access his purchased content such as allowing user to download a song, once the payment is successful.

     webln.sendPayment(Bolt 11 invoice, Metadata)
              .then(function(r) {
                // required to protect metadata while paying empty invoices
                if(r != undefined){  
                // do after payment actions with the metadata. eg. allowing user to download song after payment is done 
              }
              })
              .catch(function(e) {
                alert("Failed: " + e.message);
                console.log('err pay:', e);
              });
        })
        .catch(function(e) {
          alert("Webln error, check console");
          console.log('err, provider', e);
        });
    }

Usecases

To get a better grasp on the proposal idea I decided to create a simple prototype through which a visitor can play a song if they like they can buy a song. While buying a song during confirmation payment they get to know about the song name, artist, and song image(send as base64 encoded string, decode on Alby side). After successful payment, a song gets downloaded into users' local storage.

Video Link

Prototype Code

Alby with extended webln specification

wbobeirne commented 1 year ago

Hey @pavanjoshi914, it's been a long time since you opened this and it looks like the ecosystem has carried on with metadata in payments as a concept via LNURL's LUD-06 metadata. I'm wondering if it would make more sense to just adhere to that spec for consistency, since presumably a good few wallets have started implementing payment displays using that metadata.

I definitely would have preferred something more structured like a JSON schema as you proposed, but I also don't want to add yet another spec.