thunder-bridge 1.4.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,261 @@
1
+ /**
2
+ * How many minor units of `currency` one bitcoin costs at one venue, so 134883815
3
+ * is 1,348,838.15 CZK. Throws when that venue does not quote that currency, which
4
+ * is a normal answer rather than a fault: Kraken and Bitstamp have no CZK pair
5
+ *
6
+ * This is the plugin seam for prices. Another venue is another function of this
7
+ * shape
8
+ */
9
+ type Ticker = (currency: string) => Promise<number>;
10
+ /** How many venues have to agree, how far apart they may be, and how long an answer is held */
11
+ interface MedianOptions {
12
+ /**
13
+ * How many venues have to answer before a price is usable. Two is the floor
14
+ * worth having, because one venue is a number nobody checked
15
+ */
16
+ minVenues?: number;
17
+ /**
18
+ * Refuse the lot when the cheapest and dearest answers are further apart than
19
+ * this many basis points. Venues normally sit inside 50, so a wider spread means
20
+ * one of them is broken or stale rather than that the market moved
21
+ */
22
+ maxSpreadBps?: number;
23
+ /** Hold the last answer this long per currency, so an order page is not four requests */
24
+ holdForSecs?: number;
25
+ }
26
+ /** Coinbase, CASP authorised in Luxembourg. Quotes CZK, EUR and most fiat */
27
+ declare function coinbase(baseUrl?: string): Ticker;
28
+ /** Kraken, CASP authorised by the Central Bank of Ireland. Quotes EUR and USD, no CZK */
29
+ declare function kraken(baseUrl?: string): Ticker;
30
+ /**
31
+ * Bitstamp, CASP authorised by the CSSF in Luxembourg. Quotes EUR and USD, no CZK.
32
+ *
33
+ * An unknown pair is answered with a `200` and the whole ticker list, whose first
34
+ * entry is BTC/USD, so asking it for CZK and reading the number would quote a
35
+ * bitcoin at 64,000 crowns. Anything but a single object is therefore refused
36
+ */
37
+ declare function bitstamp(baseUrl?: string): Ticker;
38
+ /** Coinmate, on the ESMA CASP register, Czech and the one with a real BTC/CZK book */
39
+ declare function coinmate(baseUrl?: string): Ticker;
40
+ /**
41
+ * Ask several venues and take the middle answer, refusing the lot when they
42
+ * disagree too much.
43
+ *
44
+ * The default is the four MiCA authorised venues below, and every one of them is
45
+ * replaceable: pass your own list, or one venue, or a function that reads a price
46
+ * you already have. A venue that does not quote the currency is skipped rather
47
+ * than fatal, which for CZK leaves Coinbase and Coinmate.
48
+ *
49
+ * The middle is taken rather than the mean so one stuck venue moves the answer by
50
+ * nothing instead of by half its error, and the spread check is what catches the
51
+ * stuck venue that stays inside the pack.
52
+ */
53
+ declare function medianOf(tickers?: Ticker[], options?: MedianOptions): Ticker;
54
+ /**
55
+ * What to ask for over Lightning for a price named in fiat, in millisatoshi.
56
+ *
57
+ * `priceMinorPerBtc` is what a `Ticker` returns. The arithmetic is exact, in
58
+ * BigInt, because a million crown order times a hundred billion millisatoshi
59
+ * leaves what a double can count, and it rounds up, because the extra
60
+ * millisatoshi is worth nothing and belongs to the recipient rather than to a
61
+ * rounding rule.
62
+ *
63
+ * `spreadBps` is yours to set, in basis points, and defaults to none. A Lightning
64
+ * invoice lives an hour and a bank transfer takes days, so a shop pricing in fiat is
65
+ * carrying that volatility whether or not it charges for it
66
+ */
67
+ declare function msatFor(amountMinor: number, priceMinorPerBtc: number, options?: {
68
+ spreadBps?: number;
69
+ }): number;
70
+
71
+ declare const brand: unique symbol;
72
+ /**
73
+ * A whole number of millisatoshi that came from `sats`, `msat` or `fiat`, and
74
+ * could not have come from anywhere else.
75
+ *
76
+ * The brand is why: a bare number is not one of these, so `21` cannot be passed
77
+ * where a price is wanted and quietly mean twenty-one thousandths of a satoshi.
78
+ * It costs nothing at runtime, where the value is an ordinary number
79
+ */
80
+ type Msat = number & {
81
+ readonly [brand]: "Msat";
82
+ };
83
+ /**
84
+ * What a payment is worth. A `Msat` is a price known now, and a function is one
85
+ * worked out when the invoice is minted, which is what a fiat price has to be.
86
+ *
87
+ * Build one with `sats`, `msat` or `fiat`
88
+ */
89
+ type Amount = Msat | (() => Msat | Promise<Msat>);
90
+ /** How a fiat price is turned into millisatoshi at the moment of minting */
91
+ interface FiatOptions {
92
+ /**
93
+ * Where the rate comes from, the median of the four MiCA authorised venues by
94
+ * default. Pass your own to price off one venue, off your own book, or off a
95
+ * number you already hold
96
+ */
97
+ rate?: Ticker;
98
+ /**
99
+ * What you add over the rate, in basis points, none by default. A Lightning
100
+ * invoice lives an hour and a bank transfer takes days, so a shop pricing in
101
+ * fiat carries that volatility whether or not it charges for it
102
+ */
103
+ spreadBps?: number;
104
+ }
105
+ /** A whole number of satoshi, so `sats(21)` is 21000 millisatoshi */
106
+ declare function sats(whole: number): Msat;
107
+ /** An exact number of millisatoshi, for a price already in the smallest unit */
108
+ declare function msat(exact: number): Msat;
109
+ /**
110
+ * A price named in fiat, converted when the invoice is minted rather than now.
111
+ *
112
+ * Name it as a string and it is read digit by digit, exactly. Name it as a
113
+ * number and it is rounded to the currency's ISO 4217 minor unit, because
114
+ * binary floating point cannot hold 4.99 and a payment library that pretends
115
+ * otherwise moves the wrong amount.
116
+ *
117
+ * Every conversion asks the rate afresh, so two calls a second apart can
118
+ * differ. That is the honest behaviour for a fiat price, and the reason an
119
+ * amount is a function rather than a number.
120
+ *
121
+ * The answer is rounded up to a whole satoshi, because a wallet issues an
122
+ * invoice in satoshi and refuses a fraction of one. Up rather than down, so the
123
+ * rounding is never the shop's loss. `msatFor` is the raw conversion, for a rail
124
+ * that has no invoice to round for
125
+ */
126
+ declare function fiat(major: number | string, currency: string, options?: FiatOptions): () => Promise<Msat>;
127
+
128
+ /** Where a payment stands, `paid` is the only status that carries a preimage */
129
+ type PaymentStatus = "pending" | "paid" | "expired";
130
+ interface Reported {
131
+ id: string;
132
+ status: PaymentStatus;
133
+ paymentHash: string;
134
+ verifyUrl: string;
135
+ preimage: string | null;
136
+ expiresAt: number;
137
+ createdAt: number;
138
+ /** What the watcher needs and the gateway cannot read, `unseal` opens it */
139
+ sealed: string | null;
140
+ }
141
+ /**
142
+ * A payment the gateway minted. It resolved the address itself, so it knows who
143
+ * is paid, how much, and which invoice says so, and none of the three can be
144
+ * null here
145
+ */
146
+ interface MintedPayment extends Reported {
147
+ kind: "minted";
148
+ lnAddress: string;
149
+ amountMsat: number;
150
+ bolt11: string;
151
+ }
152
+ /**
153
+ * A payment the gateway was handed rather than asked to mint. It was told a hash,
154
+ * a URL and an expiry and nothing else, which is the point of `watch`, so the
155
+ * address, the amount and the invoice are all absent rather than merely unknown
156
+ */
157
+ interface WatchedPayment extends Reported {
158
+ kind: "watched";
159
+ lnAddress: null;
160
+ amountMsat: null;
161
+ bolt11: null;
162
+ }
163
+ /**
164
+ * A payment as the gateway reports it, of either sort. Check `kind` and the
165
+ * three fields a watched payment does not carry stop being null, so nothing here
166
+ * needs an assertion to read
167
+ */
168
+ type Payment = MintedPayment | WatchedPayment;
169
+ /**
170
+ * A payment as the caller already knows it: what the gateway calls it and the
171
+ * hash it settles against. Every `Payment` is one, and a caller reading back
172
+ * after a restart builds one from the two fields it stored
173
+ */
174
+ interface Held {
175
+ id: string;
176
+ paymentHash: string;
177
+ }
178
+ /**
179
+ * Who is paid and how much. `to` is a priority list when it is an array: the
180
+ * gateway takes the first address that can issue a provable invoice for the
181
+ * amount and the rest are the fallback
182
+ */
183
+ interface Charge {
184
+ paidTo: string | string[];
185
+ amount: Amount;
186
+ /** Where the gateway posts the settlement, signed with the key it publishes */
187
+ webhookUrl?: string;
188
+ }
189
+ /**
190
+ * A charge with its price settled, which is what a proof compares the gateway's
191
+ * answer against. Asking a fiat price twice gives two numbers, so the proof is
192
+ * handed the one that was actually asked for
193
+ */
194
+ interface Priced {
195
+ paidTo: string[];
196
+ amountMsat: number;
197
+ }
198
+ /**
199
+ * An invoice you obtained yourself, handed over to be watched. The gateway is
200
+ * given no address and no amount, so it cannot refuse one recipient rather than
201
+ * all of them
202
+ */
203
+ interface Handover {
204
+ paymentHash: string;
205
+ verifyUrl: string;
206
+ expiresAt: number;
207
+ /** Groups this payment with every other one carrying the same secret */
208
+ trigger?: string;
209
+ /**
210
+ * How many of this trigger's settlements the gateway keeps replayable past the
211
+ * hour it would otherwise forget them in, up to the ceiling its operator set.
212
+ * Needs `trigger`, defaults to none
213
+ */
214
+ replay?: number;
215
+ /** Sealed with `seal` for this payment hash, so the gateway stores what it cannot read or move */
216
+ sealed?: string;
217
+ webhookUrl?: string;
218
+ }
219
+ /**
220
+ * Which address would serve an amount, and what the ones ahead of it refused.
221
+ * `feeMsat` is always zero, the payer pays the recipient's own invoice and the
222
+ * gateway is never in the money's path
223
+ */
224
+ interface Quote {
225
+ lnAddress: string;
226
+ amountMsat: number;
227
+ feeMsat: number;
228
+ minMsat: number;
229
+ maxMsat: number;
230
+ metadata: string;
231
+ refusals: WalletFailure[];
232
+ }
233
+ /**
234
+ * A one minute pass onto one trigger's stream. It opens that trigger and nothing
235
+ * else, which is what makes it the thing to hand a browser when the trigger
236
+ * secret is not. `expiresAt` is unix seconds, like every other time here
237
+ */
238
+ interface SocketTicket {
239
+ ticket: string;
240
+ expiresAt: number;
241
+ }
242
+ /** Why one wallet in the list could not be used */
243
+ type WalletReason = "address-unusable" | "unreachable" | "amount-not-accepted" | "cannot-prove-delivery" | "invoice-refused";
244
+ /** One wallet on the list that could not be used, and the reason it could not */
245
+ interface WalletFailure {
246
+ address: string;
247
+ reason: WalletReason;
248
+ }
249
+ /**
250
+ * What a delivery carries. Everything needed to act on a settlement and to check
251
+ * it, and nothing else, so a retry is the same size every time
252
+ */
253
+ interface Settlement {
254
+ id: string;
255
+ status: PaymentStatus;
256
+ paymentHash: string;
257
+ preimage: string | null;
258
+ settledAt: number;
259
+ }
260
+
261
+ export { type Amount as A, type Charge as C, type FiatOptions as F, type Handover as H, type MedianOptions as M, type Payment as P, type Quote as Q, type Settlement as S, type Ticker as T, type WalletFailure as W, coinmate as a, bitstamp as b, coinbase as c, msatFor as d, type Held as e, type MintedPayment as f, type Msat as g, type PaymentStatus as h, type Priced as i, type SocketTicket as j, kraken as k, type WalletReason as l, medianOf as m, type WatchedPayment as n, fiat as o, msat as p, sats as s };
@@ -0,0 +1,261 @@
1
+ /**
2
+ * How many minor units of `currency` one bitcoin costs at one venue, so 134883815
3
+ * is 1,348,838.15 CZK. Throws when that venue does not quote that currency, which
4
+ * is a normal answer rather than a fault: Kraken and Bitstamp have no CZK pair
5
+ *
6
+ * This is the plugin seam for prices. Another venue is another function of this
7
+ * shape
8
+ */
9
+ type Ticker = (currency: string) => Promise<number>;
10
+ /** How many venues have to agree, how far apart they may be, and how long an answer is held */
11
+ interface MedianOptions {
12
+ /**
13
+ * How many venues have to answer before a price is usable. Two is the floor
14
+ * worth having, because one venue is a number nobody checked
15
+ */
16
+ minVenues?: number;
17
+ /**
18
+ * Refuse the lot when the cheapest and dearest answers are further apart than
19
+ * this many basis points. Venues normally sit inside 50, so a wider spread means
20
+ * one of them is broken or stale rather than that the market moved
21
+ */
22
+ maxSpreadBps?: number;
23
+ /** Hold the last answer this long per currency, so an order page is not four requests */
24
+ holdForSecs?: number;
25
+ }
26
+ /** Coinbase, CASP authorised in Luxembourg. Quotes CZK, EUR and most fiat */
27
+ declare function coinbase(baseUrl?: string): Ticker;
28
+ /** Kraken, CASP authorised by the Central Bank of Ireland. Quotes EUR and USD, no CZK */
29
+ declare function kraken(baseUrl?: string): Ticker;
30
+ /**
31
+ * Bitstamp, CASP authorised by the CSSF in Luxembourg. Quotes EUR and USD, no CZK.
32
+ *
33
+ * An unknown pair is answered with a `200` and the whole ticker list, whose first
34
+ * entry is BTC/USD, so asking it for CZK and reading the number would quote a
35
+ * bitcoin at 64,000 crowns. Anything but a single object is therefore refused
36
+ */
37
+ declare function bitstamp(baseUrl?: string): Ticker;
38
+ /** Coinmate, on the ESMA CASP register, Czech and the one with a real BTC/CZK book */
39
+ declare function coinmate(baseUrl?: string): Ticker;
40
+ /**
41
+ * Ask several venues and take the middle answer, refusing the lot when they
42
+ * disagree too much.
43
+ *
44
+ * The default is the four MiCA authorised venues below, and every one of them is
45
+ * replaceable: pass your own list, or one venue, or a function that reads a price
46
+ * you already have. A venue that does not quote the currency is skipped rather
47
+ * than fatal, which for CZK leaves Coinbase and Coinmate.
48
+ *
49
+ * The middle is taken rather than the mean so one stuck venue moves the answer by
50
+ * nothing instead of by half its error, and the spread check is what catches the
51
+ * stuck venue that stays inside the pack.
52
+ */
53
+ declare function medianOf(tickers?: Ticker[], options?: MedianOptions): Ticker;
54
+ /**
55
+ * What to ask for over Lightning for a price named in fiat, in millisatoshi.
56
+ *
57
+ * `priceMinorPerBtc` is what a `Ticker` returns. The arithmetic is exact, in
58
+ * BigInt, because a million crown order times a hundred billion millisatoshi
59
+ * leaves what a double can count, and it rounds up, because the extra
60
+ * millisatoshi is worth nothing and belongs to the recipient rather than to a
61
+ * rounding rule.
62
+ *
63
+ * `spreadBps` is yours to set, in basis points, and defaults to none. A Lightning
64
+ * invoice lives an hour and a bank transfer takes days, so a shop pricing in fiat is
65
+ * carrying that volatility whether or not it charges for it
66
+ */
67
+ declare function msatFor(amountMinor: number, priceMinorPerBtc: number, options?: {
68
+ spreadBps?: number;
69
+ }): number;
70
+
71
+ declare const brand: unique symbol;
72
+ /**
73
+ * A whole number of millisatoshi that came from `sats`, `msat` or `fiat`, and
74
+ * could not have come from anywhere else.
75
+ *
76
+ * The brand is why: a bare number is not one of these, so `21` cannot be passed
77
+ * where a price is wanted and quietly mean twenty-one thousandths of a satoshi.
78
+ * It costs nothing at runtime, where the value is an ordinary number
79
+ */
80
+ type Msat = number & {
81
+ readonly [brand]: "Msat";
82
+ };
83
+ /**
84
+ * What a payment is worth. A `Msat` is a price known now, and a function is one
85
+ * worked out when the invoice is minted, which is what a fiat price has to be.
86
+ *
87
+ * Build one with `sats`, `msat` or `fiat`
88
+ */
89
+ type Amount = Msat | (() => Msat | Promise<Msat>);
90
+ /** How a fiat price is turned into millisatoshi at the moment of minting */
91
+ interface FiatOptions {
92
+ /**
93
+ * Where the rate comes from, the median of the four MiCA authorised venues by
94
+ * default. Pass your own to price off one venue, off your own book, or off a
95
+ * number you already hold
96
+ */
97
+ rate?: Ticker;
98
+ /**
99
+ * What you add over the rate, in basis points, none by default. A Lightning
100
+ * invoice lives an hour and a bank transfer takes days, so a shop pricing in
101
+ * fiat carries that volatility whether or not it charges for it
102
+ */
103
+ spreadBps?: number;
104
+ }
105
+ /** A whole number of satoshi, so `sats(21)` is 21000 millisatoshi */
106
+ declare function sats(whole: number): Msat;
107
+ /** An exact number of millisatoshi, for a price already in the smallest unit */
108
+ declare function msat(exact: number): Msat;
109
+ /**
110
+ * A price named in fiat, converted when the invoice is minted rather than now.
111
+ *
112
+ * Name it as a string and it is read digit by digit, exactly. Name it as a
113
+ * number and it is rounded to the currency's ISO 4217 minor unit, because
114
+ * binary floating point cannot hold 4.99 and a payment library that pretends
115
+ * otherwise moves the wrong amount.
116
+ *
117
+ * Every conversion asks the rate afresh, so two calls a second apart can
118
+ * differ. That is the honest behaviour for a fiat price, and the reason an
119
+ * amount is a function rather than a number.
120
+ *
121
+ * The answer is rounded up to a whole satoshi, because a wallet issues an
122
+ * invoice in satoshi and refuses a fraction of one. Up rather than down, so the
123
+ * rounding is never the shop's loss. `msatFor` is the raw conversion, for a rail
124
+ * that has no invoice to round for
125
+ */
126
+ declare function fiat(major: number | string, currency: string, options?: FiatOptions): () => Promise<Msat>;
127
+
128
+ /** Where a payment stands, `paid` is the only status that carries a preimage */
129
+ type PaymentStatus = "pending" | "paid" | "expired";
130
+ interface Reported {
131
+ id: string;
132
+ status: PaymentStatus;
133
+ paymentHash: string;
134
+ verifyUrl: string;
135
+ preimage: string | null;
136
+ expiresAt: number;
137
+ createdAt: number;
138
+ /** What the watcher needs and the gateway cannot read, `unseal` opens it */
139
+ sealed: string | null;
140
+ }
141
+ /**
142
+ * A payment the gateway minted. It resolved the address itself, so it knows who
143
+ * is paid, how much, and which invoice says so, and none of the three can be
144
+ * null here
145
+ */
146
+ interface MintedPayment extends Reported {
147
+ kind: "minted";
148
+ lnAddress: string;
149
+ amountMsat: number;
150
+ bolt11: string;
151
+ }
152
+ /**
153
+ * A payment the gateway was handed rather than asked to mint. It was told a hash,
154
+ * a URL and an expiry and nothing else, which is the point of `watch`, so the
155
+ * address, the amount and the invoice are all absent rather than merely unknown
156
+ */
157
+ interface WatchedPayment extends Reported {
158
+ kind: "watched";
159
+ lnAddress: null;
160
+ amountMsat: null;
161
+ bolt11: null;
162
+ }
163
+ /**
164
+ * A payment as the gateway reports it, of either sort. Check `kind` and the
165
+ * three fields a watched payment does not carry stop being null, so nothing here
166
+ * needs an assertion to read
167
+ */
168
+ type Payment = MintedPayment | WatchedPayment;
169
+ /**
170
+ * A payment as the caller already knows it: what the gateway calls it and the
171
+ * hash it settles against. Every `Payment` is one, and a caller reading back
172
+ * after a restart builds one from the two fields it stored
173
+ */
174
+ interface Held {
175
+ id: string;
176
+ paymentHash: string;
177
+ }
178
+ /**
179
+ * Who is paid and how much. `to` is a priority list when it is an array: the
180
+ * gateway takes the first address that can issue a provable invoice for the
181
+ * amount and the rest are the fallback
182
+ */
183
+ interface Charge {
184
+ paidTo: string | string[];
185
+ amount: Amount;
186
+ /** Where the gateway posts the settlement, signed with the key it publishes */
187
+ webhookUrl?: string;
188
+ }
189
+ /**
190
+ * A charge with its price settled, which is what a proof compares the gateway's
191
+ * answer against. Asking a fiat price twice gives two numbers, so the proof is
192
+ * handed the one that was actually asked for
193
+ */
194
+ interface Priced {
195
+ paidTo: string[];
196
+ amountMsat: number;
197
+ }
198
+ /**
199
+ * An invoice you obtained yourself, handed over to be watched. The gateway is
200
+ * given no address and no amount, so it cannot refuse one recipient rather than
201
+ * all of them
202
+ */
203
+ interface Handover {
204
+ paymentHash: string;
205
+ verifyUrl: string;
206
+ expiresAt: number;
207
+ /** Groups this payment with every other one carrying the same secret */
208
+ trigger?: string;
209
+ /**
210
+ * How many of this trigger's settlements the gateway keeps replayable past the
211
+ * hour it would otherwise forget them in, up to the ceiling its operator set.
212
+ * Needs `trigger`, defaults to none
213
+ */
214
+ replay?: number;
215
+ /** Sealed with `seal` for this payment hash, so the gateway stores what it cannot read or move */
216
+ sealed?: string;
217
+ webhookUrl?: string;
218
+ }
219
+ /**
220
+ * Which address would serve an amount, and what the ones ahead of it refused.
221
+ * `feeMsat` is always zero, the payer pays the recipient's own invoice and the
222
+ * gateway is never in the money's path
223
+ */
224
+ interface Quote {
225
+ lnAddress: string;
226
+ amountMsat: number;
227
+ feeMsat: number;
228
+ minMsat: number;
229
+ maxMsat: number;
230
+ metadata: string;
231
+ refusals: WalletFailure[];
232
+ }
233
+ /**
234
+ * A one minute pass onto one trigger's stream. It opens that trigger and nothing
235
+ * else, which is what makes it the thing to hand a browser when the trigger
236
+ * secret is not. `expiresAt` is unix seconds, like every other time here
237
+ */
238
+ interface SocketTicket {
239
+ ticket: string;
240
+ expiresAt: number;
241
+ }
242
+ /** Why one wallet in the list could not be used */
243
+ type WalletReason = "address-unusable" | "unreachable" | "amount-not-accepted" | "cannot-prove-delivery" | "invoice-refused";
244
+ /** One wallet on the list that could not be used, and the reason it could not */
245
+ interface WalletFailure {
246
+ address: string;
247
+ reason: WalletReason;
248
+ }
249
+ /**
250
+ * What a delivery carries. Everything needed to act on a settlement and to check
251
+ * it, and nothing else, so a retry is the same size every time
252
+ */
253
+ interface Settlement {
254
+ id: string;
255
+ status: PaymentStatus;
256
+ paymentHash: string;
257
+ preimage: string | null;
258
+ settledAt: number;
259
+ }
260
+
261
+ export { type Amount as A, type Charge as C, type FiatOptions as F, type Handover as H, type MedianOptions as M, type Payment as P, type Quote as Q, type Settlement as S, type Ticker as T, type WalletFailure as W, coinmate as a, bitstamp as b, coinbase as c, msatFor as d, type Held as e, type MintedPayment as f, type Msat as g, type PaymentStatus as h, type Priced as i, type SocketTicket as j, kraken as k, type WalletReason as l, medianOf as m, type WatchedPayment as n, fiat as o, msat as p, sats as s };
package/openapi.yaml CHANGED
@@ -2,7 +2,7 @@ openapi: 3.1.0
2
2
 
3
3
  info:
4
4
  title: thunder-bridge, the endpoint your own service serves
5
- version: 1.4.2
5
+ version: 2.1.1
6
6
  license:
7
7
  name: MIT
8
8
  identifier: MIT
@@ -71,14 +71,17 @@ paths:
71
71
  summary: Quote the price, or mint the invoice for a quote already given
72
72
  description: |
73
73
  Without `to` this answers the payRequest. It calls your `amountMsat()` once, quotes
74
- your address list through the gateway, and returns the first address that answers and
75
- accepts the amount, along with that wallet's own LUD-06 metadata string byte for byte.
74
+ your address list, through the gateway or by itself when the trigger is blind, and
75
+ returns the first address that answers and accepts the amount, along with that
76
+ wallet's own LUD-06 metadata string byte for byte. A quote naming an address you did
77
+ not list is refused, so a gateway cannot put its own address in front of your payers.
76
78
  `minSendable` and `maxSendable` are both the price, so the payer's wallet offers no
77
79
  amount field and the payment is exactly what you asked for.
78
80
 
79
81
  With `to` this is the callback. The signature is checked first, and a request that was
80
82
  not signed by this service is refused before anything is minted, which is what stops a
81
- stranger making your endpoint mint invoices on wallets of their choosing. The invoice
83
+ stranger making your endpoint mint invoices on wallets of their choosing. An address
84
+ you have since taken off the list is refused too, signed or not. The invoice
82
85
  then comes from the recipient's own wallet, never from this service and never from the
83
86
  gateway, and `verify` on the answer points at that recipient's LUD-21 endpoint.
84
87
 
@@ -200,48 +203,35 @@ paths:
200
203
  operationId: verifyBankTransfer
201
204
  summary: Say whether the transfer landed, and release the preimage when it did
202
205
  description: |
203
- Mounted wherever you passed `verifyUrl` to `bankTransfer`, which puts what to look for
204
- in the query and signs it. The gateway polls this exactly as it polls a wallet's LUD-21
205
- endpoint, and it cannot tell the difference, which is the whole point.
206
+ Mounted wherever you passed `verifyUrl` to `bankTransfer`, which seals what to look for
207
+ into the query. The gateway polls this exactly as it polls a wallet's LUD-21 endpoint,
208
+ and it cannot tell the difference, which is the whole point.
206
209
 
207
- A query this service did not sign is refused. Without that check the endpoint would
208
- answer "did anyone send you this amount with this note" to whoever asked, which is a
209
- bank statement handed out one question at a time.
210
+ A query this service did not seal is refused, and so is one sealed for another
211
+ account. Without that check the endpoint would answer "did anyone send you this amount
212
+ with this note" to whoever asked, which is a bank statement handed out one question at
213
+ a time.
210
214
 
211
- The preimage is an HMAC of the reference, the amount and the currency under a secret
212
- only this service holds, so the gateway can check it hashes to what it was given and
213
- still cannot produce it. Nothing is stored between calls.
215
+ A credit pays when its amount and currency match exactly and the reference stands as a
216
+ whole word in what the payer wrote, across every field the bank puts a note in, so
217
+ noise around it is fine. A credit that also names another reference shaped like it, a
218
+ second order number of the same kind, pays neither.
219
+
220
+ The preimage is an HMAC of the account, the amount, the currency, the expiry and the
221
+ reference under a secret only this service holds, so the gateway can check it hashes to
222
+ what it was given and still cannot produce it, and a second order under the same
223
+ reference is another payment. Nothing is stored between calls.
214
224
  parameters:
215
- - name: ref
225
+ - name: q
216
226
  in: query
217
227
  required: true
218
228
  description: |
219
- What the payer had to leave on the transfer. Matched as a substring against every
220
- field the bank puts a note in, so noise around it is fine.
221
- schema:
222
- type: string
223
- - name: minor
224
- in: query
225
- required: true
226
- description: The price in the smallest unit of the currency, so 48055 is 480.55 CZK.
227
- schema:
228
- type: integer
229
- minimum: 1
230
- - name: cc
231
- in: query
232
- required: true
233
- description: The three letter currency code the credit has to be in.
229
+ One blob sealed by `bankTransfer` under the rail secret, naming the account, the
230
+ amount in minor units, the currency, the expiry and the reference. Padded to one
231
+ size class, so its length says nothing about what it carries.
234
232
  schema:
235
233
  type: string
236
- minLength: 3
237
- maxLength: 3
238
- - name: sig
239
- in: query
240
- required: true
241
- description: Hex HMAC-SHA256 over `<ref>|<minor>|<cc>`, minted by `bankTransfer`.
242
- schema:
243
- type: string
244
- pattern: "^[0-9a-f]{64}$"
234
+ pattern: "^v2\\."
245
235
  responses:
246
236
  "200":
247
237
  description: |
@@ -258,7 +248,7 @@ paths:
258
248
  schema:
259
249
  $ref: "#/components/schemas/unsettled"
260
250
  "403":
261
- description: The signature does not hold, so this service never minted the question.
251
+ description: The blob does not open under this service's secret, or was sealed for another account, so it never asked the question.
262
252
  content:
263
253
  application/json:
264
254
  schema: