killbill-uncached

payment: plug control APIs from JAX-RS Signed-off-by: Pierre-Alexandre

12/15/2014 7:56:38 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 6ffc71a..a5e28fc 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
@@ -83,6 +83,7 @@ import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethod;
+import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.util.api.AuditLevel;
@@ -710,6 +711,7 @@ public class AccountResource extends JaxRsResourceBase {
     public Response processPayment(final PaymentTransactionJson json,
                                    @PathParam("accountId") final String accountIdStr,
                                    @QueryParam("paymentMethodId") final String paymentMethodIdStr,
+                                   @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
                                    @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                    @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                    @HeaderParam(HDR_REASON) final String reason,
@@ -729,22 +731,23 @@ public class AccountResource extends JaxRsResourceBase {
         final UUID paymentId = json.getPaymentId() == null ? null : UUID.fromString(json.getPaymentId());
 
         final TransactionType transactionType = TransactionType.valueOf(json.getTransactionType());
+        final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
         final Payment result;
         switch (transactionType) {
             case AUTHORIZE:
-                result = paymentApi.createAuthorization(account, paymentMethodId, paymentId, json.getAmount(), currency,
-                                                        json.getPaymentExternalKey(), json.getTransactionExternalKey(),
-                                                        pluginProperties, callContext);
+                result = paymentApi.createAuthorizationWithPaymentControl(account, paymentMethodId, paymentId, json.getAmount(), currency,
+                                                                          json.getPaymentExternalKey(), json.getTransactionExternalKey(),
+                                                                          pluginProperties, paymentOptions, callContext);
                 break;
             case PURCHASE:
-                result = paymentApi.createPurchase(account, paymentMethodId, paymentId, json.getAmount(), currency,
-                                                   json.getPaymentExternalKey(), json.getTransactionExternalKey(),
-                                                   pluginProperties, callContext);
+                result = paymentApi.createPurchaseWithPaymentControl(account, paymentMethodId, paymentId, json.getAmount(), currency,
+                                                                     json.getPaymentExternalKey(), json.getTransactionExternalKey(),
+                                                                     pluginProperties, paymentOptions, callContext);
                 break;
             case CREDIT:
-                result = paymentApi.createCredit(account, paymentMethodId, paymentId, json.getAmount(), currency,
-                                                 json.getPaymentExternalKey(), json.getTransactionExternalKey(),
-                                                 pluginProperties, callContext);
+                result = paymentApi.createCreditWithPaymentControl(account, paymentMethodId, paymentId, json.getAmount(), currency,
+                                                                   json.getPaymentExternalKey(), json.getTransactionExternalKey(),
+                                                                   pluginProperties, paymentOptions, callContext);
                 break;
             default:
                 return Response.status(Status.PRECONDITION_FAILED).entity("TransactionType " + transactionType + " is not allowed for an account").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 d7c5ab8..836d8c5 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
@@ -83,6 +83,7 @@ public interface JaxrsResource {
     public static final String QUERY_PAYMENT_AMOUNT = "paymentAmount";
     public static final String QUERY_PAYMENT_WITH_REFUNDS_AND_CHARGEBACKS = "withRefundsAndChargebacks";
     public static final String QUERY_PAYMENT_PLUGIN_NAME = "pluginName";
+    public static final String QUERY_PAYMENT_CONTROL_PLUGIN_NAME = "controlPluginName";
 
     public static final String QUERY_TAGS = "tagList";
     public static final String QUERY_TAGS_INCLUDED_DELETED = "includedDeleted";
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 ac7e9b6..217453e 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
@@ -366,6 +366,14 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected PaymentOptions createInvoicePaymentControlPluginApiPaymentOptions(final boolean isExternalPayment) {
+        return createControlPluginApiPaymentOptions(isExternalPayment, ImmutableList.<String>of("__INVOICE_PAYMENT_CONTROL_PLUGIN__"));
+    }
+
+    protected PaymentOptions createControlPluginApiPaymentOptions(@Nullable final List<String> paymentControlPluginNames) {
+        return createControlPluginApiPaymentOptions(false, paymentControlPluginNames);
+    }
+
+    protected PaymentOptions createControlPluginApiPaymentOptions(final boolean isExternalPayment, final List<String> paymentControlPluginNames) {
         return new PaymentOptions() {
             @Override
             public boolean isExternalPayment() {
@@ -374,8 +382,8 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
 
             @Override
             public List<String> getPaymentControlPluginNames() {
-                /* Will default to org.killbill.payment.invoice.plugin in payment sub-system */
-                return null;
+                // DefaultPaymentApi will add the default configured ones to this list
+                return paymentControlPluginNames;
             }
         };
     }
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 3613578..2bb3816 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
@@ -32,6 +32,7 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.core.PaymentMethodProcessor;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginRoutingPaymentProcessor;
+import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -40,6 +41,8 @@ import org.killbill.billing.util.entity.Pagination;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
+
 public class DefaultPaymentApi implements PaymentApi {
 
     private static final boolean SHOULD_LOCK_ACCOUNT = true;
@@ -66,7 +69,6 @@ public class DefaultPaymentApi implements PaymentApi {
     @Override
     public Payment createAuthorization(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                        final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
         checkNotNullParameter(amount, "amount");
@@ -85,6 +87,11 @@ public class DefaultPaymentApi implements PaymentApi {
     public Payment createAuthorizationWithPaymentControl(final Account account, final UUID paymentMethodId, final UUID paymentId, final BigDecimal amount, final Currency currency,
                                                          @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                                          final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createAuthorization(account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, properties, callContext);
+        }
+
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
         checkNotNullParameter(amount, "amount");
@@ -96,7 +103,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return pluginRoutingPaymentProcessor.createAuthorization(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                                 properties, toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
+                                                                 properties, paymentControlPluginNames, callContext, internalCallContext);
     }
 
     @Override
@@ -119,7 +126,6 @@ public class DefaultPaymentApi implements PaymentApi {
     @Override
     public Payment createPurchase(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                   final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
         checkNotNullParameter(amount, "amount");
@@ -137,6 +143,10 @@ public class DefaultPaymentApi implements PaymentApi {
     @Override
     public Payment createPurchaseWithPaymentControl(final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentExternalKey, final String paymentTransactionExternalKey,
                                                     final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createPurchase(account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, properties, callContext);
+        }
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(amount, "amount");
@@ -158,7 +168,7 @@ public class DefaultPaymentApi implements PaymentApi {
                                            paymentMethodId :
                                            paymentMethodProcessor.createOrGetExternalPaymentMethod(UUID.randomUUID().toString(), account, properties, callContext, internalCallContext);
         return pluginRoutingPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                            properties, toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
+                                                            properties, paymentControlPluginNames, callContext, internalCallContext);
 
     }
 
@@ -199,6 +209,10 @@ public class DefaultPaymentApi implements PaymentApi {
     @Override
     public Payment createRefundWithPaymentControl(final Account account, final UUID paymentId, @Nullable final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties,
                                                   final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createRefund(account, paymentId, amount, currency, paymentTransactionExternalKey, properties, callContext);
+        }
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(currency, "currency");
@@ -213,7 +227,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return pluginRoutingPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                                          properties, toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
+                                                          properties, paymentControlPluginNames, callContext, internalCallContext);
 
     }
 
@@ -221,7 +235,6 @@ public class DefaultPaymentApi implements PaymentApi {
     public Payment createCredit(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
                                 @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                 final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
         checkNotNullParameter(amount, "amount");
@@ -241,6 +254,11 @@ public class DefaultPaymentApi implements PaymentApi {
     public Payment createCreditWithPaymentControl(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
                                                   @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                                   final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createCredit(account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, properties, callContext);
+        }
+
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
         checkNotNullParameter(amount, "amount");
@@ -252,12 +270,11 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return pluginRoutingPaymentProcessor.createCredit(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                          properties, toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
+                                                          properties, paymentControlPluginNames, callContext, internalCallContext);
     }
 
     @Override
     public Payment notifyPendingTransactionOfStateChanged(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final CallContext callContext) throws PaymentApiException {
-
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentTransactionId, "paymentTransactionId");
 
@@ -290,6 +307,11 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public Payment createChargebackWithPaymentControl(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createChargeback(account, paymentId, amount, currency, paymentTransactionExternalKey, callContext);
+        }
+
         checkNotNullParameter(account, "account");
         checkNotNullParameter(amount, "amount");
         checkNotNullParameter(currency, "currency");
@@ -298,7 +320,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return pluginRoutingPaymentProcessor.createChargeback(account, paymentId, paymentTransactionExternalKey, amount, currency,
-                                                              toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
+                                                              paymentControlPluginNames, callContext, internalCallContext);
     }
 
     @Override
@@ -463,7 +485,21 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     private List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions) {
-        return paymentOptions.getPaymentControlPluginNames() != null ? paymentOptions.getPaymentControlPluginNames() : paymentConfig.getPaymentControlPluginNames();
+        // Special path for JAX-RS InvoicePayment endpoints (see JaxRsResourceBase)
+        if (paymentConfig.getPaymentControlPluginNames() != null &&
+            paymentOptions.getPaymentControlPluginNames() != null &&
+            paymentOptions.getPaymentControlPluginNames().size() == 1 &&
+            InvoicePaymentRoutingPluginApi.PLUGIN_NAME.equals(paymentOptions.getPaymentControlPluginNames().get(0))) {
+            final List<String> paymentControlPluginNames = new LinkedList<String>(paymentOptions.getPaymentControlPluginNames());
+            paymentControlPluginNames.addAll(paymentConfig.getPaymentControlPluginNames());
+            return paymentControlPluginNames;
+        } else if (paymentOptions.getPaymentControlPluginNames() != null && !paymentOptions.getPaymentControlPluginNames().isEmpty()) {
+            return paymentOptions.getPaymentControlPluginNames();
+        } else if (paymentConfig.getPaymentControlPluginNames() != null && !paymentConfig.getPaymentControlPluginNames().isEmpty()) {
+            return paymentConfig.getPaymentControlPluginNames();
+        } else {
+            return ImmutableList.<String>of();
+        }
     }
 
     private void checkNotNullParameter(final Object parameter, final String parameterName) throws PaymentApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
index a904698..80c1770 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
@@ -20,6 +20,7 @@ package org.killbill.billing.payment.bus;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
@@ -32,8 +33,8 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
 import org.killbill.billing.payment.core.PluginRoutingPaymentProcessor;
+import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
@@ -45,7 +46,6 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 
@@ -93,9 +93,10 @@ public class InvoiceHandler {
             final CallContext callContext = internalContext.toCallContext(nonEntityDao.retrieveIdFromObject(internalContext.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID)));
 
             final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
-            final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames() != null ? paymentConfig.getPaymentControlPluginNames() : ImmutableList.of(InvoicePaymentRoutingPluginApi.PLUGIN_NAME);
+            final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames() != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames()) : new LinkedList<String>();
+            paymentControlPluginNames.add(InvoicePaymentRoutingPluginApi.PLUGIN_NAME);
             pluginRoutingPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUID.randomUUID().toString(), UUID.randomUUID().toString(),
-                                                            properties, paymentControlPluginNames, callContext, internalContext);
+                                                         properties, paymentControlPluginNames, callContext, internalContext);
         } catch (final AccountApiException e) {
             log.error("Failed to process invoice payment", e);
         } catch (final PaymentApiException e) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
index 3aafe01..28af33a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
@@ -47,12 +47,12 @@ public class PaymentStateContext {
     protected PaymentTransactionInfoPlugin paymentInfoPlugin;
     protected BigDecimal amount;
     protected String paymentExternalKey;
+    protected String paymentTransactionExternalKey;
     protected Currency currency;
 
     // Can be updated later via paymentTransactionModelDao (e.g. for auth or purchase)
     protected final UUID paymentId;
     protected final UUID transactionId;
-    protected final String paymentTransactionExternalKey;
     protected final Account account;
     protected final TransactionType transactionType;
     protected final boolean shouldLockAccountAndDispatch;
@@ -80,7 +80,7 @@ public class PaymentStateContext {
         this.isApiPayment = isApiPayment;
         this.paymentId = paymentId;
         this.transactionId = transactionId;
-        this.attemptId= attemptId;
+        this.attemptId = attemptId;
         this.paymentExternalKey = paymentExternalKey;
         this.paymentTransactionExternalKey = paymentTransactionExternalKey;
         this.transactionType = transactionType;
@@ -148,6 +148,10 @@ public class PaymentStateContext {
         return paymentTransactionExternalKey;
     }
 
+    public void setPaymentTransactionExternalKey(final String paymentTransactionExternalKey) {
+        this.paymentTransactionExternalKey = paymentTransactionExternalKey;
+    }
+
     public Account getAccount() {
         return account;
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java
index 8de364c..afe3140 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java
@@ -16,14 +16,17 @@
 
 package org.killbill.billing.payment.core.sm;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.killbill.automaton.OperationException;
 import org.killbill.automaton.State;
 import org.killbill.automaton.State.LeavingStateCallback;
 import org.killbill.billing.payment.api.TransactionType;
-import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.dao.PaymentModelDao;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dao.PluginPropertySerializer;
 import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
 
@@ -31,7 +34,7 @@ import com.google.common.base.Preconditions;
 
 public class RetryLeavingStateCallback implements LeavingStateCallback {
 
-    private PluginRoutingPaymentAutomatonRunner retryablePaymentAutomatonRunner;
+    private final PluginRoutingPaymentAutomatonRunner retryablePaymentAutomatonRunner;
     private final RetryablePaymentStateContext stateContext;
     private final State initialState;
     private final State retriedState;
@@ -50,37 +53,39 @@ public class RetryLeavingStateCallback implements LeavingStateCallback {
 
     @Override
     public void leavingState(final State state) throws OperationException {
-
         final DateTime utcNow = retryablePaymentAutomatonRunner.clock.getUTCNow();
 
-        Preconditions.checkState(stateContext.getPaymentExternalKey() != null || /* CAPTURE, PURCHASE, CREDIT calls will provide the paymentId */
-                                 stateContext.getPaymentId() != null);
-        if (stateContext.getPaymentExternalKey() == null) {
+        if (stateContext.getPaymentId() != null && stateContext.getPaymentExternalKey() == null) {
             final PaymentModelDao payment = paymentDao.getPayment(stateContext.getPaymentId(), stateContext.internalCallContext);
-            Preconditions.checkState(payment != null);
+            Preconditions.checkNotNull(payment, "payment cannot be null for id " + stateContext.getPaymentId());
             stateContext.setPaymentExternalKey(payment.getExternalKey());
+        } else if (stateContext.getPaymentExternalKey() == null) {
+            stateContext.setPaymentExternalKey(UUID.randomUUID().toString());
+        }
+        if (stateContext.getTransactionId() != null && stateContext.getPaymentTransactionExternalKey() == null) {
+            final PaymentTransactionModelDao paymentTransactionModelDao = paymentDao.getPaymentTransaction(stateContext.getTransactionId(), stateContext.internalCallContext);
+            Preconditions.checkNotNull(paymentTransactionModelDao, "paymentTransaction cannot be null for id " + stateContext.getTransactionId());
+            stateContext.setPaymentTransactionExternalKey(paymentTransactionModelDao.getTransactionExternalKey());
+        } else if (stateContext.getPaymentTransactionExternalKey() == null) {
+            stateContext.setPaymentTransactionExternalKey(UUID.randomUUID().toString());
         }
 
-
-        if (state.getName().equals(initialState.getName()) ||
-            state.getName().equals(retriedState.getName())) {
-
+        if (state.getName().equals(initialState.getName()) || state.getName().equals(retriedState.getName())) {
             try {
-                final byte [] serializedProperties = PluginPropertySerializer.serialize(stateContext.getProperties());
-
+                final byte[] serializedProperties = PluginPropertySerializer.serialize(stateContext.getProperties());
 
                 final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(stateContext.getAccount().getId(), stateContext.getPaymentMethodId(),
-                                                                                  utcNow, utcNow, stateContext.getPaymentExternalKey(), null,
-                                                                                  stateContext.paymentTransactionExternalKey, transactionType, initialState.getName(),
+                                                                                  utcNow, utcNow, stateContext.getPaymentExternalKey(), stateContext.getTransactionId(),
+                                                                                  stateContext.getPaymentTransactionExternalKey(), transactionType, initialState.getName(),
                                                                                   stateContext.getAmount(), stateContext.getCurrency(),
                                                                                   stateContext.getPaymentControlPluginNames(), serializedProperties);
 
-                retryablePaymentAutomatonRunner.paymentDao.insertPaymentAttemptWithProperties(attempt, stateContext.internalCallContext);
+                retryablePaymentAutomatonRunner.paymentDao.insertPaymentAttemptWithProperties(attempt, stateContext.getInternalCallContext());
+
                 stateContext.setAttemptId(attempt.getId());
-            } catch (PluginPropertySerializerException e) {
+            } catch (final PluginPropertySerializerException e) {
                 throw new OperationException(e);
             }
-
         }
     }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java b/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
index 0b5826d..86a0014 100644
--- a/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
@@ -81,8 +81,8 @@ public interface PaymentConfig extends KillbillConfig {
     public TimeSpan getJanitorRunningRate();
 
     @Config("org.killbill.payment.invoice.plugin")
-    @Default("__INVOICE_PAYMENT_CONTROL_PLUGIN__")
-    @Description("Whether the payment subsystem is off")
+    @Default("")
+    @Description("Default payment control plugin names")
     public List<String> getPaymentControlPluginNames();
 
     @Config("org.killbill.payment.off")