-
Notifications
You must be signed in to change notification settings - Fork 221
/
Copy pathPaym.js
155 lines (131 loc) · 4.7 KB
/
Paym.js
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
var crypto = require('crypto');
var lightningPayReq = require('bolt11');
import { BigNumber } from 'bignumber.js';
export class Paym {
constructor(redis, bitcoindrpc, lightning) {
this._redis = redis;
this._bitcoindrpc = bitcoindrpc;
this._lightning = lightning;
this._decoded = false;
this._bolt11 = false;
this._isPaid = null;
}
setInvoice(bolt11) {
this._bolt11 = bolt11;
}
async decodePayReqViaRpc(invoice) {
let that = this;
return new Promise(function (resolve, reject) {
that._lightning.decodePayReq({ pay_req: invoice }, function (err, info) {
if (err) return reject(err);
that._decoded = info;
return resolve(info);
});
});
}
async queryRoutes() {
if (!this._bolt11) throw new Error('bolt11 is not provided');
if (!this._decoded) await this.decodePayReqViaRpc(this._bolt11);
var request = {
pub_key: this._decoded.destination,
amt: this._decoded.num_satoshis,
final_cltv_delta: 144,
fee_limit: { fixed: Math.floor(this._decoded.num_satoshis * forwardFee) + 1 },
};
let that = this;
return new Promise(function (resolve, reject) {
that._lightning.queryRoutes(request, function (err, response) {
if (err) return reject(err);
resolve(response);
});
});
}
async sendToRouteSync(routes) {
if (!this._bolt11) throw new Error('bolt11 is not provided');
if (!this._decoded) await this.decodePayReqViaRpc(this._bolt11);
let request = {
payment_hash_string: this._decoded.payment_hash,
route: routes[0],
};
console.log('sendToRouteSync:', { request });
let that = this;
return new Promise(function (resolve, reject) {
that._lightning.sendToRouteSync(request, function (err, response) {
if (err) reject(err);
resolve(that.processSendPaymentResponse(response));
});
});
}
processSendPaymentResponse(payment) {
if (payment && payment.payment_route && payment.payment_route.total_amt_msat) {
// paid just now
this._isPaid = true;
payment.payment_route.total_fees = +payment.payment_route.total_fees + Math.floor(+payment.payment_route.total_amt * internalFee);
if (this._bolt11) payment.pay_req = this._bolt11;
if (this._decoded) payment.decoded = this._decoded;
}
if (payment.payment_error && payment.payment_error.indexOf('already paid') !== -1) {
// already paid
this._isPaid = true;
if (this._decoded) {
payment.decoded = this._decoded;
if (this._bolt11) payment.pay_req = this._bolt11;
// trying to guess the fee
payment.payment_route = payment.payment_route || {};
payment.payment_route.total_fees = Math.floor(this._decoded.num_satoshis * forwardFee); // we dont know the exact fee, so we use max (same as fee_limit)
payment.payment_route.total_amt = this._decoded.num_satoshis;
}
}
if (payment.payment_error && payment.payment_error.indexOf('unable to') !== -1) {
// failed to pay
this._isPaid = false;
}
if (payment.payment_error && payment.payment_error.indexOf('FinalExpiryTooSoon') !== -1) {
this._isPaid = false;
}
if (payment.payment_error && payment.payment_error.indexOf('UnknownPaymentHash') !== -1) {
this._isPaid = false;
}
if (payment.payment_error && payment.payment_error.indexOf('IncorrectOrUnknownPaymentDetails') !== -1) {
this._isPaid = false;
}
if (payment.payment_error && payment.payment_error.indexOf('payment is in transition') !== -1) {
this._isPaid = null; // null is default, but lets set it anyway
}
return payment;
}
/**
* Returns NULL if unknown, true if its paid, false if its unpaid
* (judging by error in sendPayment response)
*
* @returns {boolean|null}
*/
getIsPaid() {
return this._isPaid;
}
async attemptPayToRoute() {
let routes = await this.queryRoutes();
return await this.sendToRouteSync(routes.routes);
}
async listPayments() {
return new Promise((resolve, reject) => {
this._lightning.listPayments({}, function (err, response) {
if (err) return reject(err);
resolve(response);
});
});
}
async isExpired() {
if (!this._bolt11) throw new Error('bolt11 is not provided');
const decoded = await this.decodePayReqViaRpc(this._bolt11);
return +decoded.timestamp + +decoded.expiry < +new Date() / 1000;
}
decodePayReqLocally(payReq) {
this._decoded_locally = lightningPayReq.decode(payReq);
}
async getPaymentHash() {
if (!this._bolt11) throw new Error('bolt11 is not provided');
if (!this._decoded) await this.decodePayReqViaRpc(this._bolt11);
return this._decoded['payment_hash'];
}
}