diff --git a/self-api/src/main/java/com/selfxdsd/api/Invoice.java b/self-api/src/main/java/com/selfxdsd/api/Invoice.java index d89da62b..1ebe49de 100644 --- a/self-api/src/main/java/com/selfxdsd/api/Invoice.java +++ b/self-api/src/main/java/com/selfxdsd/api/Invoice.java @@ -43,16 +43,11 @@ public interface Invoice { LocalDateTime createdAt(); /** - * Timestamp of the payment. - * @return LocalDateTime or null if it's not paid. + * Latest Payment performed for this Invoice. If the Payment is successful, + * the Invoice is considered paid. + * @return Payment or null if no Payment as been performed yet. */ - LocalDateTime paymentTime(); - - /** - * Returns the transaction ID of the payment. - * @return String or null if it's not paid. - */ - String transactionId(); + Payment latest(); /** * Who emitted the Invoice? diff --git a/self-core-impl/src/main/java/com/selfxdsd/core/contracts/invoices/StoredInvoice.java b/self-core-impl/src/main/java/com/selfxdsd/core/contracts/invoices/StoredInvoice.java index 0a2604ca..aab850c5 100644 --- a/self-core-impl/src/main/java/com/selfxdsd/core/contracts/invoices/StoredInvoice.java +++ b/self-core-impl/src/main/java/com/selfxdsd/core/contracts/invoices/StoredInvoice.java @@ -48,14 +48,10 @@ public final class StoredInvoice implements Invoice { private final LocalDateTime createdAt; /** - * Time when this Invoice has been paid. + * Latest Payment performed for this Invoice. If it's successful, + * then this Invoice is considered paid. */ - private final LocalDateTime paymentTime; - - /** - * The payment's transaction ID. - */ - private final String transactionId; + private final Payment latest; /** * Who emitted this Invoice? @@ -92,8 +88,7 @@ public final class StoredInvoice implements Invoice { * @param id Invoice id. * @param contract Contract. * @param createdAt Invoice creation time. - * @param paymentTime Time when this Invoice has been paid. - * @param transactionId The payment's transaction ID. + * @param latest Latest Payment performed for this Invoice. * @param billedBy Who emitted the Invoice. * @param billedTo Who pays it. * @param billedByCountry Country of the Contributor. @@ -106,8 +101,7 @@ public StoredInvoice( final int id, final Contract contract, final LocalDateTime createdAt, - final LocalDateTime paymentTime, - final String transactionId, + final Payment latest, final String billedBy, final String billedTo, final String billedByCountry, @@ -118,8 +112,7 @@ public StoredInvoice( this.id = id; this.contract = contract; this.createdAt = createdAt; - this.paymentTime = paymentTime; - this.transactionId = transactionId; + this.latest = latest; this.billedBy = billedBy; this.billedTo = billedTo; this.billedByCountry = billedByCountry; @@ -171,13 +164,8 @@ public LocalDateTime createdAt() { } @Override - public LocalDateTime paymentTime() { - return this.paymentTime; - } - - @Override - public String transactionId() { - return this.transactionId; + public Payment latest() { + return this.latest; } @Override @@ -233,18 +221,21 @@ public InvoicedTasks tasks() { @Override public boolean isPaid() { - return this.paymentTime != null && this.transactionId != null; + return this.latest != null && this.latest.status().equalsIgnoreCase( + Payment.Status.SUCCESSFUL + ); } @Override public PlatformInvoice platformInvoice() { final PlatformInvoice found; if(this.isPaid()) { - if(this.transactionId.startsWith("fake_payment_")) { + final String transactionId = this.latest.transactionId(); + if(transactionId.startsWith("fake_payment_")) { found = null; } else { found = this.storage.platformInvoices().getByPayment( - this.transactionId, this.paymentTime + transactionId, this.latest.paymentTime() ); } } else { diff --git a/self-core-impl/src/main/java/com/selfxdsd/core/projects/FakeWallet.java b/self-core-impl/src/main/java/com/selfxdsd/core/projects/FakeWallet.java index d397fccf..b2560391 100644 --- a/self-core-impl/src/main/java/com/selfxdsd/core/projects/FakeWallet.java +++ b/self-core-impl/src/main/java/com/selfxdsd/core/projects/FakeWallet.java @@ -26,6 +26,7 @@ import com.selfxdsd.api.exceptions.PaymentMethodsException; import com.selfxdsd.api.storage.Storage; import com.selfxdsd.core.contracts.invoices.StoredInvoice; +import com.selfxdsd.core.contracts.invoices.StoredPayment; import com.stripe.model.SetupIntent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -111,7 +112,8 @@ public Payment pay(final Invoice invoice) { + " of Contract " + invoice.contract().contractId() + "..." ); - final BigDecimal newCash = this.limit.subtract(invoice.totalAmount()); + final BigDecimal totalAmount = invoice.totalAmount(); + final BigDecimal newCash = this.limit.subtract(totalAmount); final String uuid = UUID.randomUUID().toString().replace("-", ""); final Payment payment = this.storage .invoices() @@ -120,8 +122,15 @@ public Payment pay(final Invoice invoice) { invoice.invoiceId(), invoice.contract(), invoice.createdAt(), - LocalDateTime.now(), - "fake_payment_" + uuid, + new StoredPayment( + invoice, + "fake_payment_" + uuid, + LocalDateTime.now(), + totalAmount, + Payment.Status.SUCCESSFUL, + "", + this.storage + ), invoice.billedBy(), invoice.billedTo(), "FK Country", diff --git a/self-core-impl/src/main/java/com/selfxdsd/core/projects/RegisterUnsuccessfulPayments.java b/self-core-impl/src/main/java/com/selfxdsd/core/projects/RegisterUnsuccessfulPayments.java index 02d6805e..35c44a5c 100644 --- a/self-core-impl/src/main/java/com/selfxdsd/core/projects/RegisterUnsuccessfulPayments.java +++ b/self-core-impl/src/main/java/com/selfxdsd/core/projects/RegisterUnsuccessfulPayments.java @@ -63,13 +63,13 @@ public Payment pay(final Invoice invoice) { payment = this.original.pay(invoice); } catch (final WalletPaymentException failed) { payment = invoice.payments().register( - invoice, null, LocalDateTime.now(), + invoice, "", LocalDateTime.now(), invoice.totalAmount(), Payment.Status.FAILED, failed.getMessage() ); } catch (final IllegalStateException errored) { payment = invoice.payments().register( - invoice, null, LocalDateTime.now(), + invoice, "", LocalDateTime.now(), invoice.totalAmount(), Payment.Status.ERROR, errored.getMessage() ); diff --git a/self-core-impl/src/main/java/com/selfxdsd/core/projects/StripeWallet.java b/self-core-impl/src/main/java/com/selfxdsd/core/projects/StripeWallet.java index b59b09ed..2e52e150 100644 --- a/self-core-impl/src/main/java/com/selfxdsd/core/projects/StripeWallet.java +++ b/self-core-impl/src/main/java/com/selfxdsd/core/projects/StripeWallet.java @@ -27,6 +27,7 @@ import com.selfxdsd.api.storage.Storage; import com.selfxdsd.core.Env; import com.selfxdsd.core.contracts.invoices.StoredInvoice; +import com.selfxdsd.core.contracts.invoices.StoredPayment; import com.stripe.Stripe; import com.stripe.exception.StripeException; import com.stripe.model.Customer; @@ -205,11 +206,12 @@ public Payment pay(final Invoice invoice) { invoice.commission(), contributorBilling ); + final BigDecimal totalAmount = invoice.totalAmount(); final PaymentIntent paymentIntent = PaymentIntent .create( PaymentIntentCreateParams.builder() .setCurrency("eur") - .setAmount(invoice.totalAmount().longValueExact()) + .setAmount(totalAmount.longValueExact()) .setCustomer(this.identifier) .setPaymentMethod(paymentMethod.identifier()) .setTransferData( @@ -244,8 +246,15 @@ public Payment pay(final Invoice invoice) { invoice.invoiceId(), invoice.contract(), invoice.createdAt(), - paymentDate, - paymentIntent.getId(), + new StoredPayment( + invoice, + paymentIntent.getId(), + paymentDate, + totalAmount, + Payment.Status.SUCCESSFUL, + "", + this.storage + ), invoice.billedBy(), invoice.billedTo(), contributorBilling.country(), diff --git a/self-core-impl/src/test/java/com/selfxdsd/core/contracts/invoices/StoredInvoiceTestCase.java b/self-core-impl/src/test/java/com/selfxdsd/core/contracts/invoices/StoredInvoiceTestCase.java index 9d6c4fa1..83061874 100644 --- a/self-core-impl/src/test/java/com/selfxdsd/core/contracts/invoices/StoredInvoiceTestCase.java +++ b/self-core-impl/src/test/java/com/selfxdsd/core/contracts/invoices/StoredInvoiceTestCase.java @@ -42,8 +42,7 @@ public void hasCorrectId() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -54,6 +53,28 @@ public void hasCorrectId() { assertThat(invoice.invoiceId(), is(1)); } + /** + * Invoice has the latest Payment. + */ + @Test + public void hasLatestPayment() { + final Payment latest = Mockito.mock(Payment.class); + final Invoice invoice = new StoredInvoice( + 1, + Mockito.mock(Contract.class), + LocalDateTime.now(), + latest, + "mihai", + "vlad", + "RO", + "RO", + BigDecimal.valueOf(487), + Mockito.mock(Storage.class) + ); + assertThat(invoice.latest(), is(latest)); + } + + /** * Invoice has the correct contract id. */ @@ -64,8 +85,7 @@ public void returnsContract() { 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -86,8 +106,7 @@ public void returnsTasks() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transactionID", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -127,8 +146,7 @@ public void returnsTotalAmount() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transactionID", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -171,8 +189,7 @@ public void returnsAmount() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transactionID", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -215,8 +232,7 @@ public void returnsCommission() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transactionID", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -259,8 +275,7 @@ public void hasCreationTime() { 1, Mockito.mock(Contract.class), creationTime, - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -274,57 +289,6 @@ public void hasCreationTime() { ); } - /** - * A StoredInvoice can return its payment time. - */ - @Test - public void hasPaymentTime() { - final LocalDateTime paymentTime = LocalDateTime.now(); - final Storage storage = Mockito.mock(Storage.class); - final Invoice invoice = new StoredInvoice( - 1, - Mockito.mock(Contract.class), - LocalDateTime.now(), - paymentTime, - "transactionId", - "mihai", - "vlad", - "RO", - "RO", - BigDecimal.valueOf(487), - storage - ); - MatcherAssert.assertThat( - invoice.paymentTime(), - Matchers.is(paymentTime) - ); - } - - /** - * A StoredInvoice can return its transaction id. - */ - @Test - public void hasTransactionId() { - final LocalDateTime paymentTime = LocalDateTime.now(); - final Invoice invoice = new StoredInvoice( - 1, - Mockito.mock(Contract.class), - LocalDateTime.now(), - paymentTime, - "transactionId123", - "mihai", - "vlad", - "RO", - "RO", - BigDecimal.valueOf(487), - mock(Storage.class) - ); - MatcherAssert.assertThat( - invoice.transactionId(), - Matchers.is("transactionId123") - ); - } - /** * A StoredInvoice will not register a Task which is from * another Contract. @@ -344,8 +308,7 @@ public void registerRejectsForeignTask() { 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -383,12 +346,13 @@ public void registerComplainsIfInvoiceIsPaid() { Contract.Roles.DEV ) ); + final Payment payment = Mockito.mock(Payment.class); + Mockito.when(payment.status()).thenReturn(Payment.Status.SUCCESSFUL); final Invoice invoice = new StoredInvoice( 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transactionId123", + payment, "mihai", "vlad", "RO", @@ -444,7 +408,6 @@ public void registersNewTask() { contract, LocalDateTime.now(), null, - null, "mihai", "vlad", "RO", @@ -481,8 +444,7 @@ public void comparesStoredInvoiceObjects() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -494,8 +456,7 @@ public void comparesStoredInvoiceObjects() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -515,8 +476,7 @@ public void verifiesStoredInvoiceHashcode() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -528,8 +488,7 @@ public void verifiesStoredInvoiceHashcode() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -552,8 +511,7 @@ public void returnsSetBilledBy() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -578,8 +536,7 @@ public void returnsSetBilledByCountry() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "BG", @@ -604,8 +561,7 @@ public void returnsSetBilledTo() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -630,8 +586,7 @@ public void returnsSetBilledToCountry() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -656,8 +611,7 @@ public void returnsSetEurToRon() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", @@ -681,8 +635,7 @@ public void returnsBnrEurToRon() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", "vlad", null, @@ -713,8 +666,7 @@ public void returnsContractBilledBy() { 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), null, "vlad", "RO", @@ -745,8 +697,7 @@ public void returnsContractBilledByCountry() { 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), null, "vlad", null, @@ -777,8 +728,7 @@ public void returnsContractBilledTo() { 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", null, "RO", @@ -809,8 +759,7 @@ public void returnsContractBilledToCountry() { 1, contract, LocalDateTime.now(), - LocalDateTime.now(), - "transacetionId123", + Mockito.mock(Payment.class), "mihai", null, null, @@ -840,7 +789,6 @@ public void noPlatformInvoiceIfActive() { Mockito.mock(Contract.class), LocalDateTime.now(), null, - null, "mihai", "vlad", "RO", @@ -866,12 +814,14 @@ public void noPlatformInvoiceIfFakePayment() { "PlatformInvoices storage should not be called!" ) ); + final Payment fake = Mockito.mock(Payment.class); + Mockito.when(fake.status()).thenReturn(Payment.Status.SUCCESSFUL); + Mockito.when(fake.transactionId()).thenReturn("fake_payment_123"); final Invoice invoice = new StoredInvoice( 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "fake_payment_123", + fake, "mihai", "vlad", "RO", @@ -903,12 +853,15 @@ public void returnsPlatformInvoiceIfRealPayment() { Mockito.when(all.getByPayment(transactionId, payment)) .thenReturn(found); + final Payment successful = Mockito.mock(Payment.class); + Mockito.when(successful.status()).thenReturn(Payment.Status.SUCCESSFUL); + Mockito.when(successful.transactionId()).thenReturn(transactionId); + Mockito.when(successful.paymentTime()).thenReturn(payment); final Invoice invoice = new StoredInvoice( 1, Mockito.mock(Contract.class), LocalDateTime.now(), - payment, - transactionId, + successful, "mihai", "vlad", "RO", @@ -950,6 +903,8 @@ public void returnsOutputOfPdfFile() throws IOException { ), storage ); + final Payment payment = Mockito.mock(Payment.class); + Mockito.when(payment.status()).thenReturn(Payment.Status.SUCCESSFUL); final Invoice invoice = new StoredInvoice( 1, new StoredContract( @@ -965,8 +920,7 @@ public void returnsOutputOfPdfFile() throws IOException { storage ), LocalDateTime.now(), - LocalDateTime.now(), - "transaction123", + payment, "mihai", "contributro", "RO", @@ -1012,8 +966,7 @@ public void returnsPayments() { 1, Mockito.mock(Contract.class), LocalDateTime.now(), - LocalDateTime.now(), - "transactionID", + Mockito.mock(Payment.class), "mihai", "vlad", "RO", diff --git a/self-core-impl/src/test/java/com/selfxdsd/core/mock/InMemoryInvoices.java b/self-core-impl/src/test/java/com/selfxdsd/core/mock/InMemoryInvoices.java index 596e7bc3..5709acd1 100644 --- a/self-core-impl/src/test/java/com/selfxdsd/core/mock/InMemoryInvoices.java +++ b/self-core-impl/src/test/java/com/selfxdsd/core/mock/InMemoryInvoices.java @@ -80,7 +80,6 @@ public Invoice createNewInvoice(final Contract.Id contractId) { null, null, null, - null, BigDecimal.valueOf(0), this.storage ); diff --git a/self-core-impl/src/test/java/com/selfxdsd/core/projects/FakeWalletTestCase.java b/self-core-impl/src/test/java/com/selfxdsd/core/projects/FakeWalletTestCase.java index 3e865977..4dcef6bf 100644 --- a/self-core-impl/src/test/java/com/selfxdsd/core/projects/FakeWalletTestCase.java +++ b/self-core-impl/src/test/java/com/selfxdsd/core/projects/FakeWalletTestCase.java @@ -357,10 +357,7 @@ public void canPayInvoice() { MatcherAssert.assertThat(paidInvoice.isPaid(), Matchers.is(true)); MatcherAssert.assertThat(paidInvoice.invoiceId(), Matchers.is(invoice.invoiceId())); - MatcherAssert.assertThat(paidInvoice.paymentTime(), - Matchers.notNullValue()); - MatcherAssert.assertThat(paidInvoice.transactionId(), - Matchers.notNullValue()); + MatcherAssert.assertThat(paidInvoice.latest(), Matchers.notNullValue()); } /**