mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
extract transaction process in function
This commit is contained in:
parent
81cd858eb4
commit
13fe4300d2
@ -1,4 +1,4 @@
|
|||||||
import { ArgsType, Field, Int } from 'type-graphql'
|
import { ArgsType, Field } from 'type-graphql'
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
|
|
||||||
@ArgsType()
|
@ArgsType()
|
||||||
@ -11,7 +11,4 @@ export default class TransactionSendArgs {
|
|||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
memo: string
|
memo: string
|
||||||
|
|
||||||
@Field(() => Int, { nullable: true })
|
|
||||||
senderId?: number
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,8 @@ import { RIGHTS } from '@/auth/RIGHTS'
|
|||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import { User } from '@model/User'
|
import { User } from '@model/User'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
import { TransactionResolver } from './TransactionResolver'
|
import { executeTransaction } from './TransactionResolver'
|
||||||
|
import { User as dbUser } from '@entity/User'
|
||||||
|
|
||||||
// TODO: do not export, test it inside the resolver
|
// TODO: do not export, test it inside the resolver
|
||||||
export const transactionLinkCode = (date: Date): string => {
|
export const transactionLinkCode = (date: Date): string => {
|
||||||
@ -130,10 +131,11 @@ export class TransactionLinkResolver {
|
|||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
const user = await userRepository.findByPubkeyHex(context.pubKey)
|
const user = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
const transactionLink = await dbTransactionLink.findOneOrFail({ id })
|
const transactionLink = await dbTransactionLink.findOneOrFail({ id })
|
||||||
|
const linkedUser = await dbUser.findOneOrFail({ id: transactionLink.userId })
|
||||||
|
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
|
|
||||||
if (user.id === transactionLink.userId) {
|
if (user.id === linkedUser.id) {
|
||||||
throw new Error('Cannot redeem own transaction link.')
|
throw new Error('Cannot redeem own transaction link.')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,16 +147,8 @@ export class TransactionLinkResolver {
|
|||||||
throw new Error('Transaction Link already redeemed.')
|
throw new Error('Transaction Link already redeemed.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactionResolver = new TransactionResolver()
|
await executeTransaction(transactionLink.amount, transactionLink.memo, linkedUser, user)
|
||||||
transactionResolver.sendCoins(
|
|
||||||
{
|
|
||||||
email: user.email,
|
|
||||||
amount: transactionLink.amount,
|
|
||||||
memo: transactionLink.memo,
|
|
||||||
senderId: transactionLink.userId,
|
|
||||||
},
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
transactionLink.redeemedAt = now
|
transactionLink.redeemedAt = now
|
||||||
transactionLink.redeemedBy = user.id
|
transactionLink.redeemedBy = user.id
|
||||||
transactionLink.save().catch(() => {
|
transactionLink.save().catch(() => {
|
||||||
|
|||||||
@ -34,6 +34,83 @@ import { virtualDecayTransaction } from '@/util/virtualDecayTransaction'
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
|
|
||||||
|
export const executeTransaction = async (
|
||||||
|
amount: Decimal,
|
||||||
|
memo: string,
|
||||||
|
sender: dbUser,
|
||||||
|
recipient: dbUser,
|
||||||
|
): Promise<boolean> => {
|
||||||
|
if (sender.id === recipient.id) {
|
||||||
|
throw new Error('Sender and Recipient are the same.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate amount
|
||||||
|
const receivedCallDate = new Date()
|
||||||
|
const sendBalance = await calculateBalance(sender.id, amount.mul(-1), receivedCallDate)
|
||||||
|
if (!sendBalance) {
|
||||||
|
throw new Error("user hasn't enough GDD or amount is < 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
await queryRunner.connect()
|
||||||
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
|
try {
|
||||||
|
// transaction
|
||||||
|
const transactionSend = new dbTransaction()
|
||||||
|
transactionSend.typeId = TransactionTypeId.SEND
|
||||||
|
transactionSend.memo = memo
|
||||||
|
transactionSend.userId = sender.id
|
||||||
|
transactionSend.linkedUserId = recipient.id
|
||||||
|
transactionSend.amount = amount.mul(-1)
|
||||||
|
transactionSend.balance = sendBalance.balance
|
||||||
|
transactionSend.balanceDate = receivedCallDate
|
||||||
|
transactionSend.decay = sendBalance.decay.decay
|
||||||
|
transactionSend.decayStart = sendBalance.decay.start
|
||||||
|
transactionSend.previous = sendBalance.lastTransactionId
|
||||||
|
await queryRunner.manager.insert(dbTransaction, transactionSend)
|
||||||
|
|
||||||
|
const transactionReceive = new dbTransaction()
|
||||||
|
transactionReceive.typeId = TransactionTypeId.RECEIVE
|
||||||
|
transactionReceive.memo = memo
|
||||||
|
transactionReceive.userId = recipient.id
|
||||||
|
transactionReceive.linkedUserId = sender.id
|
||||||
|
transactionReceive.amount = amount
|
||||||
|
const receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate)
|
||||||
|
transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount
|
||||||
|
transactionReceive.balanceDate = receivedCallDate
|
||||||
|
transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0)
|
||||||
|
transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null
|
||||||
|
transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null
|
||||||
|
transactionReceive.linkedTransactionId = transactionSend.id
|
||||||
|
await queryRunner.manager.insert(dbTransaction, transactionReceive)
|
||||||
|
|
||||||
|
// Save linked transaction id for send
|
||||||
|
transactionSend.linkedTransactionId = transactionReceive.id
|
||||||
|
await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend)
|
||||||
|
|
||||||
|
await queryRunner.commitTransaction()
|
||||||
|
} catch (e) {
|
||||||
|
await queryRunner.rollbackTransaction()
|
||||||
|
throw new Error(`Transaction was not successful: ${e}`)
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
// send notification email
|
||||||
|
// TODO: translate
|
||||||
|
await sendTransactionReceivedEmail({
|
||||||
|
senderFirstName: sender.firstName,
|
||||||
|
senderLastName: sender.lastName,
|
||||||
|
recipientFirstName: recipient.firstName,
|
||||||
|
recipientLastName: recipient.lastName,
|
||||||
|
email: recipient.email,
|
||||||
|
amount,
|
||||||
|
memo,
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class TransactionResolver {
|
export class TransactionResolver {
|
||||||
@Authorized([RIGHTS.TRANSACTION_LIST])
|
@Authorized([RIGHTS.TRANSACTION_LIST])
|
||||||
@ -145,23 +222,15 @@ export class TransactionResolver {
|
|||||||
@Authorized([RIGHTS.SEND_COINS])
|
@Authorized([RIGHTS.SEND_COINS])
|
||||||
@Mutation(() => String)
|
@Mutation(() => String)
|
||||||
async sendCoins(
|
async sendCoins(
|
||||||
@Args() { email, amount, memo, senderId = 0 }: TransactionSendArgs,
|
@Args() { email, amount, memo }: TransactionSendArgs,
|
||||||
@Ctx() context: any,
|
@Ctx() context: any,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
// TODO this is subject to replay attacks
|
// TODO this is subject to replay attacks
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
const senderUser = senderId
|
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
? await dbUser.findOneOrFail({ id: senderId })
|
|
||||||
: await userRepository.findByPubkeyHex(context.pubKey)
|
|
||||||
if (senderUser.pubKey.length !== 32) {
|
if (senderUser.pubKey.length !== 32) {
|
||||||
throw new Error('invalid sender public key')
|
throw new Error('invalid sender public key')
|
||||||
}
|
}
|
||||||
// validate amount
|
|
||||||
const receivedCallDate = new Date()
|
|
||||||
const sendBalance = await calculateBalance(senderUser.id, amount.mul(-1), receivedCallDate)
|
|
||||||
if (!sendBalance) {
|
|
||||||
throw new Error("user hasn't enough GDD or amount is < 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate recipient user
|
// validate recipient user
|
||||||
const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true })
|
const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true })
|
||||||
@ -175,62 +244,7 @@ export class TransactionResolver {
|
|||||||
throw new Error('invalid recipient public key')
|
throw new Error('invalid recipient public key')
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
await executeTransaction(amount, memo, senderUser, recipientUser)
|
||||||
await queryRunner.connect()
|
|
||||||
await queryRunner.startTransaction('READ UNCOMMITTED')
|
|
||||||
try {
|
|
||||||
// transaction
|
|
||||||
const transactionSend = new dbTransaction()
|
|
||||||
transactionSend.typeId = TransactionTypeId.SEND
|
|
||||||
transactionSend.memo = memo
|
|
||||||
transactionSend.userId = senderUser.id
|
|
||||||
transactionSend.linkedUserId = recipientUser.id
|
|
||||||
transactionSend.amount = amount.mul(-1)
|
|
||||||
transactionSend.balance = sendBalance.balance
|
|
||||||
transactionSend.balanceDate = receivedCallDate
|
|
||||||
transactionSend.decay = sendBalance.decay.decay
|
|
||||||
transactionSend.decayStart = sendBalance.decay.start
|
|
||||||
transactionSend.previous = sendBalance.lastTransactionId
|
|
||||||
await queryRunner.manager.insert(dbTransaction, transactionSend)
|
|
||||||
|
|
||||||
const transactionReceive = new dbTransaction()
|
|
||||||
transactionReceive.typeId = TransactionTypeId.RECEIVE
|
|
||||||
transactionReceive.memo = memo
|
|
||||||
transactionReceive.userId = recipientUser.id
|
|
||||||
transactionReceive.linkedUserId = senderUser.id
|
|
||||||
transactionReceive.amount = amount
|
|
||||||
const receiveBalance = await calculateBalance(recipientUser.id, amount, receivedCallDate)
|
|
||||||
transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount
|
|
||||||
transactionReceive.balanceDate = receivedCallDate
|
|
||||||
transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0)
|
|
||||||
transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null
|
|
||||||
transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null
|
|
||||||
transactionReceive.linkedTransactionId = transactionSend.id
|
|
||||||
await queryRunner.manager.insert(dbTransaction, transactionReceive)
|
|
||||||
|
|
||||||
// Save linked transaction id for send
|
|
||||||
transactionSend.linkedTransactionId = transactionReceive.id
|
|
||||||
await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend)
|
|
||||||
|
|
||||||
await queryRunner.commitTransaction()
|
|
||||||
} catch (e) {
|
|
||||||
await queryRunner.rollbackTransaction()
|
|
||||||
throw new Error(`Transaction was not successful: ${e}`)
|
|
||||||
} finally {
|
|
||||||
await queryRunner.release()
|
|
||||||
}
|
|
||||||
|
|
||||||
// send notification email
|
|
||||||
// TODO: translate
|
|
||||||
await sendTransactionReceivedEmail({
|
|
||||||
senderFirstName: senderUser.firstName,
|
|
||||||
senderLastName: senderUser.lastName,
|
|
||||||
recipientFirstName: recipientUser.firstName,
|
|
||||||
recipientLastName: recipientUser.lastName,
|
|
||||||
email: recipientUser.email,
|
|
||||||
amount,
|
|
||||||
memo,
|
|
||||||
})
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user