killbill-memoizeit

payment: re-implement search across plugins This fixes

9/7/2018 3:45:07 AM

Details

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 07b2d12..568fd7a 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
@@ -90,10 +90,12 @@ import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
 
 import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPagination;
@@ -345,42 +347,30 @@ public class PaymentProcessor extends ProcessorBase {
                                               final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
         final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
 
-        return getEntityPagination(limit,
-                                   new SourcePaginationBuilder<PaymentTransactionInfoPlugin, PaymentApiException>() {
-                                       @Override
-                                       public Pagination<PaymentTransactionInfoPlugin> build() throws PaymentApiException {
-                                           try {
-                                               return pluginApi.searchPayments(searchKey, offset, limit, properties, tenantContext);
-                                           } catch (final PaymentPluginApiException e) {
-                                               throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
-                                           }
-                                       }
+        final Pagination<PaymentTransactionInfoPlugin> paymentTransactionInfoPlugins;
+        try {
+            paymentTransactionInfoPlugins = pluginApi.searchPayments(searchKey, offset, limit, properties, tenantContext);
+        } catch (final PaymentPluginApiException e) {
+            throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
+        }
 
-                                   },
-                                   new Function<PaymentTransactionInfoPlugin, Payment>() {
-                                       final List<PaymentTransactionInfoPlugin> cachedPaymentTransactions = new LinkedList<PaymentTransactionInfoPlugin>();
+        // Cannot easily stream here unfortunately, since we need to merge PaymentTransactionInfoPlugin into Payment (no order assumed)
+        final Multimap<UUID, PaymentTransactionInfoPlugin> payments = HashMultimap.<UUID, PaymentTransactionInfoPlugin>create();
+        for (final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin : paymentTransactionInfoPlugins) {
+            payments.put(paymentTransactionInfoPlugin.getKbPaymentId(), paymentTransactionInfoPlugin);
+        }
 
-                                       @Override
-                                       public Payment apply(final PaymentTransactionInfoPlugin pluginTransaction) {
-                                           if (pluginTransaction.getKbPaymentId() == null) {
-                                               // Garbage from the plugin?
-                                               log.debug("Plugin {} returned a payment without a kbPaymentId for searchKey {}", pluginName, searchKey);
-                                               return null;
-                                           }
+        final Collection<Payment> results = new LinkedList<Payment>();
+        for (final UUID paymentId : payments.keys()) {
+            final Payment result = toPayment(paymentId, withPluginInfo ? payments.get(paymentId) : ImmutableList.<PaymentTransactionInfoPlugin>of(), withAttempts, internalTenantContext);
+            if (result != null) {
+                results.add(result);
+            }
+        }
 
-                                           if (cachedPaymentTransactions.isEmpty() ||
-                                               (cachedPaymentTransactions.get(0).getKbPaymentId().equals(pluginTransaction.getKbPaymentId()))) {
-                                               cachedPaymentTransactions.add(pluginTransaction);
-                                               return null;
-                                           } else {
-                                               final Payment result = toPayment(pluginTransaction.getKbPaymentId(), withPluginInfo ? ImmutableList.<PaymentTransactionInfoPlugin>copyOf(cachedPaymentTransactions) : ImmutableList.<PaymentTransactionInfoPlugin>of(), withAttempts, internalTenantContext);
-                                               cachedPaymentTransactions.clear();
-                                               cachedPaymentTransactions.add(pluginTransaction);
-                                               return result;
-                                           }
-                                       }
-                                   }
-                                  );
+        return new DefaultPagination<Payment>(paymentTransactionInfoPlugins,
+                                              limit,
+                                              results.iterator());
     }
 
     public void cancelScheduledPaymentTransaction(@Nullable final UUID paymentTransactionId, @Nullable final String paymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
index 54b5848..02e383f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
@@ -41,9 +41,28 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentTransactionInfoPlugi
     private final String gatewayErrorCode;
     private final Currency currency;
     private final TransactionType transactionType;
+    private final String firstPaymentReferenceId;
+    private final String secondPaymentReferenceId;
+    private final List<PluginProperty> pluginProperties;
 
     public DefaultNoOpPaymentInfoPlugin(final UUID kbPaymentId, final UUID kbTransactionPaymentId, final TransactionType transactionType, final BigDecimal amount, final Currency currency, final DateTime effectiveDate,
                                         final DateTime createdDate, final PaymentPluginStatus status, final String gatewayErrorCode, final String gatewayError) {
+        this(kbPaymentId, kbTransactionPaymentId, transactionType, amount, currency, effectiveDate, createdDate, status, gatewayErrorCode, gatewayError, null, null, ImmutableList.<PluginProperty>of());
+    }
+
+    public DefaultNoOpPaymentInfoPlugin(final UUID kbPaymentId,
+                                        final UUID kbTransactionPaymentId,
+                                        final TransactionType transactionType,
+                                        final BigDecimal amount,
+                                        final Currency currency,
+                                        final DateTime effectiveDate,
+                                        final DateTime createdDate,
+                                        final PaymentPluginStatus status,
+                                        final String gatewayErrorCode,
+                                        final String gatewayError,
+                                        final String firstPaymentReferenceId,
+                                        final String secondPaymentReferenceId,
+                                        final List<PluginProperty> pluginProperties) {
         this.kbPaymentId = kbPaymentId;
         this.kbTransactionPaymentId = kbTransactionPaymentId;
         this.transactionType = transactionType;
@@ -54,6 +73,9 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentTransactionInfoPlugi
         this.gatewayErrorCode = gatewayErrorCode;
         this.gatewayError = gatewayError;
         this.currency = currency;
+        this.firstPaymentReferenceId = firstPaymentReferenceId;
+        this.secondPaymentReferenceId = secondPaymentReferenceId;
+        this.pluginProperties = pluginProperties;
     }
 
     @Override
@@ -108,29 +130,35 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentTransactionInfoPlugi
 
     @Override
     public String getFirstPaymentReferenceId() {
-        return null;
+        return firstPaymentReferenceId;
     }
 
     @Override
     public String getSecondPaymentReferenceId() {
-        return null;
+        return secondPaymentReferenceId;
     }
 
     @Override
     public List<PluginProperty> getProperties() {
-        return ImmutableList.<PluginProperty>of();
+        return pluginProperties;
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("DefaultNoOpPaymentInfoPlugin{");
         sb.append("kbPaymentId=").append(kbPaymentId);
+        sb.append(", kbTransactionPaymentId=").append(kbTransactionPaymentId);
         sb.append(", amount=").append(amount);
         sb.append(", effectiveDate=").append(effectiveDate);
         sb.append(", createdDate=").append(createdDate);
         sb.append(", status=").append(status);
-        sb.append(", error='").append(gatewayError).append('\'');
+        sb.append(", gatewayError='").append(gatewayError).append('\'');
+        sb.append(", gatewayErrorCode='").append(gatewayErrorCode).append('\'');
         sb.append(", currency=").append(currency);
+        sb.append(", transactionType=").append(transactionType);
+        sb.append(", firstPaymentReferenceId='").append(firstPaymentReferenceId).append('\'');
+        sb.append(", secondPaymentReferenceId='").append(secondPaymentReferenceId).append('\'');
+        sb.append(", pluginProperties=").append(pluginProperties);
         sb.append('}');
         return sb.toString();
     }
@@ -176,6 +204,15 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentTransactionInfoPlugi
         if (status != that.status) {
             return false;
         }
+        if (firstPaymentReferenceId != null ? !firstPaymentReferenceId.equals(that.firstPaymentReferenceId) : that.firstPaymentReferenceId != null) {
+            return false;
+        }
+        if (secondPaymentReferenceId != null ? !secondPaymentReferenceId.equals(that.secondPaymentReferenceId) : that.secondPaymentReferenceId != null) {
+            return false;
+        }
+        if (pluginProperties != null ? !pluginProperties.equals(that.pluginProperties) : that.pluginProperties != null) {
+            return false;
+        }
 
         return true;
     }
@@ -192,6 +229,9 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentTransactionInfoPlugi
         result = 31 * result + (gatewayError != null ? gatewayError.hashCode() : 0);
         result = 31 * result + (gatewayErrorCode != null ? gatewayErrorCode.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (firstPaymentReferenceId != null ? firstPaymentReferenceId.hashCode() : 0);
+        result = 31 * result + (secondPaymentReferenceId != null ? secondPaymentReferenceId.hashCode() : 0);
+        result = 31 * result + (pluginProperties != null ? pluginProperties.hashCode() : 0);
         return result;
     }
 }
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 d31b5ca..86be18b 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
@@ -26,10 +26,8 @@ import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
 
 import org.joda.time.LocalDate;
-import org.joda.time.LocalDate.Property;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
@@ -45,6 +43,7 @@ import org.killbill.billing.payment.dao.PaymentSqlDao;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
+import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
 import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
 import org.killbill.billing.payment.provider.MockPaymentControlProviderPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
@@ -66,10 +65,6 @@ import static org.testng.Assert.fail;
 
 public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
-    private MockPaymentProviderPlugin mockPaymentProviderPlugin;
-
-    private MockPaymentControlProviderPlugin mockPaymentControlProviderPlugin;
-
     final PaymentOptions INVOICE_PAYMENT = new PaymentOptions() {
         @Override
         public boolean isExternalPayment() {
@@ -81,7 +76,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
         }
     };
-
     final PaymentOptions CONTROL_PLUGIN_OPTIONS = new PaymentOptions() {
         @Override
         public boolean isExternalPayment() {
@@ -93,7 +87,8 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             return Arrays.asList(MockPaymentControlProviderPlugin.PLUGIN_NAME);
         }
     };
-
+    private MockPaymentProviderPlugin mockPaymentProviderPlugin;
+    private MockPaymentControlProviderPlugin mockPaymentControlProviderPlugin;
     private Account account;
 
     @BeforeClass(groups = "slow")
@@ -185,7 +180,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         checkPaymentMethodPagination(paymentMethodId, baseNbRecords + 1, false);
     }
 
-    @Test(groups = "slow", description="Verify we can make a refund on  payment whose original payment method was deleted. See 694")
+    @Test(groups = "slow", description = "Verify we can make a refund on  payment whose original payment method was deleted. See 694")
     public void testRefundAfterDeletedPaymentMethod() throws PaymentApiException {
 
         final BigDecimal requestedAmount = BigDecimal.TEN;
@@ -194,13 +189,12 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
         paymentApi.deletePaymentMethod(account, account.getPaymentMethodId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
 
-        final Payment newPayment = paymentApi.createRefund(account, payment.getId(),requestedAmount,  Currency.EUR, UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+        final Payment newPayment = paymentApi.createRefund(account, payment.getId(), requestedAmount, Currency.EUR, UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
         Assert.assertEquals(newPayment.getTransactions().size(), 2);
     }
 
     @Test(groups = "slow")
     public void testCreateSuccessPurchase() throws PaymentApiException {
-
         final BigDecimal requestedAmount = BigDecimal.TEN;
 
         final String paymentExternalKey = "bwwrr";
@@ -281,7 +275,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
     }
 
-
     @Test(groups = "slow")
     public void testCreateCancelledPurchase() throws PaymentApiException {
 
@@ -352,7 +345,8 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         mockPaymentProviderPlugin.makeNextPaymentFailWithException();
 
         final BigDecimal requestedAmount = BigDecimal.TEN;
-        final String paymentExternalKey = "pay controle external key";;
+        final String paymentExternalKey = "pay controle external key";
+
         final String transactionExternalKey = "txn control external key";
         try {
             paymentApi.createPurchaseWithPaymentControl(
@@ -369,7 +363,8 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         mockPaymentControlProviderPlugin.throwsException(new PaymentControlApiException());
 
         final BigDecimal requestedAmount = BigDecimal.TEN;
-        final String paymentExternalKey = "pay controle external key";;
+        final String paymentExternalKey = "pay controle external key";
+
         final String transactionExternalKey = "txn control external key";
         try {
             paymentApi.createPurchaseWithPaymentControl(
@@ -386,7 +381,8 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         mockPaymentControlProviderPlugin.throwsException(new IllegalStateException());
 
         final BigDecimal requestedAmount = BigDecimal.TEN;
-        final String paymentExternalKey = "pay controle external key";;
+        final String paymentExternalKey = "pay controle external key";
+
         final String transactionExternalKey = "txn control external key";
         try {
             paymentApi.createPurchaseWithPaymentControl(
@@ -934,7 +930,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             assertTrue(true);
         }
 
-
         final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments.size(), 1);
         final Payment payment = accountPayments.get(0);
@@ -959,7 +954,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
     }
 
-
     @Test(groups = "slow")
     public void testCreateCancelledPurchaseWithPaymentControl() throws PaymentApiException, InvoiceApiException, EventBusException {
 
@@ -991,7 +985,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             assertTrue(true);
         }
 
-
         final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments.size(), 1);
         final Payment payment = accountPayments.get(0);
@@ -1019,7 +1012,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
                                                     createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
 
-
         final List<Payment> accountPayments2 = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments2.size(), 1);
         final Payment payment2 = accountPayments2.get(0);
@@ -1035,7 +1027,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
     }
 
-
     @Test(groups = "slow")
     public void testCreateAbortedPurchaseWithPaymentControl() throws InvoiceApiException, EventBusException {
 
@@ -2386,6 +2377,81 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         Assert.assertNotEquals(failedAuthorization4.getTransactions().get(3).getExternalKey(), authKey);
     }
 
+    @Test(groups = "slow")
+    public void testSearchPayments() throws Exception {
+        // Add a second, non-default, payment method
+        final PaymentMethodPlugin pm = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
+        final UUID secondPmId = testHelper.addTestPaymentMethod(MockPaymentProviderPlugin.PLUGIN_NAME + "2", account, pm, ImmutableList.<PluginProperty>of());
+
+        Pagination<Payment> foundPayments = paymentApi.searchPayments("all", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertFalse(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 0L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 0L);
+
+        foundPayments = paymentApi.searchPayments("A", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertFalse(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 0L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 0L);
+
+        foundPayments = paymentApi.searchPayments("B", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertFalse(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 0L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 0L);
+
+        final Payment payment1 = paymentApi.createPurchase(account,
+                                                           account.getPaymentMethodId(),
+                                                           null,
+                                                           BigDecimal.TEN,
+                                                           Currency.USD,
+                                                           UUID.randomUUID().toString(),
+                                                           UUID.randomUUID().toString(),
+                                                           ImmutableList.<PluginProperty>of(new PluginProperty("group", "all", false), new PluginProperty("marker", "A", false)),
+                                                           callContext);
+
+        foundPayments = paymentApi.searchPayments("all", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertTrue(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 1L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 1L);
+
+        foundPayments = paymentApi.searchPayments("A", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertTrue(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 1L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 1L);
+        Assert.assertEquals(foundPayments.iterator().next().getId(), payment1.getId());
+
+        foundPayments = paymentApi.searchPayments("B", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertFalse(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 1L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 0L);
+
+        final Payment payment2 = paymentApi.createPurchase(account,
+                                                           secondPmId,
+                                                           null,
+                                                           BigDecimal.TEN,
+                                                           Currency.USD,
+                                                           UUID.randomUUID().toString(),
+                                                           UUID.randomUUID().toString(),
+                                                           ImmutableList.<PluginProperty>of(new PluginProperty("group", "all", false), new PluginProperty("marker", "B", false)),
+                                                           callContext);
+
+        foundPayments = paymentApi.searchPayments("all", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertTrue(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 2L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 2L);
+
+        foundPayments = paymentApi.searchPayments("A", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertTrue(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 2L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 1L);
+        Assert.assertEquals(foundPayments.iterator().next().getId(), payment1.getId());
+
+        foundPayments = paymentApi.searchPayments("B", 0L, 10L, true, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertTrue(foundPayments.iterator().hasNext());
+        Assert.assertEquals(foundPayments.getMaxNbRecords(), (Long) 2L);
+        Assert.assertEquals(foundPayments.getTotalNbRecords(), (Long) 1L);
+        Assert.assertEquals(foundPayments.iterator().next().getId(), payment2.getId());
+    }
+
     private void verifyRefund(final Payment refund, final String paymentExternalKey, final String paymentTransactionExternalKey, final String refundTransactionExternalKey, final BigDecimal requestedAmount, final BigDecimal refundAmount, final TransactionStatus transactionStatus) {
         Assert.assertEquals(refund.getExternalKey(), paymentExternalKey);
         Assert.assertEquals(refund.getTransactions().size(), 2);
diff --git a/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java b/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java
index a4bc6e9..447cd34 100644
--- a/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java
+++ b/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java
@@ -54,6 +54,8 @@ public class TestPaymentModule extends PaymentModule {
     @Override
     protected void installPaymentProviderPlugins(final PaymentConfig config) {
         install(new MockPaymentProviderPluginModule(MockPaymentProviderPlugin.PLUGIN_NAME, clock, configSource));
+        // Install a second instance, to test codepaths with multiple plugins (e.g. search)
+        install(new MockPaymentProviderPluginModule(MockPaymentProviderPlugin.PLUGIN_NAME + "2", clock, configSource));
     }
 
     private void installExternalApis() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index b86f476..9891ccc 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -309,7 +309,20 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
 
     @Override
     public Pagination<PaymentTransactionInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentPluginApiException {
-        throw new IllegalStateException("Not implemented");
+        final ImmutableList<PaymentTransactionInfoPlugin> results = ImmutableList.<PaymentTransactionInfoPlugin>copyOf(Iterables.<PaymentTransactionInfoPlugin>filter(Iterables.<PaymentTransactionInfoPlugin>concat(paymentTransactions.values()), new Predicate<PaymentTransactionInfoPlugin>() {
+            @Override
+            public boolean apply(final PaymentTransactionInfoPlugin input) {
+                if (input.getProperties() !=  null) {
+                    for (final PluginProperty cur : input.getProperties()) {
+                        if (cur.getValue().equals(searchKey)) {
+                            return true;
+                        }
+                    }
+                }
+                return (input.getKbPaymentId().toString().equals(searchKey));
+            }
+        }));
+        return DefaultPagination.<PaymentTransactionInfoPlugin>build(offset, limit, paymentTransactions.size(), results);
     }
 
     @Override
@@ -459,7 +472,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
             processedCurrency = currency;
         }
 
-        final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, processedCurrency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
+        final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, processedCurrency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error, null, null, ImmutableList.<PluginProperty>copyOf(pluginProperties));
         List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
         if (existingTransactions == null) {
             existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();
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 772235b..61e1f56 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
@@ -169,15 +169,19 @@ public class TestPaymentHelper {
     }
 
     public Account addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo, final Iterable<PluginProperty> pluginProperties) throws Exception {
-        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, true, paymentMethodInfo, pluginProperties, context);
-        if (isFastTest()) {
+        final UUID pmId = addTestPaymentMethod(MockPaymentProviderPlugin.PLUGIN_NAME, account, paymentMethodInfo, pluginProperties);
+        // To reflect the payment method id change
+        return accountApi.getAccountById(account.getId(), context);
+    }
+
+    public UUID addTestPaymentMethod(final String pluginName, final Account account, final PaymentMethodPlugin paymentMethodInfo, final Iterable<PluginProperty> pluginProperties) throws Exception {
+        final boolean setDefault = paymentMethodInfo.isDefaultPaymentMethod();
+        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), pluginName, setDefault, paymentMethodInfo, pluginProperties, context);
+        if (isFastTest() && setDefault) {
             final Account account1 = new MockAccountBuilder(account).paymentMethodId(paymentMethodId).build();
-            accountApi.updateAccount(account, context);
-            return account1;
-        } else {
-            // To reflect the payment method id change
-            return accountApi.getAccountById(account.getId(), context);
+            accountApi.updateAccount(account1, context);
         }
+        return paymentMethodId;
     }
 
     // Unfortunately, this helper is shared across fast and slow tests
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index 69c17a0..5c4be62 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -19,7 +19,6 @@ package org.killbill.billing.jaxrs;
 
 import java.math.BigDecimal;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
@@ -869,7 +868,7 @@ public class TestPayment extends TestJaxrsBase {
         assertEquals(Payments.size(), paymentNb);
         assertEquals(Payments.get(paymentNb - 1), payment);
 
-        final Payment retrievedPayment = killBillClient.getPayment(payment.getPaymentId());
+        final Payment retrievedPayment = killBillClient.getPayment(payment.getPaymentId(), false, requestOptions);
         assertEquals(retrievedPayment, payment);
 
         final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(account.getAccountId());