killbill-uncached

Merge pull request #584 from javier-gomez-hs/jgomez-issue-522 Issue

7/26/2016 5:24:46 PM

Details

diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 83e5192..6f8e466 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -688,7 +688,7 @@ public class AccountResource extends JaxRsResourceBase {
                                            invoice.getBalance() : remainingRequestPayment;
             if (amountToPay.compareTo(BigDecimal.ZERO) > 0) {
                 final UUID paymentMethodId = externalPayment ? null : account.getPaymentMethodId();
-                createPurchaseForInvoice(account, invoice.getId(), amountToPay, paymentMethodId, externalPayment, pluginProperties, callContext);
+                createPurchaseForInvoice(account, invoice.getId(), amountToPay, paymentMethodId, externalPayment, null, null, pluginProperties, callContext);
             }
             remainingRequestPayment = remainingRequestPayment.subtract(amountToPay);
             if (remainingRequestPayment.compareTo(BigDecimal.ZERO) == 0) {
@@ -742,7 +742,7 @@ public class AccountResource extends JaxRsResourceBase {
         final UUID paymentMethodId = paymentApi.addPaymentMethod(account, data.getExternalKey(), data.getPluginName(), isDefault, data.getPluginDetail(), pluginProperties, callContext);
         if (payAllUnpaidInvoices && unpaidInvoices.size() > 0) {
             for (final Invoice invoice : unpaidInvoices) {
-                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), paymentMethodId, false, pluginProperties, callContext);
+                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), paymentMethodId, false, null, null, pluginProperties, callContext);
             }
         }
         return uriBuilder.buildResponse(PaymentMethodResource.class, "getPaymentMethod", paymentMethodId, uriInfo.getBaseUri().toString());
@@ -830,7 +830,7 @@ public class AccountResource extends JaxRsResourceBase {
         if (payAllUnpaidInvoices) {
             final Collection<Invoice> unpaidInvoices = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
             for (final Invoice invoice : unpaidInvoices) {
-                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), newPaymentMethodId, false, pluginProperties, callContext);
+                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), newPaymentMethodId, false, null, null, pluginProperties, callContext);
             }
         }
         return Response.status(Status.OK).build();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index fcda3be..a5f5ab1 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -93,6 +93,7 @@ import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.LocaleUtils;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -110,6 +111,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -490,6 +492,8 @@ public class InvoiceResource extends JaxRsResourceBase {
                                           @QueryParam(QUERY_PAY_INVOICE) @DefaultValue("false") final Boolean payInvoice,
                                           @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                           @QueryParam(QUERY_AUTO_COMMIT) @DefaultValue("false") final Boolean autoCommit,
+                                          @QueryParam(QUERY_PAYMENT_EXTERNAL_KEY) final String paymentExternalKey,
+                                          @QueryParam(QUERY_TRANSACTION_EXTERNAL_KEY) final String transactionExternalKey,
                                           @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                           @HeaderParam(HDR_REASON) final String reason,
                                           @HeaderParam(HDR_COMMENT) final String comment,
@@ -505,13 +509,24 @@ public class InvoiceResource extends JaxRsResourceBase {
         final LocalDate requestedDate = toLocalDateDefaultToday(account, requestedDateTimeString, callContext);
         final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, sanitizedExternalChargesJson, autoCommit, callContext);
 
+        // if all createdExternalCharges point to the same invoiceId, use the provided paymentExternalKey and / or transactionExternalKey
+        final boolean haveSameInvoiceId = Iterables.all(createdExternalCharges, new Predicate<InvoiceItem>() {
+            @Override
+            public boolean apply(final InvoiceItem input) {
+                return input.getInvoiceId().equals(createdExternalCharges.get(0).getInvoiceId());
+            }
+        });
+
         if (payInvoice) {
             final Collection<UUID> paidInvoices = new HashSet<UUID>();
             for (final InvoiceItem externalCharge : createdExternalCharges) {
                 if (!paidInvoices.contains(externalCharge.getInvoiceId())) {
                     paidInvoices.add(externalCharge.getInvoiceId());
                     final Invoice invoice = invoiceApi.getInvoice(externalCharge.getInvoiceId(), callContext);
-                    createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), account.getPaymentMethodId(), false, pluginProperties, callContext);
+                    createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), account.getPaymentMethodId(), false,
+                                             (haveSameInvoiceId && paymentExternalKey != null) ? paymentExternalKey : null,
+                                             (haveSameInvoiceId && transactionExternalKey != null) ? transactionExternalKey : null,
+                                             pluginProperties, callContext);
                 }
             }
         }
@@ -635,6 +650,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                              payment.getPurchasedAmount(), "InvoicePaymentJson purchasedAmount needs to be set");
         Preconditions.checkArgument(!externalPayment || payment.getPaymentMethodId() == null, "InvoicePaymentJson should not contain a paymwentMethodId when this is an external payment");
 
+
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
@@ -644,7 +660,8 @@ public class InvoiceResource extends JaxRsResourceBase {
 
         final UUID invoiceId = UUID.fromString(payment.getTargetInvoiceId());
 
-        final Payment result = createPurchaseForInvoice(account, invoiceId, payment.getPurchasedAmount(), paymentMethodId, externalPayment, pluginProperties, callContext);
+        final Payment result = createPurchaseForInvoice(account, invoiceId, payment.getPurchasedAmount(), paymentMethodId, externalPayment,
+                                                        (payment.getPaymentExternalKey() != null) ? payment.getPaymentExternalKey() : null, null, pluginProperties, callContext);
         return result != null ?
                uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId()) :
                Response.status(Status.NO_CONTENT).build();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index c4f18d3..0c833c8 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -69,6 +69,8 @@ public interface JaxrsResource {
     public static final String QUERY_EXTERNAL_KEY = "externalKey";
     public static final String QUERY_API_KEY = "apiKey";
     public static final String QUERY_REQUESTED_DT = "requestedDate";
+    public static final String QUERY_PAYMENT_EXTERNAL_KEY = "paymentExternalKey";
+    public static final String QUERY_TRANSACTION_EXTERNAL_KEY = "transactionExternalKey";
     public static final String QUERY_ENTITLEMENT_REQUESTED_DT = "entitlementDate";
     public static final String QUERY_BILLING_REQUESTED_DT = "billingDate";
     public static final String QUERY_CALL_COMPLETION = "callCompletion";
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index 92b5571..35e4b29 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -454,7 +454,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return properties;
     }
 
-    protected Payment createPurchaseForInvoice(final Account account, final UUID invoiceId, final BigDecimal amountToPay, final UUID paymentMethodId, final Boolean externalPayment, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
+    protected Payment createPurchaseForInvoice(final Account account, final UUID invoiceId, final BigDecimal amountToPay, final UUID paymentMethodId, final Boolean externalPayment, final String paymentExternalKey, final String transactionExternalKey, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
 
         final List<PluginProperty> properties = new ArrayList<PluginProperty>();
         final Iterator<PluginProperty> pluginPropertyIterator = pluginProperties.iterator();
@@ -462,8 +462,6 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
             properties.add(pluginPropertyIterator.next());
         }
 
-        final String paymentExternalKey = UUIDs.randomUUID().toString();
-        final String transactionExternalKey = UUIDs.randomUUID().toString();
         final PluginProperty invoiceProperty = new PluginProperty("IPCD_INVOICE_ID" /* InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID (contract with plugin)  */,
                                                                   invoiceId.toString(), false);
         properties.add(invoiceProperty);
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 07c4669..394320d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -292,7 +292,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkNotNullParameter(amount, "amount");
             checkNotNullParameter(currency, "currency");
         }
-        checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
+
         checkNotNullParameter(properties, "plugin properties");
 
         if (paymentMethodId == null && !paymentOptions.isExternalPayment()) {

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index ebde347..cb64711 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.117</version>
+        <version>0.118-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.17.2-SNAPSHOT</version>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 424b1dc..dfd8d8c 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -30,6 +30,7 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.AuditLog;
 import org.killbill.billing.client.model.Credit;
@@ -40,6 +41,7 @@ import org.killbill.billing.client.model.InvoicePayment;
 import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Invoices;
 import org.killbill.billing.client.model.PaymentMethod;
+import org.killbill.billing.client.model.Payments;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.invoice.api.DryRunType;
 import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -54,6 +56,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
@@ -489,6 +492,56 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
     }
 
+    @Test(groups = "slow", description = "Can create multiple external charges with same invoice and external keys")
+    public void testExternalChargesWithSameInvoiceAndExternalKeys() throws Exception {
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        // Get the invoices
+        assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 2);
+
+        // Post an external charge
+        final BigDecimal chargeAmount = BigDecimal.TEN;
+
+        final List<InvoiceItem> externalCharges = new ArrayList<InvoiceItem>();
+
+        // Does not pass currency to test on purpose that we will default to account currency
+        final InvoiceItem externalCharge1 = new InvoiceItem();
+        externalCharge1.setAccountId(accountJson.getAccountId());
+        externalCharge1.setAmount(chargeAmount);
+        externalCharge1.setDescription(UUID.randomUUID().toString());
+        externalCharges.add(externalCharge1);
+
+        final InvoiceItem externalCharge2 = new InvoiceItem();
+        externalCharge2.setAccountId(accountJson.getAccountId());
+        externalCharge2.setAmount(chargeAmount);
+        externalCharge2.setCurrency(accountJson.getCurrency());
+        externalCharge2.setDescription(UUID.randomUUID().toString());
+        externalCharges.add(externalCharge2);
+
+        String paymentExternalKey = "anyPaymentExternalKey";
+        String transactionExternalKey = "anyTransactionExternalKey";
+
+        final List<InvoiceItem> createdExternalCharges =
+                killBillClient.createExternalCharges(externalCharges, clock.getUTCToday(), true, true,
+                                                     paymentExternalKey, transactionExternalKey, RequestOptions.builder()
+                                                                                                               .withCreatedBy(createdBy)
+                                                                                                               .withReason(reason)
+                                                                                                               .withComment(comment).build());
+        assertEquals(createdExternalCharges.size(), 2);
+        assertEquals(createdExternalCharges.get(0).getCurrency().toString(), accountJson.getCurrency());
+        assertEquals(createdExternalCharges.get(1).getCurrency().toString(), accountJson.getCurrency());
+
+        // Verify the total number of invoices
+        assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
+
+        Payments payments = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), AuditLevel.NONE, RequestOptions.empty());
+        assertNotNull(payments);
+        // Verify payment with paymentExternalKey provided
+        assertEquals(payments.get(payments.size() - 1).getPaymentExternalKey(), paymentExternalKey);
+        // Verify transactions with transactionExternalKey provided
+        assertEquals(payments.get(payments.size() - 1).getTransactions().get(0).getTransactionExternalKey(), transactionExternalKey);
+    }
+
     @Test(groups = "slow", description = "Can create an external charge and trigger a payment")
     public void testExternalChargeOnNewInvoiceWithAutomaticPayment() throws Exception {
         final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();