|
1 | 1 | import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; |
2 | 2 | import { InjectRepository } from '@nestjs/typeorm'; |
3 | | -import { Repository } from 'typeorm'; |
| 3 | +import { In, Repository } from 'typeorm'; |
4 | 4 | import { PaymentIntent } from '../entities/payment-intent.entity'; |
5 | 5 | import { PaymentIntentStatus } from '../enums/payment.enums'; |
6 | 6 |
|
@@ -97,6 +97,65 @@ export class PaymentIntentService { |
97 | 97 | return intent || null; |
98 | 98 | } |
99 | 99 |
|
| 100 | + /** |
| 101 | + * All payment intents for this user that have a target with the given contextId. |
| 102 | + * Ordered by intent updatedAt descending (newest first). |
| 103 | + */ |
| 104 | + /** |
| 105 | + * Latest PAID intent for this user with a target for the given contextId, if any. |
| 106 | + */ |
| 107 | + async findPaidByUserIdAndContextId( |
| 108 | + userId: string, |
| 109 | + contextId: string, |
| 110 | + ): Promise<Pick<PaymentIntent, 'id'> | null> { |
| 111 | + const row = await this.paymentIntentRepository |
| 112 | + .createQueryBuilder('intent') |
| 113 | + .select('intent.id', 'id') |
| 114 | + .innerJoin('intent.targets', 't') |
| 115 | + .where('intent.userId = :userId', { userId }) |
| 116 | + .andWhere('t.contextId = :contextId', { contextId }) |
| 117 | + .andWhere('intent.status = :status', { status: PaymentIntentStatus.PAID }) |
| 118 | + .groupBy('intent.id') |
| 119 | + .orderBy('MAX(intent.updatedAt)', 'DESC') |
| 120 | + .addOrderBy('intent.id', 'DESC') |
| 121 | + .limit(1) |
| 122 | + .getRawOne(); |
| 123 | + |
| 124 | + const id = row?.id as string | undefined; |
| 125 | + return id ? { id } : null; |
| 126 | + } |
| 127 | + |
| 128 | + async findAllByUserIdAndContextId( |
| 129 | + userId: string, |
| 130 | + contextId: string, |
| 131 | + ): Promise<PaymentIntent[]> { |
| 132 | + const rows = await this.paymentIntentRepository |
| 133 | + .createQueryBuilder('intent') |
| 134 | + .select('intent.id', 'id') |
| 135 | + .innerJoin('intent.targets', 't') |
| 136 | + .where('intent.userId = :userId', { userId }) |
| 137 | + .andWhere('t.contextId = :contextId', { contextId }) |
| 138 | + .groupBy('intent.id') |
| 139 | + .orderBy('MAX(intent.updatedAt)', 'DESC') |
| 140 | + .addOrderBy('intent.id', 'DESC') |
| 141 | + .getRawMany(); |
| 142 | + |
| 143 | + const ids = rows.map((r) => r.id as string).filter(Boolean); |
| 144 | + if (ids.length === 0) { |
| 145 | + return []; |
| 146 | + } |
| 147 | + |
| 148 | + const intents = await this.paymentIntentRepository.find({ |
| 149 | + where: { id: In(ids) }, |
| 150 | + relations: ['transactions', 'targets'], |
| 151 | + }); |
| 152 | + const order = new Map(ids.map((id, i) => [id, i])); |
| 153 | + intents.sort( |
| 154 | + (a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0), |
| 155 | + ); |
| 156 | + return intents; |
| 157 | + } |
| 158 | + |
100 | 159 | /** |
101 | 160 | * Update payment intent status |
102 | 161 | */ |
|
0 commit comments