killbill-uncached

Implement notifyPendingTransactionOfStateChanged through

7/11/2014 10:03:07 PM

Changes

Details

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 087174e..6fb9f44 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
@@ -72,7 +72,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.AUTHORIZE.name(), account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        logAPICall(TransactionType.AUTHORIZE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -89,7 +89,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.CAPTURE.name(), account, null, paymentId, amount, currency, null, paymentTransactionExternalKey);
+        logAPICall(TransactionType.CAPTURE.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -107,7 +107,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.PURCHASE.name(), account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        logAPICall(TransactionType.PURCHASE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -126,7 +126,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.PURCHASE.name(), account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        logAPICall(TransactionType.PURCHASE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
 
         if (paymentMethodId == null && !paymentOptions.isExternalPayment()) {
             throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "paymentMethodId", "should not be null");
@@ -150,7 +150,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.VOID.name(), account, null, paymentId, null, null, null, paymentTransactionExternalKey);
+        logAPICall(TransactionType.VOID.name(), account, null, paymentId, null, null, null, null, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
@@ -169,7 +169,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.REFUND.name(), account, null, paymentId, amount, currency, null, paymentTransactionExternalKey);
+        logAPICall(TransactionType.REFUND.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -189,7 +189,7 @@ public class DefaultPaymentApi implements PaymentApi {
             checkPositiveAmount(amount);
         }
 
-        logAPICall(TransactionType.REFUND.name(), account, null, paymentId, amount, currency, null, paymentTransactionExternalKey);
+        logAPICall(TransactionType.REFUND.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return pluginControlledPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -209,7 +209,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.CREDIT.name(), account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        logAPICall(TransactionType.CREDIT.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -218,20 +218,20 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public void notifyPendingTransactionOfStateChanged(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final CallContext callContext) throws PaymentApiException {
+    public Payment notifyPendingTransactionOfStateChanged(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentTransactionId, "paymentTransactionId");
 
-        logAPICall("NOTIFY_STATE_CHANGE", account, null, paymentTransactionId /* STEPH TBD if this is paymentId or transactionId */, null, null, null, null);
+        logAPICall("NOTIFY_STATE_CHANGE", account, null, null, paymentTransactionId, null, null, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        paymentProcessor.notifyPendingPaymentOfStateChanged(account, paymentTransactionId, isSuccess, callContext, internalCallContext);
+        return paymentProcessor.notifyPendingPaymentOfStateChanged(account, paymentTransactionId, isSuccess, callContext, internalCallContext);
     }
 
     @Override
-    public void notifyPendingTransactionOfStateChangedWithPaymentControl(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final PaymentOptions paymentOptions, final CallContext context) throws PaymentApiException {
-
+    public Payment notifyPendingTransactionOfStateChangedWithPaymentControl(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final PaymentOptions paymentOptions, final CallContext context) throws PaymentApiException {
+        throw new IllegalStateException("Not implemented");
     }
 
 
@@ -243,7 +243,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkNotNullParameter(paymentId, "paymentId");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.CHARGEBACK.name(), account, null, paymentId, amount, currency, null, paymentTransactionExternalKey);
+        logAPICall(TransactionType.CHARGEBACK.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
@@ -388,7 +388,7 @@ public class DefaultPaymentApi implements PaymentApi {
         return paymentMethods;
     }
 
-    private void logAPICall(final String transactionType, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey) {
+    private void logAPICall(final String transactionType, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId,  @Nullable final UUID transactionId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey) {
         if (log.isInfoEnabled()) {
             final StringBuilder logLine = new StringBuilder();
             logLine.append("PaymentApi : ")
@@ -411,6 +411,10 @@ public class DefaultPaymentApi implements PaymentApi {
                 logLine.append(", paymentId = ")
                        .append(paymentId);
             }
+            if (transactionId != null) {
+                logLine.append(", transactionId = ")
+                       .append(transactionId);
+            }
             if (amount != null) {
                 logLine.append(", amount = ")
                        .append(amount);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index 646f4b2..4ecddab 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -28,7 +28,7 @@ import java.util.concurrent.ExecutorService;
 import javax.annotation.Nullable;
 import javax.inject.Inject;
 
-import org.killbill.automaton.State;
+import org.killbill.automaton.OperationResult;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountInternalApi;
@@ -61,7 +61,6 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.EntityPaginationBuilder;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
-import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
@@ -110,42 +109,42 @@ public class PaymentProcessor extends ProcessorBase {
     public Payment createAuthorization(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
                                        @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                                        final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.AUTHORIZE, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.AUTHORIZE, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
     public Payment createCapture(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency,
                                  @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                                  final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
 
-        return performOperation(isApiPayment, attemptId, TransactionType.CAPTURE, account, null, paymentId, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.CAPTURE, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
     public Payment createPurchase(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
-                                  @Nullable  final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+                                  @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                                   final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.PURCHASE, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.PURCHASE, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
     public Payment createVoid(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                               final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.VOID, account, null, paymentId, null, null, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.VOID, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
     public Payment createRefund(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency,
                                 final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                                 final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.REFUND, account, null, paymentId, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.REFUND, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
     public Payment createCredit(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
                                 @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                                 final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.CREDIT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.CREDIT, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
     public Payment createChargeback(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
                                     final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
+        return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
     }
 
     public List<Payment> getAccountPayments(final UUID accountId, final InternalTenantContext tenantContext) throws PaymentApiException {
@@ -158,29 +157,22 @@ public class PaymentProcessor extends ProcessorBase {
                                                              public Payment apply(final PaymentModelDao curPaymentModelDao) {
                                                                  return toPayment(curPaymentModelDao, transactionsModelDao, null);
                                                              }
-                                                         }
-                                                        );
+                                                         });
     }
 
-    public void notifyPendingPaymentOfStateChanged(final Account account, UUID transactionId, final boolean isSuccess, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+    public Payment notifyPendingPaymentOfStateChanged(final Account account, final UUID transactionId, final boolean isSuccess, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
 
         final PaymentTransactionModelDao transactionModelDao = paymentDao.getPaymentTransaction(transactionId, internalCallContext);
         if (transactionModelDao.getTransactionStatus() != TransactionStatus.PENDING) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, transactionModelDao.getPaymentId());
 
         }
-        final PaymentModelDao paymentModelDao = paymentDao.getPayment(transactionModelDao.getPaymentId(), internalCallContext);
-        Preconditions.checkState(paymentModelDao != null);
-
-        final TransactionStatus newStatus = isSuccess ? TransactionStatus.SUCCESS : TransactionStatus.PAYMENT_FAILURE;
-        // STEPH This works if the pending transaction we are trying to update matches is the one that gave the state to the payment. Also can we have multiple PENDING for a given payment?
-        final State currentPaymentState;
-        final String stateName = paymentModelDao.getStateName();
-        final String lastSuccessPaymentStateStrOrNull = paymentSMHelper.isSuccessState(stateName) ? stateName : null;
-        paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), transactionModelDao.getPaymentId(), transactionModelDao.getTransactionType(),
-                                                           stateName, lastSuccessPaymentStateStrOrNull, transactionModelDao.getId(), newStatus,
-                                                           transactionModelDao.getProcessedAmount(), transactionModelDao.getProcessedCurrency(),
-                                                           transactionModelDao.getGatewayErrorCode(), transactionModelDao.getGatewayErrorMsg(), internalCallContext);
+
+        final OperationResult overridePluginResult = isSuccess ? OperationResult.SUCCESS : OperationResult.FAILURE;
+
+        return performOperation(true, null, transactionModelDao.getTransactionType(), account, null, transactionModelDao.getPaymentId(),
+                                transactionModelDao.getId(), transactionModelDao.getAmount(), transactionModelDao.getCurrency(), null, transactionModelDao.getTransactionExternalKey(), true,
+                                overridePluginResult, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
     }
 
     public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
@@ -313,24 +305,29 @@ public class PaymentProcessor extends ProcessorBase {
         return toPayment(paymentModelDao, transactionsForAccount, pluginTransactions);
     }
 
-    private Payment performOperation(final boolean isApiPayment, @Nullable final UUID attemptId, final TransactionType transactionType, final Account account, final UUID paymentMethodId, final UUID paymentId, final BigDecimal amount, final Currency currency,
+    private Payment performOperation(final boolean isApiPayment, @Nullable final UUID attemptId,
+                                     final TransactionType transactionType, final Account account,
+                                     @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final UUID transactionId,
+                                     @Nullable final BigDecimal amount, @Nullable final Currency currency,
                                      @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
-                                     final boolean shouldLockAccountAndDispatch, final Iterable<PluginProperty> properties, final CallContext callContext,
-                                     final InternalCallContext internalCallContext) throws PaymentApiException {
+                                     final boolean shouldLockAccountAndDispatch, @Nullable final OperationResult overridePluginOperationResult,
+                                     final Iterable<PluginProperty> properties,
+                                     final CallContext callContext,final InternalCallContext internalCallContext) throws PaymentApiException {
 
         validateUniqueTransactionExternalKey(paymentTransactionExternalKey, internalCallContext);
-
         final UUID nonNullPaymentId = paymentAutomatonRunner.run(isApiPayment,
                                                                  transactionType,
                                                                  account,
                                                                  attemptId,
                                                                  paymentMethodId,
                                                                  paymentId,
+                                                                 transactionId,
                                                                  paymentExternalKey,
                                                                  paymentTransactionExternalKey,
                                                                  amount,
                                                                  currency,
                                                                  shouldLockAccountAndDispatch,
+                                                                 overridePluginOperationResult,
                                                                  properties,
                                                                  callContext,
                                                                  internalCallContext);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/AuthorizeOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/AuthorizeOperation.java
index e76c928..b7e2d37 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/AuthorizeOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/AuthorizeOperation.java
@@ -41,7 +41,7 @@ public class AuthorizeOperation extends PaymentOperation {
         logger.debug("Starting AUTHORIZE for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
         return plugin.authorizePayment(paymentStateContext.getAccount().getId(),
                                        paymentStateContext.getPaymentId(),
-                                       paymentStateContext.getTransactionPaymentId(),
+                                       paymentStateContext.getTransactionId(),
                                        paymentStateContext.getPaymentMethodId(),
                                        paymentStateContext.getAmount(),
                                        paymentStateContext.getCurrency(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/CaptureOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/CaptureOperation.java
index c3facf5..b5e20ef 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/CaptureOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/CaptureOperation.java
@@ -41,7 +41,7 @@ public class CaptureOperation extends PaymentOperation {
         logger.debug("Starting CAPTURE for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
         return plugin.capturePayment(paymentStateContext.getAccount().getId(),
                                      paymentStateContext.getPaymentId(),
-                                     paymentStateContext.getTransactionPaymentId(),
+                                     paymentStateContext.getTransactionId(),
                                      paymentStateContext.getPaymentMethodId(),
                                      paymentStateContext.getAmount(),
                                      paymentStateContext.getCurrency(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java
index 14436a6..04afc77 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java
@@ -68,7 +68,7 @@ public class ChargebackOperation extends PaymentOperation {
             status = PaymentPluginStatus.PROCESSED;
         }
         return new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(),
-                                                 paymentStateContext.getTransactionPaymentId(),
+                                                 paymentStateContext.getTransactionId(),
                                                  TransactionType.CHARGEBACK,
                                                  paymentStateContext.getAmount(),
                                                  paymentStateContext.getCurrency(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/CreditOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/CreditOperation.java
index 1e6201e..06be146 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/CreditOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/CreditOperation.java
@@ -41,7 +41,7 @@ public class CreditOperation extends PaymentOperation {
         logger.debug("Starting CREDIT for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
         return plugin.creditPayment(paymentStateContext.getAccount().getId(),
                                     paymentStateContext.getPaymentId(),
-                                    paymentStateContext.getTransactionPaymentId(),
+                                    paymentStateContext.getTransactionId(),
                                     paymentStateContext.getPaymentMethodId(),
                                     paymentStateContext.getAmount(),
                                     paymentStateContext.getCurrency(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
index 440ddde..49d48f6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
@@ -36,7 +36,6 @@ import org.killbill.automaton.State.EnteringStateCallback;
 import org.killbill.automaton.State.LeavingStateCallback;
 import org.killbill.automaton.StateMachine;
 import org.killbill.automaton.StateMachineConfig;
-import org.killbill.automaton.Transition;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -47,6 +46,7 @@ import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 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.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.glue.PaymentModule;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -58,9 +58,6 @@ import org.killbill.commons.locker.GlobalLocker;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.inject.name.Named;
 
 import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
@@ -98,15 +95,16 @@ public class PaymentAutomatonRunner {
     }
 
     public UUID run(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID attemptId, @Nullable final UUID paymentMethodId,
-                    @Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
+                    @Nullable final UUID paymentId,  @Nullable final UUID transactionId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                     @Nullable final BigDecimal amount, @Nullable final Currency currency,
-                    final boolean shouldLockAccount, final Iterable<PluginProperty> properties,
+                    final boolean shouldLockAccount, final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties,
                     final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
 
         final DateTime utcNow = clock.getUTCNow();
 
-        final PaymentStateContext paymentStateContext = new PaymentStateContext(isApiPayment, paymentId, attemptId, paymentExternalKey, paymentTransactionExternalKey, transactionType,
-                                                                                                  account, paymentMethodId, amount, currency, shouldLockAccount, properties, internalCallContext, callContext);
+        final PaymentStateContext paymentStateContext = new PaymentStateContext(isApiPayment, paymentId, transactionId, attemptId,  paymentExternalKey, paymentTransactionExternalKey, transactionType,
+                                                                                                  account, paymentMethodId, amount, currency, shouldLockAccount, overridePluginOperationResult, properties, internalCallContext, callContext);
+
         final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, utcNow, paymentDao, pluginRegistry, internalCallContext, eventBus, paymentSMHelper);
 
         final UUID effectivePaymentMethodId;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentLeavingStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentLeavingStateCallback.java
index ad0419c..898669e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentLeavingStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentLeavingStateCallback.java
@@ -22,6 +22,7 @@ import org.killbill.automaton.State;
 import org.killbill.automaton.State.LeavingStateCallback;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,8 +49,14 @@ public abstract class PaymentLeavingStateCallback implements LeavingStateCallbac
                 throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, paymentStateContext.getAccount().getId());
             }
 
-
-            daoHelper.createNewPaymentTransaction();
+            // If the transactionId has been specified, it means this is an operation in several stage (INIT -> PENDING -> XXX), so we don't need to create the row,
+            // but we do need to set the transactionModelDao in the context so enteringState logic can take place.
+            if (paymentStateContext.getTransactionId() == null) {
+                daoHelper.createNewPaymentTransaction();
+            } else {
+                final PaymentTransactionModelDao transactionModelDao =  daoHelper.paymentDao.getPaymentTransaction(paymentStateContext.getTransactionId(), paymentStateContext.getInternalCallContext());
+                paymentStateContext.setPaymentTransactionModelDao(transactionModelDao);
+            }
         } catch (PaymentApiException e) {
             throw new OperationException(e);
         }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
index 786171c..b4ff388 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
@@ -35,7 +35,9 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
+import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.LockFailedException;
 
@@ -151,11 +153,27 @@ public abstract class PaymentOperation extends OperationCallbackBase implements 
 
     private OperationResult doOperation() throws PaymentApiException {
         try {
-            final PaymentTransactionInfoPlugin paymentInfoPlugin = doCallSpecificOperationCallback();
-
-            paymentStateContext.setPaymentInfoPlugin(paymentInfoPlugin);
-
-            return processPaymentInfoPlugin();
+            //
+            // If the OperationResult was specified in the plugin, it means we want to bypass the plugin and just care
+            // about running through the state machine to bring the transaction/payment into a new state.
+            //
+            if (paymentStateContext.getOverridePluginOperationResult() == null) {
+                final PaymentTransactionInfoPlugin paymentInfoPlugin = doCallSpecificOperationCallback();
+                paymentStateContext.setPaymentInfoPlugin(paymentInfoPlugin);
+                return processPaymentInfoPlugin();
+            } else {
+                final PaymentTransactionInfoPlugin paymentInfoPlugin = new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(),
+                                                                                                        paymentStateContext.getTransactionId(),
+                                                                                                        paymentStateContext.getTransactionType(),
+                                                                                                        paymentStateContext.getPaymentTransactionModelDao().getProcessedAmount(),
+                                                                                                        paymentStateContext.getPaymentTransactionModelDao().getProcessedCurrency(),
+                                                                                                        paymentStateContext.getPaymentTransactionModelDao().getEffectiveDate(),
+                                                                                                        paymentStateContext.getPaymentTransactionModelDao().getCreatedDate(),
+                                                                                                        PaymentPluginStatus.PROCESSED,
+                                                                                                        null);
+                paymentStateContext.setPaymentInfoPlugin(paymentInfoPlugin);
+                return paymentStateContext.getOverridePluginOperationResult();
+            }
         } catch (final PaymentPluginApiException e) {
             throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
         }
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 a6b0cc2..32b18a2 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
@@ -23,6 +23,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.killbill.automaton.OperationResult;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.Currency;
@@ -45,11 +46,11 @@ public class PaymentStateContext {
     protected PaymentTransactionModelDao paymentTransactionModelDao;
     protected PaymentTransactionInfoPlugin paymentInfoPlugin;
     protected BigDecimal amount;
-    protected UUID transactionPaymentId;
     protected String paymentExternalKey;
 
     // 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 Currency currency;
@@ -59,24 +60,26 @@ public class PaymentStateContext {
     protected final InternalCallContext internalCallContext;
     protected final CallContext callContext;
     protected final boolean isApiPayment;
+    protected final OperationResult overridePluginOperationResult;
 
     // Use to create new transactions only
     public PaymentStateContext(final boolean isApiPayment, @Nullable final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final TransactionType transactionType,
                                final Account account, @Nullable final UUID paymentMethodId, final BigDecimal amount, final Currency currency,
                                final boolean shouldLockAccountAndDispatch, final Iterable<PluginProperty> properties,
                                final InternalCallContext internalCallContext, final CallContext callContext) {
-        this(isApiPayment, paymentId, null, null, paymentTransactionExternalKey, transactionType, account, paymentMethodId,
-             amount, currency, shouldLockAccountAndDispatch, properties, internalCallContext, callContext);
+        this(isApiPayment, paymentId, null, null, null, paymentTransactionExternalKey, transactionType, account, paymentMethodId,
+             amount, currency, shouldLockAccountAndDispatch, null, properties, internalCallContext, callContext);
     }
 
     // Used to create new payment and transactions
-    public PaymentStateContext(final boolean isApiPayment, @Nullable final UUID paymentId, @Nullable final UUID attemptId, @Nullable final String paymentExternalKey,
+    public PaymentStateContext(final boolean isApiPayment, @Nullable final UUID paymentId, final UUID transactionId, @Nullable final UUID attemptId, @Nullable final String paymentExternalKey,
                                @Nullable final String paymentTransactionExternalKey, final TransactionType transactionType,
                                final Account account, @Nullable final UUID paymentMethodId, final BigDecimal amount, final Currency currency,
-                               final boolean shouldLockAccountAndDispatch, final Iterable<PluginProperty> properties,
+                               final boolean shouldLockAccountAndDispatch, final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties,
                                final InternalCallContext internalCallContext, final CallContext callContext) {
         this.isApiPayment = isApiPayment;
         this.paymentId = paymentId;
+        this.transactionId = transactionId;
         this.attemptId= attemptId;
         this.paymentExternalKey = paymentExternalKey;
         this.paymentTransactionExternalKey = paymentTransactionExternalKey;
@@ -86,6 +89,7 @@ public class PaymentStateContext {
         this.amount = amount;
         this.currency = currency;
         this.shouldLockAccountAndDispatch = shouldLockAccountAndDispatch;
+        this.overridePluginOperationResult = overridePluginOperationResult;
         this.properties = properties;
         this.internalCallContext = internalCallContext;
         this.callContext = callContext;
@@ -128,8 +132,8 @@ public class PaymentStateContext {
         return paymentId != null ? paymentId : (paymentTransactionModelDao != null ? paymentTransactionModelDao.getPaymentId() : null);
     }
 
-    public UUID getTransactionPaymentId() {
-        return transactionPaymentId != null ? transactionPaymentId : (paymentTransactionModelDao != null ? paymentTransactionModelDao.getId() : null);
+    public UUID getTransactionId() {
+        return transactionId != null ? transactionId : (paymentTransactionModelDao != null ? paymentTransactionModelDao.getId() : null);
     }
 
     public String getPaymentExternalKey() {
@@ -176,6 +180,10 @@ public class PaymentStateContext {
         return shouldLockAccountAndDispatch;
     }
 
+    public OperationResult getOverridePluginOperationResult() {
+        return overridePluginOperationResult;
+    }
+
     public Iterable<PluginProperty> getProperties() {
         return properties;
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PurchaseOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PurchaseOperation.java
index 39ef3c6..38757f5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PurchaseOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PurchaseOperation.java
@@ -41,7 +41,7 @@ public class PurchaseOperation extends PaymentOperation {
         logger.debug("Starting PURCHASE for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
         return plugin.purchasePayment(paymentStateContext.getAccount().getId(),
                                      paymentStateContext.getPaymentId(),
-                                     paymentStateContext.getTransactionPaymentId(),
+                                     paymentStateContext.getTransactionId(),
                                      paymentStateContext.getPaymentMethodId(),
                                      paymentStateContext.getAmount(),
                                      paymentStateContext.getCurrency(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RefundOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RefundOperation.java
index b78700e..3a7fca4 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RefundOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RefundOperation.java
@@ -41,7 +41,7 @@ public class RefundOperation extends PaymentOperation {
         logger.debug("Starting REFUND for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
         return plugin.refundPayment(paymentStateContext.getAccount().getId(),
                                     paymentStateContext.getPaymentId(),
-                                    paymentStateContext.getTransactionPaymentId(),
+                                    paymentStateContext.getTransactionId(),
                                     paymentStateContext.getPaymentMethodId(),
                                     paymentStateContext.getAmount(),
                                     paymentStateContext.getCurrency(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java
index 41b769a..678d987 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java
@@ -45,7 +45,7 @@ public class RetryablePaymentStateContext extends PaymentStateContext {
                                         @Nullable final String paymentTransactionExternalKey, final TransactionType transactionType,
                                         final Account account, @Nullable final UUID paymentMethodId, final BigDecimal amount, final Currency currency,
                                         final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext, final CallContext callContext) {
-        super(isApiPayment, paymentId, null, paymentExternalKey, paymentTransactionExternalKey, transactionType, account, paymentMethodId, amount, currency, true, properties, internalCallContext, callContext);
+        super(isApiPayment, paymentId, null, null, paymentExternalKey, paymentTransactionExternalKey, transactionType, account, paymentMethodId, amount, currency, true, null, properties, internalCallContext, callContext);
         this.pluginName = pluginName;
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/VoidOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/VoidOperation.java
index 5427c27..0262abf 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/VoidOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/VoidOperation.java
@@ -41,7 +41,7 @@ public class VoidOperation extends PaymentOperation {
         logger.debug("Starting VOID for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
         return plugin.voidPayment(paymentStateContext.getAccount().getId(),
                                   paymentStateContext.getPaymentId(),
-                                  paymentStateContext.getTransactionPaymentId(),
+                                  paymentStateContext.getTransactionId(),
                                   paymentStateContext.getPaymentMethodId(),
                                   paymentStateContext.getProperties(),
                                   paymentStateContext.getCallContext());
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index 4737399..6ef4484 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -114,7 +114,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         final String transactionExternalKey2 = "kapu2t";
 
         final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, authAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
-                                                                     ImmutableList.<PluginProperty>of(), callContext);
+                                                               ImmutableList.<PluginProperty>of(), callContext);
 
         assertEquals(payment.getExternalKey(), paymentExternalKey);
         assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
@@ -139,7 +139,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         assertNull(payment.getTransactions().get(0).getGatewayErrorCode());
 
         final Payment payment2 = paymentApi.createCapture(account, payment.getId(), captureAmount, Currency.AED, transactionExternalKey2,
-                                                                ImmutableList.<PluginProperty>of(), callContext);
+                                                          ImmutableList.<PluginProperty>of(), callContext);
 
         assertEquals(payment2.getExternalKey(), paymentExternalKey);
         assertEquals(payment2.getPaymentMethodId(), account.getPaymentMethodId());
@@ -430,7 +430,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testNotifyPaymentPaymentOfChargeback() throws PaymentApiException {
+    public void testCreateChargeback() throws PaymentApiException {
         final BigDecimal requestedAmount = BigDecimal.TEN;
 
         final String paymentExternalKey = "couic";
@@ -477,6 +477,50 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
+    public void testNotifyPendingTransactionOfStateChanged() throws PaymentApiException {
+
+        final BigDecimal authAmount = BigDecimal.TEN;
+
+        final String paymentExternalKey = "rouge";
+        final String transactionExternalKey = "vert";
+
+        final Payment initialPayment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, authAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
+                                                               ImmutableList.<PluginProperty>of(), callContext);
+
+        // Update the payment/transaction by hand to simulate a PENDING state.
+        final PaymentTransaction paymentTransaction = initialPayment.getTransactions().get(0);
+        paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), initialPayment.getId(), TransactionType.AUTHORIZE, "AUTH_PENDING", "AUTH_PENDING",
+                                                           paymentTransaction.getId(), TransactionStatus.PENDING, paymentTransaction.getProcessedAmount(), paymentTransaction.getProcessedCurrency(),
+                                                           null, null, internalCallContext);
+
+
+        final Payment payment = paymentApi.notifyPendingTransactionOfStateChanged(account, paymentTransaction.getId(), true, callContext);
+
+        assertEquals(payment.getExternalKey(), paymentExternalKey);
+        assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
+        assertEquals(payment.getAccountId(), account.getId());
+        assertEquals(payment.getAuthAmount().compareTo(authAmount), 0);
+        assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(payment.getCurrency(), Currency.AED);
+
+        assertEquals(payment.getTransactions().size(), 1);
+        assertEquals(payment.getTransactions().get(0).getExternalKey(), transactionExternalKey);
+        assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
+        assertEquals(payment.getTransactions().get(0).getAmount().compareTo(authAmount), 0);
+        assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.AED);
+        assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(authAmount), 0);
+        assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.AED);
+
+        assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+        assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.AUTHORIZE);
+        assertNull(payment.getTransactions().get(0).getGatewayErrorMsg());
+        assertNull(payment.getTransactions().get(0).getGatewayErrorCode());
+    }
+
+
+    @Test(groups = "slow")
     public void testSimpleAuthCaptureWithInvalidPaymentId() throws Exception {
         final BigDecimal requestedAmount = new BigDecimal("80.0091");
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java
index a5f96fa..f6dcd0a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java
@@ -115,7 +115,7 @@ public class TestPaymentAutomatonDAOHelper extends PaymentTestSuiteWithEmbeddedD
         // No default payment method
 
         paymentStateContext = new PaymentStateContext(true, paymentId,
-                                                      null,
+                                                      null, null,
                                                       paymentExternalKey,
                                                       paymentTransactionExternalKey,
                                                       TransactionType.CAPTURE,
@@ -124,7 +124,7 @@ public class TestPaymentAutomatonDAOHelper extends PaymentTestSuiteWithEmbeddedD
                                                       amount,
                                                       currency,
                                                       false,
-                                                      ImmutableList.<PluginProperty>of(),
+                                                      null, ImmutableList.<PluginProperty>of(),
                                                       internalCallContext,
                                                       callContext);
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
index 172a623..c37f577 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
@@ -97,7 +97,7 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
         Mockito.when(account.getId()).thenReturn(UUID.randomUUID());
         paymentStateContext = new PaymentStateContext(true,
                                                       paymentId,
-                                                      null,
+                                                      null, null,
                                                       UUID.randomUUID().toString(),
                                                       UUID.randomUUID().toString(),
                                                       TransactionType.CAPTURE,
@@ -106,7 +106,7 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
                                                       new BigDecimal("192.3920111"),
                                                       Currency.BRL,
                                                       false,
-                                                      ImmutableList.<PluginProperty>of(),
+                                                      null, ImmutableList.<PluginProperty>of(),
                                                       internalCallContext,
                                                       callContext);
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
index f8c14a5..4d11b09 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
@@ -105,7 +105,7 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
         final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(1, Executors.newCachedThreadPool());
         paymentStateContext = new PaymentStateContext(true,
                                                       UUID.randomUUID(),
-                                                      null,
+                                                      null, null,
                                                       UUID.randomUUID().toString(),
                                                       UUID.randomUUID().toString(),
                                                       TransactionType.CAPTURE,
@@ -114,7 +114,7 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
                                                       new BigDecimal("192.3920111"),
                                                       Currency.BRL,
                                                       false,
-                                                      ImmutableList.<PluginProperty>of(),
+                                                      null, ImmutableList.<PluginProperty>of(),
                                                       internalCallContext,
                                                       callContext);
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index bd13896..007c1f0 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -22,7 +22,6 @@ import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -181,7 +180,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
         final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(timeoutSeconds, Executors.newCachedThreadPool());
 
         final PaymentStateContext paymentStateContext = new PaymentStateContext(true, UUID.randomUUID(),
-                                                                                null,
+                                                                                null, null,
                                                                                 UUID.randomUUID().toString(),
                                                                                 UUID.randomUUID().toString(),
                                                                                 TransactionType.CAPTURE,
@@ -190,7 +189,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
                                                                                 new BigDecimal("192.3920111"),
                                                                                 Currency.BRL,
                                                                                 shouldLockAccount,
-                                                                                ImmutableList.<PluginProperty>of(),
+                                                                                null, ImmutableList.<PluginProperty>of(),
                                                                                 internalCallContext,
                                                                                 callContext);