fees
This commit is contained in:
parent
9555b7be51
commit
80933b1ecf
6 changed files with 95 additions and 41 deletions
20
env.example
20
env.example
|
|
@ -3,14 +3,22 @@ LND_CERT_PATH=C:\Users\user\.polar\networks\2\volumes\lnd\alice\tls.cert
|
||||||
LND_MACAROON_PATH=C:\Users\user\.polar\networks\2\volumes\lnd\alice\data\chain\bitcoin\regtest\admin.macaroon
|
LND_MACAROON_PATH=C:\Users\user\.polar\networks\2\volumes\lnd\alice\data\chain\bitcoin\regtest\admin.macaroon
|
||||||
DATABASE_FILE=db.sqlite
|
DATABASE_FILE=db.sqlite
|
||||||
JWT_SECRET=bigsecrethere
|
JWT_SECRET=bigsecrethere
|
||||||
LIMIT_FEE_RATE_MILLISATS=6
|
OUTBOUND_MAX_FEE_BPS=60
|
||||||
LIMIT_FEE_FIXED_SATS=100
|
OUTBOUND_MAX_FEE_EXTRA_SATS=100
|
||||||
SERVICE_FEE_INCOMING_TX_PERCENT=0
|
INCOMING_CHAIN_FEE_ROOT_BPS=0
|
||||||
SERVICE_FEE_OUTGOING_TX_PERCENT=0
|
OUTGOING_CHAIN_FEE_ROOT_BPS=60 #this is applied only to withdrawls from application wallets
|
||||||
SERVICE_FEE_INCOMING_INVOICE_PERCENT=0
|
INCOMING_INVOICE_FEE_ROOT_BPS=0
|
||||||
SERVICE_FEE_OUTGOING_INVOICE_PERCENT=0
|
OUTGOING_INVOICE_FEE_ROOT_BPS=60 #this is applied only to withdrawals from application wallets
|
||||||
|
INCOMING_INVOICE_FEE_USER_BPS=0 #defined by app this is just default
|
||||||
|
OUTGOING_INVOICE_FEE_USER_BPS=60 #defined by app this is just default
|
||||||
|
TX_FEE_INTERNAL_ROOT_BPS=60 #this is applied only to withdrawls from application wallets
|
||||||
|
TX_FEE_INTERNAL_USER_BPS=60 #defined by app this is just default
|
||||||
NOSTR_PUBLIC_KEY=
|
NOSTR_PUBLIC_KEY=
|
||||||
NOSTR_PRIVATE_KEY=
|
NOSTR_PRIVATE_KEY=
|
||||||
NOSTR_RELAYS=wss://nostr-relay.wlvs.space
|
NOSTR_RELAYS=wss://nostr-relay.wlvs.space
|
||||||
NOSTR_ALLOWED_PUBS=
|
NOSTR_ALLOWED_PUBS=
|
||||||
SERVICE_URL=http://localhost:8080
|
SERVICE_URL=http://localhost:8080
|
||||||
|
ADMIN_TOKEN=
|
||||||
|
ALLOW_BALANCE_MIGRATION=false
|
||||||
|
PORT=8080
|
||||||
|
MOCK_LND=false
|
||||||
|
|
@ -8,8 +8,8 @@ export const LoadLndSettingsFromEnv = (test = false): LndSettings => {
|
||||||
const lndAddr = EnvMustBeNonEmptyString("LND_ADDRESS")
|
const lndAddr = EnvMustBeNonEmptyString("LND_ADDRESS")
|
||||||
const lndCertPath = EnvMustBeNonEmptyString("LND_CERT_PATH")
|
const lndCertPath = EnvMustBeNonEmptyString("LND_CERT_PATH")
|
||||||
const lndMacaroonPath = EnvMustBeNonEmptyString("LND_MACAROON_PATH")
|
const lndMacaroonPath = EnvMustBeNonEmptyString("LND_MACAROON_PATH")
|
||||||
const feeRateLimit = EnvMustBeInteger("LIMIT_FEE_RATE_MILLISATS") / 1000
|
const feeRateLimit = EnvMustBeInteger("OUTBOUND_MAX_FEE_BPS") / 10000
|
||||||
const feeFixedLimit = EnvMustBeInteger("LIMIT_FEE_FIXED_SATS")
|
const feeFixedLimit = EnvMustBeInteger("OUTBOUND_MAX_FEE_EXTRA_SATS")
|
||||||
const mockLnd = EnvCanBeBoolean("MOCK_LND")
|
const mockLnd = EnvCanBeBoolean("MOCK_LND")
|
||||||
return { lndAddr, lndCertPath, lndMacaroonPath, feeRateLimit, feeFixedLimit, mockLnd }
|
return { lndAddr, lndCertPath, lndMacaroonPath, feeRateLimit, feeFixedLimit, mockLnd }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ export default class {
|
||||||
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)
|
max_withdrawable: this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,9 +91,10 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
async AddAppUserInvoice(appId: string, req: Types.AddAppUserInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
async AddAppUserInvoice(appId: string, req: Types.AddAppUserInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
||||||
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
const receiver = await this.storage.applicationStorage.GetApplicationUser(appId, req.receiver_identifier)
|
const receiver = await this.storage.applicationStorage.GetApplicationUser(appId, req.receiver_identifier)
|
||||||
const payer = await this.storage.applicationStorage.GetOrCreateApplicationUser(appId, req.payer_identifier, 0)
|
const payer = await this.storage.applicationStorage.GetOrCreateApplicationUser(appId, req.payer_identifier, 0)
|
||||||
const opts: InboundOptionals = { callbackUrl: req.http_callback_url, expiry: defaultInvoiceExpiry, expectedPayer: payer.user }
|
const opts: InboundOptionals = { callbackUrl: req.http_callback_url, expiry: defaultInvoiceExpiry, expectedPayer: payer.user, linkedApplication: app }
|
||||||
const appUserInvoice = await this.paymentManager.NewInvoice(receiver.user.user_id, req.invoice_req, opts)
|
const appUserInvoice = await this.paymentManager.NewInvoice(receiver.user.user_id, req.invoice_req, opts)
|
||||||
return {
|
return {
|
||||||
invoice: appUserInvoice.invoice
|
invoice: appUserInvoice.invoice
|
||||||
|
|
@ -102,7 +103,7 @@ export default class {
|
||||||
|
|
||||||
async GetAppUser(appId: string, req: Types.GetAppUserRequest): Promise<Types.AppUser> {
|
async GetAppUser(appId: string, req: Types.GetAppUserRequest): Promise<Types.AppUser> {
|
||||||
const user = await this.storage.applicationStorage.GetApplicationUser(appId, req.user_identifier)
|
const user = await this.storage.applicationStorage.GetApplicationUser(appId, req.user_identifier)
|
||||||
const max = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats)
|
const max = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats, true)
|
||||||
console.log(max, user.user.balance_sats)
|
console.log(max, user.user.balance_sats)
|
||||||
return {
|
return {
|
||||||
max_withdrawable: max, identifier: req.user_identifier, info: {
|
max_withdrawable: max, identifier: req.user_identifier, info: {
|
||||||
|
|
@ -127,7 +128,7 @@ export default class {
|
||||||
async SendAppUserToAppPayment(appId: string, req: Types.SendAppUserToAppPaymentRequest): Promise<void> {
|
async SendAppUserToAppPayment(appId: string, req: Types.SendAppUserToAppPaymentRequest): Promise<void> {
|
||||||
const fromUser = await this.storage.applicationStorage.GetApplicationUser(appId, req.from_user_identifier)
|
const fromUser = await this.storage.applicationStorage.GetApplicationUser(appId, req.from_user_identifier)
|
||||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
await this.paymentManager.SendUserToUserPayment(fromUser.user.user_id, app.owner.user_id, req.amount)
|
await this.paymentManager.SendUserToUserPayment(fromUser.user.user_id, app.owner.user_id, req.amount, app)
|
||||||
}
|
}
|
||||||
async GetAppUserLNURLInfo(appId: string, req: Types.GetAppUserLNURLInfoRequest): Promise<Types.LnurlPayInfoResponse> {
|
async GetAppUserLNURLInfo(appId: string, req: Types.GetAppUserLNURLInfoRequest): Promise<Types.LnurlPayInfoResponse> {
|
||||||
const user = await this.storage.applicationStorage.GetApplicationUser(appId, req.user_identifier)
|
const user = await this.storage.applicationStorage.GetApplicationUser(appId, req.user_identifier)
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,14 @@ export const LoadMainSettingsFromEnv = (test = false): MainSettings => {
|
||||||
lndSettings: LoadLndSettingsFromEnv(test),
|
lndSettings: LoadLndSettingsFromEnv(test),
|
||||||
storageSettings: LoadStorageSettingsFromEnv(test),
|
storageSettings: LoadStorageSettingsFromEnv(test),
|
||||||
jwtSecret: EnvMustBeNonEmptyString("JWT_SECRET"),
|
jwtSecret: EnvMustBeNonEmptyString("JWT_SECRET"),
|
||||||
incomingTxFee: EnvMustBeInteger("SERVICE_FEE_INCOMING_TX_PERCENT") / 100,
|
incomingTxFee: EnvMustBeInteger("INCOMING_CHAIN_FEE_ROOT_BPS") / 10000,
|
||||||
outgoingTxFee: EnvMustBeInteger("SERVICE_FEE_OUTGOING_TX_PERCENT") / 100,
|
outgoingTxFee: EnvMustBeInteger("OUTGOING_CHAIN_FEE_ROOT_BPS") / 10000,
|
||||||
incomingInvoiceFee: EnvMustBeInteger("SERVICE_FEE_INCOMING_INVOICE_PERCENT") / 100,
|
incomingAppInvoiceFee: EnvMustBeInteger("INCOMING_INVOICE_FEE_ROOT_BPS") / 10000,
|
||||||
outgoingInvoiceFee: EnvMustBeInteger("SERVICE_FEE_OUTGOING_INVOICE_PERCENT") / 100,
|
outgoingAppInvoiceFee: EnvMustBeInteger("OUTGOING_INVOICE_FEE_ROOT_BPS") / 10000,
|
||||||
userToUserFee: EnvMustBeInteger("SERVICE_FEE_USER_TO_USER_PERCENT") / 100,
|
incomingAppUserInvoiceFee: EnvMustBeInteger("INCOMING_INVOICE_FEE_USER_BPS") / 10000,
|
||||||
|
outgoingAppUserInvoiceFee: EnvMustBeInteger("OUTGOING_INVOICE_FEE_USER_BPS") / 10000,
|
||||||
|
userToUserFee: EnvMustBeInteger("TX_FEE_INTERNAL_USER_BPS") / 10000,
|
||||||
|
appToUserFee: EnvMustBeInteger("TX_FEE_INTERNAL_ROOT_BPS") / 10000,
|
||||||
serviceUrl: EnvMustBeNonEmptyString("SERVICE_URL"),
|
serviceUrl: EnvMustBeNonEmptyString("SERVICE_URL"),
|
||||||
servicePort: EnvMustBeInteger("PORT")
|
servicePort: EnvMustBeInteger("PORT")
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +60,7 @@ export default class {
|
||||||
this.storage.StartTransaction(async tx => {
|
this.storage.StartTransaction(async tx => {
|
||||||
const userAddress = await this.storage.paymentStorage.GetAddressOwner(address, tx)
|
const userAddress = await this.storage.paymentStorage.GetAddressOwner(address, tx)
|
||||||
if (!userAddress) { return }
|
if (!userAddress) { return }
|
||||||
const fee = this.paymentManager.getServiceFee(Types.UserOperationType.INCOMING_TX, amount)
|
const fee = this.paymentManager.getServiceFee(Types.UserOperationType.INCOMING_TX, amount, false)
|
||||||
try {
|
try {
|
||||||
// This call will fail if the transaction is already registered
|
// This call will fail if the transaction is already registered
|
||||||
const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, tx)
|
const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, tx)
|
||||||
|
|
@ -72,12 +75,21 @@ export default class {
|
||||||
this.storage.StartTransaction(async tx => {
|
this.storage.StartTransaction(async tx => {
|
||||||
const userInvoice = await this.storage.paymentStorage.GetInvoiceOwner(paymentRequest, tx)
|
const userInvoice = await this.storage.paymentStorage.GetInvoiceOwner(paymentRequest, tx)
|
||||||
if (!userInvoice || userInvoice.paid_at_unix > 0) { return }
|
if (!userInvoice || userInvoice.paid_at_unix > 0) { return }
|
||||||
const fee = this.paymentManager.getServiceFee(Types.UserOperationType.INCOMING_INVOICE, amount)
|
if (!userInvoice.linkedApplication) {
|
||||||
|
console.error("an invoice was paid, that has no linked application")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const isAppUserPayment = userInvoice.user.user_id !== userInvoice.linkedApplication.owner.user_id
|
||||||
|
let fee = this.paymentManager.getServiceFee(Types.UserOperationType.INCOMING_INVOICE, amount, isAppUserPayment)
|
||||||
|
if (userInvoice.linkedApplication && userInvoice.linkedApplication.owner.user_id === userInvoice.user.user_id) {
|
||||||
|
fee = 0
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// This call will fail if the invoice is already registered
|
// This call will fail if the invoice is already registered
|
||||||
await this.storage.paymentStorage.FlagInvoiceAsPaid(userInvoice, amount, fee, tx)
|
await this.storage.paymentStorage.FlagInvoiceAsPaid(userInvoice, amount, fee, tx)
|
||||||
|
|
||||||
await this.storage.userStorage.IncrementUserBalance(userInvoice.user.user_id, amount - fee, tx)
|
await this.storage.userStorage.IncrementUserBalance(userInvoice.user.user_id, amount - fee, tx)
|
||||||
if (userInvoice.linkedApplication) {
|
if (isAppUserPayment && fee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(userInvoice.linkedApplication.owner.user_id, fee, tx)
|
await this.storage.userStorage.IncrementUserBalance(userInvoice.linkedApplication.owner.user_id, fee, tx)
|
||||||
}
|
}
|
||||||
await this.triggerPaidCallback(userInvoice.callbackUrl)
|
await this.triggerPaidCallback(userInvoice.callbackUrl)
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,27 @@ export default class {
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
this.lnd = lnd
|
this.lnd = lnd
|
||||||
}
|
}
|
||||||
getServiceFee(action: Types.UserOperationType, amount: number): number {
|
getServiceFee(action: Types.UserOperationType, amount: number, appUser: boolean): number {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Types.UserOperationType.INCOMING_TX:
|
case Types.UserOperationType.INCOMING_TX:
|
||||||
return Math.ceil(this.settings.incomingTxFee * amount)
|
return Math.ceil(this.settings.incomingTxFee * amount)
|
||||||
case Types.UserOperationType.OUTGOING_TX:
|
case Types.UserOperationType.OUTGOING_TX:
|
||||||
return Math.ceil(this.settings.outgoingTxFee * amount)
|
return Math.ceil(this.settings.outgoingTxFee * amount)
|
||||||
case Types.UserOperationType.INCOMING_INVOICE:
|
case Types.UserOperationType.INCOMING_INVOICE:
|
||||||
return Math.ceil(this.settings.incomingInvoiceFee * amount)
|
if (appUser) {
|
||||||
|
return Math.ceil(this.settings.incomingAppUserInvoiceFee * amount)
|
||||||
|
}
|
||||||
|
return Math.ceil(this.settings.incomingAppInvoiceFee * amount)
|
||||||
case Types.UserOperationType.OUTGOING_INVOICE:
|
case Types.UserOperationType.OUTGOING_INVOICE:
|
||||||
return Math.ceil(this.settings.outgoingInvoiceFee * amount)
|
if (appUser) {
|
||||||
|
return Math.ceil(this.settings.outgoingAppUserInvoiceFee * amount)
|
||||||
|
}
|
||||||
|
return Math.ceil(this.settings.outgoingAppInvoiceFee * amount)
|
||||||
case Types.UserOperationType.OUTGOING_USER_TO_USER || Types.UserOperationType.INCOMING_USER_TO_USER:
|
case Types.UserOperationType.OUTGOING_USER_TO_USER || Types.UserOperationType.INCOMING_USER_TO_USER:
|
||||||
return Math.ceil(this.settings.userToUserFee * amount)
|
if (appUser) {
|
||||||
|
return Math.ceil(this.settings.userToUserFee * amount)
|
||||||
|
}
|
||||||
|
return Math.ceil(this.settings.appToUserFee * amount)
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown service action type")
|
throw new Error("Unknown service action type")
|
||||||
}
|
}
|
||||||
|
|
@ -86,8 +95,13 @@ export default class {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMaxPayableInvoice(balance: number): number {
|
GetMaxPayableInvoice(balance: number, appUser: boolean): number {
|
||||||
const maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.outgoingInvoiceFee)))
|
let maxWithinServiceFee = 0
|
||||||
|
if (appUser) {
|
||||||
|
maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.outgoingAppUserInvoiceFee)))
|
||||||
|
} else {
|
||||||
|
maxWithinServiceFee = Math.max(0, Math.floor(balance * (1 - this.settings.outgoingAppInvoiceFee)))
|
||||||
|
}
|
||||||
return this.lnd.GetMaxWithinLimit(maxWithinServiceFee)
|
return this.lnd.GetMaxWithinLimit(maxWithinServiceFee)
|
||||||
}
|
}
|
||||||
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
||||||
|
|
@ -105,17 +119,19 @@ export default class {
|
||||||
throw new Error("invoice has no value, an amount must be provided in the request")
|
throw new Error("invoice has no value, an amount must be provided in the request")
|
||||||
}
|
}
|
||||||
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
||||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount)
|
if (!linkedApplication) {
|
||||||
|
throw new Error("only application operations are supported") // TODO - make this check obsolete
|
||||||
|
}
|
||||||
|
const isAppUserPayment = userId !== linkedApplication.owner.user_id
|
||||||
|
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount, isAppUserPayment)
|
||||||
const totalAmountToDecrement = payAmount + serviceFee
|
const totalAmountToDecrement = payAmount + serviceFee
|
||||||
|
|
||||||
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||||
await this.lockUserWithMinBalance(userId, totalAmountToDecrement + routingFeeLimit)
|
await this.lockUserWithMinBalance(userId, totalAmountToDecrement + routingFeeLimit)
|
||||||
const payment = await this.lnd.PayInvoice(req.invoice, req.amount, routingFeeLimit)
|
const payment = await this.lnd.PayInvoice(req.invoice, req.amount, routingFeeLimit)
|
||||||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + Number(payment.feeSat))
|
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + Number(payment.feeSat))
|
||||||
if (linkedApplication) {
|
if (isAppUserPayment && serviceFee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, serviceFee)
|
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, serviceFee)
|
||||||
} else {
|
|
||||||
//const appOwner = await this.storage.applicationStorage.IsApplicationOwner(userId)
|
|
||||||
}
|
}
|
||||||
await this.storage.userStorage.UnlockUser(userId)
|
await this.storage.userStorage.UnlockUser(userId)
|
||||||
await this.storage.paymentStorage.AddUserInvoicePayment(userId, req.invoice, payAmount, Number(payment.feeSat), serviceFee)
|
await this.storage.paymentStorage.AddUserInvoicePayment(userId, req.invoice, payAmount, Number(payment.feeSat), serviceFee)
|
||||||
|
|
@ -125,12 +141,18 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async PayAddress(userId: string, req: Types.PayAddressRequest): Promise<Types.PayAddressResponse> {
|
async PayAddress(userId: string, req: Types.PayAddressRequest, linkedApplication?: Application): Promise<Types.PayAddressResponse> {
|
||||||
const estimate = await this.lnd.EstimateChainFees(req.address, req.amoutSats, req.targetConf)
|
const estimate = await this.lnd.EstimateChainFees(req.address, req.amoutSats, req.targetConf)
|
||||||
const satPerVByte = Number(estimate.satPerVbyte)
|
const satPerVByte = Number(estimate.satPerVbyte)
|
||||||
const chainFees = Number(estimate.feeSat)
|
const chainFees = Number(estimate.feeSat)
|
||||||
const total = req.amoutSats + chainFees
|
const total = req.amoutSats + chainFees
|
||||||
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, req.amoutSats)
|
if (!linkedApplication) {
|
||||||
|
throw new Error("only application operations are supported") // TODO - make this check obsolete
|
||||||
|
}
|
||||||
|
if (userId !== linkedApplication.owner.user_id) {
|
||||||
|
throw new Error("chain operations only supported for applications")
|
||||||
|
}
|
||||||
|
const serviceFee = this.getServiceFee(Types.UserOperationType.OUTGOING_INVOICE, req.amoutSats, false)
|
||||||
await this.lockUserWithMinBalance(userId, total + serviceFee)
|
await this.lockUserWithMinBalance(userId, total + serviceFee)
|
||||||
const payment = await this.lnd.PayAddress(req.address, req.amoutSats, satPerVByte)
|
const payment = await this.lnd.PayAddress(req.address, req.amoutSats, satPerVByte)
|
||||||
await this.storage.userStorage.DecrementUserBalance(userId, total + serviceFee)
|
await this.storage.userStorage.DecrementUserBalance(userId, total + serviceFee)
|
||||||
|
|
@ -154,7 +176,8 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
async GetLnurlWithdrawInfo(balanceCheckK1: string): Promise<Types.LnurlWithdrawInfoResponse> {
|
async GetLnurlWithdrawInfo(balanceCheckK1: string): Promise<Types.LnurlWithdrawInfoResponse> {
|
||||||
const key = await this.storage.paymentStorage.UseUserEphemeralKey(balanceCheckK1, 'balanceCheck')
|
throw new Error("LNURL withdraw currenlty not supported for non application users")
|
||||||
|
/*const key = await this.storage.paymentStorage.UseUserEphemeralKey(balanceCheckK1, 'balanceCheck')
|
||||||
const maxWithdrawable = this.GetMaxPayableInvoice(key.user.balance_sats)
|
const maxWithdrawable = this.GetMaxPayableInvoice(key.user.balance_sats)
|
||||||
const callbackK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'withdraw')
|
const callbackK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'withdraw')
|
||||||
const newBalanceCheckK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'balanceCheck')
|
const newBalanceCheckK1 = await this.storage.paymentStorage.AddUserEphemeralKey(key.user.user_id, 'balanceCheck')
|
||||||
|
|
@ -165,10 +188,10 @@ export default class {
|
||||||
defaultDescription: "lnurl withdraw from lightning.pub",
|
defaultDescription: "lnurl withdraw from lightning.pub",
|
||||||
k1: callbackK1.key,
|
k1: callbackK1.key,
|
||||||
maxWithdrawable: maxWithdrawable * 1000,
|
maxWithdrawable: maxWithdrawable * 1000,
|
||||||
minWithdrawable: 0,
|
minWithdrawable: 10000,
|
||||||
balanceCheck: this.balanceCheckUrl(newBalanceCheckK1.key),
|
balanceCheck: this.balanceCheckUrl(newBalanceCheckK1.key),
|
||||||
payLink: `${this.settings.serviceUrl}/api/guest/lnurl_pay/info?k1=${payInfoK1.key}`,
|
payLink: `${this.settings.serviceUrl}/api/guest/lnurl_pay/info?k1=${payInfoK1.key}`,
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
async HandleLnurlWithdraw(k1: string, invoice: string): Promise<void> {
|
async HandleLnurlWithdraw(k1: string, invoice: string): Promise<void> {
|
||||||
|
|
@ -188,7 +211,7 @@ export default class {
|
||||||
tag: 'payRequest',
|
tag: 'payRequest',
|
||||||
callback: `${url}?k1=${payK1.key}`,
|
callback: `${url}?k1=${payK1.key}`,
|
||||||
maxSendable: 10000000000,
|
maxSendable: 10000000000,
|
||||||
minSendable: 0,
|
minSendable: 10000,
|
||||||
metadata: defaultLnurlPayMetadata
|
metadata: defaultLnurlPayMetadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +223,7 @@ export default class {
|
||||||
tag: 'payRequest',
|
tag: 'payRequest',
|
||||||
callback: `${this.settings.serviceUrl}/api/guest/lnurl_pay/handle?k1=${payK1.key}`,
|
callback: `${this.settings.serviceUrl}/api/guest/lnurl_pay/handle?k1=${payK1.key}`,
|
||||||
maxSendable: 10000000,
|
maxSendable: 10000000,
|
||||||
minSendable: 0,
|
minSendable: 10000,
|
||||||
metadata: defaultLnurlPayMetadata
|
metadata: defaultLnurlPayMetadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,18 +286,25 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number, linkedApplication?: Application) {
|
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number, linkedApplication?: Application) {
|
||||||
|
if (!linkedApplication) {
|
||||||
|
throw new Error("only application operations are supported") // TODO - make this check obsolete
|
||||||
|
}
|
||||||
await this.storage.StartTransaction(async tx => {
|
await this.storage.StartTransaction(async tx => {
|
||||||
const fromUser = await this.storage.userStorage.GetUser(fromUserId, tx)
|
const fromUser = await this.storage.userStorage.GetUser(fromUserId, tx)
|
||||||
const toUser = await this.storage.userStorage.GetUser(toUserId, tx)
|
const toUser = await this.storage.userStorage.GetUser(toUserId, tx)
|
||||||
if (fromUser.balance_sats < amount) {
|
if (fromUser.balance_sats < amount) {
|
||||||
throw new Error("not enough balance to send user to user payment")
|
throw new Error("not enough balance to send user to user payment")
|
||||||
}
|
}
|
||||||
const fee = this.getServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount)
|
if (!linkedApplication) {
|
||||||
|
throw new Error("only application operations are supported") // TODO - make this check obsolete
|
||||||
|
}
|
||||||
|
const isAppUserPayment = fromUser.user_id !== linkedApplication.owner.user_id
|
||||||
|
let fee = this.getServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount, isAppUserPayment)
|
||||||
const toIncrement = amount - fee
|
const toIncrement = amount - fee
|
||||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, amount, tx)
|
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, amount, tx)
|
||||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, toIncrement, tx)
|
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, toIncrement, tx)
|
||||||
await this.storage.paymentStorage.AddUserToUserPayment(fromUserId, toUserId, amount, fee)
|
await this.storage.paymentStorage.AddUserToUserPayment(fromUserId, toUserId, amount, fee)
|
||||||
if (linkedApplication) {
|
if (isAppUserPayment && fee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee)
|
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ export type MainSettings = {
|
||||||
jwtSecret: string
|
jwtSecret: string
|
||||||
incomingTxFee: number
|
incomingTxFee: number
|
||||||
outgoingTxFee: number
|
outgoingTxFee: number
|
||||||
incomingInvoiceFee: number
|
incomingAppInvoiceFee: number
|
||||||
outgoingInvoiceFee: number
|
incomingAppUserInvoiceFee: number
|
||||||
|
outgoingAppInvoiceFee: number
|
||||||
|
outgoingAppUserInvoiceFee: number
|
||||||
userToUserFee: number
|
userToUserFee: number
|
||||||
|
appToUserFee: number
|
||||||
serviceUrl: string
|
serviceUrl: string
|
||||||
servicePort: number
|
servicePort: number
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue