Skip to content

Commit 52aac39

Browse files
feat: implement nodeless payments processor (#305)
* chore: hide powered by zebedee if payment processor is not * chore: add nodeless as payments processor to settings * fix: bad content type on zebedee callback req handler * chore(release): 1.23.0 [skip ci] # [1.23.0](v1.22.6...v1.23.0) (2023-05-12) ### Bug Fixes * add SECRET as env variable ([#298](#298)) ([58a1254](58a1254)) * invoice auto marked as paid ([be6d6f1](be6d6f1)) * issues with invoices ([#271](#271)) ([e1561e7](e1561e7)) ### Features * add LNURL processor ([#202](#202)) ([f237400](f237400)) * allow lightning zap receipts on paid relays ([#303](#303)) ([14bc96f](14bc96f)) * feat: implement nodeless payments processor * docs: add accepting payments section * chore: validate nodeless webhook secret * chore: hide powered-by-zebedee for non-zebedee processors --------- Co-authored-by: semantic-release-bot <[email protected]>
1 parent 62c1dbe commit 52aac39

27 files changed

+395
-72
lines changed

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ Install Docker from their [official guide](https://docs.docker.com/engine/instal
8686
- [Set up a Paid Nostr relay with Nostream and ZBD](https://andreneves.xyz/p/how-to-setup-a-paid-nostr-relay) by [André Neves](https://snort.social/p/npub1rvg76s0gz535txd9ypg2dfqv0x7a80ar6e096j3v343xdxyrt4ksmkxrck) (CTO & Co-Founder at [ZEBEDEE](https://zebedee.io/))
8787
- [Set up a Nostr relay in under 5 minutes](https://andreneves.xyz/p/set-up-a-nostr-relay-server-in-under) by [André Neves](https://twitter.com/andreneves) (CTO & Co-Founder at [ZEBEDEE](https://zebedee.io/))
8888

89+
### Accepting payments
90+
91+
1. Zebedee
92+
- You must set ZEBEDEE_API_KEY with an API Key from one of your projects in your Zebedee Developer Dashboard. Contact @foxp2zeb on Telegram or npub1rvg76s0gz535txd9ypg2dfqv0x7a80ar6e096j3v343xdxyrt4ksmkxrck on Nostr requesting access to the Zebedee Developer Dashboard. See the Zebedee full guide on how to set up a paid relay.
93+
94+
2. Nodeless.io
95+
- Sign up for a new account at https://nodeless.io and create a new store. Take note of the store ID.
96+
- Create a store webhook and make sure to check all of the events. Grab the store webhook secret.
97+
- Go to Profile > API Tokens and generate a new key and keep note of it.
98+
- Set NODELESS_API_KEY and NODELESS_WEBHOOK_SECRET environment variables with generated key and webhook secret, respectively.
99+
- On your .nostr/settings.yaml file, update the field `paymentsProcessors.nodeless.storeId1 with your store ID.
100+
89101
## Quick Start (Docker Compose)
90102

91103
Install Docker following the [official guide](https://docs.docker.com/engine/install/).
@@ -201,10 +213,7 @@ You may want to use `openssl rand -hex 128` to generate a secret.
201213
# Secret shortened for brevity
202214
```
203215

204-
In addition, if using Zebedee for payments, you must also set ZEBEDEE_API_KEY with
205-
an API Key from one of your projects in your Zebedee Developer Dashboard. Contact
206-
@foxp2zeb on Telegram or npub1rvg76s0gz535txd9ypg2dfqv0x7a80ar6e096j3v343xdxyrt4ksmkxrck on Nostr requesting
207-
access to the Zebedee Developer Dashboard.
216+
### Initializing the database
208217

209218
Create `nostr_ts_relay` database:
210219

docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ services:
4646
TOR_CONTROL_PORT: 9051
4747
TOR_PASSWORD: nostr_ts_relay
4848
HIDDEN_SERVICE_PORT: 80
49+
# Nodeless.io
50+
NODELESS_API_KEY: ${NODELESS_API_KEY}
51+
NODELESS_WEBHOOK_SECRET: ${NODELESS_WEBHOOK_SECRET}
4952
# Enable DEBUG for troubleshooting. Examples:
5053
# DEBUG: "primary:*"
5154
# DEBUG: "worker:*"

resources/default-settings.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ paymentsProcessors:
2929
callbackBaseURL: https://nostream.your-domain.com/callbacks/lnbits
3030
lnurl:
3131
invoiceURL: https://getalby.com/lnurlp/your-username
32+
nodeless:
33+
baseURL: https://nodeless.io
34+
storeId: your-nodeless-io-store-id
3235
network:
3336
maxPayloadSize: 524288
3437
# Comment the next line if using CloudFlare proxy

resources/index.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ <h1 class="mt-4 mb-4 text-center text-nowrap">{{name}}</h1>
5858
<button id="submitBtn" class="btn btn-lg btn-warning" type="submit">Pay {{amount}} sats</button>
5959
</div>
6060
</div>
61-
<div class="row">
61+
<div class="row d-none" id="powered-by-zebedee">
6262
<div class="d-flex justify-content-center mb-3 mt-4">
6363
<a href="https://zeb.gg/nostr-zbd-quickstart" target="_blank">
6464
<img class="poweredbyzbd-img" src="https://cdn.zebedee.io/an/nostr/poweredbyzbd.png" />
@@ -129,6 +129,7 @@ <h5 class="modal-title">Terms of Service</h5>
129129
</div>
130130
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-cuYeSxntonz0PPNlHhBs68uyIAVpIIOZZ5JqeqvYYIcEL727kskC66kF92t6Xl2V" crossorigin="anonymous"></script>
131131
<script>
132+
var processor = "{{processor}}"
132133
function attemptGetPubkey() {
133134
const maxRetries = 10
134135
function getPubKey(retries) {
@@ -147,6 +148,9 @@ <h5 class="modal-title">Terms of Service</h5>
147148

148149
function onLoad() {
149150
setTimeout(attemptGetPubkey, 300)
151+
if (processor === 'zebedee') {
152+
document.getElementById('powered-by-zebedee').classList.remove('d-none')
153+
}
150154
}
151155
</script>
152156
</body>

resources/invoices.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ <h2 class="text-danger">Invoice expired!</h2>
8686
<button class="btn btn-lg btn-primary" type="submit">Get another invoice</button>
8787
</div>
8888
</div>
89-
<div class="row">
89+
<div class="row d-none" id="powered-by-zebedee">
9090
<div class="d-flex justify-content-center mb-3 mt-4">
9191
<a href="https://zeb.gg/nostr-zbd-quickstart" target="_blank">
9292
<img class="poweredbyzbd-img" src="https://cdn.zebedee.io/an/nostr/poweredbyzbd.png" />
@@ -104,6 +104,7 @@ <h2 class="text-danger">Invoice expired!</h2>
104104
var invoice = "{{invoice}}";
105105
var pubkey = "{{pubkey}}"
106106
var expiresAt = "{{expires_at}}"
107+
var processor = "{{processor}}"
107108
var timeout
108109
var paid = false
109110
var fallbackTimeout
@@ -254,6 +255,9 @@ <h2 class="text-danger">Invoice expired!</h2>
254255
sendPayment().catch(() => {
255256
document.getElementById('sendPaymentBtn').classList.remove('d-none')
256257
})
258+
if (processor === 'zebedee') {
259+
document.getElementById('powered-by-zebedee').classList.remove('d-none')
260+
}
257261
</script>
258262
</body>
259263
</html>

src/@types/invoice.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ export enum InvoiceUnit {
88

99
export enum InvoiceStatus {
1010
PENDING = 'pending',
11-
COMPLETED = 'completed'
11+
COMPLETED = 'completed',
12+
EXPIRED = 'expired',
1213
}
1314

1415
export interface Invoice {

src/@types/repositories.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export interface IEventRepository {
2323
export interface IInvoiceRepository {
2424
findById(id: string, client?: DatabaseClient): Promise<Invoice | undefined>
2525
upsert(invoice: Partial<Invoice>, client?: DatabaseClient): Promise<number>
26+
updateStatus(
27+
invoice: Pick<Invoice, 'id' | 'status'>,
28+
client?: DatabaseClient,
29+
): Promise<Invoice | undefined>
2630
confirmInvoice(
2731
invoiceId: string,
2832
amountReceived: bigint,

src/@types/services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export interface IPaymentsService {
99
description: string,
1010
): Promise<Invoice>
1111
updateInvoice(invoice: Partial<Invoice>): Promise<void>
12-
updateInvoiceStatus(invoice: Partial<Invoice>): Promise<void>
12+
updateInvoiceStatus(invoice: Pick<Invoice, 'id' | 'status'>): Promise<Invoice>
1313
confirmInvoice(
1414
invoice: Pick<Invoice, 'id' | 'amountPaid' | 'confirmedAt'>,
1515
): Promise<void>

src/@types/settings.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,26 @@ export interface ZebedeePaymentsProcessor {
157157
ipWhitelist: string[]
158158
}
159159

160-
export interface LNbitsPaymentProcessor {
160+
export interface NodelessPaymentsProcessor {
161+
baseURL: string
162+
storeId: string
163+
}
164+
165+
export interface LNbitsPaymentsProcessor {
161166
baseURL: string
162167
callbackBaseURL: string
163168
}
164169

170+
export interface NodelessPaymentsProcessor {
171+
baseURL: string
172+
storeId: string
173+
}
174+
165175
export interface PaymentsProcessors {
166176
lnurl?: LnurlPaymentsProcessor,
167177
zebedee?: ZebedeePaymentsProcessor
168-
lnbits?: LNbitsPaymentProcessor
178+
lnbits?: LNbitsPaymentsProcessor
179+
nodeless?: NodelessPaymentsProcessor
169180
}
170181

171182
export interface Local {

src/adapters/web-server-adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter
3333
}
3434

3535
private onClientError(error: Error, socket: Duplex) {
36-
console.error('web-server-adapter: client socket error:', error)
3736
if (error['code'] === 'ECONNRESET' || !socket.writable) {
3837
return
3938
}
39+
console.error('web-server-adapter: client socket error:', error)
4040
socket.end('HTTP/1.1 400 Bad Request\r\nContent-Type: text/html\r\n')
4141
}
4242

0 commit comments

Comments
 (0)