- 
                Notifications
    You must be signed in to change notification settings 
- Fork 3
feat: job to clean up pending old payments #1066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
60531a2
              f987b0c
              7c43b62
              5690c86
              ec9ac14
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,82 @@ | ||||||||||||||||||||||
| import prisma from 'prisma-local/clientInstance' | ||||||||||||||||||||||
| import { v4 as uuidv4 } from 'uuid' | ||||||||||||||||||||||
| import { multiBlockchainClient } from 'services/chronikService' | ||||||||||||||||||||||
| import { Prisma, ClientPaymentStatus } from '@prisma/client' | ||||||||||||||||||||||
| import { NETWORK_IDS_FROM_SLUGS, CLIENT_PAYMENT_EXPIRATION_TIME } from 'constants/index' | ||||||||||||||||||||||
| import { parseAddress } from 'utils/validators' | ||||||||||||||||||||||
| import { addressExists } from './addressService' | ||||||||||||||||||||||
| import moment from 'moment' | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| export const generatePaymentId = async (address: string, amount?: Prisma.Decimal): Promise<string> => { | ||||||||||||||||||||||
| const rawUUID = uuidv4() | ||||||||||||||||||||||
| const cleanUUID = rawUUID.replace(/-/g, '') | ||||||||||||||||||||||
| const status = 'PENDING' as ClientPaymentStatus | ||||||||||||||||||||||
| address = parseAddress(address) | ||||||||||||||||||||||
| const prefix = address.split(':')[0].toLowerCase() | ||||||||||||||||||||||
| const networkId = NETWORK_IDS_FROM_SLUGS[prefix] | ||||||||||||||||||||||
| 
      Comment on lines
    
      +14
     to 
      +16
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate  If the address prefix is not in  Apply this diff to add validation:    address = parseAddress(address)
   const prefix = address.split(':')[0].toLowerCase()
   const networkId = NETWORK_IDS_FROM_SLUGS[prefix]
+  if (networkId === undefined) {
+    throw new Error(`Invalid network prefix: ${prefix}`)
+  }
   const isAddressRegistered = await addressExists(address)📝 Committable suggestion
 
        Suggested change
       
 🤖 Prompt for AI Agents | ||||||||||||||||||||||
| const isAddressRegistered = await addressExists(address) | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| const clientPayment = await prisma.clientPayment.create({ | ||||||||||||||||||||||
| data: { | ||||||||||||||||||||||
| address: { | ||||||||||||||||||||||
| connectOrCreate: { | ||||||||||||||||||||||
| where: { address }, | ||||||||||||||||||||||
| create: { | ||||||||||||||||||||||
| address, | ||||||||||||||||||||||
| networkId | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| paymentId: cleanUUID, | ||||||||||||||||||||||
| status, | ||||||||||||||||||||||
| amount | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| include: { | ||||||||||||||||||||||
| address: true | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }) | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| if (!isAddressRegistered) { | ||||||||||||||||||||||
| void multiBlockchainClient.syncAndSubscribeAddresses([clientPayment.address]) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| 
      Comment on lines
    
      +39
     to 
      +41
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for fire-and-forget synchronization. The  Apply this diff to add error handling:    if (!isAddressRegistered) {
-    void multiBlockchainClient.syncAndSubscribeAddresses([clientPayment.address])
+    multiBlockchainClient.syncAndSubscribeAddresses([clientPayment.address])
+      .catch(err => console.error(`[CLIENT_PAYMENT] Failed to sync address ${clientPayment.address.address}: ${err.message}`))
   }📝 Committable suggestion
 
        Suggested change
       
 🤖 Prompt for AI Agents | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| return clientPayment.paymentId | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| export const updateClientPaymentStatus = async (paymentId: string, status: ClientPaymentStatus): Promise<void> => { | ||||||||||||||||||||||
| await prisma.clientPayment.update({ | ||||||||||||||||||||||
| where: { paymentId }, | ||||||||||||||||||||||
| data: { status } | ||||||||||||||||||||||
| }) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| export const getClientPayment = async (paymentId: string): Promise<Prisma.ClientPaymentGetPayload<{ include: { address: true } }> | null> => { | ||||||||||||||||||||||
| return await prisma.clientPayment.findUnique({ | ||||||||||||||||||||||
| where: { paymentId }, | ||||||||||||||||||||||
| include: { address: true } | ||||||||||||||||||||||
| }) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| export const cleanupExpiredClientPayments = async (): Promise<void> => { | ||||||||||||||||||||||
| const cutoff = moment.utc().subtract(CLIENT_PAYMENT_EXPIRATION_TIME, 'milliseconds').toDate() | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| const oldPaymentsUnpaid = await prisma.clientPayment.findMany({ | ||||||||||||||||||||||
| where: { | ||||||||||||||||||||||
| status: 'PENDING', | ||||||||||||||||||||||
| createdAt: { lt: cutoff } | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| select: { paymentId: true } | ||||||||||||||||||||||
| }) | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| if (oldPaymentsUnpaid.length === 0) { | ||||||||||||||||||||||
| console.log('[CLIENT_PAYMENT CLEANUP] no expired pending payments found.') | ||||||||||||||||||||||
| return | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|  | ||||||||||||||||||||||
| console.log(`[CLIENT_PAYMENT CLEANUP] deleting ${oldPaymentsUnpaid.length} expired pending payments...`) | ||||||||||||||||||||||
| await prisma.clientPayment.deleteMany({ | ||||||||||||||||||||||
| where: { | ||||||||||||||||||||||
| paymentId: { in: oldPaymentsUnpaid.map(p => p.paymentId) } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
as stringon a potentially undefined value can cause runtime errors. The job parameter can be null/undefined in failed events.