zap method

Future<ZapResponse> zap({
  1. required NwcConnection nwcConnection,
  2. required String lnurl,
  3. required int amountSats,
  4. bool fetchZapReceipt = false,
  5. EventSigner? signer,
  6. Iterable<String>? relays,
  7. String? pubKey,
  8. String? comment,
  9. String? eventId,
})

zap or pay some lnurl, for zap to be created it is necessary:

  • that the lnurl has the allowsNostr: true
  • non empty relays
  • non empty pubKey
  • non empty _signer

Implementation

Future<ZapResponse> zap({
  required NwcConnection nwcConnection,
  required String lnurl,
  required int amountSats,
  bool fetchZapReceipt = false,
  EventSigner? signer,
  Iterable<String>? relays,
  String? pubKey,
  String? comment,
  String? eventId,
}) async {
  String? lud16Link = Lnurl.getLud16LinkFromLud16(lnurl);
  ZapRequest? zapRequest;
  if (pubKey != null &&
      signer != null &&
      relays != null &&
      relays.isNotEmpty) {
    zapRequest = await createZapRequest(
        amountSats: amountSats,
        signer: signer,
        pubKey: pubKey,
        comment: comment,
        relays: relays,
        eventId: eventId);
  }
  final invoice = await fecthInvoice(
    lud16Link: lud16Link!,
    comment: comment,
    amountSats: amountSats,
    zapRequest: zapRequest,
  );
  if (invoice == null) {
    return ZapResponse(error: "couldn't get invoice from $lnurl");
  }
  try {
    final payResponse = await _nwc.payInvoice(nwcConnection,
        invoice: invoice.invoice, timeout: Duration(seconds: 10));
    if (payResponse.preimage.isNotEmpty && payResponse.errorCode == null) {
      final zapResponse = ZapResponse(payInvoiceResponse: payResponse);
      if (zapRequest != null &&
          fetchZapReceipt &&
          invoice.nostrPubkey != null &&
          invoice.nostrPubkey!.isNotEmpty) {
        // if it's a zap, try to find the zap receipt
        zapResponse.receiptResponse = _requests.subscription(filters: [
          eventId != null
              ? Filter(
                  kinds: [ZapReceipt.kKind],
                  eTags: [eventId],
                  pTags: [pubKey!])
              : Filter(kinds: [ZapReceipt.kKind], pTags: [pubKey!])
        ]);
        // TODO make timeout waiting for receipt parameterizable somehow
        StreamSubscription<Nip01Event>? streamSubscription;
        final timeout = Timer(Duration(seconds: 30), () {
          _requests
              .closeSubscription(zapResponse.zapReceiptResponse!.requestId);
          if (streamSubscription != null) {
            streamSubscription.cancel();
          }
          Logger.log
              .w("timed out waiting for zap receipt for invoice $invoice");
        });

        streamSubscription =
            zapResponse.zapReceiptResponse!.stream.listen((event) {
          String? bolt11 = event.getFirstTag("bolt11");
          String? preimage = event.getFirstTag("preimage");
          if (bolt11 != null && bolt11 == invoice.invoice ||
              preimage != null && preimage == payResponse.preimage) {
            ZapReceipt receipt = ZapReceipt.fromEvent(event);
            Logger.log.d("Zap Receipt: $receipt");
            if (receipt.isValid(
                nostrPubKey: invoice.nostrPubkey!, recipientLnurl: lnurl)) {
              zapResponse.emitReceipt(receipt);
            } else {
              Logger.log.w("Zap Receipt invalid: $receipt");
            }
            timeout.cancel();
            _requests
                .closeSubscription(zapResponse.zapReceiptResponse!.requestId);
            if (streamSubscription != null) {
              streamSubscription.cancel();
            }
          }
        });
      } else {
        zapResponse.emitReceipt(null);
      }
      return zapResponse;
    }
    return ZapResponse(error: payResponse.errorMessage);
  } catch (e) {
    return ZapResponse(error: e.toString());
  }
}