mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 14:20:58 +00:00
fix: reference type in payment and billing
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { forwardRef, Inject, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
||||
import { EventEmitter2 } from "@nestjs/event-emitter";
|
||||
import { Freight } from "@edr/types";
|
||||
import { Freight, PaymentReferenceType } from "@edr/types";
|
||||
import { DataSource, EntityManager, In } from "typeorm";
|
||||
|
||||
import { Invoice } from "./entities/invoice.entity";
|
||||
@@ -417,6 +417,12 @@ export class BillingService {
|
||||
|
||||
const result = await this.payment.initiate({
|
||||
referenceId: sourceId,
|
||||
source: invoice.source,
|
||||
// Gateway reference type derives from the invoice source by convention
|
||||
// (source.toUpperCase() ∈ PaymentReferenceType) — no domain word here, and
|
||||
// the domain never supplies it. New sources add their uppercased value to
|
||||
// the PaymentReferenceType enum.
|
||||
referenceType: invoice.source.toUpperCase() as PaymentReferenceType,
|
||||
orderRef: invoice.invoiceNumber,
|
||||
amountMinor: Math.round(Number(invoice.totalAmount)),
|
||||
currency: invoice.currency,
|
||||
|
||||
@@ -2,7 +2,8 @@ import { BaseEntity, Column, CreateDateColumn, Entity, OneToMany, PrimaryGenerat
|
||||
import { PaymentRefundEntity } from "./payment-refund.entity";
|
||||
|
||||
|
||||
type PaymentType = "booking"
|
||||
/** Invoice source that owns the intent ('booking', 'demurrage', …) — caller-supplied. */
|
||||
type PaymentType = string
|
||||
type PaymentMethod = "telebirr" | "cbe-birr" | "ebirr" | "waafi" | "card" | "dmoney" | "cac-bank"
|
||||
type Currency = "ETB" | "USD"
|
||||
export type PaymentStatus = "action-required" | "processing" | "success" | "failed" | "canceled" | "refunded"
|
||||
@@ -15,9 +16,12 @@ export class PaymentEntity extends BaseEntity {
|
||||
@Column({ type: 'varchar', length: 255, name: "ref_id" })
|
||||
refId!: string
|
||||
|
||||
@Column({ type: "enum", enum: ["booking"] })
|
||||
@Column({ type: "varchar", length: 50 })
|
||||
type!: PaymentType;
|
||||
|
||||
@Column({ type: "varchar", length: 40, nullable: true, name: "reference_type" })
|
||||
referenceType?: string;
|
||||
|
||||
@Column({ type: "enum", enum: ["telebirr", "cbe-birr", "ebirr", "waafi", "card", "dmoney", "cac-bank"] })
|
||||
method!: PaymentMethod
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@ import {
|
||||
export interface InitiateIntentInput {
|
||||
/** Opaque domain reference (booking id, …). */
|
||||
referenceId: string;
|
||||
/** Invoice source that owns the intent ('booking', …) — stored on the projection. */
|
||||
source: string;
|
||||
/** Gateway reference type the intent is opened with (caller's domain decides it). */
|
||||
referenceType: PaymentReferenceType;
|
||||
/** Human-readable order ref shown on provider pages. */
|
||||
orderRef: string;
|
||||
/** Authoritative amount in minor units, computed by the caller. */
|
||||
@@ -194,7 +198,7 @@ export class PaymentService {
|
||||
async initiate(input: InitiateIntentInput): Promise<InitiateIntentResult> {
|
||||
const snapshot = await this.paymentClient.initiate({
|
||||
service: PaymentServiceEnum.FREIGHT,
|
||||
referenceType: PaymentReferenceType.SHIPMENT,
|
||||
referenceType: input.referenceType,
|
||||
referenceId: input.referenceId,
|
||||
orderRef: input.orderRef,
|
||||
amountMinor: input.amountMinor,
|
||||
@@ -240,7 +244,6 @@ export class PaymentService {
|
||||
): Promise<PaymentEntity> {
|
||||
const existing = await this.paymentRepo.findOneBy({
|
||||
refId: input.referenceId,
|
||||
type: "booking",
|
||||
});
|
||||
|
||||
const method: PaymentEntity["method"] =
|
||||
@@ -270,7 +273,8 @@ export class PaymentService {
|
||||
|
||||
return this.paymentRepo.create({
|
||||
refId: input.referenceId,
|
||||
type: "booking",
|
||||
type: input.source,
|
||||
referenceType: input.referenceType,
|
||||
amount: input.amountMinor,
|
||||
currency: input.currency as PaymentEntity["currency"],
|
||||
reason: input.reason ?? `Payment for ${input.orderRef}`,
|
||||
@@ -287,12 +291,12 @@ export class PaymentService {
|
||||
* (the booking id, but this service does not load it).
|
||||
*/
|
||||
async getIntentByBookingId(referenceId: string): Promise<IntentStatusDto> {
|
||||
const local = await this.paymentRepo.findOneBy({ refId: referenceId, type: "booking" });
|
||||
const local = await this.paymentRepo.findOneBy({ refId: referenceId });
|
||||
|
||||
let snapshot: PaymentIntentSnapshot | null = null;
|
||||
try {
|
||||
snapshot = await this.paymentClient.getIntentByReference(
|
||||
PaymentReferenceType.SHIPMENT,
|
||||
(local?.referenceType as PaymentReferenceType) ?? PaymentReferenceType.SHIPMENT,
|
||||
referenceId,
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -423,7 +427,7 @@ export class PaymentService {
|
||||
}
|
||||
|
||||
findBookingById(id: string) {
|
||||
return this.paymentRepo.findOneBy({ refId: id, type: "booking" });
|
||||
return this.paymentRepo.findOneBy({ refId: id });
|
||||
}
|
||||
|
||||
formatIntentResponse(intent: PaymentEntity): InitiateResponseDto {
|
||||
@@ -459,7 +463,7 @@ export class PaymentService {
|
||||
failureMessage?: string;
|
||||
}): Promise<{ processed: boolean; alreadyFinalized?: boolean; reason?: string }> {
|
||||
if (event.eventType === "payment.succeeded") {
|
||||
const intent = await this.paymentRepo.findOneBy({ refId: event.referenceId, type: "booking" });
|
||||
const intent = await this.paymentRepo.findOneBy({ refId: event.referenceId });
|
||||
if (!intent) {
|
||||
return { processed: false, reason: `No local intent for reference ${event.referenceId}` };
|
||||
}
|
||||
@@ -472,7 +476,7 @@ export class PaymentService {
|
||||
}
|
||||
|
||||
if (event.eventType === "payment.failed") {
|
||||
const intent = await this.paymentRepo.findOneBy({ refId: event.referenceId, type: "booking" });
|
||||
const intent = await this.paymentRepo.findOneBy({ refId: event.referenceId });
|
||||
if (!intent) {
|
||||
return { processed: false, reason: `No local intent for reference ${event.referenceId}` };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user