• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cameri / nostream / 24609919997

18 Apr 2026 05:23PM UTC coverage: 30.829% (-32.0%) from 62.807%
24609919997

Pull #454

github

web-flow
Merge c337d7af3 into 26bcdd51d
Pull Request #454: fix: OpenNode callback accepts unauthenticated requests

268 of 1385 branches covered (19.35%)

Branch coverage included in aggregate %.

29 of 41 new or added lines in 3 files covered. (70.73%)

976 existing lines in 41 files now uncovered.

1164 of 3260 relevant lines covered (35.71%)

5.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

14.52
/src/controllers/callbacks/zebedee-callback-controller.ts
1
import { Request, Response } from 'express'
2

3
import { Invoice, InvoiceStatus } from '../../@types/invoice'
1✔
4
import { createLogger } from '../../factories/logger-factory'
1✔
5
import { createSettings } from '../../factories/settings-factory'
1✔
6
import { fromZebedeeInvoice } from '../../utils/transform'
1✔
7
import { getRemoteAddress } from '../../utils/http'
1✔
8
import { IController } from '../../@types/controllers'
9
import { IPaymentsService } from '../../@types/services'
10
import { validateSchema } from '../../utils/validation'
1✔
11
import { zebedeeCallbackBodySchema } from '../../schemas/zebedee-callback-schema'
1✔
12

13
const debug = createLogger('zebedee-callback-controller')
1✔
14

15
export class ZebedeeCallbackController implements IController {
1✔
16
  public constructor(
UNCOV
17
    private readonly paymentsService: IPaymentsService,
×
18
  ) {}
19

20
  public async handleRequest(
21
    request: Request,
22
    response: Response,
23
  ) {
UNCOV
24
    debug('request headers: %o', request.headers)
×
UNCOV
25
    debug('request body: %O', request.body)
×
26

UNCOV
27
    const bodyValidation = validateSchema(zebedeeCallbackBodySchema)(request.body)
×
UNCOV
28
    if (bodyValidation.error) {
×
UNCOV
29
      debug('zebedee callback request rejected: invalid body %o', bodyValidation.error)
×
UNCOV
30
      response
×
31
        .status(400)
32
        .setHeader('content-type', 'text/plain; charset=utf8')
33
        .send('Malformed body')
UNCOV
34
      return
×
35
    }
36

UNCOV
37
    const settings = createSettings()
×
38

UNCOV
39
    const { ipWhitelist = [] } = settings.paymentsProcessors?.zebedee ?? {}
×
UNCOV
40
    const remoteAddress = getRemoteAddress(request, settings)
×
UNCOV
41
    const paymentProcessor = settings.payments?.processor
×
42

UNCOV
43
    if (ipWhitelist.length && !ipWhitelist.includes(remoteAddress)) {
×
UNCOV
44
      debug('unauthorized request from %s to /callbacks/zebedee', remoteAddress)
×
UNCOV
45
      response
×
46
        .status(403)
47
        .send('Forbidden')
UNCOV
48
      return
×
49
    }
50

UNCOV
51
    if (paymentProcessor !== 'zebedee') {
×
UNCOV
52
      debug('denied request from %s to /callbacks/zebedee which is not the current payment processor', remoteAddress)
×
UNCOV
53
      response
×
54
        .status(403)
55
        .send('Forbidden')
UNCOV
56
      return
×
57
    }
58

UNCOV
59
    const invoice = fromZebedeeInvoice(request.body)
×
60

UNCOV
61
    debug('invoice', invoice)
×
62

63
    let updatedInvoice: Invoice
UNCOV
64
    try {
×
UNCOV
65
      updatedInvoice = await this.paymentsService.updateInvoiceStatus(invoice)
×
66
    } catch (error) {
UNCOV
67
      console.error(`Unable to persist invoice ${invoice.id}`, error)
×
68

UNCOV
69
      throw error
×
70
    }
71

UNCOV
72
    if (
×
73
      updatedInvoice.status !== InvoiceStatus.COMPLETED
×
74
      && !updatedInvoice.confirmedAt
75
    ) {
UNCOV
76
      response
×
77
        .status(200)
78
        .send()
79

UNCOV
80
      return
×
81
    }
82

UNCOV
83
    invoice.amountPaid = invoice.amountRequested
×
UNCOV
84
    invoice.status = updatedInvoice.status
×
UNCOV
85
    updatedInvoice.amountPaid = invoice.amountRequested
×
86

UNCOV
87
    try {
×
UNCOV
88
      await this.paymentsService.confirmInvoice({
×
89
        id: invoice.id,
90
        pubkey: invoice.pubkey,
91
        status: invoice.status,
92
        confirmedAt: invoice.confirmedAt,
93
        amountPaid: invoice.amountRequested,
94
      })
UNCOV
95
      await this.paymentsService.sendInvoiceUpdateNotification(updatedInvoice)
×
96
    } catch (error) {
UNCOV
97
      console.error(`Unable to confirm invoice ${invoice.id}`, error)
×
98

UNCOV
99
      throw error
×
100
    }
101

UNCOV
102
    response
×
103
      .status(200)
104
      .setHeader('content-type', 'text/plain; charset=utf8')
105
      .send('OK')
106
  }
107
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc