killbill-aplcache

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index a79a99b..a2b2e5b 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -129,4 +129,7 @@ public interface PaymentApi {
     public void setDefaultPaymentMethod(Account account, UUID paymentMethodId, CallContext context)
             throws PaymentApiException;
 
+    public List<PaymentMethod> refreshPaymentMethods(String pluginName, Account account, CallContext context)
+            throws PaymentApiException;
+
 }
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
index 690584a..0433003 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
@@ -20,6 +20,7 @@ import java.util.List;
 
 public interface PaymentMethodPlugin {
 
+
     public String getExternalPaymentMethodId();
 
     public boolean isDefaultPaymentMethod();
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentMethodInfoPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentMethodInfoPlugin.java
new file mode 100644
index 0000000..cd25e95
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentMethodInfoPlugin.java
@@ -0,0 +1,49 @@
+/*
+ * 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.plugin.api;
+
+import java.util.UUID;
+
+/**
+ * Returns the plugin view of existing payment methods
+ */
+public interface PaymentMethodInfoPlugin {
+
+    /**
+     *
+     * @return the Killbill accountId
+     */
+    public UUID getAccountId();
+
+    /**
+     *
+     * @return the killbillPaymentMethodId
+     */
+    public UUID getPaymentMethodId();
+
+    /**
+     *
+     * @return default payment method set on the gateway
+     */
+    public boolean isDefault();
+
+    /**
+     *
+     * @return the external paymentMethodId on the gateway
+     */
+    public String getExternalPaymentMethodId();
+}
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
index 6d6844a..7cfe8e8 100644
--- a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
@@ -17,6 +17,7 @@
 package com.ning.billing.payment.plugin.api;
 
 import java.math.BigDecimal;
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.payment.api.PaymentMethodPlugin;
@@ -98,4 +99,28 @@ public interface PaymentPluginApi {
      */
     public void setDefaultPaymentMethod(UUID kbPaymentMethodId, CallContext context)
             throws PaymentPluginApiException;
+
+    /**
+     *
+     * This is used to see the view of paymentMethods kept by the plugin or the view of
+     * existing payment method on the gateway.
+     *
+     * Sometimes payment methods have to be added directly to the gateway for PCI compliance issues
+     * and so Killbill needs to refresh its state.
+     *
+     * @param kbAccountId           killbill accountId
+     * @param refreshFromGateway    fetch the list of existing  payment methods from gateway-- if supported
+     * @param context               call context
+     * @return
+     */
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(UUID kbAccountId, boolean refreshFromGateway, CallContext context)
+            throws PaymentPluginApiException;
+
+    /**
+     * This is used after Killbill decided to refresh its state from the gateway
+     *
+     * @param paymentMethods        the list of payment methods
+     */
+    public void resetPaymentMethods(List<PaymentMethodInfoPlugin> paymentMethods)
+            throws PaymentPluginApiException;
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 4720a01..c64dd1a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -83,7 +83,6 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     protected TestApiListener testListener;
     @Inject
     protected TestListenerStatus testListenerStatus;
-
     @Inject
     protected EntitlementTestInitializer entitlementTestInitializer;
 
diff --git a/osgi-bundles/hello/src/main/java/com/ning/billing/osgi/bundles/hello/HelloActivator.java b/osgi-bundles/hello/src/main/java/com/ning/billing/osgi/bundles/hello/HelloActivator.java
index 667d450..7af98e7 100644
--- a/osgi-bundles/hello/src/main/java/com/ning/billing/osgi/bundles/hello/HelloActivator.java
+++ b/osgi-bundles/hello/src/main/java/com/ning/billing/osgi/bundles/hello/HelloActivator.java
@@ -34,6 +34,7 @@ import com.ning.billing.beatrix.bus.api.ExternalBus;
 import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+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;
@@ -162,6 +163,15 @@ public class HelloActivator implements BundleActivator {
             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) throws PaymentPluginApiException {
+                return null;
+            }
+
+            @Override
+            public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
+            }
+
         }, props);
     }
 
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
index 1071a79..2f061f0 100644
--- a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
@@ -19,6 +19,7 @@ package com.ning.billing.osgi.bundles.jruby;
 import java.math.BigDecimal;
 import java.util.Dictionary;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -34,6 +35,7 @@ import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.osgi.api.config.PluginRubyConfig;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+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;
@@ -145,4 +147,24 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi 
         pluginInstance.callMethod("set_default_payment_method",
                                   JavaEmbedUtils.javaToRuby(runtime, kbPaymentMethodId.toString()));
     }
+
+    @Override
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) throws PaymentPluginApiException {
+        checkPluginIsRunning();
+
+        final Ruby runtime = getRuntime();
+        pluginInstance.callMethod("get_payment_methods",
+                                  JavaEmbedUtils.javaToRuby(runtime, kbAccountId.toString()));
+        // TODO
+        return null;
+    }
+
+    @Override
+    public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
+        checkPluginIsRunning();
+
+        final Ruby runtime = getRuntime();
+        pluginInstance.callMethod("reset_payment_methods",
+                                  JavaEmbedUtils.javaToRuby(runtime, paymentMethods));
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 0c2bf82..e27edac 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -180,4 +180,10 @@ public class DefaultPaymentApi implements PaymentApi {
             throws PaymentApiException {
         methodProcessor.setDefaultPaymentMethod(account, paymentMethodId, internalCallContextFactory.createInternalCallContext(account.getId(), context));
     }
+
+    @Override
+    public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final CallContext context)
+            throws PaymentApiException {
+        return methodProcessor.refreshPaymentMethods(pluginName, account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java
index 0c48976..a906dc0 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java
@@ -45,6 +45,10 @@ public class DefaultPaymentMethod extends EntityBase implements PaymentMethod {
         this(UUID.randomUUID(), null, null, accountId, true, pluginName, pluginDetail);
     }
 
+    public DefaultPaymentMethod(final UUID paymentMethodId, final UUID accountId, final String pluginName) {
+        this(paymentMethodId, null, null, accountId, true, pluginName, null);
+    }
+
     public DefaultPaymentMethod(final PaymentMethodModelDao input, final PaymentMethodPlugin pluginDetail) {
         this(input.getId(), input.getCreatedDate(), input.getUpdatedDate(), input.getAccountId(), input.isActive(), input.getPluginName(), pluginDetail);
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index e5c2855..89098ff 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -36,9 +36,11 @@ import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.api.PaymentMethodPlugin.PaymentMethodKVInfo;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentMethodModelDao;
+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.provider.DefaultNoOpPaymentMethodPlugin;
+import com.ning.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
 import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.util.callcontext.InternalCallContext;
@@ -48,6 +50,8 @@ import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.tag.TagInternalApi;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
@@ -242,4 +246,86 @@ public class PaymentMethodProcessor extends ProcessorBase {
         }
         return pluginRegistry.getPlugin(paymentMethod.getPluginName());
     }
+
+    /**
+     * This refreshed the payment methods from the plugin for cases when adding payment method does not flow through KB because of PCI compliance
+     * issues. The logic below is not optimal because there is no atomicity in the step but the goos news is that this is idempotent so can always be
+     * replayed if necessary-- partial failure scenario.
+     *
+     * @param pluginName
+     * @param account
+     * @param context
+     * @return the list of payment methods -- should be identical between KB, the plugin view-- if it keeps a state-- and the gateway.
+     * @throws PaymentApiException
+     */
+    public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final InternalCallContext context) throws PaymentApiException {
+
+
+        // Don't hold the account lock while fetching the payment methods from the gateway as those could change anyway
+        final PaymentPluginApi pluginApi = pluginRegistry.getPlugin(pluginName);
+        final List<PaymentMethodInfoPlugin> pluginPms;
+        try {
+            pluginPms = pluginApi.getPaymentMethods(account.getId(), true, context.toCallContext());
+            // The method should never return null by convention, but let's not trust the plugin...
+            if (pluginPms == null) {
+                log.warn("No payment methods defined on the account {} for plugin {}", account.getId(), pluginName);
+                return ImmutableList.<PaymentMethod>of();
+            }
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
+        }
+
+        return new WithAccountLock<List<PaymentMethod>>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<List<PaymentMethod>>() {
+
+            @Override
+            public List<PaymentMethod> doOperation() throws PaymentApiException {
+
+                UUID defaultPaymentMethodId = null;
+
+                final List<PaymentMethodInfoPlugin> pluginPmsWithId = new ArrayList<PaymentMethodInfoPlugin>();
+                final List<PaymentMethodModelDao> finalPaymentMethods = new ArrayList<PaymentMethodModelDao>();
+                for (final PaymentMethodInfoPlugin cur : pluginPms) {
+
+                    // If the kbPaymentId is NULL, the plugin does not know about it, so we create a new UUID
+                    final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUID.randomUUID();
+                    final PaymentMethod input = new DefaultPaymentMethod(paymentMethodId, account.getId(), pluginName);
+                    final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getCreatedDate(), input.getUpdatedDate(),
+                                                                                    input.getAccountId(), input.getPluginName(), input.isActive());
+                    finalPaymentMethods.add(pmModel);
+
+                    pluginPmsWithId.add(new DefaultPaymentMethodInfoPlugin(cur, paymentMethodId));
+
+                    if (cur.isDefault()) {
+                        defaultPaymentMethodId = paymentMethodId;
+                    }
+                }
+
+                final List<PaymentMethodModelDao> refreshedPaymentMethods = paymentDao.refreshPaymentMethods(account.getId(),
+                                                                                                             finalPaymentMethods,
+                                                                                                             context);
+                try {
+                    pluginApi.resetPaymentMethods(pluginPmsWithId);
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
+                }
+
+                try {
+                    if (defaultPaymentMethodId != null) {
+                        accountInternalApi.updatePaymentMethod(account.getId(), defaultPaymentMethodId, context);
+                    } else {
+                        accountInternalApi.removePaymentMethod(account.getId(), context);
+                    }
+                } catch (AccountApiException e) {
+                    throw new PaymentApiException(e);
+                }
+
+                return ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
+                    @Override
+                    public PaymentMethod apply(final PaymentMethodModelDao input) {
+                        return new DefaultPaymentMethod(input, null);
+                    }
+                }));
+            }
+        });
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index 271eab1..7e81ac1 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -16,7 +16,9 @@
 
 package com.ning.billing.payment.dao;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -289,4 +291,47 @@ public class DefaultPaymentDao implements PaymentDao {
             }
         });
     }
+
+    @Override
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> newPaymentMethods, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
+
+            @Override
+            public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+                final List<PaymentMethodModelDao> existingPaymentMethods = transactional.getByAccountId(accountId.toString(), context);
+
+                for (final PaymentMethodModelDao finalPaymentMethod : newPaymentMethods) {
+
+                    PaymentMethodModelDao foundExistingPaymentMethod = null;
+                    for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+                        if (existingPaymentMethod.equals(finalPaymentMethod)) {
+                            // We already have it - nothing to do
+                            foundExistingPaymentMethod = existingPaymentMethod;
+                            break;
+                        } else if (existingPaymentMethod.equalsButActive(finalPaymentMethod)) {
+                            // We already have it but its status has changed - update it accordingly
+                            undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
+                            foundExistingPaymentMethod = existingPaymentMethod;
+                            break;
+                        }
+                        // Otherwise, we don't have it
+                    }
+
+                    if (foundExistingPaymentMethod == null) {
+                        insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, finalPaymentMethod, context);
+                    } else {
+                        existingPaymentMethods.remove(foundExistingPaymentMethod);
+                    }
+                }
+
+                // Finally, all payment methods left in the existingPaymentMethods should be marked as deleted
+                for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+                        deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
+                }
+                return transactional.getByAccountId(accountId.toString(), context);
+            }
+        });
+    }
+
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 050c693..a5eaef5 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -67,5 +67,7 @@ public interface PaymentDao {
 
     public void deletedPaymentMethod(UUID paymentMethodId, InternalCallContext context);
 
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context);
+
     public void undeletedPaymentMethod(UUID paymentMethodId, InternalCallContext context);
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
index 0d7cdd9..99ae81d 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
@@ -95,6 +95,9 @@ public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<
     }
 
     public boolean equalsButActive(final PaymentMethodModelDao that) {
+        if (id != null ? !id.equals(that.id) : that.id != null) {
+            return false;
+        }
         if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
             return false;
         }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
index 8c5437c..bab3566 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
@@ -28,6 +28,7 @@ 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.PaymentPluginApiException;
 import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
 import com.ning.billing.payment.plugin.api.RefundInfoPlugin.RefundPluginStatus;
@@ -142,6 +143,15 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) {
+        return null;
+    }
+
+    @Override
+    public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) {
+    }
+
+    @Override
     public RefundInfoPlugin processRefund(final UUID kbPaymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
         final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(kbPaymentId, context);
         if (paymentInfoPlugin == null) {
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentMethodInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentMethodInfoPlugin.java
new file mode 100644
index 0000000..82ace37
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentMethodInfoPlugin.java
@@ -0,0 +1,60 @@
+/*
+ * 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.provider;
+
+import java.util.UUID;
+
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+
+public class DefaultPaymentMethodInfoPlugin implements PaymentMethodInfoPlugin {
+
+    private final UUID accountId;
+    private final UUID paymentMethodId;
+    private final boolean isDefault;
+    private final String externalPaymentMethodId;
+
+    public DefaultPaymentMethodInfoPlugin(final UUID accountId, final UUID paymentMethodId, final boolean aDefault, final String externalPaymentMethodId) {
+        this.accountId = accountId;
+        this.paymentMethodId = paymentMethodId;
+        isDefault = aDefault;
+        this.externalPaymentMethodId = externalPaymentMethodId;
+    }
+
+    public DefaultPaymentMethodInfoPlugin(PaymentMethodInfoPlugin input, final UUID paymentMethodId) {
+        this(input.getAccountId(), paymentMethodId, input.isDefault(), input.getExternalPaymentMethodId());
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    @Override
+    public boolean isDefault() {
+        return isDefault;
+    }
+
+    @Override
+    public String getExternalPaymentMethodId() {
+        return externalPaymentMethodId;
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 19a516e..0262bce 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -16,6 +16,11 @@
 
 package com.ning.billing.payment.provider;
 
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.clock.Clock;
 
 import com.google.inject.Inject;
@@ -40,4 +45,5 @@ public class ExternalPaymentProviderPlugin extends DefaultNoOpPaymentProviderPlu
     public String getName() {
         return PLUGIN_NAME;
     }
+
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index d79598d..5bb0288 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -160,6 +160,11 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context) {
+        return null;
+    }
+
+    @Override
     public void undeletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
         throw new UnsupportedOperationException();
     }