killbill-aplcache
Merge remote-tracking branch 'origin/master' into changes-for-0.15.2 Signed-off-by: …
Changes
account/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
currency/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
invoice/pom.xml 2(+1 -1)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
NEWS 4(+4 -0)
overdue/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java 34(+32 -2)
payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java 4(+4 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java 90(+84 -6)
payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java 4(+2 -2)
payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java 5(+2 -3)
payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java 69(+66 -3)
pom.xml 2(+1 -1)
profiles/killbill/pom.xml 2(+1 -1)
profiles/killpay/pom.xml 2(+1 -1)
profiles/pom.xml 2(+1 -1)
subscription/pom.xml 2(+1 -1)
tenant/pom.xml 2(+1 -1)
usage/pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index 8dd4baf..248888c 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index 693edc4..24958d8 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-internal-api</artifactId>
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 0a7ab00..765fdf3 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index 5543d21..165e905 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
currency/pom.xml 2(+1 -1)
diff --git a/currency/pom.xml b/currency/pom.xml
index 9fb4e1d..e03a711 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-currency</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 6d0260b..7b012fa 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 7646dad..987a318 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index c365864..401b515 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index fca6924..6445dde 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
NEWS 4(+4 -0)
diff --git a/NEWS b/NEWS
index e2d4501..c41cb0c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+0.15.1
+ See https://github.com/killbill/killbill/issues?q=milestone%3ARelease-0.15.1+is%3Aclosed
+ Also Add new support for entitlement plugin API
+
0.15.0
See https://github.com/killbill/killbill/issues?q=milestone%3ARelease-0.15.0+is%3Aclosed
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index 7d01545..1892035 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index 56ac663..45ce609 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
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 0af54d5..53e1606 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
@@ -322,7 +322,6 @@ public class PaymentProcessor extends ProcessorBase {
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,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index ae4e3a2..f8c2d67 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -164,27 +164,8 @@ public abstract class ProcessorBase {
return internalCallContextFactory.createCallContext(context);
}
- protected void validateUniqueTransactionExternalKey(@Nullable final String transactionExternalKey, final InternalTenantContext tenantContext) throws PaymentApiException {
- // If no key specified, system will allocate a unique one later.
- if (transactionExternalKey == null) {
- return;
- }
-
- final List<PaymentTransactionModelDao> transactions = paymentDao.getPaymentTransactionsByExternalKey(transactionExternalKey, tenantContext);
- final PaymentTransactionModelDao transactionAlreadyExists = Iterables.tryFind(transactions, new Predicate<PaymentTransactionModelDao>() {
- @Override
- public boolean apply(final PaymentTransactionModelDao input) {
- return input.getTransactionStatus() == TransactionStatus.SUCCESS;
- }
- }).orNull();
- if (transactionAlreadyExists != null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS, transactionExternalKey);
- }
- }
-
// TODO Rename - there is no lock!
public interface WithAccountLockCallback<PluginDispatcherReturnType, ExceptionType extends Exception> {
-
public PluginDispatcherReturnType doOperation() throws ExceptionType;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
index b8dc704..ea11242 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
@@ -17,6 +17,7 @@
package org.killbill.billing.payment.core.sm.control;
+import java.util.List;
import java.util.UUID;
import org.killbill.automaton.Operation.OperationCallback;
@@ -26,19 +27,27 @@ import org.killbill.automaton.State.EnteringStateCallback;
import org.killbill.automaton.State.LeavingStateCallback;
import org.killbill.billing.ObjectType;
import org.killbill.billing.payment.core.sm.PluginControlPaymentAutomatonRunner;
+import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
public class DefaultControlCompleted implements EnteringStateCallback {
private final PaymentStateControlContext paymentStateContext;
private final RetryServiceScheduler retryServiceScheduler;
+ private final State retriedState;
+
private PluginControlPaymentAutomatonRunner retryablePaymentAutomatonRunner;
public DefaultControlCompleted(final PluginControlPaymentAutomatonRunner retryablePaymentAutomatonRunner, final PaymentStateControlContext paymentStateContext,
- final RetryServiceScheduler retryServiceScheduler) {
+ final State retriedState, final RetryServiceScheduler retryServiceScheduler) {
this.retryablePaymentAutomatonRunner = retryablePaymentAutomatonRunner;
this.paymentStateContext = paymentStateContext;
+ this.retriedState = retriedState;
this.retryServiceScheduler = retryServiceScheduler;
}
@@ -50,9 +59,30 @@ public class DefaultControlCompleted implements EnteringStateCallback {
null;
retryablePaymentAutomatonRunner.getPaymentDao().updatePaymentAttempt(attempt.getId(), transactionId, state.getName(), paymentStateContext.getInternalCallContext());
- if ("RETRIED".equals(state.getName())) {
+ if (retriedState.getName().equals(state.getName()) && !isUnknownTransaction()) {
retryServiceScheduler.scheduleRetry(ObjectType.PAYMENT_ATTEMPT, attempt.getId(), attempt.getId(), attempt.getTenantRecordId(),
paymentStateContext.getPaymentControlPluginNames(), paymentStateContext.getRetryDate());
}
}
+
+ //
+ // If we see an UNKNOWN transaction we prevent it to be rescheduled as the Janitor will *try* to fix it, and that could lead to infinite retries from a badly behaved plugin
+ // (In other words, plugin should ONLY retry 'known' transaction)
+ //
+ private boolean isUnknownTransaction() {
+ if (paymentStateContext.getCurrentTransaction() != null) {
+ return paymentStateContext.getCurrentTransaction().getTransactionStatus() == TransactionStatus.UNKNOWN;
+ } else {
+ final List<PaymentTransactionModelDao> transactions = retryablePaymentAutomatonRunner.getPaymentDao().getPaymentTransactionsByExternalKey(paymentStateContext.getPaymentTransactionExternalKey(), paymentStateContext.getInternalCallContext());
+ return Iterables.any(transactions, new Predicate<PaymentTransactionModelDao>() {
+ @Override
+ public boolean apply(final PaymentTransactionModelDao input) {
+ return input.getTransactionStatus() == TransactionStatus.UNKNOWN &&
+ // Not strictly required
+ // (Note, we don't match on AttemptId as it is risky, the row on disk would match the first attempt, not necessarily the current one)
+ input.getAccountRecordId().equals(paymentStateContext.getInternalCallContext().getAccountRecordId());
+ }
+ });
+ }
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java
index 8cdc6eb..9860f6b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java
@@ -52,6 +52,10 @@ public abstract class PaymentEnteringStateCallback implements EnteringStateCallb
public void enteringState(final State newState, final Operation.OperationCallback operationCallback, final OperationResult operationResult, final LeavingStateCallback leavingStateCallback) {
logger.debug("Entering state {} with result {}", newState.getName(), operationResult);
+ if (paymentStateContext.isSkipOperationForUnknownTransaction()) {
+ return;
+ }
+
// If the transaction was not created -- for instance we had an exception in leavingState callback then we bail; if not, then update state:
if (paymentStateContext.getPaymentTransactionModelDao() != null && paymentStateContext.getPaymentTransactionModelDao().getId() != null) {
final PaymentTransactionInfoPlugin paymentInfoPlugin = paymentStateContext.getPaymentTransactionInfoPlugin();
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java
index 18dd659..6fb17d7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java
@@ -17,17 +17,24 @@
package org.killbill.billing.payment.core.sm.payments;
+import java.util.List;
+
import org.killbill.automaton.OperationException;
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.api.TransactionStatus;
import org.killbill.billing.payment.core.sm.PaymentAutomatonDAOHelper;
import org.killbill.billing.payment.core.sm.PaymentStateContext;
import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
public abstract class PaymentLeavingStateCallback implements LeavingStateCallback {
private final Logger logger = LoggerFactory.getLogger(PaymentLeavingStateCallback.class);
@@ -51,16 +58,87 @@ public abstract class PaymentLeavingStateCallback implements LeavingStateCallbac
throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, paymentStateContext.getAccount().getId());
}
- // 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 {
+ //
+ // Extract existing transaction matching the transactionId if specified (for e.g notifyPendingTransactionOfStateChanged), or based on transactionExternalKey
+ //
+ final List<PaymentTransactionModelDao> existingPaymentTransactions;
+ if (paymentStateContext.getTransactionId() != null) {
final PaymentTransactionModelDao transactionModelDao = daoHelper.getPaymentDao().getPaymentTransaction(paymentStateContext.getTransactionId(), paymentStateContext.getInternalCallContext());
- paymentStateContext.setPaymentTransactionModelDao(transactionModelDao);
+ existingPaymentTransactions = ImmutableList.of(transactionModelDao);
+ } else if (paymentStateContext.getPaymentTransactionExternalKey() != null) {
+ existingPaymentTransactions = daoHelper.getPaymentDao().getPaymentTransactionsByExternalKey(paymentStateContext.getPaymentTransactionExternalKey(), paymentStateContext.getInternalCallContext());
+ } else {
+ existingPaymentTransactions = ImmutableList.of();
+ }
+
+ // Validate some constraints on the unicity of that paymentTransactionExternalKey
+ validateUniqueTransactionExternalKey(existingPaymentTransactions);
+
+ // Handle UNKNOWN cases, where we skip the whole state machine and let the getPayment (through Janitor) logic refresh the state.
+ final PaymentTransactionModelDao unknownPaymentTransaction = getUnknownPaymentTransaction(existingPaymentTransactions);
+ if (unknownPaymentTransaction != null) {
+ // Reset the attemptId on the existing paymentTransaction row since it is not accurate
+ unknownPaymentTransaction.setAttemptId(paymentStateContext.getAttemptId());
+ // Set the current paymentTransaction in the context (needed for the state machine logic)
+ paymentStateContext.setPaymentTransactionModelDao(unknownPaymentTransaction);
+ // Set special flag to bypass the state machine altogether (plugin will not be called, state will not be updated, no event will be sent unless state is fixed)
+ paymentStateContext.setSkipOperationForUnknownTransaction(true);
+ return;
+ }
+
+ // Handle PENDING cases, where we want to re-use the same transaction
+ final PaymentTransactionModelDao pendingPaymentTransaction = getPendingPaymentTransaction(existingPaymentTransactions);
+ if (pendingPaymentTransaction != null) {
+ // Set the current paymentTransaction in the context (needed for the state machine logic)
+ paymentStateContext.setPaymentTransactionModelDao(pendingPaymentTransaction);
+ return;
}
+
+ // At this point we are left with PAYMENT_FAILURE, PLUGIN_FAILURE or nothing, and we validated the uniquess of the paymentTransactionExternalKey so we will create a new row
+ daoHelper.createNewPaymentTransaction();
+
} catch (PaymentApiException e) {
throw new OperationException(e);
}
}
+
+ protected PaymentTransactionModelDao getUnknownPaymentTransaction(final List<PaymentTransactionModelDao> existingPaymentTransactions) throws PaymentApiException {
+ return Iterables.tryFind(existingPaymentTransactions, new Predicate<PaymentTransactionModelDao>() {
+ @Override
+ public boolean apply(final PaymentTransactionModelDao input) {
+ return input.getTransactionStatus() == TransactionStatus.UNKNOWN;
+ }
+ }).orNull();
+ }
+
+ protected PaymentTransactionModelDao getPendingPaymentTransaction(final List<PaymentTransactionModelDao> existingPaymentTransactions) throws PaymentApiException {
+ return Iterables.tryFind(existingPaymentTransactions, new Predicate<PaymentTransactionModelDao>() {
+ @Override
+ public boolean apply(final PaymentTransactionModelDao input) {
+ return input.getTransactionStatus() == TransactionStatus.PENDING;
+ }
+ }).orNull();
+ }
+
+ protected void validateUniqueTransactionExternalKey(final List<PaymentTransactionModelDao> existingPaymentTransactions) throws PaymentApiException {
+ // If no key specified, system will allocate a unique one later, there is nothing to check
+ if (paymentStateContext.getPaymentTransactionExternalKey() == null) {
+ return;
+ }
+
+ if (Iterables.any(existingPaymentTransactions, new Predicate<PaymentTransactionModelDao>() {
+ @Override
+ public boolean apply(final PaymentTransactionModelDao input) {
+ // An existing transaction in a SUCCESS state
+ return input.getTransactionStatus() == TransactionStatus.SUCCESS ||
+ // Or, an existing transaction for a different payment (to do really well, we should also check on paymentExternalKey which is not available here)
+ (paymentStateContext.getPaymentId() != null && input.getPaymentId().compareTo(paymentStateContext.getPaymentId()) != 0) ||
+ // Or, an existing transaction for a different account.
+ (!input.getAccountRecordId().equals(paymentStateContext.getInternalCallContext().getAccountRecordId()));
+
+ }
+ })) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS, paymentStateContext.getPaymentTransactionExternalKey());
+ }
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
index 6781ecd..6d57033 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
@@ -66,6 +66,11 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
@Override
public OperationResult doOperationCallback() throws OperationException {
+
+ if (paymentStateContext.isSkipOperationForUnknownTransaction()) {
+ return OperationResult.SUCCESS;
+ }
+
try {
this.plugin = daoHelper.getPaymentProviderPlugin();
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 034fbc4..d9c097c 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
@@ -50,6 +50,7 @@ public class PaymentStateContext {
protected String paymentTransactionExternalKey;
protected Currency currency;
protected Iterable<PluginProperty> properties;
+ protected boolean skipOperationForUnknownTransaction;
// Can be updated later via paymentTransactionModelDao (e.g. for auth or purchase)
protected final UUID paymentId;
@@ -94,6 +95,7 @@ public class PaymentStateContext {
this.internalCallContext = internalCallContext;
this.callContext = callContext;
this.onLeavingStateExistingTransactions = ImmutableList.of();
+ this.skipOperationForUnknownTransaction = false;
}
public boolean isApiPayment() {
@@ -199,4 +201,12 @@ public class PaymentStateContext {
public CallContext getCallContext() {
return callContext;
}
+
+ public boolean isSkipOperationForUnknownTransaction() {
+ return skipOperationForUnknownTransaction;
+ }
+
+ public void setSkipOperationForUnknownTransaction(final boolean skipOperationForUnknownTransaction) {
+ this.skipOperationForUnknownTransaction = skipOperationForUnknownTransaction;
+ }
}
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 dfea014..d6a12c4 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
@@ -57,7 +57,7 @@ public class PaymentStateMachineHelper {
private static final String VOID_SUCCESS = "VOID_SUCCESS";
private static final String CHARGEBACK_SUCCESS = "CHARGEBACK_SUCCESS";
- private static final String AUTHORIZE_PENDING = "AUTHORIZE_PENDING";
+ private static final String AUTHORIZE_PENDING = "AUTH_PENDING";
private static final String AUTHORIZE_FAILED = "AUTH_FAILED";
private static final String CAPTURE_FAILED = "CAPTURE_FAILED";
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
index 5878bd9..1f4a1ee 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
@@ -112,7 +112,7 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner
try {
final OperationCallback callback = createOperationCallback(transactionType, paymentStateContext);
final LeavingStateCallback leavingStateCallback = new DefaultControlInitiated(this, paymentStateContext, paymentDao, paymentControlStateMachineHelper.getInitialState(), paymentControlStateMachineHelper.getRetriedState(), transactionType);
- final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, retryServiceScheduler);
+ final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, paymentControlStateMachineHelper.getRetriedState(), retryServiceScheduler);
state.runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
} catch (final MissingEntryException e) {
@@ -134,7 +134,7 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner
try {
final OperationCallback callback = new CompletionControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
final LeavingStateCallback leavingStateCallback = new NoopControlInitiated();
- final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, retryServiceScheduler);
+ final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, paymentControlStateMachineHelper.getRetriedState(), retryServiceScheduler);
paymentControlStateMachineHelper.getInitialState().runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
} catch (final MissingEntryException e) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index e2877fc..63314b7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -348,10 +348,10 @@ public final class InvoicePaymentControlPluginApi implements PaymentRoutingPlugi
case PAYMENT_FAILURE:
return getNextRetryDateForPaymentFailure(purchasedTransactions);
- case UNKNOWN:
case PLUGIN_FAILURE:
return getNextRetryDateForPluginFailure(purchasedTransactions);
+ case UNKNOWN:
default:
return null;
}
@@ -379,8 +379,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentRoutingPlugi
private DateTime getNextRetryDateForPluginFailure(final List<PaymentTransactionModelDao> purchasedTransactions) {
DateTime result = null;
- // Not clear whether or not we should really include TransactionStatus.UNKNOWN
- final int attemptsInState = getNumberAttemptsInState(purchasedTransactions, TransactionStatus.PLUGIN_FAILURE, TransactionStatus.UNKNOWN);
+ final int attemptsInState = getNumberAttemptsInState(purchasedTransactions, TransactionStatus.PLUGIN_FAILURE);
final int retryAttempt = (attemptsInState - 1) >= 0 ? (attemptsInState - 1) : 0;
if (retryAttempt < paymentConfig.getPluginFailureRetryMaxAttempts()) {
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 88d421d..83ffc0d 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
@@ -524,7 +524,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
final Payment initialPayment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
UUID.randomUUID().toString(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
-
try {
paymentApi.createCapture(account, UUID.randomUUID(), requestedAmount, account.getCurrency(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
Assert.fail("Expected capture to fail...");
@@ -579,6 +578,62 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
}
}
+ @Test(groups = "slow")
+ public void testApiRetryWithUnknownPaymentTransaction() throws Exception {
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ final String paymentExternalKey = UUID.randomUUID().toString();
+ final String paymentTransactionExternalKey = UUID.randomUUID().toString();
+
+ final Payment badPayment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
+ paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+ final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
+ paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), badPayment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+ badPayment.getTransactions().get(0).getId(), TransactionStatus.UNKNOWN, requestedAmount, account.getCurrency(),
+ "eroor 64", "bad something happened", internalCallContext);
+
+ final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
+ paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+ Assert.assertEquals(payment.getId(), badPayment.getId());
+ Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+ Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+
+ Assert.assertEquals(payment.getTransactions().size(), 1);
+ Assert.assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+ Assert.assertEquals(payment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+ }
+
+ // Example of a 3D secure payment for instance
+ @Test(groups = "slow")
+ public void testApiWithPendingPaymentTransaction() throws Exception {
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ final String paymentExternalKey = UUID.randomUUID().toString();
+ final String paymentTransactionExternalKey = UUID.randomUUID().toString();
+
+ final Payment pendingPayment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
+ paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+ final String paymentStateName = paymentSMHelper.getPendingStateForTransaction(TransactionType.AUTHORIZE).toString();
+ paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), pendingPayment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+ pendingPayment.getTransactions().get(0).getId(), TransactionStatus.PENDING, requestedAmount, account.getCurrency(),
+ null, null, internalCallContext);
+
+ final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), pendingPayment.getId(), requestedAmount, account.getCurrency(),
+ paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+ Assert.assertEquals(payment.getId(), pendingPayment.getId());
+ Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+ Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+
+ Assert.assertEquals(payment.getTransactions().size(), 1);
+ Assert.assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+ Assert.assertEquals(payment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+
+ }
+
private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
final List<PluginProperty> result = new ArrayList<PluginProperty>();
result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
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 0097eaf..36800f1 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
@@ -18,12 +18,15 @@
package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
+import org.killbill.automaton.OperationException;
import org.killbill.automaton.State;
import org.killbill.billing.account.api.Account;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
import org.killbill.billing.payment.api.PaymentApiException;
@@ -45,6 +48,7 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
private PaymentStateContext paymentStateContext;
private PaymentLeavingStateTestCallback callback;
+ private Account account;
@Test(groups = "slow")
public void testLeaveStateForNewPayment() throws Exception {
@@ -70,8 +74,11 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
setUp(paymentId);
// Verify the payment has only one transaction
- Assert.assertEquals(paymentDao.getTransactionsForPayment(paymentId, internalCallContext).size(), 1);
+ final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(paymentId, internalCallContext);
+ Assert.assertEquals(transactions.size(), 1);
+ // Reset paymentExternalKey to something else
+ paymentStateContext.setPaymentTransactionExternalKey(UUID.randomUUID().toString());
callback.leavingState(state);
// Verify a new transaction was created
@@ -81,6 +88,61 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
Assert.assertEquals(paymentDao.getTransactionsForPayment(paymentId, internalCallContext).size(), 2);
}
+ @Test(groups = "slow", expectedExceptions = OperationException.class)
+ public void testLeaveStateForConflictingPaymentTransactionExternalKey() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ setUp(paymentId);
+
+ // Verify the payment has only one transaction
+ final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(paymentId, internalCallContext);
+ Assert.assertEquals(transactions.size(), 1);
+
+ final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.CAPTURE).toString();
+ paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), paymentId, TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+ transactions.get(0).getId(), TransactionStatus.SUCCESS, BigDecimal.ONE, Currency.BRL,
+ "foo", "bar", internalCallContext);
+
+ // Will validate the validateUniqueTransactionExternalKey logic for when we reuse the same payment transactionExternalKey
+ callback.leavingState(state);
+
+ }
+
+ @Test(groups = "slow", expectedExceptions = OperationException.class)
+ public void testLeaveStateForConflictingPaymentTransactionExternalKeyAcrossAccounts() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ setUp(paymentId);
+
+ // Verify the payment has only one transaction
+ final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(paymentId, internalCallContext);
+ Assert.assertEquals(transactions.size(), 1);
+
+ final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.CAPTURE).toString();
+ paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), paymentId, TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+ transactions.get(0).getId(), TransactionStatus.SUCCESS, BigDecimal.ONE, Currency.BRL,
+ "foo", "bar", internalCallContext);
+
+ final InternalCallContext internalCallContextForOtherAccount = new InternalCallContext(paymentStateContext.getInternalCallContext(), 123L);
+
+ paymentStateContext = new PaymentStateContext(true,
+ paymentId,
+ null,
+ null,
+ paymentStateContext.getPaymentExternalKey(),
+ paymentStateContext.getPaymentTransactionExternalKey(),
+ paymentStateContext.getTransactionType(),
+ paymentStateContext.getAccount(),
+ paymentStateContext.getPaymentMethodId(),
+ paymentStateContext.getAmount(),
+ paymentStateContext.getCurrency(),
+ paymentStateContext.shouldLockAccountAndDispatch,
+ paymentStateContext.overridePluginOperationResult,
+ paymentStateContext.getProperties(),
+ internalCallContextForOtherAccount,
+ callContext);
+
+ callback.leavingState(state);
+ }
+
private void verifyPaymentTransaction() {
Assert.assertNotNull(paymentStateContext.getPaymentTransactionModelDao().getPaymentId());
Assert.assertEquals(paymentStateContext.getPaymentTransactionModelDao().getTransactionExternalKey(), paymentStateContext.getPaymentTransactionExternalKey());
@@ -94,11 +156,12 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
}
private void setUp(@Nullable final UUID paymentId) throws Exception {
- final Account account = Mockito.mock(Account.class);
+ account = Mockito.mock(Account.class);
Mockito.when(account.getId()).thenReturn(UUID.randomUUID());
paymentStateContext = new PaymentStateContext(true,
paymentId,
- null, null,
+ null,
+ null,
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
TransactionType.CAPTURE,
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
index c761832..7109927 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
@@ -83,10 +83,12 @@ public class MockPaymentDao implements PaymentDao {
@Override
public PaymentAttemptModelDao insertPaymentAttemptWithProperties(final PaymentAttemptModelDao attempt, final InternalCallContext context) {
attempt.setTenantRecordId(context.getTenantRecordId());
+ attempt.setAccountRecordId(context.getAccountRecordId());
synchronized (this) {
attempts.put(attempt.getId(), attempt);
mockNonEntityDao.addTenantRecordIdMapping(attempt.getId(), context);
+ mockNonEntityDao.addAccountRecordIdMapping(attempt.getId(), context);
return attempt;
}
}
@@ -177,15 +179,20 @@ public class MockPaymentDao implements PaymentDao {
@Override
public PaymentModelDao insertPaymentWithFirstTransaction(final PaymentModelDao payment, final PaymentTransactionModelDao paymentTransaction, final InternalCallContext context) {
+
payment.setTenantRecordId(context.getTenantRecordId());
paymentTransaction.setTenantRecordId(context.getTenantRecordId());
+ payment.setAccountRecordId(context.getAccountRecordId());
+ paymentTransaction.setAccountRecordId(context.getAccountRecordId());
synchronized (this) {
payments.put(payment.getId(), payment);
mockNonEntityDao.addTenantRecordIdMapping(payment.getId(), context);
+ mockNonEntityDao.addAccountRecordIdMapping((payment.getId()), context);
transactions.put(paymentTransaction.getId(), paymentTransaction);
mockNonEntityDao.addTenantRecordIdMapping(paymentTransaction.getId(), context);
+ mockNonEntityDao.addAccountRecordIdMapping((paymentTransaction.getId()), context);
}
return payment;
}
@@ -193,10 +200,12 @@ public class MockPaymentDao implements PaymentDao {
@Override
public PaymentTransactionModelDao updatePaymentWithNewTransaction(final UUID paymentId, final PaymentTransactionModelDao paymentTransaction, final InternalCallContext context) {
paymentTransaction.setTenantRecordId(context.getTenantRecordId());
+ paymentTransaction.setAccountRecordId(context.getAccountRecordId());
synchronized (this) {
transactions.put(paymentTransaction.getId(), paymentTransaction);
mockNonEntityDao.addTenantRecordIdMapping(paymentId, context);
+ mockNonEntityDao.addAccountRecordIdMapping((paymentTransaction.getId()), context);
}
return paymentTransaction;
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
index 4cf6eee..2aaa167 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
@@ -137,6 +137,7 @@ public class TestPaymentHelper {
Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
Mockito.when(accountInternalApi.getAccountByKey(Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(account);
mockNonEntityDao.addTenantRecordIdMapping(account.getId(), internalCallContext);
+ mockNonEntityDao.addAccountRecordIdMapping(account.getId(), internalCallContext);
} else {
account = accountApi.createAccount(accountData, context);
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index 8b5fcf9..2c46cfc 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -88,19 +88,10 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
return payment;
}
- @Test(groups = "fast")
- public void testFailedPluginWithOneSuccessfulRetry() throws Exception {
- testSchedulesRetryInternal(1, true, FailureType.PLUGIN_EXCEPTION);
- }
-
- @Test(groups = "fast")
- public void testFailedPluginWithLastRetrySuccess() throws Exception {
- testSchedulesRetryInternal(paymentConfig.getPluginFailureRetryMaxAttempts(), true, FailureType.PLUGIN_EXCEPTION);
- }
-
+ // PLUGIN_EXCEPTION will lead to UNKNOWN row that will not be retried by the plugin
@Test(groups = "fast")
public void testAbortedPlugin() throws Exception {
- testSchedulesRetryInternal(paymentConfig.getPluginFailureRetryMaxAttempts(), false, FailureType.PLUGIN_EXCEPTION);
+ testSchedulesRetryInternal(0, false, FailureType.PLUGIN_EXCEPTION);
}
@Test(groups = "fast")
@@ -165,6 +156,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
setPaymentFailure(failureType);
}
+
moveClockForFailureType(failureType, curFailure);
final int curFailureCondition = curFailure;
@@ -235,7 +227,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
if (failureType == FailureType.PAYMENT_FAILURE) {
return paymentConfig.getPaymentFailureRetryDays().size();
} else {
- return paymentConfig.getPluginFailureRetryMaxAttempts();
+ return 0;
}
}
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index b217bcf..7d8a298 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
<version>0.28-SNAPSHOT</version>
</parent>
<artifactId>killbill</artifactId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
profiles/killbill/pom.xml 2(+1 -1)
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 2dc774c..a3889a2 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killbill</artifactId>
profiles/killpay/pom.xml 2(+1 -1)
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index eb72d48..ad52eb1 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killpay</artifactId>
profiles/pom.xml 2(+1 -1)
diff --git a/profiles/pom.xml b/profiles/pom.xml
index 7d26dc8..65b88bb 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles</artifactId>
subscription/pom.xml 2(+1 -1)
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 8d2682a..ddcae7a 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-subscription</artifactId>
tenant/pom.xml 2(+1 -1)
diff --git a/tenant/pom.xml b/tenant/pom.xml
index e874891..f4bb849 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-tenant</artifactId>
usage/pom.xml 2(+1 -1)
diff --git a/usage/pom.xml b/usage/pom.xml
index b8aa03b..7c1229d 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-usage</artifactId>
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index fb16656..ddcb261 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.1-SNAPSHOT</version>
+ <version>0.15.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>
diff --git a/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java b/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
index 896240a..d7bfc00 100644
--- a/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
+++ b/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
@@ -34,11 +34,16 @@ import org.killbill.billing.util.dao.TableName;
public class MockNonEntityDao implements NonEntityDao {
private final Map<UUID, Long> tenantRecordIdMappings = new HashMap<UUID, Long>();
+ private final Map<UUID, Long> accountRecordIdMappings = new HashMap<UUID, Long>();
public void addTenantRecordIdMapping(final UUID objectId, final InternalTenantContext context) {
tenantRecordIdMappings.put(objectId, context.getTenantRecordId());
}
+ public void addAccountRecordIdMapping(final UUID objectId, final InternalTenantContext context) {
+ accountRecordIdMappings.put(objectId, context.getAccountRecordId());
+ }
+
@Override
public Long retrieveRecordIdFromObject(final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
return null;
@@ -46,7 +51,7 @@ public class MockNonEntityDao implements NonEntityDao {
@Override
public Long retrieveAccountRecordIdFromObject(final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
- return null;
+ return accountRecordIdMappings.get(objectId);
}
@Override