diff --git a/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessorRefreshWithDB.java b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessorRefreshWithDB.java
new file mode 100644
index 0000000..d0967a1
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessorRefreshWithDB.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.core;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.payment.PaymentTestSuiteWithEmbeddedDB;
+import com.ning.billing.payment.TestPaymentHelper;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.payment.dao.PaymentMethodModelDao;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+
+public class TestPaymentMethodProcessorRefreshWithDB extends PaymentTestSuiteWithEmbeddedDB {
+
+ @Test(groups = "slow")
+ public void testRefreshWithNewPaymentMethod() throws Exception {
+
+ final Account account = testHelper.createTestAccount("foo@bar.com", true);
+ Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 1);
+ final UUID existingPMId = account.getPaymentMethodId();
+
+ // Add new payment in plugin directly
+ final UUID newPmId = UUID.randomUUID();
+ getPluginApi().addPaymentMethod(newPmId, null, false, callContext);
+
+ // Verify that the refresh does indeed show 2 PMs
+ final List<PaymentMethod> methods = paymentMethodProcessor.refreshPaymentMethods(TestPaymentHelper.PLUGIN_TEST_NAME, account, internalCallContext);
+ Assert.assertEquals(methods.size(), 2);
+ checkPaymentMethodExistsWithStatus(methods, existingPMId, true);
+ checkPaymentMethodExistsWithStatus(methods, newPmId, true);
+ }
+
+
+ @Test(groups = "slow")
+ public void testRefreshWithDeletedPaymentMethod() throws Exception {
+
+ final Account account = testHelper.createTestAccount("foo@bar.com", true);
+ Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 1);
+ final UUID firstPmId = account.getPaymentMethodId();
+
+ final UUID secondPmId = paymentApi.addPaymentMethod(TestPaymentHelper.PLUGIN_TEST_NAME, account, true, new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null), callContext);
+ Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 2);
+ Assert.assertEquals(paymentApi.getPaymentMethods(account, callContext).size(), 2);
+
+ // Remove second PM from plugin
+ getPluginApi().deletePaymentMethod(secondPmId, callContext);
+ Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 1);
+ Assert.assertEquals(paymentApi.getPaymentMethods(account, callContext).size(), 2);
+
+ // Verify that the refresh sees that PM as being deleted now
+ final List<PaymentMethod> methods = paymentMethodProcessor.refreshPaymentMethods(TestPaymentHelper.PLUGIN_TEST_NAME, account, internalCallContext);
+ Assert.assertEquals(methods.size(), 1);
+ checkPaymentMethodExistsWithStatus(methods, firstPmId, true);
+
+ PaymentMethodModelDao deletedPMModel = paymentDao.getPaymentMethodIncludedDeleted(secondPmId, internalCallContext);
+ Assert.assertNotNull(deletedPMModel);
+ Assert.assertFalse(deletedPMModel.isActive());
+ }
+
+
+ private void checkPaymentMethodExistsWithStatus(final List<PaymentMethod> methods, UUID expectedPaymentMethodId, boolean expectedActive) {
+ PaymentMethod foundPM = null;
+ for (PaymentMethod cur : methods) {
+ if (cur.getId().equals(expectedPaymentMethodId)) {
+ foundPM = cur;
+ break;
+ }
+ }
+ Assert.assertNotNull(foundPM);
+ Assert.assertEquals(foundPM.isActive().booleanValue(), expectedActive);
+ }
+
+
+ private PaymentPluginApi getPluginApi() {
+ final PaymentPluginApi pluginApi = registry.getServiceForName(TestPaymentHelper.PLUGIN_TEST_NAME);
+ Assert.assertNotNull(pluginApi);
+ return pluginApi;
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index cbda487..6fc0f11 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -16,14 +16,151 @@
package com.ning.billing.payment.provider;
+import java.math.BigDecimal;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
import com.google.inject.Inject;
+
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.plugin.api.NoOpPaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin.PaymentPluginStatus;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin.RefundPluginStatus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.clock.Clock;
-public class MockPaymentProviderPlugin extends DefaultNoOpPaymentProviderPlugin implements PaymentPluginApi {
+/**
+ * This MockPaymentProviderPlugin only works for a single accounts as we don't specify the accountId
+ * for opeartions such as addPaymentMethod.
+ */
+public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
+
+ private static final String PLUGIN_NAME = "__NO_OP__";
+
+ private final AtomicBoolean makeNextInvoiceFailWithError = new AtomicBoolean(false);
+ private final AtomicBoolean makeNextInvoiceFailWithException = new AtomicBoolean(false);
+ private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
+
+ private final Map<String, PaymentInfoPlugin> payments = new ConcurrentHashMap<String, PaymentInfoPlugin>();
+ // Note: we can't use HashMultiMap as we care about storing duplicate key/value pairs
+ private final Multimap<String, RefundInfoPlugin> refunds = LinkedListMultimap.<String, RefundInfoPlugin>create();
+ private final Map<String, PaymentMethodInfoPlugin> paymentMethods = new ConcurrentHashMap<String, PaymentMethodInfoPlugin>();
+
+ private final Clock clock;
@Inject
public MockPaymentProviderPlugin(final Clock clock) {
- super(clock);
+ this.clock = clock;
+ clear();
+ }
+
+ @Override
+ public void clear() {
+ makeNextInvoiceFailWithException.set(false);
+ makeAllInvoicesFailWithError.set(false);
+ makeNextInvoiceFailWithError.set(false);
+ }
+
+ @Override
+ public void makeNextPaymentFailWithError() {
+ makeNextInvoiceFailWithError.set(true);
+ }
+
+ @Override
+ public void makeNextPaymentFailWithException() {
+ makeNextInvoiceFailWithException.set(true);
+ }
+
+ @Override
+ public void makeAllInvoicesFailWithError(final boolean failure) {
+ makeAllInvoicesFailWithError.set(failure);
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public PaymentInfoPlugin processPayment(final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final CallContext context) throws PaymentPluginApiException {
+ if (makeNextInvoiceFailWithException.getAndSet(false)) {
+ throw new PaymentPluginApiException("", "test error");
+ }
+
+ final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
+ final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), status, null);
+ payments.put(kbPaymentId.toString(), result);
+ return result;
+ }
+
+ @Override
+ public PaymentInfoPlugin getPaymentInfo(final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+ final PaymentInfoPlugin payment = payments.get(kbPaymentId.toString());
+ if (payment == null) {
+ throw new PaymentPluginApiException("", "No payment found for payment id " + kbPaymentId.toString());
+ }
+ return payment;
+ }
+
+
+ @Override
+ public void addPaymentMethod(final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
+ // both AccountId and externalPaymentMethodId are set to random values
+ final PaymentMethodInfoPlugin realWithID = new DefaultPaymentMethodInfoPlugin(UUID.randomUUID(), kbPaymentMethodId, setDefault, UUID.randomUUID().toString());
+ paymentMethods.put(kbPaymentMethodId.toString(), realWithID);
+ }
+
+ @Override
+ public void deletePaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+ paymentMethods.remove(kbPaymentMethodId.toString());
+ }
+
+ @Override
+ public void setDefaultPaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+ }
+
+ @Override
+ public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) {
+ return ImmutableList.<PaymentMethodInfoPlugin>copyOf(paymentMethods.values());
+ }
+
+ @Override
+ public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) {
+ paymentMethods.clear();
+ }
+
+ @Override
+ public RefundInfoPlugin processRefund(final UUID kbPaymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
+ final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(kbPaymentId, context);
+ if (paymentInfoPlugin == null) {
+ throw new PaymentPluginApiException("", String.format("No payment found for payment id %s (plugin %s)", kbPaymentId.toString(), getName()));
+ }
+
+ BigDecimal maxAmountRefundable = paymentInfoPlugin.getAmount();
+ for (final RefundInfoPlugin refund : refunds.get(kbPaymentId.toString())) {
+ maxAmountRefundable = maxAmountRefundable.add(refund.getAmount().negate());
+ }
+ if (maxAmountRefundable.compareTo(refundAmount) < 0) {
+ throw new PaymentPluginApiException("", String.format("Refund amount of %s for payment id %s is bigger than the payment amount %s (plugin %s)",
+ refundAmount, kbPaymentId.toString(), paymentInfoPlugin.getAmount(), getName()));
+ }
+
+ final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+ refunds.put(kbPaymentId.toString(), refundInfoPlugin);
+
+ return refundInfoPlugin;
}
}