killbill-memoizeit

fixes #225 Introduced a first initial state (a.k.a BIG_BANG_INIT),

9/4/2014 6:00:59 PM

Details

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 49d48f6..eeeaba6 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
@@ -46,7 +46,6 @@ 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;
@@ -112,7 +111,7 @@ public class PaymentAutomatonRunner {
         if (paymentId != null) {
             final PaymentModelDao paymentModelDao = daoHelper.getPayment();
             effectivePaymentMethodId = paymentModelDao.getPaymentMethodId();
-            currentStateName = paymentModelDao.getLastSuccessStateName() != null ? paymentModelDao.getLastSuccessStateName() : paymentSMHelper.getInitStateNameForTransaction(transactionType);
+            currentStateName = paymentModelDao.getLastSuccessStateName() != null ? paymentModelDao.getLastSuccessStateName() : paymentSMHelper.getInitStateNameForTransaction();
 
             // Check for illegal states (should never happen)
             Preconditions.checkState(currentStateName != null, "State name cannot be null for payment " + paymentId);
@@ -121,7 +120,7 @@ public class PaymentAutomatonRunner {
             // If the payment method is not specified, retrieve the default one on the account; it could still be null, in which case
             //
             effectivePaymentMethodId = paymentMethodId != null ? paymentMethodId : account.getPaymentMethodId();
-            currentStateName = paymentSMHelper.getInitStateNameForTransaction(transactionType);
+            currentStateName = paymentSMHelper.getInitStateNameForTransaction();
         }
 
         paymentStateContext.setPaymentMethodId(effectivePaymentMethodId);
@@ -183,7 +182,7 @@ public class PaymentAutomatonRunner {
 
             initialState.runOperation(operation, operationCallback, enteringStateCallback, leavingStateCallback);
         } catch (final MissingEntryException e) {
-            throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+            throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INVALID_OPERATION, transactionType, initialStateName);
         } catch (final OperationException e) {
             if (e.getCause() == null) {
                 throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
index 009d70b..763ec9c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
@@ -35,6 +35,7 @@ import com.google.common.collect.Iterables;
 
 public class PaymentStateMachineHelper {
 
+    private static final String BIG_BANG_STATE_MACHINE_NAME = "BIG_BANG";
     private static final String AUTHORIZE_STATE_MACHINE_NAME = "AUTHORIZE";
     private static final String CAPTURE_STATE_MACHINE_NAME = "CAPTURE";
     private static final String PURCHASE_STATE_MACHINE_NAME = "PURCHASE";
@@ -43,6 +44,8 @@ public class PaymentStateMachineHelper {
     private static final String VOID_STATE_MACHINE_NAME = "VOID";
     private static final String CHARGEBACK_STATE_MACHINE_NAME = "CHARGEBACK";
 
+
+    private static final String BIG_BANG_INIT_STATE_NAME = "BIG_BANG_INIT";
     private static final String AUTHORIZE_INIT_STATE_NAME = "AUTH_INIT";
     private static final String CAPTURE_INIT_STATE_NAME = "CAPTURE_INIT";
     private static final String PURCHASE_INIT_STATE_NAME = "PURCHASE_INIT";
@@ -63,25 +66,8 @@ public class PaymentStateMachineHelper {
         return stateMachine.getState(stateName);
     }
 
-    public String getInitStateNameForTransaction(final TransactionType transactionType) {
-        switch (transactionType) {
-            case AUTHORIZE:
-                return AUTHORIZE_INIT_STATE_NAME;
-            case CAPTURE:
-                return CAPTURE_INIT_STATE_NAME;
-            case PURCHASE:
-                return PURCHASE_INIT_STATE_NAME;
-            case REFUND:
-                return REFUND_INIT_STATE_NAME;
-            case CREDIT:
-                return CREDIT_INIT_STATE_NAME;
-            case VOID:
-                return VOID_INIT_STATE_NAME;
-            case CHARGEBACK:
-                return CHARGEBACK_INIT_STATE_NAME;
-            default:
-                throw new IllegalStateException("Unsupported transaction type " + transactionType + " for null payment id");
-        }
+    public String getInitStateNameForTransaction() {
+        return BIG_BANG_INIT_STATE_NAME;
     }
 
     public StateMachine getStateMachineForStateName(final String stateName) throws MissingEntryException {
diff --git a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
index c6f32d3..f4aeef9 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
+++ b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
@@ -19,6 +19,22 @@
                     xsi:noNamespaceSchemaLocation="StateMachineConfig.xsd">
 
     <stateMachines>
+        <stateMachine name="BIG_BANG">
+        <states>
+            <state name="BIG_BANG_INIT"/>
+        </states>
+            <transitions>
+                <transition>
+                    <initialState>BIG_BANG_INIT</initialState>
+                    <operation>OP_DUMMY</operation>
+                    <operationResult>SUCCESS</operationResult>
+                    <finalState>BIG_BANG_INIT</finalState>
+                </transition>
+            </transitions>
+            <operations>
+                <operation name="OP_DUMMY"/>
+            </operations>
+        </stateMachine>
         <stateMachine name="AUTHORIZE">
             <states>
                 <state name="AUTH_INIT"/>
@@ -270,6 +286,24 @@
 
     <linkStateMachines>
         <linkStateMachine>
+            <initialStateMachine>BIG_BANG</initialStateMachine>
+            <initialState>BIG_BANG_INIT</initialState>
+            <finalStateMachine>AUTHORIZE</finalStateMachine>
+            <finalState>AUTH_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
+            <initialStateMachine>BIG_BANG</initialStateMachine>
+            <initialState>BIG_BANG_INIT</initialState>
+            <finalStateMachine>PURCHASE</finalStateMachine>
+            <finalState>PURCHASE_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
+            <initialStateMachine>BIG_BANG</initialStateMachine>
+            <initialState>BIG_BANG_INIT</initialState>
+            <finalStateMachine>CREDIT</finalStateMachine>
+            <finalState>CREDIT_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
             <initialStateMachine>AUTHORIZE</initialStateMachine>
             <initialState>AUTH_SUCCESS</initialState>
             <finalStateMachine>AUTHORIZE</finalStateMachine>
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 6ef4484..ce075e3 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
@@ -35,6 +35,7 @@ import org.killbill.billing.payment.MockRecurringInvoiceItem;
 import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import org.killbill.billing.payment.control.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
+import org.killbill.billing.payment.dao.PaymentSqlDao;
 import org.killbill.billing.retry.plugin.api.PaymentControlApiException;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.testng.Assert;
@@ -78,7 +79,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         final String transactionExternalKey = "krapaut";
 
         final Payment payment = paymentApi.createPurchase(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
-                                                                ImmutableList.<PluginProperty>of(), callContext);
+                                                          ImmutableList.<PluginProperty>of(), callContext);
 
         assertEquals(payment.getExternalKey(), paymentExternalKey);
         assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
@@ -559,6 +560,32 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         }
     }
 
+    @Test(groups = "slow")
+    public void testInvalidTransitionAfterFailure() throws PaymentApiException {
+
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+
+        final String paymentExternalKey = "krapo";
+        final String transactionExternalKey = "grenouye";
+
+        final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, Currency.EUR, paymentExternalKey, transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(), callContext);
+
+        // Hack the Database to make it look like it was a failure
+        paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, "AUTH_ERRORED", null,
+                                                           payment.getTransactions().get(0).getId(), TransactionStatus.PLUGIN_FAILURE, null, null, null, null, internalCallContext);
+        PaymentSqlDao paymentSqlDao = dbi.onDemand(PaymentSqlDao.class);
+        paymentSqlDao.updateLastSuccessPaymentStateName(payment.getId().toString(), "AUTH_ERRORED", null, internalCallContext);
+
+        try {
+            paymentApi.createCapture(account, payment.getId(), requestedAmount, Currency.EUR, "tetard", ImmutableList.<PluginProperty>of(), callContext);
+            Assert.fail("Unexpected success");
+        } catch (PaymentApiException e){
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_OPERATION.getCode());
+        }
+    }
+
+
 
 
     private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {