aboutsummaryrefslogtreecommitdiffstats
path: root/routes/lnurl.js
blob: dcd1657c278dffba6494ac55699cc6d803113f14 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { g, s } from "$lib/db";
import { l, warn } from "$lib/logging";
import { bail, fail } from "$lib/utils";
import { v4 } from "uuid";
import got from "got";
import { generate } from "$lib/invoices";
import { bech32 } from "bech32";
import { fields, pick } from "$lib/utils";
import { types } from "$lib/payments";
import crypto from "crypto";

import config from "$config";
let { admin, classic } = config;

let { URL } = process.env;
let host = URL.split("/").at(-1);

export default {
  async encode({ query: { address } }, res) {
    let [name, domain] = address.split("@");
    let url = `https://${domain}/.well-known/lnurlp/${name}`;

    try {
      let r = await got(url).json();
      if (r.tag !== "payRequest") fail("not an ln address");
    } catch (e) {
      let m = `failed to lookup lightning address ${address}`;
      warn(m);
      return bail(res, m);
    }

    let enc = bech32.encode("lnurl", bech32.toWords(Buffer.from(url)), 20000);
    res.send(enc);
  },

  async decode({ query: { text } }, res) {
    let url = Buffer.from(
      bech32.fromWords(bech32.decode(text, 20000).words)
    ).toString();

    res.send(await got(url).json());
  },

  async lnurlp({ params: { username } }, res) {
    let uid = await g(`user:${username.toLowerCase()}`);
    if (!uid) {
      let u = await got(`${classic}/admin/migrate/${username}?zero=true`, {
        headers: { authorization: `Bearer ${admin}` }
      }).json();

      if (!u) fail("user not found");
      let { balance, pubkey } = u;

      uid = u.uuid;

      u = { id: uid, about: u.address, ...pick(u, fields) };
      delete u.address;

      await s(`user:${username.toLowerCase()}`, uid);
      await s(`user:${uid}`, u);
      await s(`balance:${uid}`, balance);

      l("added missing user", username);
    }

    let metadata = JSON.stringify([
      ["text/plain", `Paying ${username}@${host}`],
      ["text/identifier", `${username}@${host}`]
    ]);

    let id = v4();
    await s(`lnurl:${id}`, uid);

    res.send({
      minSendable: 1000,
      maxSendable: 100000000000,
      metadata,
      callback: `${URL}/api/lnurl/${id}`,
      tag: "payRequest"
    });
  },

  async lnurl({ params: { id }, query: { amount } }, res) {
    let pr = await g(`lnurlp:${id}`);

    if (!pr) {
      let uid = await g(`lnurl:${id}`);
      let user = await g(`user:${uid}`);
      let { username } = user;

      let metadata = JSON.stringify([
        ["text/plain", `Paying ${username}@${host}`],
        ["text/identifier", `${username}@${host}`]
      ]);

      ({ text: pr } = await generate({
        invoice: {
          amount: Math.round(amount / 1000),
          type: types.lightning
        },
        memo: metadata,
        user
      }));

      await s(`lnurlp:${id}`, pr);
    }

    res.send({ pr, routes: [] });
  }
};