validate access rules
This commit is contained in:
parent
9a1aff58d0
commit
a2d502baa9
9 changed files with 161 additions and 42 deletions
|
|
@ -800,6 +800,7 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
- __admin_token__: _string_
|
- __admin_token__: _string_
|
||||||
|
|
||||||
### FrequencyRule
|
### FrequencyRule
|
||||||
|
- __amount__: _number_
|
||||||
- __interval__: _[IntervalType](#IntervalType)_
|
- __interval__: _[IntervalType](#IntervalType)_
|
||||||
- __number_of_intervals__: _number_
|
- __number_of_intervals__: _number_
|
||||||
|
|
||||||
|
|
@ -855,7 +856,6 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
- __token__: _string_
|
- __token__: _string_
|
||||||
|
|
||||||
### LiveDebitRequest
|
### LiveDebitRequest
|
||||||
- __amount__: _number_
|
|
||||||
- __debit__: _[LiveDebitRequest_debit](#LiveDebitRequest_debit)_
|
- __debit__: _[LiveDebitRequest_debit](#LiveDebitRequest_debit)_
|
||||||
- __npub__: _string_
|
- __npub__: _string_
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ type EnrollAdminTokenRequest struct {
|
||||||
Admin_token string `json:"admin_token"`
|
Admin_token string `json:"admin_token"`
|
||||||
}
|
}
|
||||||
type FrequencyRule struct {
|
type FrequencyRule struct {
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
Interval IntervalType `json:"interval"`
|
Interval IntervalType `json:"interval"`
|
||||||
Number_of_intervals int64 `json:"number_of_intervals"`
|
Number_of_intervals int64 `json:"number_of_intervals"`
|
||||||
}
|
}
|
||||||
|
|
@ -257,9 +258,8 @@ type LinkNPubThroughTokenRequest struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
type LiveDebitRequest struct {
|
type LiveDebitRequest struct {
|
||||||
Amount int64 `json:"amount"`
|
Debit *LiveDebitRequest_debit `json:"debit"`
|
||||||
Debit *LiveDebitRequest_debit `json:"debit"`
|
Npub string `json:"npub"`
|
||||||
Npub string `json:"npub"`
|
|
||||||
}
|
}
|
||||||
type LiveUserOperation struct {
|
type LiveUserOperation struct {
|
||||||
Operation *UserOperation `json:"operation"`
|
Operation *UserOperation `json:"operation"`
|
||||||
|
|
|
||||||
|
|
@ -1094,12 +1094,14 @@ export const EnrollAdminTokenRequestValidate = (o?: EnrollAdminTokenRequest, opt
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FrequencyRule = {
|
export type FrequencyRule = {
|
||||||
|
amount: number
|
||||||
interval: IntervalType
|
interval: IntervalType
|
||||||
number_of_intervals: number
|
number_of_intervals: number
|
||||||
}
|
}
|
||||||
export const FrequencyRuleOptionalFields: [] = []
|
export const FrequencyRuleOptionalFields: [] = []
|
||||||
export type FrequencyRuleOptions = OptionsBaseMessage & {
|
export type FrequencyRuleOptions = OptionsBaseMessage & {
|
||||||
checkOptionalsAreSet?: []
|
checkOptionalsAreSet?: []
|
||||||
|
amount_CustomCheck?: (v: number) => boolean
|
||||||
interval_CustomCheck?: (v: IntervalType) => boolean
|
interval_CustomCheck?: (v: IntervalType) => boolean
|
||||||
number_of_intervals_CustomCheck?: (v: number) => boolean
|
number_of_intervals_CustomCheck?: (v: number) => boolean
|
||||||
}
|
}
|
||||||
|
|
@ -1107,6 +1109,9 @@ export const FrequencyRuleValidate = (o?: FrequencyRule, opts: FrequencyRuleOpti
|
||||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
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')
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`)
|
||||||
|
if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`)
|
||||||
|
|
||||||
if (!enumCheckIntervalType(o.interval)) return new Error(`${path}.interval: is not a valid IntervalType`)
|
if (!enumCheckIntervalType(o.interval)) return new Error(`${path}.interval: is not a valid IntervalType`)
|
||||||
if (opts.interval_CustomCheck && !opts.interval_CustomCheck(o.interval)) return new Error(`${path}.interval: custom check failed`)
|
if (opts.interval_CustomCheck && !opts.interval_CustomCheck(o.interval)) return new Error(`${path}.interval: custom check failed`)
|
||||||
|
|
||||||
|
|
@ -1419,14 +1424,12 @@ export const LinkNPubThroughTokenRequestValidate = (o?: LinkNPubThroughTokenRequ
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LiveDebitRequest = {
|
export type LiveDebitRequest = {
|
||||||
amount: number
|
|
||||||
debit: LiveDebitRequest_debit
|
debit: LiveDebitRequest_debit
|
||||||
npub: string
|
npub: string
|
||||||
}
|
}
|
||||||
export const LiveDebitRequestOptionalFields: [] = []
|
export const LiveDebitRequestOptionalFields: [] = []
|
||||||
export type LiveDebitRequestOptions = OptionsBaseMessage & {
|
export type LiveDebitRequestOptions = OptionsBaseMessage & {
|
||||||
checkOptionalsAreSet?: []
|
checkOptionalsAreSet?: []
|
||||||
amount_CustomCheck?: (v: number) => boolean
|
|
||||||
debit_Options?: LiveDebitRequest_debitOptions
|
debit_Options?: LiveDebitRequest_debitOptions
|
||||||
npub_CustomCheck?: (v: string) => boolean
|
npub_CustomCheck?: (v: string) => boolean
|
||||||
}
|
}
|
||||||
|
|
@ -1434,9 +1437,6 @@ export const LiveDebitRequestValidate = (o?: LiveDebitRequest, opts: LiveDebitRe
|
||||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
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')
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`)
|
|
||||||
if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`)
|
|
||||||
|
|
||||||
const debitErr = LiveDebitRequest_debitValidate(o.debit, opts.debit_Options, `${path}.debit`)
|
const debitErr = LiveDebitRequest_debitValidate(o.debit, opts.debit_Options, `${path}.debit`)
|
||||||
if (debitErr !== null) return debitErr
|
if (debitErr !== null) return debitErr
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -512,6 +512,7 @@ enum IntervalType {
|
||||||
message FrequencyRule {
|
message FrequencyRule {
|
||||||
int64 number_of_intervals = 1;
|
int64 number_of_intervals = 1;
|
||||||
IntervalType interval = 2;
|
IntervalType interval = 2;
|
||||||
|
int64 amount = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DebitRule {
|
message DebitRule {
|
||||||
|
|
@ -523,7 +524,6 @@ message DebitRule {
|
||||||
|
|
||||||
message LiveDebitRequest {
|
message LiveDebitRequest {
|
||||||
string npub = 1;
|
string npub = 1;
|
||||||
int64 amount = 2;
|
|
||||||
oneof debit {
|
oneof debit {
|
||||||
string invoice = 3;
|
string invoice = 3;
|
||||||
FrequencyRule frequency = 4;
|
FrequencyRule frequency = 4;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import Storage from '../storage/index.js'
|
||||||
import LND from "../lnd/lnd.js"
|
import LND from "../lnd/lnd.js"
|
||||||
import { ERROR, getLogger } from "../helpers/logger.js";
|
import { ERROR, getLogger } from "../helpers/logger.js";
|
||||||
import { DebitAccess, DebitAccessRules } from '../storage/entity/DebitAccess.js';
|
import { DebitAccess, DebitAccessRules } from '../storage/entity/DebitAccess.js';
|
||||||
|
import paymentManager from './paymentManager.js';
|
||||||
|
import { Application } from '../storage/entity/Application.js';
|
||||||
|
import { ApplicationUser } from '../storage/entity/ApplicationUser.js';
|
||||||
export const expirationRuleName = 'expiration'
|
export const expirationRuleName = 'expiration'
|
||||||
export const frequencyRuleName = 'frequency'
|
export const frequencyRuleName = 'frequency'
|
||||||
type RecurringDebitTimeUnit = 'day' | 'week' | 'month'
|
type RecurringDebitTimeUnit = 'day' | 'week' | 'month'
|
||||||
|
|
@ -16,8 +19,78 @@ const unitToIntervalType = (unit: RecurringDebitTimeUnit) => {
|
||||||
case 'month': return Types.IntervalType.MONTH
|
case 'month': return Types.IntervalType.MONTH
|
||||||
default: throw new Error("invalid unit")
|
default: throw new Error("invalid unit")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
const intervalTypeToUnit = (interval: Types.IntervalType): RecurringDebitTimeUnit => {
|
||||||
|
switch (interval) {
|
||||||
|
case Types.IntervalType.DAY: return 'day'
|
||||||
|
case Types.IntervalType.WEEK: return 'week'
|
||||||
|
case Types.IntervalType.MONTH: return 'month'
|
||||||
|
default: throw new Error("invalid interval")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const IntervalTypeToSeconds = (interval: Types.IntervalType) => {
|
||||||
|
switch (interval) {
|
||||||
|
case Types.IntervalType.DAY: return 24 * 60 * 60
|
||||||
|
case Types.IntervalType.WEEK: return 7 * 24 * 60 * 60
|
||||||
|
case Types.IntervalType.MONTH: return 30 * 24 * 60 * 60
|
||||||
|
default: throw new Error("invalid interval")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const debitRulesToDebitAccessRules = (rule: Types.DebitRule[]): DebitAccessRules | undefined => {
|
||||||
|
let rules: DebitAccessRules | undefined = undefined
|
||||||
|
rule.forEach(r => {
|
||||||
|
if (!rules) {
|
||||||
|
rules = {}
|
||||||
|
}
|
||||||
|
const { rule } = r
|
||||||
|
switch (rule.type) {
|
||||||
|
case Types.DebitRule_rule_type.EXPIRATION_RULE:
|
||||||
|
|
||||||
|
rules[expirationRuleName] = [rule.expiration_rule.expires_at_unix.toString()]
|
||||||
|
break
|
||||||
|
case Types.DebitRule_rule_type.FREQUENCY_RULE:
|
||||||
|
const intervals = rule.frequency_rule.number_of_intervals.toString()
|
||||||
|
const unit = intervalTypeToUnit(rule.frequency_rule.interval)
|
||||||
|
return { key: frequencyRuleName, val: [intervals, unit, rule.frequency_rule.amount.toString()] }
|
||||||
|
default:
|
||||||
|
throw new Error("invalid rule")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
const debitAccessRulesToDebitRules = (rules: DebitAccessRules | null): Types.DebitRule[] => {
|
||||||
|
if (!rules) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return Object.entries(rules).map(([key, val]) => {
|
||||||
|
switch (key) {
|
||||||
|
case expirationRuleName:
|
||||||
|
return {
|
||||||
|
rule: {
|
||||||
|
type: Types.DebitRule_rule_type.EXPIRATION_RULE,
|
||||||
|
expiration_rule: {
|
||||||
|
expires_at_unix: +val[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case frequencyRuleName:
|
||||||
|
return {
|
||||||
|
rule: {
|
||||||
|
type: Types.DebitRule_rule_type.FREQUENCY_RULE,
|
||||||
|
frequency_rule: {
|
||||||
|
number_of_intervals: +val[0],
|
||||||
|
interval: unitToIntervalType(val[1] as RecurringDebitTimeUnit),
|
||||||
|
amount: +val[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error("invalid rule")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export type NdebitData = { pointer?: string, amount_sats: number } & (RecurringDebit | { bolt11: string })
|
export type NdebitData = { pointer?: string, amount_sats: number } & (RecurringDebit | { bolt11: string })
|
||||||
export type NdebitSuccess = { res: 'ok' }
|
export type NdebitSuccess = { res: 'ok' }
|
||||||
export type NdebitSuccessPayment = { res: 'ok', preimage: string }
|
export type NdebitSuccessPayment = { res: 'ok', preimage: string }
|
||||||
|
|
@ -31,14 +104,15 @@ const nip68errs = {
|
||||||
6: "Invalid Request",
|
6: "Invalid Request",
|
||||||
}
|
}
|
||||||
type HandleNdebitRes = { status: 'fail', debitRes: NdebitFailure }
|
type HandleNdebitRes = { status: 'fail', debitRes: NdebitFailure }
|
||||||
| { status: 'invoicePaid', op: Types.UserOperation, appUserId: string, debitRes: NdebitSuccessPayment }
|
| { status: 'invoicePaid', op: Types.UserOperation, app: Application, appUser: ApplicationUser, debitRes: NdebitSuccessPayment }
|
||||||
| { status: 'authRequired', liveDebitReq: Types.LiveDebitRequest, appUserId: string }
|
| { status: 'authRequired', liveDebitReq: Types.LiveDebitRequest, app: Application, appUser: ApplicationUser }
|
||||||
| { status: 'authOk', debitRes: NdebitSuccess }
|
| { status: 'authOk', debitRes: NdebitSuccess }
|
||||||
export class DebitManager {
|
export class DebitManager {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
applicationManager: ApplicationManager
|
applicationManager: ApplicationManager
|
||||||
|
|
||||||
storage: Storage
|
storage: Storage
|
||||||
lnd: LND
|
lnd: LND
|
||||||
logger = getLogger({ component: 'DebitManager' })
|
logger = getLogger({ component: 'DebitManager' })
|
||||||
|
|
@ -49,12 +123,16 @@ export class DebitManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthorizeDebit = async (ctx: Types.UserContext, req: Types.DebitAuthorizationRequest): Promise<Types.DebitAuthorization> => {
|
AuthorizeDebit = async (ctx: Types.UserContext, req: Types.DebitAuthorizationRequest): Promise<Types.DebitAuthorization> => {
|
||||||
const access = await this.storage.debitStorage.AddDebitAccess(ctx.app_user_id, req.authorize_npub)
|
const access = await this.storage.debitStorage.AddDebitAccess(ctx.app_user_id, {
|
||||||
|
authorize: true,
|
||||||
|
npub: req.authorize_npub,
|
||||||
|
rules: debitRulesToDebitAccessRules(req.rules)
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
debit_id: access.serial_id.toString(),
|
debit_id: access.serial_id.toString(),
|
||||||
npub: req.authorize_npub,
|
npub: req.authorize_npub,
|
||||||
authorized: true,
|
authorized: true,
|
||||||
rules: []
|
rules: req.rules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +142,7 @@ export class DebitManager {
|
||||||
debit_id: access.serial_id.toString(),
|
debit_id: access.serial_id.toString(),
|
||||||
authorized: access.authorized,
|
authorized: access.authorized,
|
||||||
npub: access.npub,
|
npub: access.npub,
|
||||||
rules: []
|
rules: debitAccessRulesToDebitRules(access.rules)
|
||||||
}))
|
}))
|
||||||
return { debits }
|
return { debits }
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +170,8 @@ export class DebitManager {
|
||||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||||
}
|
}
|
||||||
const appUserId = pointer
|
const appUserId = pointer
|
||||||
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
|
const appUser = await this.storage.applicationStorage.GetApplicationUser(app, appUserId)
|
||||||
const pointerFreq = pointerdata as RecurringDebit
|
const pointerFreq = pointerdata as RecurringDebit
|
||||||
if (pointerFreq.frequency) {
|
if (pointerFreq.frequency) {
|
||||||
if (!amount_sats) {
|
if (!amount_sats) {
|
||||||
|
|
@ -100,14 +180,14 @@ export class DebitManager {
|
||||||
const debitAccess = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
const debitAccess = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
||||||
if (!debitAccess) {
|
if (!debitAccess) {
|
||||||
return {
|
return {
|
||||||
status: 'authRequired', appUserId, liveDebitReq: {
|
status: 'authRequired', app, appUser, liveDebitReq: {
|
||||||
amount: pointerdata.amount_sats,
|
|
||||||
npub: requestorPub,
|
npub: requestorPub,
|
||||||
debit: {
|
debit: {
|
||||||
type: Types.LiveDebitRequest_debit_type.FREQUENCY,
|
type: Types.LiveDebitRequest_debit_type.FREQUENCY,
|
||||||
frequency: {
|
frequency: {
|
||||||
interval: unitToIntervalType(pointerFreq.frequency.unit),
|
interval: unitToIntervalType(pointerFreq.frequency.unit),
|
||||||
number_of_intervals: pointerFreq.frequency.number,
|
number_of_intervals: pointerFreq.frequency.number,
|
||||||
|
amount: pointerdata.amount_sats,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,8 +212,7 @@ export class DebitManager {
|
||||||
const authorization = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
const authorization = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
||||||
if (!authorization) {
|
if (!authorization) {
|
||||||
return {
|
return {
|
||||||
status: 'authRequired', appUserId, liveDebitReq: {
|
status: 'authRequired', app, appUser, liveDebitReq: {
|
||||||
amount: pointerdata.amount_sats,
|
|
||||||
npub: requestorPub,
|
npub: requestorPub,
|
||||||
debit: {
|
debit: {
|
||||||
type: Types.LiveDebitRequest_debit_type.INVOICE,
|
type: Types.LiveDebitRequest_debit_type.INVOICE,
|
||||||
|
|
@ -145,33 +224,46 @@ export class DebitManager {
|
||||||
if (!authorization.authorized) {
|
if (!authorization.authorized) {
|
||||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||||
}
|
}
|
||||||
await this.validateAccessRules(authorization)
|
await this.validateAccessRules(authorization, app, appUser)
|
||||||
const payment = await this.applicationManager.PayAppUserInvoice(appId, { amount: 0, invoice: bolt11, user_identifier: appUserId })
|
const payment = await this.applicationManager.PayAppUserInvoice(appId, { amount: 0, invoice: bolt11, user_identifier: appUserId })
|
||||||
await this.storage.debitStorage.IncrementDebitAccess(appUserId, requestorPub, payment.amount_paid + payment.service_fee + payment.network_fee)
|
await this.storage.debitStorage.IncrementDebitAccess(appUserId, requestorPub, payment.amount_paid + payment.service_fee + payment.network_fee)
|
||||||
const op = this.newPaymentOperation(payment, bolt11)
|
const op = this.newPaymentOperation(payment, bolt11)
|
||||||
return { status: 'invoicePaid', op, appUserId, debitRes: { res: 'ok', preimage: payment.preimage } }
|
return { status: 'invoicePaid', op, app, appUser, debitRes: { res: 'ok', preimage: payment.preimage } }
|
||||||
}
|
}
|
||||||
|
|
||||||
validateAccessRules = async (access: DebitAccess): Promise<boolean> => {
|
validateAccessRules = async (access: DebitAccess, app: Application, appUser: ApplicationUser): Promise<boolean> => {
|
||||||
const { rules } = access
|
const { rules } = access
|
||||||
if (!rules) {
|
if (!rules) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
if (rules[expirationRuleName]) {
|
||||||
// TODO: rules validation
|
const [expiration] = rules[expirationRuleName]
|
||||||
/* if (rules[expirationRuleName]) {
|
if (+expiration < Date.now()) {
|
||||||
|
await this.storage.debitStorage.RemoveDebitAccess(access.app_user_id, access.npub)
|
||||||
}
|
return false
|
||||||
if (rules[frequencyRuleName]) {
|
}
|
||||||
|
}
|
||||||
} */
|
if (rules[frequencyRuleName]) {
|
||||||
|
const [number, unit, max] = rules[frequencyRuleName]
|
||||||
|
const intervalType = unitToIntervalType(unit as RecurringDebitTimeUnit)
|
||||||
|
const seconds = IntervalTypeToSeconds(intervalType) * (+number)
|
||||||
|
const sinceUnix = Math.floor(Date.now() / 1000) * seconds
|
||||||
|
const payments = await this.storage.paymentStorage.GetUserDebitPayments(appUser.user.user_id, sinceUnix, access.npub)
|
||||||
|
let total = 0
|
||||||
|
for (const payment of payments) {
|
||||||
|
total += payment.paid_amount
|
||||||
|
}
|
||||||
|
if (total > +max) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
newPaymentOperation = (payment: Types.PayInvoiceResponse, bolt11: string) => {
|
newPaymentOperation = (payment: Types.PayInvoiceResponse, bolt11: string) => {
|
||||||
return {
|
return {
|
||||||
amount: payment.amount_paid,
|
amount: payment.amount_paid,
|
||||||
paidAtUnix: Date.now() / 1000,
|
paidAtUnix: Math.floor(Date.now() / 1000),
|
||||||
inbound: false,
|
inbound: false,
|
||||||
type: Types.UserOperationType.OUTGOING_INVOICE,
|
type: Types.UserOperationType.OUTGOING_INVOICE,
|
||||||
identifier: bolt11,
|
identifier: bolt11,
|
||||||
|
|
|
||||||
|
|
@ -328,8 +328,7 @@ export default class {
|
||||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const app = await this.storage.applicationStorage.GetApplication(event.appId)
|
const { appUser } = res
|
||||||
const appUser = await this.storage.applicationStorage.GetApplicationUser(app, res.appUserId)
|
|
||||||
if (res.status === 'authRequired') {
|
if (res.status === 'authRequired') {
|
||||||
const message: Types.LiveDebitRequest & { requestId: string, status: 'OK' } = { ...res.liveDebitReq, requestId: "GetLiveDebitRequests", status: 'OK' }
|
const message: Types.LiveDebitRequest & { requestId: string, status: 'OK' } = { ...res.liveDebitReq, requestId: "GetLiveDebitRequests", status: 'OK' }
|
||||||
if (appUser.nostr_public_key) {// TODO - fix before support for http streams
|
if (appUser.nostr_public_key) {// TODO - fix before support for http streams
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@ import { DataSource, EntityManager } from "typeorm"
|
||||||
import UserStorage from './userStorage.js';
|
import UserStorage from './userStorage.js';
|
||||||
import TransactionsQueue from "./transactionsQueue.js";
|
import TransactionsQueue from "./transactionsQueue.js";
|
||||||
import { DebitAccess, DebitAccessRules } from "./entity/DebitAccess.js";
|
import { DebitAccess, DebitAccessRules } from "./entity/DebitAccess.js";
|
||||||
|
type AccessToAdd = {
|
||||||
|
npub: string
|
||||||
|
rules?: DebitAccessRules
|
||||||
|
authorize: boolean
|
||||||
|
}
|
||||||
export default class {
|
export default class {
|
||||||
DB: DataSource | EntityManager
|
DB: DataSource | EntityManager
|
||||||
txQueue: TransactionsQueue
|
txQueue: TransactionsQueue
|
||||||
|
|
@ -10,11 +15,12 @@ export default class {
|
||||||
this.txQueue = txQueue
|
this.txQueue = txQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
async AddDebitAccess(appUserId: string, pubToAuthorize: string, authorize = true, entityManager = this.DB) {
|
async AddDebitAccess(appUserId: string, access: AccessToAdd, entityManager = this.DB) {
|
||||||
const entry = entityManager.getRepository(DebitAccess).create({
|
const entry = entityManager.getRepository(DebitAccess).create({
|
||||||
app_user_id: appUserId,
|
app_user_id: appUserId,
|
||||||
npub: pubToAuthorize,
|
npub: access.npub,
|
||||||
authorized: authorize,
|
authorized: access.authorize,
|
||||||
|
rules: access.rules,
|
||||||
})
|
})
|
||||||
return this.txQueue.PushToQueue<DebitAccess>({ exec: async db => db.getRepository(DebitAccess).save(entry), dbTx: false })
|
return this.txQueue.PushToQueue<DebitAccess>({ exec: async db => db.getRepository(DebitAccess).save(entry), dbTx: false })
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +47,7 @@ export default class {
|
||||||
async DenyDebitAccess(appUserId: string, pub: string) {
|
async DenyDebitAccess(appUserId: string, pub: string) {
|
||||||
const access = await this.GetDebitAccess(appUserId, pub)
|
const access = await this.GetDebitAccess(appUserId, pub)
|
||||||
if (!access) {
|
if (!access) {
|
||||||
await this.AddDebitAccess(appUserId, pub, false)
|
await this.AddDebitAccess(appUserId, { npub: pub, authorize: false })
|
||||||
}
|
}
|
||||||
await this.UpdateDebitAccess(appUserId, pub, false)
|
await this.UpdateDebitAccess(appUserId, pub, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ export class UserInvoicePayment {
|
||||||
})
|
})
|
||||||
paymentIndex: number
|
paymentIndex: number
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
debit_to_pub: string
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
created_at: Date
|
created_at: Date
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThan, MoreThanOrEqual } from "typeorm"
|
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThan, MoreThanOrEqual, Not } from "typeorm"
|
||||||
import { User } from './entity/User.js';
|
import { User } from './entity/User.js';
|
||||||
import { UserTransactionPayment } from './entity/UserTransactionPayment.js';
|
import { UserTransactionPayment } from './entity/UserTransactionPayment.js';
|
||||||
import { EphemeralKeyType, UserEphemeralKey } from './entity/UserEphemeralKey.js';
|
import { EphemeralKeyType, UserEphemeralKey } from './entity/UserEphemeralKey.js';
|
||||||
|
|
@ -154,9 +154,9 @@ export default class {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async AddPendingExternalPayment(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, networkFee: number }, linkedApplication: Application, liquidityProvider: string | undefined,dbTx:DataSource|EntityManager): Promise<UserInvoicePayment> {
|
async AddPendingExternalPayment(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, networkFee: number }, linkedApplication: Application, liquidityProvider: string | undefined, dbTx: DataSource | EntityManager): Promise<UserInvoicePayment> {
|
||||||
const newPayment = dbTx.getRepository(UserInvoicePayment).create({
|
const newPayment = dbTx.getRepository(UserInvoicePayment).create({
|
||||||
user: await this.userStorage.GetUser(userId,dbTx),
|
user: await this.userStorage.GetUser(userId, dbTx),
|
||||||
paid_amount: amounts.payAmount,
|
paid_amount: amounts.payAmount,
|
||||||
invoice,
|
invoice,
|
||||||
routing_fees: amounts.networkFee,
|
routing_fees: amounts.networkFee,
|
||||||
|
|
@ -215,6 +215,25 @@ export default class {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetUserDebitPayments(userId: string, sinceUnix: number, debitToNpub: string, entityManager = this.DB): Promise<UserInvoicePayment[]> {
|
||||||
|
const pending = {
|
||||||
|
user: { user_id: userId },
|
||||||
|
debit_to_pub: debitToNpub,
|
||||||
|
paid_at_unix: 0,
|
||||||
|
}
|
||||||
|
const paid = {
|
||||||
|
user: { user_id: userId },
|
||||||
|
debit_to_pub: debitToNpub,
|
||||||
|
paid_at_unix: MoreThan(sinceUnix),
|
||||||
|
}
|
||||||
|
return entityManager.getRepository(UserInvoicePayment).find({
|
||||||
|
where: [pending, paid],
|
||||||
|
order: {
|
||||||
|
paid_at_unix: 'DESC'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, internal: boolean, height: number, linkedApplication: Application): Promise<UserTransactionPayment> {
|
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, internal: boolean, height: number, linkedApplication: Application): Promise<UserTransactionPayment> {
|
||||||
const newTx = this.DB.getRepository(UserTransactionPayment).create({
|
const newTx = this.DB.getRepository(UserTransactionPayment).create({
|
||||||
user: await this.userStorage.GetUser(userId),
|
user: await this.userStorage.GetUser(userId),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue