Files
edr-platform/apps/edr-payment-api/src/modules/intents/intents.repository.ts
2026-06-11 15:25:24 +03:00

74 lines
2.0 KiB
TypeScript

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { In, LessThan, Not, Repository } from "typeorm";
import { BaseRepository } from "@edr/api-common";
import {
PaymentReferenceType,
PaymentService,
ProviderPaymentStatus,
} from "@edr/types";
import { PaymentIntent } from "./entities/payment-intent.entity";
@Injectable()
export class IntentsRepository extends BaseRepository<PaymentIntent> {
constructor(
@InjectRepository(PaymentIntent)
repository: Repository<PaymentIntent>,
) {
super(repository);
}
/** The single non-FAILED/CANCELLED intent for a domain order (matches the partial unique index). */
async findActiveByReference(
service: PaymentService,
referenceType: PaymentReferenceType,
referenceId: string,
): Promise<PaymentIntent | null> {
return this.repository.findOne({
where: {
service,
referenceType,
referenceId,
status: Not(
In([ProviderPaymentStatus.FAILED, ProviderPaymentStatus.CANCELLED]),
),
},
order: { createdAt: "DESC" },
});
}
async findByMerchantOrderId(
merchantOrderId: string,
): Promise<PaymentIntent | null> {
return this.repository.findOne({ where: { merchantOrderId } });
}
async findByIdempotencyKey(
service: PaymentService,
idempotencyKey: string,
): Promise<PaymentIntent | null> {
return this.repository.findOne({
where: { service, idempotencyKey },
order: { createdAt: "DESC" },
});
}
/** Non-terminal intents untouched since `updatedBefore` — input for the reconciliation sweep. */
async findStale(
updatedBefore: Date,
limit: number,
): Promise<PaymentIntent[]> {
return this.repository.find({
where: {
status: In([
ProviderPaymentStatus.REQUIRES_ACTION,
ProviderPaymentStatus.PROCESSING,
]),
updatedAt: LessThan(updatedBefore),
},
order: { updatedAt: "ASC" },
take: limit,
});
}
}