payment stream + unreachable provider + fess calc fix
This commit is contained in:
parent
d319c1d4be
commit
c8ede119d6
16 changed files with 365 additions and 64 deletions
|
|
@ -275,6 +275,11 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
||||||
- output: [PayInvoiceResponse](#PayInvoiceResponse)
|
- output: [PayInvoiceResponse](#PayInvoiceResponse)
|
||||||
|
|
||||||
|
- PayInvoiceStream
|
||||||
|
- auth type: __User__
|
||||||
|
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
||||||
|
- output: [InvoicePaymentStream](#InvoicePaymentStream)
|
||||||
|
|
||||||
- PingSubProcesses
|
- PingSubProcesses
|
||||||
- auth type: __Metrics__
|
- auth type: __Metrics__
|
||||||
- This methods has an __empty__ __request__ body
|
- This methods has an __empty__ __request__ body
|
||||||
|
|
@ -860,6 +865,13 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
||||||
- output: [PayInvoiceResponse](#PayInvoiceResponse)
|
- output: [PayInvoiceResponse](#PayInvoiceResponse)
|
||||||
|
|
||||||
|
- PayInvoiceStream
|
||||||
|
- auth type: __User__
|
||||||
|
- http method: __post__
|
||||||
|
- http route: __/api/user/invoice/pay/stream__
|
||||||
|
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
||||||
|
- output: [InvoicePaymentStream](#InvoicePaymentStream)
|
||||||
|
|
||||||
- PingSubProcesses
|
- PingSubProcesses
|
||||||
- auth type: __Metrics__
|
- auth type: __Metrics__
|
||||||
- http method: __post__
|
- http method: __post__
|
||||||
|
|
@ -1256,6 +1268,9 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
- __token__: _string_
|
- __token__: _string_
|
||||||
- __url__: _string_
|
- __url__: _string_
|
||||||
|
|
||||||
|
### InvoicePaymentStream
|
||||||
|
- __update__: _[InvoicePaymentStream_update](#InvoicePaymentStream_update)_
|
||||||
|
|
||||||
### LatestBundleMetricReq
|
### LatestBundleMetricReq
|
||||||
- __limit__: _number_ *this field is optional
|
- __limit__: _number_ *this field is optional
|
||||||
|
|
||||||
|
|
@ -1460,12 +1475,14 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
### PayAppUserInvoiceRequest
|
### PayAppUserInvoiceRequest
|
||||||
- __amount__: _number_
|
- __amount__: _number_
|
||||||
- __debit_npub__: _string_ *this field is optional
|
- __debit_npub__: _string_ *this field is optional
|
||||||
|
- __fee_limit_sats__: _number_ *this field is optional
|
||||||
- __invoice__: _string_
|
- __invoice__: _string_
|
||||||
- __user_identifier__: _string_
|
- __user_identifier__: _string_
|
||||||
|
|
||||||
### PayInvoiceRequest
|
### PayInvoiceRequest
|
||||||
- __amount__: _number_
|
- __amount__: _number_
|
||||||
- __debit_npub__: _string_ *this field is optional
|
- __debit_npub__: _string_ *this field is optional
|
||||||
|
- __fee_limit_sats__: _number_ *this field is optional
|
||||||
- __invoice__: _string_
|
- __invoice__: _string_
|
||||||
|
|
||||||
### PayInvoiceResponse
|
### PayInvoiceResponse
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ type Client struct {
|
||||||
PayAddress func(req PayAddressRequest) (*PayAddressResponse, error)
|
PayAddress func(req PayAddressRequest) (*PayAddressResponse, error)
|
||||||
PayAppUserInvoice func(req PayAppUserInvoiceRequest) (*PayInvoiceResponse, error)
|
PayAppUserInvoice func(req PayAppUserInvoiceRequest) (*PayInvoiceResponse, error)
|
||||||
PayInvoice func(req PayInvoiceRequest) (*PayInvoiceResponse, error)
|
PayInvoice func(req PayInvoiceRequest) (*PayInvoiceResponse, error)
|
||||||
|
PayInvoiceStream func(req PayInvoiceRequest) (*InvoicePaymentStream, error)
|
||||||
PingSubProcesses func() error
|
PingSubProcesses func() error
|
||||||
RequestNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error)
|
RequestNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error)
|
||||||
ResetDebit func(req DebitOperation) error
|
ResetDebit func(req DebitOperation) error
|
||||||
|
|
@ -1835,6 +1836,7 @@ func NewClient(params ClientParams) *Client {
|
||||||
}
|
}
|
||||||
return &res, nil
|
return &res, nil
|
||||||
},
|
},
|
||||||
|
// server streaming method: PayInvoiceStream not implemented
|
||||||
PingSubProcesses: func() error {
|
PingSubProcesses: func() error {
|
||||||
auth, err := params.RetrieveMetricsAuth()
|
auth, err := params.RetrieveMetricsAuth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,9 @@ type HttpCreds struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
}
|
}
|
||||||
|
type InvoicePaymentStream struct {
|
||||||
|
Update *InvoicePaymentStream_update `json:"update"`
|
||||||
|
}
|
||||||
type LatestBundleMetricReq struct {
|
type LatestBundleMetricReq struct {
|
||||||
Limit int64 `json:"limit"`
|
Limit int64 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
@ -545,13 +548,15 @@ type PayAddressResponse struct {
|
||||||
type PayAppUserInvoiceRequest struct {
|
type PayAppUserInvoiceRequest struct {
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
Debit_npub string `json:"debit_npub"`
|
Debit_npub string `json:"debit_npub"`
|
||||||
|
Fee_limit_sats int64 `json:"fee_limit_sats"`
|
||||||
Invoice string `json:"invoice"`
|
Invoice string `json:"invoice"`
|
||||||
User_identifier string `json:"user_identifier"`
|
User_identifier string `json:"user_identifier"`
|
||||||
}
|
}
|
||||||
type PayInvoiceRequest struct {
|
type PayInvoiceRequest struct {
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
Debit_npub string `json:"debit_npub"`
|
Debit_npub string `json:"debit_npub"`
|
||||||
Invoice string `json:"invoice"`
|
Fee_limit_sats int64 `json:"fee_limit_sats"`
|
||||||
|
Invoice string `json:"invoice"`
|
||||||
}
|
}
|
||||||
type PayInvoiceResponse struct {
|
type PayInvoiceResponse struct {
|
||||||
Amount_paid int64 `json:"amount_paid"`
|
Amount_paid int64 `json:"amount_paid"`
|
||||||
|
|
@ -751,6 +756,18 @@ type DebitRule_rule struct {
|
||||||
Expiration_rule *DebitExpirationRule `json:"expiration_rule"`
|
Expiration_rule *DebitExpirationRule `json:"expiration_rule"`
|
||||||
Frequency_rule *FrequencyRule `json:"frequency_rule"`
|
Frequency_rule *FrequencyRule `json:"frequency_rule"`
|
||||||
}
|
}
|
||||||
|
type InvoicePaymentStream_update_type string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ACK InvoicePaymentStream_update_type = "ack"
|
||||||
|
DONE InvoicePaymentStream_update_type = "done"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InvoicePaymentStream_update struct {
|
||||||
|
Type InvoicePaymentStream_update_type `json:"type"`
|
||||||
|
Ack *Empty `json:"ack"`
|
||||||
|
Done *PayInvoiceResponse `json:"done"`
|
||||||
|
}
|
||||||
type LiveDebitRequest_debit_type string
|
type LiveDebitRequest_debit_type string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -881,6 +881,7 @@ export default (params: ClientParams) => ({
|
||||||
}
|
}
|
||||||
return { status: 'ERROR', reason: 'invalid response' }
|
return { status: 'ERROR', reason: 'invalid response' }
|
||||||
},
|
},
|
||||||
|
PayInvoiceStream: async (request: Types.PayInvoiceRequest, cb: (v:ResultError | ({ status: 'OK' }& Types.InvoicePaymentStream)) => void): Promise<void> => { throw new Error('http streams are not supported')},
|
||||||
PingSubProcesses: async (): Promise<ResultError | ({ status: 'OK' })> => {
|
PingSubProcesses: async (): Promise<ResultError | ({ status: 'OK' })> => {
|
||||||
const auth = await params.retrieveMetricsAuth()
|
const auth = await params.retrieveMetricsAuth()
|
||||||
if (auth === null) throw new Error('retrieveMetricsAuth() returned null')
|
if (auth === null) throw new Error('retrieveMetricsAuth() returned null')
|
||||||
|
|
|
||||||
|
|
@ -755,6 +755,22 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
||||||
}
|
}
|
||||||
return { status: 'ERROR', reason: 'invalid response' }
|
return { status: 'ERROR', reason: 'invalid response' }
|
||||||
},
|
},
|
||||||
|
PayInvoiceStream: async (request: Types.PayInvoiceRequest, cb: (res:ResultError | ({ status: 'OK' }& Types.InvoicePaymentStream)) => void): Promise<void> => {
|
||||||
|
const auth = await params.retrieveNostrUserAuth()
|
||||||
|
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||||
|
const nostrRequest: NostrRequest = {}
|
||||||
|
nostrRequest.body = request
|
||||||
|
subscribe(params.pubDestination, {rpcName:'PayInvoiceStream',authIdentifier:auth, ...nostrRequest }, (data) => {
|
||||||
|
if (data.status === 'ERROR' && typeof data.reason === 'string') return cb(data)
|
||||||
|
if (data.status === 'OK') {
|
||||||
|
const result = data
|
||||||
|
if(!params.checkResult) return cb({ status: 'OK', ...result })
|
||||||
|
const error = Types.InvoicePaymentStreamValidate(result)
|
||||||
|
if (error === null) { return cb({ status: 'OK', ...result }) } else return cb({ status: 'ERROR', reason: error.message })
|
||||||
|
}
|
||||||
|
return cb({ status: 'ERROR', reason: 'invalid response' })
|
||||||
|
})
|
||||||
|
},
|
||||||
PingSubProcesses: async (): Promise<ResultError | ({ status: 'OK' })> => {
|
PingSubProcesses: async (): Promise<ResultError | ({ status: 'OK' })> => {
|
||||||
const auth = await params.retrieveNostrMetricsAuth()
|
const auth = await params.retrieveNostrMetricsAuth()
|
||||||
if (auth === null) throw new Error('retrieveNostrMetricsAuth() returned null')
|
if (auth === null) throw new Error('retrieveNostrMetricsAuth() returned null')
|
||||||
|
|
|
||||||
|
|
@ -1190,6 +1190,22 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
||||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||||
break
|
break
|
||||||
|
case 'PayInvoiceStream':
|
||||||
|
try {
|
||||||
|
if (!methods.PayInvoiceStream) throw new Error('method: PayInvoiceStream is not implemented')
|
||||||
|
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||||
|
stats.guard = process.hrtime.bigint()
|
||||||
|
authCtx = authContext
|
||||||
|
const request = req.body
|
||||||
|
const error = Types.PayInvoiceRequestValidate(request)
|
||||||
|
stats.validate = process.hrtime.bigint()
|
||||||
|
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||||
|
methods.PayInvoiceStream({rpcName:'PayInvoiceStream', ctx:authContext , req: request ,cb: (response, err) => {
|
||||||
|
stats.handle = process.hrtime.bigint()
|
||||||
|
if (err) { logErrorAndReturnResponse(err, err.message, res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)} else { res({status: 'OK', ...response});opts.metricsCallback([{ ...info, ...stats, ...authContext }])}
|
||||||
|
}})
|
||||||
|
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||||
|
break
|
||||||
case 'PingSubProcesses':
|
case 'PingSubProcesses':
|
||||||
try {
|
try {
|
||||||
if (!methods.PingSubProcesses) throw new Error('method: PingSubProcesses is not implemented')
|
if (!methods.PingSubProcesses) throw new Error('method: PingSubProcesses is not implemented')
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,9 @@ export type PayAppUserInvoice_Output = ResultError | ({ status: 'OK' } & PayInvo
|
||||||
export type PayInvoice_Input = {rpcName:'PayInvoice', req: PayInvoiceRequest}
|
export type PayInvoice_Input = {rpcName:'PayInvoice', req: PayInvoiceRequest}
|
||||||
export type PayInvoice_Output = ResultError | ({ status: 'OK' } & PayInvoiceResponse)
|
export type PayInvoice_Output = ResultError | ({ status: 'OK' } & PayInvoiceResponse)
|
||||||
|
|
||||||
|
export type PayInvoiceStream_Input = {rpcName:'PayInvoiceStream', req: PayInvoiceRequest, cb:(res: InvoicePaymentStream, err:Error|null)=> void}
|
||||||
|
export type PayInvoiceStream_Output = ResultError | { status: 'OK' }
|
||||||
|
|
||||||
export type PingSubProcesses_Input = {rpcName:'PingSubProcesses'}
|
export type PingSubProcesses_Input = {rpcName:'PingSubProcesses'}
|
||||||
export type PingSubProcesses_Output = ResultError | { status: 'OK' }
|
export type PingSubProcesses_Output = ResultError | { status: 'OK' }
|
||||||
|
|
||||||
|
|
@ -389,6 +392,7 @@ export type ServerMethods = {
|
||||||
PayAddress?: (req: PayAddress_Input & {ctx: UserContext }) => Promise<PayAddressResponse>
|
PayAddress?: (req: PayAddress_Input & {ctx: UserContext }) => Promise<PayAddressResponse>
|
||||||
PayAppUserInvoice?: (req: PayAppUserInvoice_Input & {ctx: AppContext }) => Promise<PayInvoiceResponse>
|
PayAppUserInvoice?: (req: PayAppUserInvoice_Input & {ctx: AppContext }) => Promise<PayInvoiceResponse>
|
||||||
PayInvoice?: (req: PayInvoice_Input & {ctx: UserContext }) => Promise<PayInvoiceResponse>
|
PayInvoice?: (req: PayInvoice_Input & {ctx: UserContext }) => Promise<PayInvoiceResponse>
|
||||||
|
PayInvoiceStream?: (req: PayInvoiceStream_Input & {ctx: UserContext }) => Promise<void>
|
||||||
PingSubProcesses?: (req: PingSubProcesses_Input & {ctx: MetricsContext }) => Promise<void>
|
PingSubProcesses?: (req: PingSubProcesses_Input & {ctx: MetricsContext }) => Promise<void>
|
||||||
RequestNPubLinkingToken?: (req: RequestNPubLinkingToken_Input & {ctx: AppContext }) => Promise<RequestNPubLinkingTokenResponse>
|
RequestNPubLinkingToken?: (req: RequestNPubLinkingToken_Input & {ctx: AppContext }) => Promise<RequestNPubLinkingTokenResponse>
|
||||||
ResetDebit?: (req: ResetDebit_Input & {ctx: UserContext }) => Promise<void>
|
ResetDebit?: (req: ResetDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||||
|
|
@ -1980,6 +1984,25 @@ export const HttpCredsValidate = (o?: HttpCreds, opts: HttpCredsOptions = {}, pa
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InvoicePaymentStream = {
|
||||||
|
update: InvoicePaymentStream_update
|
||||||
|
}
|
||||||
|
export const InvoicePaymentStreamOptionalFields: [] = []
|
||||||
|
export type InvoicePaymentStreamOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
update_Options?: InvoicePaymentStream_updateOptions
|
||||||
|
}
|
||||||
|
export const InvoicePaymentStreamValidate = (o?: InvoicePaymentStream, opts: InvoicePaymentStreamOptions = {}, path: string = 'InvoicePaymentStream::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
const updateErr = InvoicePaymentStream_updateValidate(o.update, opts.update_Options, `${path}.update`)
|
||||||
|
if (updateErr !== null) return updateErr
|
||||||
|
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export type LatestBundleMetricReq = {
|
export type LatestBundleMetricReq = {
|
||||||
limit?: number
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
@ -3192,15 +3215,17 @@ export const PayAddressResponseValidate = (o?: PayAddressResponse, opts: PayAddr
|
||||||
export type PayAppUserInvoiceRequest = {
|
export type PayAppUserInvoiceRequest = {
|
||||||
amount: number
|
amount: number
|
||||||
debit_npub?: string
|
debit_npub?: string
|
||||||
|
fee_limit_sats?: number
|
||||||
invoice: string
|
invoice: string
|
||||||
user_identifier: string
|
user_identifier: string
|
||||||
}
|
}
|
||||||
export type PayAppUserInvoiceRequestOptionalField = 'debit_npub'
|
export type PayAppUserInvoiceRequestOptionalField = 'debit_npub' | 'fee_limit_sats'
|
||||||
export const PayAppUserInvoiceRequestOptionalFields: PayAppUserInvoiceRequestOptionalField[] = ['debit_npub']
|
export const PayAppUserInvoiceRequestOptionalFields: PayAppUserInvoiceRequestOptionalField[] = ['debit_npub', 'fee_limit_sats']
|
||||||
export type PayAppUserInvoiceRequestOptions = OptionsBaseMessage & {
|
export type PayAppUserInvoiceRequestOptions = OptionsBaseMessage & {
|
||||||
checkOptionalsAreSet?: PayAppUserInvoiceRequestOptionalField[]
|
checkOptionalsAreSet?: PayAppUserInvoiceRequestOptionalField[]
|
||||||
amount_CustomCheck?: (v: number) => boolean
|
amount_CustomCheck?: (v: number) => boolean
|
||||||
debit_npub_CustomCheck?: (v?: string) => boolean
|
debit_npub_CustomCheck?: (v?: string) => boolean
|
||||||
|
fee_limit_sats_CustomCheck?: (v?: number) => boolean
|
||||||
invoice_CustomCheck?: (v: string) => boolean
|
invoice_CustomCheck?: (v: string) => boolean
|
||||||
user_identifier_CustomCheck?: (v: string) => boolean
|
user_identifier_CustomCheck?: (v: string) => boolean
|
||||||
}
|
}
|
||||||
|
|
@ -3214,6 +3239,9 @@ export const PayAppUserInvoiceRequestValidate = (o?: PayAppUserInvoiceRequest, o
|
||||||
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
|
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
|
||||||
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
|
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
|
||||||
|
|
||||||
|
if ((o.fee_limit_sats || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('fee_limit_sats')) && typeof o.fee_limit_sats !== 'number') return new Error(`${path}.fee_limit_sats: is not a number`)
|
||||||
|
if (opts.fee_limit_sats_CustomCheck && !opts.fee_limit_sats_CustomCheck(o.fee_limit_sats)) return new Error(`${path}.fee_limit_sats: custom check failed`)
|
||||||
|
|
||||||
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
||||||
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
||||||
|
|
||||||
|
|
@ -3226,14 +3254,16 @@ export const PayAppUserInvoiceRequestValidate = (o?: PayAppUserInvoiceRequest, o
|
||||||
export type PayInvoiceRequest = {
|
export type PayInvoiceRequest = {
|
||||||
amount: number
|
amount: number
|
||||||
debit_npub?: string
|
debit_npub?: string
|
||||||
|
fee_limit_sats?: number
|
||||||
invoice: string
|
invoice: string
|
||||||
}
|
}
|
||||||
export type PayInvoiceRequestOptionalField = 'debit_npub'
|
export type PayInvoiceRequestOptionalField = 'debit_npub' | 'fee_limit_sats'
|
||||||
export const PayInvoiceRequestOptionalFields: PayInvoiceRequestOptionalField[] = ['debit_npub']
|
export const PayInvoiceRequestOptionalFields: PayInvoiceRequestOptionalField[] = ['debit_npub', 'fee_limit_sats']
|
||||||
export type PayInvoiceRequestOptions = OptionsBaseMessage & {
|
export type PayInvoiceRequestOptions = OptionsBaseMessage & {
|
||||||
checkOptionalsAreSet?: PayInvoiceRequestOptionalField[]
|
checkOptionalsAreSet?: PayInvoiceRequestOptionalField[]
|
||||||
amount_CustomCheck?: (v: number) => boolean
|
amount_CustomCheck?: (v: number) => boolean
|
||||||
debit_npub_CustomCheck?: (v?: string) => boolean
|
debit_npub_CustomCheck?: (v?: string) => boolean
|
||||||
|
fee_limit_sats_CustomCheck?: (v?: number) => boolean
|
||||||
invoice_CustomCheck?: (v: string) => boolean
|
invoice_CustomCheck?: (v: string) => boolean
|
||||||
}
|
}
|
||||||
export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoiceRequestOptions = {}, path: string = 'PayInvoiceRequest::root.'): Error | null => {
|
export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoiceRequestOptions = {}, path: string = 'PayInvoiceRequest::root.'): Error | null => {
|
||||||
|
|
@ -3246,6 +3276,9 @@ export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoic
|
||||||
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
|
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
|
||||||
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
|
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
|
||||||
|
|
||||||
|
if ((o.fee_limit_sats || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('fee_limit_sats')) && typeof o.fee_limit_sats !== 'number') return new Error(`${path}.fee_limit_sats: is not a number`)
|
||||||
|
if (opts.fee_limit_sats_CustomCheck && !opts.fee_limit_sats_CustomCheck(o.fee_limit_sats)) return new Error(`${path}.fee_limit_sats: custom check failed`)
|
||||||
|
|
||||||
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
||||||
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
||||||
|
|
||||||
|
|
@ -4322,6 +4355,43 @@ export const DebitRule_ruleValidate = (o?: DebitRule_rule, opts:DebitRule_ruleOp
|
||||||
if (frequency_ruleErr !== null) return frequency_ruleErr
|
if (frequency_ruleErr !== null) return frequency_ruleErr
|
||||||
|
|
||||||
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return new Error(path + ': unknown type '+ stringType)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
export enum InvoicePaymentStream_update_type {
|
||||||
|
ACK = 'ack',
|
||||||
|
DONE = 'done',
|
||||||
|
}
|
||||||
|
export const enumCheckInvoicePaymentStream_update_type = (e?: InvoicePaymentStream_update_type): boolean => {
|
||||||
|
for (const v in InvoicePaymentStream_update_type) if (e === v) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
export type InvoicePaymentStream_update =
|
||||||
|
{type:InvoicePaymentStream_update_type.ACK, ack:Empty}|
|
||||||
|
{type:InvoicePaymentStream_update_type.DONE, done:PayInvoiceResponse}
|
||||||
|
|
||||||
|
export type InvoicePaymentStream_updateOptions = {
|
||||||
|
ack_Options?: EmptyOptions
|
||||||
|
done_Options?: PayInvoiceResponseOptions
|
||||||
|
}
|
||||||
|
export const InvoicePaymentStream_updateValidate = (o?: InvoicePaymentStream_update, opts:InvoicePaymentStream_updateOptions = {}, path: string = 'InvoicePaymentStream_update::root.'): Error | null => {
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
const stringType: string = o.type
|
||||||
|
switch (o.type) {
|
||||||
|
case InvoicePaymentStream_update_type.ACK:
|
||||||
|
const ackErr = EmptyValidate(o.ack, opts.ack_Options, `${path}.ack`)
|
||||||
|
if (ackErr !== null) return ackErr
|
||||||
|
|
||||||
|
|
||||||
|
break
|
||||||
|
case InvoicePaymentStream_update_type.DONE:
|
||||||
|
const doneErr = PayInvoiceResponseValidate(o.done, opts.done_Options, `${path}.done`)
|
||||||
|
if (doneErr !== null) return doneErr
|
||||||
|
|
||||||
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return new Error(path + ': unknown type '+ stringType)
|
return new Error(path + ': unknown type '+ stringType)
|
||||||
|
|
|
||||||
|
|
@ -517,6 +517,13 @@ service LightningPub {
|
||||||
option (nostr) = true;
|
option (nostr) = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc PayInvoiceStream(structs.PayInvoiceRequest) returns (stream structs.InvoicePaymentStream){
|
||||||
|
option (auth_type) = "User";
|
||||||
|
option (http_method) = "post";
|
||||||
|
option (http_route) = "/api/user/invoice/pay/stream";
|
||||||
|
option (nostr) = true;
|
||||||
|
}
|
||||||
|
|
||||||
rpc GetPaymentState(structs.GetPaymentStateRequest) returns (structs.PaymentState){
|
rpc GetPaymentState(structs.GetPaymentStateRequest) returns (structs.PaymentState){
|
||||||
option (auth_type) = "User";
|
option (auth_type) = "User";
|
||||||
option (http_method) = "post";
|
option (http_method) = "post";
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,7 @@ message PayAppUserInvoiceRequest {
|
||||||
string invoice = 2;
|
string invoice = 2;
|
||||||
int64 amount = 3;
|
int64 amount = 3;
|
||||||
optional string debit_npub = 4;
|
optional string debit_npub = 4;
|
||||||
|
optional int64 fee_limit_sats = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SendAppUserToAppUserPaymentRequest {
|
message SendAppUserToAppUserPaymentRequest {
|
||||||
|
|
@ -466,6 +467,7 @@ message PayInvoiceRequest{
|
||||||
string invoice = 1;
|
string invoice = 1;
|
||||||
int64 amount = 2;
|
int64 amount = 2;
|
||||||
optional string debit_npub = 3;
|
optional string debit_npub = 3;
|
||||||
|
optional int64 fee_limit_sats = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PayInvoiceResponse{
|
message PayInvoiceResponse{
|
||||||
|
|
@ -476,6 +478,13 @@ message PayInvoiceResponse{
|
||||||
int64 network_fee = 5;
|
int64 network_fee = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message InvoicePaymentStream {
|
||||||
|
oneof update {
|
||||||
|
Empty ack = 1;
|
||||||
|
PayInvoiceResponse done = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
message GetPaymentStateRequest{
|
message GetPaymentStateRequest{
|
||||||
string invoice = 1;
|
string invoice = 1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -346,9 +346,9 @@ export default class {
|
||||||
return Math.ceil(amount * this.getSettings().lndSettings.feeRateLimit + this.getSettings().lndSettings.feeFixedLimit);
|
return Math.ceil(amount * this.getSettings().lndSettings.feeRateLimit + this.getSettings().lndSettings.feeFixedLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMaxWithinLimit(amount: number): number {
|
/* GetMaxWithinLimit(amount: number): number {
|
||||||
return Math.max(0, Math.floor(amount * (1 - this.getSettings().lndSettings.feeRateLimit) - this.getSettings().lndSettings.feeFixedLimit))
|
return Math.max(0, Math.floor(amount * (1 - this.getSettings().lndSettings.feeRateLimit) - this.getSettings().lndSettings.feeFixedLimit))
|
||||||
}
|
} */
|
||||||
|
|
||||||
async ChannelBalance(): Promise<{ local: number, remote: number }> {
|
async ChannelBalance(): Promise<{ local: number, remote: number }> {
|
||||||
// console.log("Getting channel balance")
|
// console.log("Getting channel balance")
|
||||||
|
|
@ -363,7 +363,7 @@ export default class {
|
||||||
throw new Error("lnd node is currently out of sync")
|
throw new Error("lnd node is currently out of sync")
|
||||||
}
|
}
|
||||||
if (useProvider) {
|
if (useProvider) {
|
||||||
const res = await this.liquidProvider.PayInvoice(invoice, decodedAmount, from)
|
const res = await this.liquidProvider.PayInvoice(invoice, decodedAmount, from, feeLimit)
|
||||||
const providerDst = this.liquidProvider.GetProviderDestination()
|
const providerDst = this.liquidProvider.GetProviderDestination()
|
||||||
return { feeSat: res.network_fee + res.service_fee, valueSat: res.amount_paid, paymentPreimage: res.preimage, providerDst }
|
return { feeSat: res.network_fee + res.service_fee, valueSat: res.amount_paid, paymentPreimage: res.preimage, providerDst }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,15 @@ export default class {
|
||||||
throw new Error(`app user ${ctx.user_id} not found`) // TODO: fix logs doxing
|
throw new Error(`app user ${ctx.user_id} not found`) // TODO: fix logs doxing
|
||||||
}
|
}
|
||||||
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
||||||
|
const { max, networkFeeBps, networkFeeFixed, serviceFeeBps } = this.applicationManager.paymentManager.GetMaxPayableInvoice(user.balance_sats, true)
|
||||||
return {
|
return {
|
||||||
userId: ctx.user_id,
|
userId: ctx.user_id,
|
||||||
balance: user.balance_sats,
|
balance: user.balance_sats,
|
||||||
max_withdrawable: this.applicationManager.paymentManager.GetMaxPayableInvoice(user.balance_sats, true),
|
max_withdrawable: max,
|
||||||
user_identifier: appUser.identifier,
|
user_identifier: appUser.identifier,
|
||||||
network_max_fee_bps: this.settings.getSettings().lndSettings.feeRateBps,
|
network_max_fee_bps: networkFeeBps,
|
||||||
network_max_fee_fixed: this.settings.getSettings().lndSettings.feeFixedLimit,
|
network_max_fee_fixed: networkFeeFixed,
|
||||||
service_fee_bps: this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFeeBps,
|
service_fee_bps: serviceFeeBps,
|
||||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
||||||
nmanage: nmanageEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
nmanage: nmanageEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
||||||
|
|
@ -104,9 +105,21 @@ export default class {
|
||||||
return this.applicationManager.PayAppUserInvoice(ctx.app_id, {
|
return this.applicationManager.PayAppUserInvoice(ctx.app_id, {
|
||||||
amount: req.amount,
|
amount: req.amount,
|
||||||
invoice: req.invoice,
|
invoice: req.invoice,
|
||||||
user_identifier: ctx.app_user_id
|
user_identifier: ctx.app_user_id,
|
||||||
|
debit_npub: req.debit_npub,
|
||||||
|
fee_limit_sats: req.fee_limit_sats
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async PayInvoiceStream(ctx: Types.UserContext, req: Types.PayInvoiceRequest, cb: (res: Types.InvoicePaymentStream, err: Error | null) => void) {
|
||||||
|
return await this.applicationManager.PayAppUserInvoiceStream(ctx.app_id, {
|
||||||
|
amount: req.amount,
|
||||||
|
invoice: req.invoice,
|
||||||
|
user_identifier: ctx.app_user_id,
|
||||||
|
debit_npub: req.debit_npub,
|
||||||
|
fee_limit_sats: req.fee_limit_sats
|
||||||
|
}, cb)
|
||||||
|
}
|
||||||
async PayAddress(ctx: Types.UserContext, req: Types.PayInvoiceRequest): Promise<Types.PayInvoiceResponse> {
|
async PayAddress(ctx: Types.UserContext, req: Types.PayInvoiceRequest): Promise<Types.PayInvoiceResponse> {
|
||||||
return this.applicationManager.PayAppUserInvoice(ctx.app_id, {
|
return this.applicationManager.PayAppUserInvoice(ctx.app_id, {
|
||||||
amount: req.amount,
|
amount: req.amount,
|
||||||
|
|
|
||||||
|
|
@ -154,17 +154,17 @@ export default class {
|
||||||
|
|
||||||
const ndebitString = ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] })
|
const ndebitString = ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] })
|
||||||
log("🔗 [DEBUG] Generated ndebit for user", { userId: u.user.user_id, ndebit: ndebitString })
|
log("🔗 [DEBUG] Generated ndebit for user", { userId: u.user.user_id, ndebit: ndebitString })
|
||||||
|
const { max, networkFeeBps, networkFeeFixed, serviceFeeBps } = this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats, true)
|
||||||
return {
|
return {
|
||||||
identifier: u.identifier,
|
identifier: u.identifier,
|
||||||
info: {
|
info: {
|
||||||
userId: u.user.user_id,
|
userId: u.user.user_id,
|
||||||
balance: u.user.balance_sats,
|
balance: u.user.balance_sats,
|
||||||
max_withdrawable: this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats, true),
|
max_withdrawable: max,
|
||||||
user_identifier: u.identifier,
|
user_identifier: u.identifier,
|
||||||
network_max_fee_bps: this.settings.getSettings().lndSettings.feeRateBps,
|
network_max_fee_bps: networkFeeBps,
|
||||||
network_max_fee_fixed: this.settings.getSettings().lndSettings.feeFixedLimit,
|
network_max_fee_fixed: networkFeeFixed,
|
||||||
service_fee_bps: this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFeeBps,
|
service_fee_bps: serviceFeeBps,
|
||||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
||||||
nmanage: nmanageEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
nmanage: nmanageEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
||||||
|
|
@ -172,7 +172,7 @@ export default class {
|
||||||
bridge_url: this.settings.getSettings().serviceSettings.bridgeUrl
|
bridge_url: this.settings.getSettings().serviceSettings.bridgeUrl
|
||||||
|
|
||||||
},
|
},
|
||||||
max_withdrawable: this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats, true)
|
max_withdrawable: max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,16 +211,16 @@ export default class {
|
||||||
async GetAppUser(appId: string, req: Types.GetAppUserRequest): Promise<Types.AppUser> {
|
async GetAppUser(appId: string, req: Types.GetAppUserRequest): Promise<Types.AppUser> {
|
||||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
const user = await this.storage.applicationStorage.GetApplicationUser(app, req.user_identifier)
|
const user = await this.storage.applicationStorage.GetApplicationUser(app, req.user_identifier)
|
||||||
const max = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats, true)
|
|
||||||
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
||||||
|
const { max, networkFeeBps, networkFeeFixed, serviceFeeBps } = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats, true)
|
||||||
return {
|
return {
|
||||||
max_withdrawable: max, identifier: req.user_identifier, info: {
|
max_withdrawable: max, identifier: req.user_identifier, info: {
|
||||||
userId: user.user.user_id, balance: user.user.balance_sats,
|
userId: user.user.user_id, balance: user.user.balance_sats,
|
||||||
max_withdrawable: this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats, true),
|
max_withdrawable: max,
|
||||||
user_identifier: user.identifier,
|
user_identifier: user.identifier,
|
||||||
network_max_fee_bps: this.settings.getSettings().lndSettings.feeRateBps,
|
network_max_fee_bps: networkFeeBps,
|
||||||
network_max_fee_fixed: this.settings.getSettings().lndSettings.feeFixedLimit,
|
network_max_fee_fixed: networkFeeFixed,
|
||||||
service_fee_bps: this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFeeBps,
|
service_fee_bps: serviceFeeBps,
|
||||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
||||||
nmanage: nmanageEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
nmanage: nmanageEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
||||||
|
|
@ -238,6 +238,12 @@ export default class {
|
||||||
return paid
|
return paid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async PayAppUserInvoiceStream(appId: string, req: Types.PayAppUserInvoiceRequest, cb: (res: Types.InvoicePaymentStream, err: Error | null) => void) {
|
||||||
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
|
const appUser = await this.storage.applicationStorage.GetApplicationUser(app, req.user_identifier)
|
||||||
|
return this.paymentManager.PayInvoiceStream(appUser.user.user_id, req, app, cb)
|
||||||
|
}
|
||||||
|
|
||||||
async SendAppUserToAppUserPayment(appId: string, req: Types.SendAppUserToAppUserPaymentRequest): Promise<void> {
|
async SendAppUserToAppUserPayment(appId: string, req: Types.SendAppUserToAppUserPaymentRequest): Promise<void> {
|
||||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
const fromUser = await this.storage.applicationStorage.GetApplicationUser(app, req.from_user_identifier)
|
const fromUser = await this.storage.applicationStorage.GetApplicationUser(app, req.from_user_identifier)
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,11 @@ export class LiquidityManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeInvoiceCreation = async (amount: number): Promise<'lnd' | 'provider'> => {
|
beforeInvoiceCreation = async (amount: number): Promise<'lnd' | 'provider'> => {
|
||||||
|
const providerReady = this.liquidityProvider.IsReady()
|
||||||
if (this.settings.getSettings().liquiditySettings.useOnlyLiquidityProvider) {
|
if (this.settings.getSettings().liquiditySettings.useOnlyLiquidityProvider) {
|
||||||
|
if (!providerReady) {
|
||||||
|
throw new Error("cannot use liquidity provider, it is not ready")
|
||||||
|
}
|
||||||
return 'provider'
|
return 'provider'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,16 +81,26 @@ export class LiquidityManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeOutInvoicePayment = async (amount: number): Promise<'lnd' | 'provider'> => {
|
beforeOutInvoicePayment = async (amount: number): Promise<{ use: 'lnd' } | { use: 'provider', feeLimit: number }> => {
|
||||||
|
const providerReady = this.liquidityProvider.IsReady()
|
||||||
if (this.settings.getSettings().liquiditySettings.useOnlyLiquidityProvider) {
|
if (this.settings.getSettings().liquiditySettings.useOnlyLiquidityProvider) {
|
||||||
return 'provider'
|
if (!providerReady) {
|
||||||
|
throw new Error("cannot use liquidity provider, it is not ready")
|
||||||
|
}
|
||||||
|
const feeLimit = await this.liquidityProvider.GetExpectedFeeLimit(amount)
|
||||||
|
return { use: 'provider', feeLimit }
|
||||||
|
}
|
||||||
|
if (!providerReady) {
|
||||||
|
return { use: 'lnd' }
|
||||||
}
|
}
|
||||||
const canHandle = await this.liquidityProvider.CanProviderHandle({ action: 'spend', amount })
|
const canHandle = await this.liquidityProvider.CanProviderHandle({ action: 'spend', amount })
|
||||||
if (canHandle) {
|
if (!canHandle) {
|
||||||
return 'provider'
|
return { use: 'lnd' }
|
||||||
}
|
}
|
||||||
return 'lnd'
|
const feeLimit = this.liquidityProvider.CalculateExpectedFeeLimit(amount, canHandle)
|
||||||
|
return { use: 'provider', feeLimit }
|
||||||
}
|
}
|
||||||
|
|
||||||
afterOutInvoicePaid = async () => { }
|
afterOutInvoicePaid = async () => { }
|
||||||
|
|
||||||
shouldDrainProvider = async () => {
|
shouldDrainProvider = async () => {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ export class LiquidityProvider {
|
||||||
queue: ((state: 'ready') => void)[] = []
|
queue: ((state: 'ready') => void)[] = []
|
||||||
utils: Utils
|
utils: Utils
|
||||||
pendingPayments: Record<string, number> = {}
|
pendingPayments: Record<string, number> = {}
|
||||||
|
stateCache: Types.UserInfo | null = null
|
||||||
|
unreachableSince: number | null = null
|
||||||
|
reconnecting = false
|
||||||
incrementProviderBalance: (balance: number) => Promise<void>
|
incrementProviderBalance: (balance: number) => Promise<void>
|
||||||
// make the sub process accept client
|
// make the sub process accept client
|
||||||
constructor(getSettings: () => LiquiditySettings, utils: Utils, invoicePaidCb: InvoicePaidCb, incrementProviderBalance: (balance: number) => Promise<any>) {
|
constructor(getSettings: () => LiquiditySettings, utils: Utils, invoicePaidCb: InvoicePaidCb, incrementProviderBalance: (balance: number) => Promise<any>) {
|
||||||
|
|
@ -68,7 +71,11 @@ export class LiquidityProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
IsReady = () => {
|
IsReady = () => {
|
||||||
return this.ready && !this.getSettings().disableLiquidityProvider
|
const elapsed = this.unreachableSince ? Date.now() - this.unreachableSince : 0
|
||||||
|
if (!this.reconnecting && elapsed > 1000 * 60 * 5) {
|
||||||
|
this.GetUserState().then(() => this.reconnecting = false)
|
||||||
|
}
|
||||||
|
return this.ready && !this.getSettings().disableLiquidityProvider && !this.unreachableSince
|
||||||
}
|
}
|
||||||
|
|
||||||
AwaitProviderReady = async (): Promise<'inactive' | 'ready'> => {
|
AwaitProviderReady = async (): Promise<'inactive' | 'ready'> => {
|
||||||
|
|
@ -119,14 +126,29 @@ export class LiquidityProvider {
|
||||||
if (res.status === 'ERROR') {
|
if (res.status === 'ERROR') {
|
||||||
if (res.reason !== 'timeout') {
|
if (res.reason !== 'timeout') {
|
||||||
this.log("error getting user info", res.reason)
|
this.log("error getting user info", res.reason)
|
||||||
|
if (!this.unreachableSince) this.unreachableSince = Date.now()
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
this.unreachableSince = null
|
||||||
|
this.stateCache = res
|
||||||
this.utils.stateBundler.AddBalancePoint('providerBalance', res.balance)
|
this.utils.stateBundler.AddBalancePoint('providerBalance', res.balance)
|
||||||
this.utils.stateBundler.AddBalancePoint('providerMaxWithdrawable', res.max_withdrawable)
|
this.utils.stateBundler.AddBalancePoint('providerMaxWithdrawable', res.max_withdrawable)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetFees = () => {
|
||||||
|
if (!this.stateCache) {
|
||||||
|
throw new Error("user state not cached")
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
serviceFeeBps: this.stateCache.service_fee_bps,
|
||||||
|
networkFeeBps: this.stateCache.network_max_fee_bps,
|
||||||
|
networkFeeFixed: this.stateCache.network_max_fee_fixed,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GetLatestMaxWithdrawable = async () => {
|
GetLatestMaxWithdrawable = async () => {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -163,21 +185,37 @@ export class LiquidityProvider {
|
||||||
return serviceFee + networkFeeLimit
|
return serviceFee + networkFeeLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
CanProviderHandle = async (req: LiquidityRequest) => {
|
GetExpectedFeeLimit = async (amount: number) => {
|
||||||
|
if (!this.IsReady()) {
|
||||||
|
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
||||||
|
}
|
||||||
|
const state = await this.GetUserState()
|
||||||
|
if (state.status === 'ERROR') {
|
||||||
|
throw new Error(state.reason)
|
||||||
|
}
|
||||||
|
return this.CalculateExpectedFeeLimit(amount, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
CanProviderHandle = async (req: LiquidityRequest): Promise<false | Types.UserInfo> => {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const maxW = await this.GetLatestMaxWithdrawable()
|
const state = await this.GetUserState()
|
||||||
if (req.action === 'spend') {
|
if (state.status === 'ERROR') {
|
||||||
return maxW > req.amount
|
this.log("error getting user state", state.reason)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return true
|
const maxW = state.max_withdrawable
|
||||||
|
if (req.action === 'spend' && maxW < req.amount) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
AddInvoice = async (amount: number, memo: string, from: 'user' | 'system', expiry: number) => {
|
AddInvoice = async (amount: number, memo: string, from: 'user' | 'system', expiry: number) => {
|
||||||
try {
|
try {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
throw new Error("liquidity provider is not ready yet or disabled")
|
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
||||||
}
|
}
|
||||||
const res = await this.client.NewInvoice({ amountSats: amount, memo, expiry })
|
const res = await this.client.NewInvoice({ amountSats: amount, memo, expiry })
|
||||||
if (res.status === 'ERROR') {
|
if (res.status === 'ERROR') {
|
||||||
|
|
@ -193,21 +231,43 @@ export class LiquidityProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PayInvoice = async (invoice: string, decodedAmount: number, from: 'user' | 'system') => {
|
PayInvoice = async (invoice: string, decodedAmount: number, from: 'user' | 'system', feeLimit?: number) => {
|
||||||
try {
|
try {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
throw new Error("liquidity provider is not ready yet or disabled")
|
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
||||||
}
|
}
|
||||||
const userInfo = await this.GetUserState()
|
/* const userInfo = await this.GetUserState()
|
||||||
if (userInfo.status === 'ERROR') {
|
if (userInfo.status === 'ERROR') {
|
||||||
throw new Error(userInfo.reason)
|
throw new Error(userInfo.reason)
|
||||||
}
|
} */
|
||||||
this.pendingPayments[invoice] = decodedAmount + this.CalculateExpectedFeeLimit(decodedAmount, userInfo)
|
const feeLimitToUse = feeLimit ? feeLimit : await this.GetExpectedFeeLimit(decodedAmount)
|
||||||
const res = await this.client.PayInvoice({ invoice, amount: 0 })
|
this.pendingPayments[invoice] = decodedAmount + feeLimitToUse //this.CalculateExpectedFeeLimit(decodedAmount, userInfo)
|
||||||
if (res.status === 'ERROR') {
|
let acked = false
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
this.log("10 seconds passed, still waiting for ack")
|
||||||
|
this.GetUserState()
|
||||||
|
}, 1000 * 10)
|
||||||
|
const res = await new Promise<Types.PayInvoiceResponse>((resolve, reject) => {
|
||||||
|
this.client.PayInvoiceStream({ invoice, amount: 0, fee_limit_sats: feeLimitToUse }, (resp) => {
|
||||||
|
if (resp.status === 'ERROR') {
|
||||||
|
this.log("error paying invoice", resp.reason)
|
||||||
|
reject(new Error(resp.reason))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (resp.update.type === Types.InvoicePaymentStream_update_type.ACK) {
|
||||||
|
this.log("acked")
|
||||||
|
clearTimeout(timeout)
|
||||||
|
acked = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(resp.update.done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
//const res = await this.client.PayInvoice({ invoice, amount: 0, fee_limit_sats: feeLimitToUse })
|
||||||
|
/* if (res.status === 'ERROR') {
|
||||||
this.log("error paying invoice", res.reason)
|
this.log("error paying invoice", res.reason)
|
||||||
throw new Error(res.reason)
|
throw new Error(res.reason)
|
||||||
}
|
} */
|
||||||
const totalPaid = res.amount_paid + res.network_fee + res.service_fee
|
const totalPaid = res.amount_paid + res.network_fee + res.service_fee
|
||||||
this.incrementProviderBalance(-totalPaid).then(() => { delete this.pendingPayments[invoice] })
|
this.incrementProviderBalance(-totalPaid).then(() => { delete this.pendingPayments[invoice] })
|
||||||
this.utils.stateBundler.AddTxPoint('paidAnInvoice', decodedAmount, { used: 'provider', from, timeDiscount: true })
|
this.utils.stateBundler.AddTxPoint('paidAnInvoice', decodedAmount, { used: 'provider', from, timeDiscount: true })
|
||||||
|
|
@ -221,7 +281,7 @@ export class LiquidityProvider {
|
||||||
|
|
||||||
GetPaymentState = async (invoice: string) => {
|
GetPaymentState = async (invoice: string) => {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
throw new Error("liquidity provider is not ready yet or disabled")
|
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
||||||
}
|
}
|
||||||
const res = await this.client.GetPaymentState({ invoice })
|
const res = await this.client.GetPaymentState({ invoice })
|
||||||
if (res.status === 'ERROR') {
|
if (res.status === 'ERROR') {
|
||||||
|
|
@ -233,7 +293,7 @@ export class LiquidityProvider {
|
||||||
|
|
||||||
GetOperations = async () => {
|
GetOperations = async () => {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
throw new Error("liquidity provider is not ready yet or disabled")
|
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
||||||
}
|
}
|
||||||
const res = await this.client.GetUserOperations({
|
const res = await this.client.GetUserOperations({
|
||||||
latestIncomingInvoice: { ts: 0, id: 0 }, latestOutgoingInvoice: { ts: 0, id: 0 },
|
latestIncomingInvoice: { ts: 0, id: 0 }, latestOutgoingInvoice: { ts: 0, id: 0 },
|
||||||
|
|
|
||||||
|
|
@ -232,14 +232,29 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMaxPayableInvoice(balance: number, appUser: boolean): number {
|
GetMaxPayableInvoice(balance: number, appUser: boolean): { max: number, serviceFeeBps: number, networkFeeBps: number, networkFeeFixed: number } {
|
||||||
let maxWithinServiceFee = 0
|
const { outgoingAppInvoiceFee, outgoingAppUserInvoiceFee, outgoingAppUserInvoiceFeeBps } = this.settings.getSettings().serviceFeeSettings
|
||||||
if (appUser) {
|
const serviceFee = appUser ? outgoingAppUserInvoiceFee : outgoingAppInvoiceFee
|
||||||
maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFee)))
|
if (this.lnd.liquidProvider.IsReady()) {
|
||||||
} else {
|
const fees = this.lnd.liquidProvider.GetFees()
|
||||||
maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.getSettings().serviceFeeSettings.outgoingAppInvoiceFee)))
|
const providerServiceFee = fees.serviceFeeBps / 10000
|
||||||
|
const providerNetworkFee = fees.networkFeeBps / 10000
|
||||||
|
const div = 1 + serviceFee + providerServiceFee + providerNetworkFee
|
||||||
|
const max = Math.floor((balance - fees.networkFeeFixed) / div)
|
||||||
|
const networkFeeBps = fees.networkFeeBps + fees.serviceFeeBps
|
||||||
|
return { max, serviceFeeBps: outgoingAppUserInvoiceFeeBps, networkFeeBps, networkFeeFixed: fees.networkFeeFixed }
|
||||||
}
|
}
|
||||||
return this.lnd.GetMaxWithinLimit(maxWithinServiceFee)
|
const { feeFixedLimit, feeRateLimit, feeRateBps } = this.settings.getSettings().lndSettings
|
||||||
|
const div = 1 + serviceFee + feeRateLimit
|
||||||
|
const max = Math.floor((balance - feeFixedLimit) / div)
|
||||||
|
return { max, serviceFeeBps: outgoingAppUserInvoiceFeeBps, networkFeeBps: feeRateBps, networkFeeFixed: feeFixedLimit }
|
||||||
|
/* let maxWithinServiceFee = 0
|
||||||
|
if (appUser) {
|
||||||
|
maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFee)))
|
||||||
|
} else {
|
||||||
|
maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.getSettings().serviceFeeSettings.outgoingAppInvoiceFee)))
|
||||||
|
}
|
||||||
|
return this.lnd.GetMaxWithinLimit(maxWithinServiceFee) */
|
||||||
}
|
}
|
||||||
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
||||||
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
||||||
|
|
@ -248,7 +263,17 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async PayInvoice(userId: string, req: Types.PayInvoiceRequest, linkedApplication: Application): Promise<Types.PayInvoiceResponse> {
|
async PayInvoiceStream(userId: string, req: Types.PayInvoiceRequest, linkedApplication: Application, cb: (res: Types.InvoicePaymentStream, err: Error | null) => void) {
|
||||||
|
const ack = () => cb({ update: { type: Types.InvoicePaymentStream_update_type.ACK, ack: {} } }, null)
|
||||||
|
try {
|
||||||
|
const paid = await this.PayInvoice(userId, req, linkedApplication, ack)
|
||||||
|
cb({ update: { type: Types.InvoicePaymentStream_update_type.DONE, done: paid } }, null)
|
||||||
|
} catch (err: any) {
|
||||||
|
cb({ update: { type: Types.InvoicePaymentStream_update_type.ACK, ack: {} } }, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async PayInvoice(userId: string, req: Types.PayInvoiceRequest, linkedApplication: Application, ack?: () => void): Promise<Types.PayInvoiceResponse> {
|
||||||
await this.watchDog.PaymentRequested()
|
await this.watchDog.PaymentRequested()
|
||||||
const maybeBanned = await this.storage.userStorage.GetUser(userId)
|
const maybeBanned = await this.storage.userStorage.GetUser(userId)
|
||||||
if (maybeBanned.locked) {
|
if (maybeBanned.locked) {
|
||||||
|
|
@ -264,6 +289,9 @@ export default class {
|
||||||
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
||||||
const isAppUserPayment = userId !== linkedApplication.owner.user_id
|
const isAppUserPayment = userId !== linkedApplication.owner.user_id
|
||||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount, isAppUserPayment)
|
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount, isAppUserPayment)
|
||||||
|
if (req.fee_limit_sats && req.fee_limit_sats < serviceFee) {
|
||||||
|
throw new Error("fee limit provided is too low to cover service fees")
|
||||||
|
}
|
||||||
const internalInvoice = await this.storage.paymentStorage.GetInvoiceOwner(req.invoice)
|
const internalInvoice = await this.storage.paymentStorage.GetInvoiceOwner(req.invoice)
|
||||||
if (internalInvoice && internalInvoice.paid_at_unix > 0) {
|
if (internalInvoice && internalInvoice.paid_at_unix > 0) {
|
||||||
throw new Error("this invoice was already paid")
|
throw new Error("this invoice was already paid")
|
||||||
|
|
@ -276,7 +304,7 @@ export default class {
|
||||||
if (internalInvoice) {
|
if (internalInvoice) {
|
||||||
paymentInfo = await this.PayInternalInvoice(userId, internalInvoice, { payAmount, serviceFee }, linkedApplication, req.debit_npub)
|
paymentInfo = await this.PayInternalInvoice(userId, internalInvoice, { payAmount, serviceFee }, linkedApplication, req.debit_npub)
|
||||||
} else {
|
} else {
|
||||||
paymentInfo = await this.PayExternalInvoice(userId, req.invoice, { payAmount, serviceFee, amountForLnd: req.amount }, linkedApplication, req.debit_npub)
|
paymentInfo = await this.PayExternalInvoice(userId, req.invoice, { payAmount, serviceFee, amountForLnd: req.amount, feeLimit: req.fee_limit_sats }, linkedApplication, req.debit_npub, ack)
|
||||||
}
|
}
|
||||||
if (isAppUserPayment && serviceFee > 0) {
|
if (isAppUserPayment && serviceFee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, serviceFee, "fees")
|
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, serviceFee, "fees")
|
||||||
|
|
@ -292,7 +320,22 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async PayExternalInvoice(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, amountForLnd: number }, linkedApplication: Application, debitNpub?: string) {
|
getUse = async (payAmount: number, inputLimit: number | undefined): Promise<{ use: 'lnd' | 'provider', feeLimit: number }> => {
|
||||||
|
const use = await this.liquidityManager.beforeOutInvoicePayment(payAmount)
|
||||||
|
if (use.use === 'lnd') {
|
||||||
|
const lndFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||||
|
if (inputLimit && inputLimit < lndFeeLimit) {
|
||||||
|
this.log("WARNING requested fee limit is lower than suggested, payment might fail")
|
||||||
|
}
|
||||||
|
return { use: 'lnd', feeLimit: inputLimit || lndFeeLimit }
|
||||||
|
}
|
||||||
|
if (inputLimit && inputLimit < use.feeLimit) {
|
||||||
|
this.log("WARNING requested fee limit is lower than suggested by provider, payment might fail")
|
||||||
|
}
|
||||||
|
return { use: 'provider', feeLimit: inputLimit || use.feeLimit }
|
||||||
|
}
|
||||||
|
|
||||||
|
async PayExternalInvoice(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, amountForLnd: number, feeLimit?: number }, linkedApplication: Application, debitNpub?: string, ack?: () => void) {
|
||||||
if (this.settings.getSettings().serviceSettings.disableExternalPayments) {
|
if (this.settings.getSettings().serviceSettings.disableExternalPayments) {
|
||||||
throw new Error("something went wrong sending payment, please try again later")
|
throw new Error("something went wrong sending payment, please try again later")
|
||||||
}
|
}
|
||||||
|
|
@ -305,16 +348,20 @@ export default class {
|
||||||
}
|
}
|
||||||
throw new Error("payment already in progress")
|
throw new Error("payment already in progress")
|
||||||
}
|
}
|
||||||
|
|
||||||
const { amountForLnd, payAmount, serviceFee } = amounts
|
const { amountForLnd, payAmount, serviceFee } = amounts
|
||||||
const totalAmountToDecrement = payAmount + serviceFee
|
const totalAmountToDecrement = payAmount + serviceFee
|
||||||
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
/* const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||||
const use = await this.liquidityManager.beforeOutInvoicePayment(payAmount)
|
const use = await this.liquidityManager.beforeOutInvoicePayment(payAmount) */
|
||||||
|
const remainingLimit = amounts.feeLimit ? amounts.feeLimit - serviceFee : undefined
|
||||||
|
const { use, feeLimit: routingFeeLimit } = await this.getUse(payAmount, remainingLimit)
|
||||||
const provider = use === 'provider' ? this.lnd.liquidProvider.GetProviderDestination() : undefined
|
const provider = use === 'provider' ? this.lnd.liquidProvider.GetProviderDestination() : undefined
|
||||||
const pendingPayment = await this.storage.StartTransaction(async tx => {
|
const pendingPayment = await this.storage.StartTransaction(async tx => {
|
||||||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice, tx)
|
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice, tx)
|
||||||
return await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, { payAmount, serviceFee, networkFee: routingFeeLimit }, linkedApplication, provider, tx, debitNpub)
|
return await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, { payAmount, serviceFee, networkFee: routingFeeLimit }, linkedApplication, provider, tx, debitNpub)
|
||||||
}, "payment started")
|
}, "payment started")
|
||||||
this.log("ready to pay")
|
this.log("ready to pay")
|
||||||
|
ack?.()
|
||||||
try {
|
try {
|
||||||
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit, payAmount, { useProvider: use === 'provider', from: 'user' }, index => {
|
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit, payAmount, { useProvider: use === 'provider', from: 'user' }, index => {
|
||||||
this.storage.paymentStorage.SetExternalPaymentIndex(pendingPayment.serial_id, index)
|
this.storage.paymentStorage.SetExternalPaymentIndex(pendingPayment.serial_id, index)
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,13 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
||||||
if (err != null) throw new Error(err.message)
|
if (err != null) throw new Error(err.message)
|
||||||
return mainHandler.appUserManager.PayInvoice(ctx, req)
|
return mainHandler.appUserManager.PayInvoice(ctx, req)
|
||||||
},
|
},
|
||||||
|
PayInvoiceStream: async ({ ctx, req, cb }) => {
|
||||||
|
const err = Types.PayInvoiceRequestValidate(req, {
|
||||||
|
invoice_CustomCheck: invoice => invoice !== ''
|
||||||
|
})
|
||||||
|
if (err != null) throw new Error(err.message)
|
||||||
|
mainHandler.appUserManager.PayInvoiceStream(ctx, req, cb)
|
||||||
|
},
|
||||||
GetLnurlWithdrawLink: ({ ctx }) => mainHandler.paymentManager.GetLnurlWithdrawLink(ctx),
|
GetLnurlWithdrawLink: ({ ctx }) => mainHandler.paymentManager.GetLnurlWithdrawLink(ctx),
|
||||||
GetLnurlWithdrawInfo: async ({ ctx, query }) => {
|
GetLnurlWithdrawInfo: async ({ ctx, query }) => {
|
||||||
if (!query.k1) {
|
if (!query.k1) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue