Review payment refund orchestration

src/refund-types.tsTypeScript
@@ -0,0 +1,80 @@
1+export interface PaymentRecord {
2+ id: string;
3+ merchantId: string;
4+ capturedAmountMinor: number;
5+ currency: string;
6+}
7+
8+export interface RefundRecord {
9+ id: string;
10+ merchantId: string;
11+ paymentId: string;
12+ requestKey: string;
13+ amountMinor: number;
14+ currency: string;
15+ status: "pending" | "succeeded" | "failed";
16+}
17+
18+export interface RefundRepository {
19+ findPayment(paymentId: string, transaction?: object): Promise<PaymentRecord | null>;
20+ findByRequestKey(
21+ merchantId: string,
22+ requestKey: string,
23+ transaction?: object,
24+ ): Promise<RefundRecord | null>;
25+ listForPayment(paymentId: string, transaction?: object): Promise<RefundRecord[]>;
26+ create(refund: RefundRecord, transaction?: object): Promise<void>;
27+ markSucceeded(
28+ refundId: string,
29+ providerRefundId: string,
30+ transaction?: object,
31+ ): Promise<void>;
32+}
33+
34+export interface RefundCommandOutbox {
35+ enqueue(
36+ command: { refundId: string; paymentId: string; amountMinor: number; currency: string },
37+ transaction?: object,
38+ ): Promise<void>;
39+}
40+
41+export interface PaymentProvider {
42+ startRefund(input: {
43+ paymentId: string;
44+ amountMinor: number;
45+ currency: string;
46+ idempotencyKey: string;
47+ }): Promise<
48+ | { status: "accepted"; providerRefundId: string }
49+ | { status: "rejected"; reason: string }
50+ >;
51+}
52+
53+export interface RefundDependencies {
54+ refunds: RefundRepository;
55+ commands: RefundCommandOutbox;
56+ provider: PaymentProvider;
57+ ids: { next(): string };
58+ database: {
59+ withPaymentLock<T>(paymentId: string, work: (transaction: object) => Promise<T>): Promise<T>;
60+ };
61+}
62+
63+export interface RefundWebhookEvent {
64+ eventId: string;
65+ type: "refund.succeeded";
66+ refundId: string;
67+ providerRefundId: string;
68+}
69+
70+export interface RefundWebhookVerifier {
71+ verify(rawBody: string, signature: string): Promise<RefundWebhookEvent>;
72+}
73+
74+export interface WebhookEventStore {
75+ claimOnce(eventId: string): Promise<boolean>;
76+ claimAndApply(
77+ eventId: string,
78+ apply: (transaction: object) => Promise<void>,
79+ ): Promise<boolean>;
80+}