Skip to content

Commit

Permalink
feat: payment events
Browse files Browse the repository at this point in the history
  • Loading branch information
SewerynKras committed Jun 4, 2024
1 parent 1d89cb0 commit 428f9ca
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 80 deletions.
44 changes: 22 additions & 22 deletions src/payment/agreement_payment_process.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ import { Invoice } from "./invoice";
import { DebitNote } from "./debit_note";
import { GolemPaymentError, PaymentErrorCode } from "./error";
import { GolemUserError } from "../shared/error/golem-error";
import { IPaymentApi } from "./types";
import { Subject } from "rxjs";
import { RejectionReason } from "./rejection";
import { PaymentModule } from "./payment.module";

const agreementMock = mock(Agreement);
const allocationMock = mock(Allocation);
const invoiceMock = mock(Invoice);
const debitNoteMock = mock(DebitNote);

const mockPaymentApi = imock<IPaymentApi>();
const mockPaymentModule = imock<PaymentModule>();

beforeEach(() => {
reset(agreementMock);
reset(allocationMock);
reset(invoiceMock);
reset(debitNoteMock);
reset(mockPaymentApi);
reset(mockPaymentModule);

const testProviderInfo = {
id: "test-provider-id",
Expand All @@ -32,8 +32,8 @@ beforeEach(() => {

when(agreementMock.getProviderInfo()).thenReturn(testProviderInfo);
when(invoiceMock.provider).thenReturn(testProviderInfo);
when(mockPaymentApi.receivedInvoices$).thenReturn(new Subject());
when(mockPaymentApi.receivedDebitNotes$).thenReturn(new Subject());
when(mockPaymentModule.observeInvoices()).thenReturn(new Subject());
when(mockPaymentModule.observeDebitNotes()).thenReturn(new Subject());
});

describe("AgreementPaymentProcess", () => {
Expand All @@ -45,7 +45,7 @@ describe("AgreementPaymentProcess", () => {
when(invoiceMock.amount).thenReturn("0.123");
when(invoiceMock.getStatus()).thenReturn("RECEIVED");

const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => true,
});
Expand All @@ -54,7 +54,7 @@ describe("AgreementPaymentProcess", () => {
const success = await process.addInvoice(invoice);

expect(success).toEqual(true);
verify(mockPaymentApi.acceptInvoice(invoice, allocation, "0.123")).called();
verify(mockPaymentModule.acceptInvoice(invoice, allocation, "0.123")).called();
expect(process.isFinished()).toEqual(true);
});

Expand All @@ -67,7 +67,7 @@ describe("AgreementPaymentProcess", () => {
const process = new AgreementPaymentProcess(
instance(agreementMock),
instance(allocationMock),
instance(mockPaymentApi),
instance(mockPaymentModule),
{
debitNoteFilter: () => true,
invoiceFilter: () => false,
Expand All @@ -79,7 +79,7 @@ describe("AgreementPaymentProcess", () => {

expect(success).toEqual(false);
verify(
mockPaymentApi.rejectInvoice(
mockPaymentModule.rejectInvoice(
invoice,
"Invoice invoice-id for agreement agreement-id rejected by Invoice Filter",
),
Expand All @@ -94,7 +94,7 @@ describe("AgreementPaymentProcess", () => {
when(invoiceMock.getStatus()).thenReturn("ACCEPTED");
const allocation = instance(allocationMock);

const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => true,
});
Expand All @@ -109,7 +109,7 @@ describe("AgreementPaymentProcess", () => {
),
);

verify(mockPaymentApi.acceptInvoice(invoice, allocation, "0.123")).never();
verify(mockPaymentModule.acceptInvoice(invoice, allocation, "0.123")).never();
expect(process.isFinished()).toEqual(false);
});
});
Expand All @@ -124,7 +124,7 @@ describe("AgreementPaymentProcess", () => {

when(agreementMock.id).thenReturn("agreement-id");

const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => true,
});
Expand Down Expand Up @@ -155,7 +155,7 @@ describe("AgreementPaymentProcess", () => {
const allocation = instance(allocationMock);
const agreement = instance(agreementMock);

const process = new AgreementPaymentProcess(agreement, allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(agreement, allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => true,
});
Expand Down Expand Up @@ -184,7 +184,7 @@ describe("AgreementPaymentProcess", () => {
const allocation = instance(allocationMock);
const agreement = instance(agreementMock);

const process = new AgreementPaymentProcess(agreement, allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(agreement, allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => {
throw new Error("invoiceFilter error");
Expand All @@ -207,7 +207,7 @@ describe("AgreementPaymentProcess", () => {

when(debitNoteMock.totalAmountDue).thenReturn("0.123");

const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => true,
});
Expand All @@ -217,7 +217,7 @@ describe("AgreementPaymentProcess", () => {
const success = await process.addDebitNote(debitNote);

expect(success).toEqual(true);
verify(mockPaymentApi.acceptDebitNote(debitNote, allocation, "0.123")).called();
verify(mockPaymentModule.acceptDebitNote(debitNote, allocation, "0.123")).called();
expect(process.isFinished()).toEqual(false);
});

Expand All @@ -231,7 +231,7 @@ describe("AgreementPaymentProcess", () => {
const process = new AgreementPaymentProcess(
instance(agreementMock),
instance(allocationMock),
instance(mockPaymentApi),
instance(mockPaymentModule),
{
debitNoteFilter: () => false,
invoiceFilter: () => true,
Expand Down Expand Up @@ -264,7 +264,7 @@ describe("AgreementPaymentProcess", () => {
when(debitNoteMock.id).thenReturn("debit-note-id");
when(debitNoteMock.agreementId).thenReturn("agreement-id");

const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentApi), {
const process = new AgreementPaymentProcess(instance(agreementMock), allocation, instance(mockPaymentModule), {
debitNoteFilter: () => true,
invoiceFilter: () => true,
});
Expand All @@ -277,7 +277,7 @@ describe("AgreementPaymentProcess", () => {
const debitNoteSuccess = await process.addDebitNote(debitNote);

expect(invoiceSuccess).toEqual(true);
verify(mockPaymentApi.acceptInvoice(invoice, allocation, "0.123")).called();
verify(mockPaymentModule.acceptInvoice(invoice, allocation, "0.123")).called();

expect(debitNoteSuccess).toEqual(false);
verify(
Expand All @@ -298,7 +298,7 @@ describe("AgreementPaymentProcess", () => {
const process = new AgreementPaymentProcess(
instance(agreementMock),
instance(allocationMock),
instance(mockPaymentApi),
instance(mockPaymentModule),
{
debitNoteFilter: () => true,
invoiceFilter: () => true,
Expand All @@ -312,7 +312,7 @@ describe("AgreementPaymentProcess", () => {

const secondSuccess = await process.addDebitNote(debitNote);
expect(secondSuccess).toEqual(false);
verify(mockPaymentApi.rejectDebitNote(debitNote, anything())).never();
verify(mockPaymentModule.rejectDebitNote(debitNote, anything())).never();
expect(process.isFinished()).toEqual(false);
});
});
Expand All @@ -324,7 +324,7 @@ describe("AgreementPaymentProcess", () => {
const process = new AgreementPaymentProcess(
instance(agreementMock),
instance(allocationMock),
instance(mockPaymentApi),
instance(mockPaymentModule),
{
debitNoteFilter: () => {
throw new Error("debitNoteFilter error");
Expand Down
41 changes: 10 additions & 31 deletions src/payment/agreement_payment_process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { defaultLogger, Logger } from "../shared/utils";
import AsyncLock from "async-lock";
import { GolemPaymentError, PaymentErrorCode } from "./error";
import { GolemUserError } from "../shared/error/golem-error";
import { IPaymentApi } from "./types";
import { getMessageFromApiError } from "../shared/utils/apiErrorMessage";
import { Demand } from "../market";
import { filter } from "rxjs";
import { PaymentModule } from "./payment.module";

export type DebitNoteFilter = (
debitNote: DebitNote,
Expand Down Expand Up @@ -58,7 +58,7 @@ export class AgreementPaymentProcess {
constructor(
public readonly agreement: Agreement,
public readonly allocation: Allocation,
public readonly paymentApi: IPaymentApi,
public readonly paymentModule: PaymentModule,
options?: Partial<PaymentProcessOptions>,
logger?: Logger,
) {
Expand All @@ -68,11 +68,13 @@ export class AgreementPaymentProcess {
debitNoteFilter: options?.debitNoteFilter || (() => true),
};

const invoiceSubscription = this.paymentApi.receivedInvoices$
const invoiceSubscription = this.paymentModule
.observeInvoices()
.pipe(filter((invoice) => invoice.agreementId === this.agreement.id))
.subscribe((invoice) => this.addInvoice(invoice));

const debitNoteSubscription = this.paymentApi.receivedDebitNotes$
const debitNoteSubscription = this.paymentModule
.observeDebitNotes()
.pipe(filter((debitNote) => debitNote.agreementId === this.agreement.id))
.subscribe((debitNote) => this.addDebitNote(debitNote));

Expand Down Expand Up @@ -155,22 +157,14 @@ export class AgreementPaymentProcess {
}

try {
await this.paymentApi.acceptDebitNote(debitNote, this.allocation, debitNote.totalAmountDue);
await this.paymentModule.acceptDebitNote(debitNote, this.allocation, debitNote.totalAmountDue);
this.logger.debug(`DebitNote accepted`, {
debitNoteId: debitNote.id,
agreementId: debitNote.agreementId,
});
// this.events.emit("accepted", {
// id: this.id,
// agreementId: this.agreementId,
// amount: totalAmountAccepted,
// provider: this.provider,
// });

return true;
} catch (error) {
const message = getMessageFromApiError(error);
// this.events.emit("paymentFailed", { id: this.id, agreementId: this.agreementId, reason });
throw new GolemPaymentError(
`Unable to accept debit note ${debitNote.id}. ${message}`,
PaymentErrorCode.DebitNoteAcceptanceFailed,
Expand All @@ -189,8 +183,7 @@ export class AgreementPaymentProcess {

private async rejectDebitNote(debitNote: DebitNote, rejectionReason: RejectionReason, rejectMessage: string) {
try {
// FIXME yagna 0.15 still doesn't support invoice rejections
// await this.paymentApi.rejectDebitNote(debitNote, rejectMessage);
await this.paymentModule.rejectDebitNote(debitNote, rejectMessage);
this.logger.warn(`DebitNote rejected`, { reason: rejectMessage });
} catch (error) {
const message = getMessageFromApiError(error);
Expand All @@ -201,8 +194,6 @@ export class AgreementPaymentProcess {
debitNote.provider,
error,
);
} finally {
// this.events.emit("paymentFailed", { id: this.id, agreementId: this.agreementId, reason: rejection.message });
}
}

Expand Down Expand Up @@ -260,25 +251,15 @@ export class AgreementPaymentProcess {
}

try {
await this.paymentApi.acceptInvoice(invoice, this.allocation, invoice.amount);
await this.paymentModule.acceptInvoice(invoice, this.allocation, invoice.amount);
this.logger.info(`Invoice has been accepted`, {
invoiceId: invoice.id,
agreementId: invoice.agreementId,
amount: invoice.amount,
provider: this.agreement.getProviderInfo(),
});

// this.events.emit("invoiceAccepted", {
// invoiceId: invoice.id,
// agreementId: invoice.agreementId,
// amount: totalAmountAccepted,
// provider: invoice.provider,
// });
} catch (error) {
const message = getMessageFromApiError(error);

// this.events.emit("paymentFailed", { invoiceId: invoice.id, agreementId: invoice.agreementId, reason });

throw new GolemPaymentError(
`Unable to accept invoice ${invoice.id} ${message}`,
PaymentErrorCode.InvoiceAcceptanceFailed,
Expand All @@ -297,7 +278,7 @@ export class AgreementPaymentProcess {
message: string,
) {
try {
await this.paymentApi.rejectInvoice(invoice, message);
await this.paymentModule.rejectInvoice(invoice, message);
this.logger.warn(`Invoice rejected`, { reason: message });
} catch (error) {
const message = getMessageFromApiError(error);
Expand All @@ -308,8 +289,6 @@ export class AgreementPaymentProcess {
invoice.provider,
error,
);
} finally {
// this.events.emit("paymentFailed", { id: this.id, agreementId: this.agreementId, reason: rejection.message });
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/payment/types.ts → src/payment/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@ import { Invoice } from "./invoice";
import { DebitNote } from "./debit_note";
import { Allocation } from "./allocation";

export type PaymentEvents = {
allocationCreated: (allocation: Allocation) => void;
errorCreatingAllocation: (error: Error) => void;

allocationReleased: (allocation: Allocation) => void;
errorReleasingAllocation: (allocation: Allocation, error: Error) => void;

allocationAmended: (allocation: Allocation) => void;
errorAmendingAllocation: (allocation: Allocation, error: Error) => void;

invoiceReceived: (invoice: Invoice) => void;
debitNoteReceived: (debitNote: DebitNote) => void;

invoiceAccepted: (invoice: Invoice) => void;
invoiceRejected: (invoice: Invoice) => void;
errorAcceptingInvoice: (invoice: Invoice, error: Error) => void;
errorRejectingInvoice: (invoice: Invoice, error: Error) => void;

debitNoteAccepted: (debitNote: DebitNote) => void;
debitNoteRejected: (debitNote: DebitNote) => void;
errorAcceptingDebitNote: (debitNote: DebitNote, error: Error) => void;
errorRejectingDebitNote: (debitNote: DebitNote, error: Error) => void;
};

export interface IPaymentApi {
receivedInvoices$: Subject<Invoice>;
receivedDebitNotes$: Subject<DebitNote>;
Expand Down
3 changes: 1 addition & 2 deletions src/payment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ export * as PaymentFilters from "./strategy";
export { GolemPaymentError, PaymentErrorCode } from "./error";
export { InvoiceProcessor, InvoiceAcceptResult } from "./InvoiceProcessor";
export * from "./payment.module";
export { IPaymentApi } from "./types";
export { CreateAllocationParams } from "./types";
export * from "./api";
export { InvoiceFilter, DebitNoteFilter } from "./agreement_payment_process";
Loading

0 comments on commit 428f9ca

Please sign in to comment.