killbill-memoizeit

payment: update to killbill-api 0.9.2 Signed-off-by: Pierre-Alexandre

4/24/2014 4:19:44 PM

Details

diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 889b975..33faf6c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -615,12 +615,9 @@ public class AccountResource extends JaxRsResourceBase {
     @Path("/{accountId:" + UUID_PATTERN + "}/" + DIRECT_PAYMENTS)
     @Produces(APPLICATION_JSON)
     public Response getDirectPaymentsForAccount(@PathParam("accountId") final String accountIdStr,
-                                                @QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
-                                                @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
-        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final UUID accountId = UUID.fromString(accountIdStr);
-        final List<DirectPayment> payments = directPaymentApi.getAccountPayments(accountId, withPluginInfo, pluginProperties, context.createContext(request));
+        final List<DirectPayment> payments = directPaymentApi.getAccountPayments(accountId, context.createContext(request));
         final List<DirectPaymentJson> result = ImmutableList.copyOf(Iterables.transform(payments, new Function<DirectPayment, DirectPaymentJson>() {
             @Override
             public DirectPaymentJson apply(final DirectPayment input) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java
index 8783076..a090091 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java
@@ -1,5 +1,6 @@
 /*
  * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -22,6 +23,7 @@ import java.util.UUID;
 
 import javax.inject.Inject;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.payment.api.DirectPayment;
 import org.killbill.billing.payment.api.DirectPaymentApi;
@@ -31,19 +33,17 @@ import org.killbill.billing.payment.core.DirectPaymentProcessor;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.clock.Clock;
+import org.killbill.billing.util.entity.Pagination;
 
 public class DefaultDirectPaymentApi implements DirectPaymentApi {
 
     private final DirectPaymentProcessor directPaymentProcessor;
     private final InternalCallContextFactory internalCallContextFactory;
-    private final Clock clock;
 
     @Inject
-    public DefaultDirectPaymentApi(final DirectPaymentProcessor directPaymentProcessor, final InternalCallContextFactory internalCallContextFactory, final Clock clock) {
+    public DefaultDirectPaymentApi(final DirectPaymentProcessor directPaymentProcessor, final InternalCallContextFactory internalCallContextFactory) {
         this.directPaymentProcessor = directPaymentProcessor;
         this.internalCallContextFactory = internalCallContextFactory;
-        this.clock = clock;
     }
 
     @Override
@@ -63,21 +63,45 @@ public class DefaultDirectPaymentApi implements DirectPaymentApi {
 
     @Override
     public DirectPayment createVoid(final Account account, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-        return null;
+        return directPaymentProcessor.createVoid(account, directPaymentId, properties, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
     }
 
     @Override
-    public DirectPayment createCredit(final Account account, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-        return null;
+    public DirectPayment createCredit(final Account account, final UUID directPaymentId, final BigDecimal amount, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+        return directPaymentProcessor.createCredit(account, directPaymentId, amount, properties, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
     }
 
     @Override
-    public List<DirectPayment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
-        return directPaymentProcessor.getAccountPayments(accountId, withPluginInfo, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
+    public List<DirectPayment> getAccountPayments(final UUID accountId, final TenantContext tenantContext) throws PaymentApiException {
+        return directPaymentProcessor.getAccountPayments(accountId, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
-    public DirectPayment getPayment(final UUID directPaymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
-        return directPaymentProcessor.getPayment(directPaymentId, withPluginInfo, internalCallContextFactory.createInternalTenantContext(tenantContext));
+    public Pagination<DirectPayment> getPayments(final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext context) {
+        return directPaymentProcessor.getPayments(offset, limit, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+    }
+
+    @Override
+    public Pagination<DirectPayment> getPayments(final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
+        return directPaymentProcessor.getPayments(offset, limit, pluginName, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
+    }
+
+    @Override
+    public DirectPayment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
+        final DirectPayment payment = directPaymentProcessor.getPayment(paymentId, withPluginInfo, properties, internalCallContextFactory.createInternalTenantContext(context));
+        if (payment == null) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId);
+        }
+        return payment;
+    }
+
+    @Override
+    public Pagination<DirectPayment> searchPayments(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext context) {
+        return directPaymentProcessor.searchPayments(searchKey, offset, limit, properties, internalCallContextFactory.createInternalTenantContext(context));
+    }
+
+    @Override
+    public Pagination<DirectPayment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
+        return directPaymentProcessor.searchPayments(searchKey, offset, limit, pluginName, properties, internalCallContextFactory.createInternalTenantContext(context));
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
index 685a244..69da111 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
@@ -19,16 +19,18 @@
 package org.killbill.billing.payment.core;
 
 import java.math.BigDecimal;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountInternalApi;
@@ -40,7 +42,6 @@ import org.killbill.billing.payment.api.DefaultDirectPayment;
 import org.killbill.billing.payment.api.DefaultDirectPaymentTransaction;
 import org.killbill.billing.payment.api.DirectPayment;
 import org.killbill.billing.payment.api.DirectPaymentTransaction;
-import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentStatus;
 import org.killbill.billing.payment.api.PluginProperty;
@@ -52,11 +53,15 @@ import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
-import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.entity.Pagination;
+import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.EntityPaginationBuilder;
+import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
@@ -65,21 +70,20 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 import com.google.inject.name.Named;
 
 import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPagination;
+import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationFromPlugins;
 
 public class DirectPaymentProcessor extends ProcessorBase {
 
     private final Clock clock;
 
-    private final PaymentConfig paymentConfig;
-
-    private final PluginDispatcher<Payment> paymentPluginDispatcher;
-    private final PluginDispatcher<Void> voidPluginDispatcher;
+    private final PluginDispatcher<DirectPayment> paymentPluginDispatcher;
     private final InternalCallContextFactory internalCallContextFactory;
 
     private static final Logger log = LoggerFactory.getLogger(DirectPaymentProcessor.class);
@@ -100,119 +104,222 @@ public class DirectPaymentProcessor extends ProcessorBase {
         super(pluginRegistry, accountUserApi, eventBus, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi);
         this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
-        this.paymentConfig = paymentConfig;
         final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
-        this.paymentPluginDispatcher = new PluginDispatcher<Payment>(paymentPluginTimeoutSec, executor);
-        this.voidPluginDispatcher = new PluginDispatcher<Void>(paymentPluginTimeoutSec, executor);
+        this.paymentPluginDispatcher = new PluginDispatcher<DirectPayment>(paymentPluginTimeoutSec, executor);
     }
 
     public DirectPayment createAuthorization(final Account account, final BigDecimal amount, final String externalKey, final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
-
-        final PaymentPluginApi plugin = getPaymentProviderPlugin(account, callContext);
-
-        final DateTime utcNow = clock.getUTCNow();
-        final DirectPaymentModelDao pmd = new DirectPaymentModelDao(utcNow, utcNow, account.getId(), account.getPaymentMethodId(), externalKey);
-        final DirectPaymentTransactionModelDao ptmd = new DirectPaymentTransactionModelDao(utcNow, utcNow, pmd.getId(),
-                                                                                           TransactionType.AUTHORIZE, utcNow, PaymentStatus.UNKNOWN,
-                                                                                           amount, account.getCurrency(), null, null);
-
-        final DirectPaymentModelDao inserted = paymentDao.insertDirectPaymentWithFirstTransaction(pmd, ptmd, callContext);
-        final UUID tenantId = nonEntityDao.retrieveIdFromObject(callContext.getTenantRecordId(), ObjectType.TENANT);
-
-        PaymentStatus paymentStatus;
-        PaymentInfoPlugin infoPlugin;
-        try {
-
-            try {
-                infoPlugin = plugin.authorizePayment(account.getId(), pmd.getId(), ptmd.getId(), amount, account.getCurrency(), properties, callContext.toCallContext(tenantId));
-            } catch (final RuntimeException e) {
-                // Handle case of plugin RuntimeException to be handled the same as a Plugin failure (PaymentPluginApiException)
-                final String formatError = String.format("Plugin threw RuntimeException for payment %s", pmd.getId());
-                throw new PaymentPluginApiException(formatError, e);
-            }
-
-            switch (infoPlugin.getStatus()) {
-                case PROCESSED:
-                case PENDING:
-                    // Update Payment/PaymentAttempt status
-                    paymentStatus = infoPlugin.getStatus() == PaymentPluginStatus.PROCESSED ? PaymentStatus.SUCCESS : PaymentStatus.PENDING;
-                    paymentDao.updateDirectPaymentAndTransactionOnCompletion(pmd.getId(), paymentStatus, amount, account.getCurrency(),
-                                                                             ptmd.getId(), infoPlugin.getGatewayErrorCode(), null, callContext);
-                    break;
-
-                case ERROR:
-                    paymentStatus = PaymentStatus.PLUGIN_FAILURE_ABORTED;
-                    paymentDao.updateDirectPaymentAndTransactionOnCompletion(pmd.getId(), paymentStatus, amount, account.getCurrency(),
-                                                                             ptmd.getId(), infoPlugin.getGatewayErrorCode(), infoPlugin.getGatewayError(), callContext);
-                    break;
-
-                case UNDEFINED:
-                default:
-                    final String formatError = String.format("Plugin return status %s for payment %s", infoPlugin.getStatus(), pmd.getId());
-                    // This caught right below as a retryable Plugin failure
-                    throw new PaymentPluginApiException("", formatError);
-            }
-        } catch (final PaymentPluginApiException e) {
-            paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED;
-            infoPlugin = null;
-            paymentDao.updateDirectPaymentAndTransactionOnCompletion(pmd.getId(), paymentStatus, amount, account.getCurrency(),
-                                                                     ptmd.getId(), null, e.getMessage(), callContext);
-        } finally {
-        }
-
-        final DirectPaymentTransaction transaction = new DefaultDirectPaymentTransaction(ptmd.getId(), utcNow, utcNow, pmd.getId(), ptmd.getTransactionType(), utcNow, 0,
-                                                                                         paymentStatus, amount, account.getCurrency(),
-                                                                                         ((infoPlugin != null) ? infoPlugin.getGatewayErrorCode() : null),
-                                                                                         ((infoPlugin != null) ? infoPlugin.getGatewayError() : null),
-                                                                                         infoPlugin);
-        final List<DirectPaymentTransaction> transactions = Collections.singletonList(transaction);
-        final DirectPayment result = new DefaultDirectPayment(inserted.getId(), utcNow, utcNow, account.getId(), account.getPaymentMethodId(), inserted.getPaymentNumber(), externalKey, transactions);
-        return result;
+        return initiateDirectPayment(TransactionType.AUTHORIZE,
+                                     new PluginWrapper() {
+                                         @Override
+                                         public PaymentInfoPlugin doPluginOperation(final PaymentPluginApi plugin, final Account account, final BigDecimal amount, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
+                                             return plugin.authorizePayment(account.getId(), directPaymentId, account.getPaymentMethodId(), amount, account.getCurrency(), properties, callContext);
+                                         }
+                                     },
+                                     externalKey,
+                                     account,
+                                     amount,
+                                     properties,
+                                     callContext
+                                    );
     }
 
     public DirectPayment createCapture(final Account account, final UUID directPaymentId, final BigDecimal amount, final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
-        return null;
+        return createDirectPayment(TransactionType.CAPTURE,
+                                   new PluginWrapper() {
+                                       @Override
+                                       public PaymentInfoPlugin doPluginOperation(final PaymentPluginApi plugin, final Account account, final BigDecimal amount, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
+                                           return plugin.capturePayment(account.getId(), directPaymentId, account.getPaymentMethodId(), amount, account.getCurrency(), properties, callContext);
+                                       }
+                                   },
+                                   directPaymentId,
+                                   account,
+                                   amount,
+                                   properties,
+                                   callContext
+                                  );
     }
 
     public DirectPayment createPurchase(final Account account, final BigDecimal amount, final String externalKey, final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
-        return null;
+        return initiateDirectPayment(TransactionType.PURCHASE,
+                                     new PluginWrapper() {
+                                         @Override
+                                         public PaymentInfoPlugin doPluginOperation(final PaymentPluginApi plugin, final Account account, final BigDecimal amount, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
+                                             return plugin.authorizePayment(account.getId(), directPaymentId, account.getPaymentMethodId(), amount, account.getCurrency(), properties, callContext);
+                                         }
+                                     },
+                                     externalKey,
+                                     account,
+                                     amount,
+                                     properties,
+                                     callContext
+                                    );
     }
 
     public DirectPayment createVoid(final Account account, final UUID directPaymentId, final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
-        return null;
+        return createDirectPayment(TransactionType.VOID,
+                                   new PluginWrapper() {
+                                       @Override
+                                       public PaymentInfoPlugin doPluginOperation(final PaymentPluginApi plugin, final Account account, final BigDecimal amount, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
+                                           return plugin.voidPayment(account.getId(), directPaymentId, account.getPaymentMethodId(), properties, callContext);
+                                       }
+                                   },
+                                   directPaymentId,
+                                   account,
+                                   null,
+                                   properties,
+                                   callContext
+                                  );
     }
 
-    public DirectPayment createCredit(final Account account, final UUID directPaymentId, final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
-        return null;
+    public DirectPayment createCredit(final Account account, final UUID directPaymentId, final BigDecimal amount, final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
+        return createDirectPayment(TransactionType.CREDIT,
+                                   new PluginWrapper() {
+                                       @Override
+                                       public PaymentInfoPlugin doPluginOperation(final PaymentPluginApi plugin, final Account account, final BigDecimal amount, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
+                                           return plugin.voidPayment(account.getId(), directPaymentId, account.getPaymentMethodId(), properties, callContext);
+                                       }
+                                   },
+                                   directPaymentId,
+                                   account,
+                                   amount,
+                                   properties,
+                                   callContext
+                                  );
     }
 
-    public List<DirectPayment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final InternalTenantContext tenantContext) throws PaymentApiException {
-
+    public List<DirectPayment> getAccountPayments(final UUID accountId, final InternalTenantContext tenantContext) throws PaymentApiException {
         final List<DirectPaymentModelDao> paymentsModelDao = paymentDao.getDirectPaymentsForAccount(accountId, tenantContext);
         final List<DirectPaymentTransactionModelDao> transactionsModelDao = paymentDao.getDirectTransactionsForAccount(accountId, tenantContext);
 
-        final Iterable<DirectPayment> payments = Iterables.transform(paymentsModelDao, new Function<DirectPaymentModelDao, DirectPayment>() {
+        return Lists.transform(paymentsModelDao, new Function<DirectPaymentModelDao, DirectPayment>() {
 
             @Override
             public DirectPayment apply(final DirectPaymentModelDao curDirectPaymentModelDao) {
-                return toDirectPayment(curDirectPaymentModelDao, transactionsModelDao);
+                return toDirectPayment(curDirectPaymentModelDao, transactionsModelDao, null);
             }
-
         });
-        return ImmutableList.copyOf(payments);
     }
 
-    public DirectPayment getPayment(final UUID directPaymentId, final boolean withPluginInfo, final InternalTenantContext tenantContext) throws PaymentApiException {
+    public DirectPayment getPayment(final UUID directPaymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final InternalTenantContext tenantContext) throws PaymentApiException {
         final DirectPaymentModelDao paymentModelDao = paymentDao.getDirectPayment(directPaymentId, tenantContext);
+        if (paymentModelDao == null) {
+            return null;
+        }
 
         final InternalTenantContext tenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(paymentModelDao.getAccountId(), tenantContext);
         final List<DirectPaymentTransactionModelDao> transactionsForAccount = paymentDao.getDirectTransactionsForAccount(paymentModelDao.getAccountId(), tenantContextWithAccountRecordId);
 
-        return toDirectPayment(paymentModelDao, transactionsForAccount);
+        final PaymentPluginApi plugin = withPluginInfo ? getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext) : null;
+        PaymentInfoPlugin pluginInfo = null;
+        if (plugin != null) {
+            try {
+                pluginInfo = plugin.getPaymentInfo(paymentModelDao.getAccountId(), directPaymentId, properties, buildTenantContext(tenantContext));
+            } catch (final PaymentPluginApiException e) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO, directPaymentId, e.toString());
+            }
+        }
+        return toDirectPayment(paymentModelDao, transactionsForAccount, pluginInfo);
+    }
+
+    public Pagination<DirectPayment> getPayments(final Long offset, final Long limit, final Iterable<PluginProperty> properties,
+                                                 final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
+        return getEntityPaginationFromPlugins(getAvailablePlugins(),
+                                              offset,
+                                              limit,
+                                              new EntityPaginationBuilder<DirectPayment, PaymentApiException>() {
+                                                  @Override
+                                                  public Pagination<DirectPayment> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
+                                                      return getPayments(offset, limit, pluginName, properties, tenantContext, internalTenantContext);
+                                                  }
+                                              }
+                                             );
     }
 
-    private DirectPayment toDirectPayment(final DirectPaymentModelDao curDirectPaymentModelDao, final List<DirectPaymentTransactionModelDao> transactionsModelDao) {
+    public Pagination<DirectPayment> getPayments(final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+
+        return getEntityPagination(limit,
+                                   new SourcePaginationBuilder<DirectPaymentModelDao, PaymentApiException>() {
+                                       @Override
+                                       public Pagination<DirectPaymentModelDao> build() {
+                                           // Find all payments for all accounts
+                                           return paymentDao.getDirectPayments(pluginName, offset, limit, internalTenantContext);
+                                       }
+                                   },
+                                   new Function<DirectPaymentModelDao, DirectPayment>() {
+                                       @Override
+                                       public DirectPayment apply(final DirectPaymentModelDao paymentModelDao) {
+                                           PaymentInfoPlugin pluginInfo = null;
+                                           try {
+                                               pluginInfo = pluginApi.getPaymentInfo(paymentModelDao.getAccountId(), paymentModelDao.getId(), properties, tenantContext);
+                                           } catch (final PaymentPluginApiException e) {
+                                               log.warn("Unable to find payment id " + paymentModelDao.getId() + " in plugin " + pluginName);
+                                               // We still want to return a payment object, even though the plugin details are missing
+                                           }
+
+                                           return toDirectPayment(paymentModelDao.getId(), pluginInfo, internalTenantContext);
+                                       }
+                                   }
+                                  );
+    }
+
+    public Pagination<DirectPayment> searchPayments(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final InternalTenantContext internalTenantContext) {
+        return getEntityPaginationFromPlugins(getAvailablePlugins(),
+                                              offset,
+                                              limit,
+                                              new EntityPaginationBuilder<DirectPayment, PaymentApiException>() {
+                                                  @Override
+                                                  public Pagination<DirectPayment> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
+                                                      return searchPayments(searchKey, offset, limit, pluginName, properties, internalTenantContext);
+                                                  }
+                                              }
+                                             );
+    }
+
+    public Pagination<DirectPayment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+
+        return getEntityPagination(limit,
+                                   new SourcePaginationBuilder<PaymentInfoPlugin, PaymentApiException>() {
+                                       @Override
+                                       public Pagination<PaymentInfoPlugin> build() throws PaymentApiException {
+                                           try {
+                                               return pluginApi.searchPayments(searchKey, offset, limit, properties, buildTenantContext(internalTenantContext));
+                                           } catch (final PaymentPluginApiException e) {
+                                               throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
+                                           }
+                                       }
+                                   },
+                                   new Function<PaymentInfoPlugin, DirectPayment>() {
+                                       @Override
+                                       public DirectPayment apply(final PaymentInfoPlugin paymentInfoPlugin) {
+                                           if (paymentInfoPlugin.getKbPaymentId() == null) {
+                                               // Garbage from the plugin?
+                                               log.debug("Plugin {} returned a payment without a kbPaymentId for searchKey {}", pluginName, searchKey);
+                                               return null;
+                                           }
+
+                                           return toDirectPayment(paymentInfoPlugin.getKbPaymentId(), paymentInfoPlugin, internalTenantContext);
+                                       }
+                                   }
+                                  );
+    }
 
+    private DirectPayment toDirectPayment(final UUID directPaymentId, @Nullable final PaymentInfoPlugin pluginInfo, final InternalTenantContext tenantContext) {
+        final DirectPaymentModelDao paymentModelDao = paymentDao.getDirectPayment(directPaymentId, tenantContext);
+        if (paymentModelDao == null) {
+            log.warn("Unable to find direct payment id " + directPaymentId);
+            return null;
+        }
+
+        final InternalTenantContext tenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(paymentModelDao.getAccountId(), tenantContext);
+        final List<DirectPaymentTransactionModelDao> transactionsForAccount = paymentDao.getDirectTransactionsForAccount(paymentModelDao.getAccountId(), tenantContextWithAccountRecordId);
+
+        return toDirectPayment(paymentModelDao, transactionsForAccount, pluginInfo);
+    }
+
+    private DirectPayment toDirectPayment(final DirectPaymentModelDao curDirectPaymentModelDao, final Iterable<DirectPaymentTransactionModelDao> transactionsModelDao, @Nullable final PaymentInfoPlugin pluginInfo) {
         final Ordering<DirectPaymentTransaction> perPaymentTransactionOrdering = Ordering.<DirectPaymentTransaction>from(new Comparator<DirectPaymentTransaction>() {
             @Override
             public int compare(final DirectPaymentTransaction o1, final DirectPaymentTransaction o2) {
@@ -232,8 +339,7 @@ public class DirectPaymentProcessor extends ProcessorBase {
             public DirectPaymentTransaction apply(final DirectPaymentTransactionModelDao input) {
                 return new DefaultDirectPaymentTransaction(input.getId(), input.getCreatedDate(), input.getUpdatedDate(), input.getDirectPaymentId(),
                                                            input.getTransactionType(), input.getEffectiveDate(), 0, input.getPaymentStatus(), input.getAmount(), input.getCurrency(),
-                                                           // STEPH_DP fill in details plugin info if required
-                                                           input.getGatewayErrorCode(), input.getGatewayErrorMsg(), null);
+                                                           input.getGatewayErrorCode(), input.getGatewayErrorMsg(), pluginInfo);
             }
         });
 
@@ -241,4 +347,118 @@ public class DirectPaymentProcessor extends ProcessorBase {
         return new DefaultDirectPayment(curDirectPaymentModelDao.getId(), curDirectPaymentModelDao.getCreatedDate(), curDirectPaymentModelDao.getUpdatedDate(), curDirectPaymentModelDao.getAccountId(),
                                         curDirectPaymentModelDao.getPaymentMethodId(), curDirectPaymentModelDao.getPaymentNumber(), curDirectPaymentModelDao.getExternalKey(), sortedTransactions);
     }
+
+    private static interface PluginWrapper {
+
+        PaymentInfoPlugin doPluginOperation(final PaymentPluginApi plugin, final Account account, final BigDecimal amount, final UUID directPaymentId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException;
+    }
+
+    private DirectPayment initiateDirectPayment(final TransactionType transactionType, final PluginWrapper pluginWrapper, final String externalKey, final Account account, final BigDecimal amount,
+                                                final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
+        try {
+            return paymentPluginDispatcher.dispatchWithAccountLock(new CallableWithAccountLock<DirectPayment>(locker,
+                                                                                                              account.getExternalKey(),
+                                                                                                              new WithAccountLockCallback<DirectPayment>() {
+
+                                                                                                                  @Override
+                                                                                                                  public DirectPayment doOperation() throws PaymentApiException {
+                                                                                                                      final DateTime utcNow = clock.getUTCNow();
+                                                                                                                      final DirectPaymentModelDao pmd = new DirectPaymentModelDao(utcNow, utcNow, account.getId(), account.getPaymentMethodId(), externalKey);
+                                                                                                                      final DirectPaymentTransactionModelDao ptmd = new DirectPaymentTransactionModelDao(utcNow, utcNow, pmd.getId(),
+                                                                                                                                                                                                         transactionType, utcNow, PaymentStatus.UNKNOWN,
+                                                                                                                                                                                                         amount, account.getCurrency(), null, null);
+
+                                                                                                                      final DirectPaymentModelDao inserted = paymentDao.insertDirectPaymentWithFirstTransaction(pmd, ptmd, callContext);
+                                                                                                                      final DirectPaymentTransactionModelDao insertedTransaction = paymentDao.getDirectTransactionsForAccount(account.getId(), callContext).get(0);
+
+                                                                                                                      return getDirectPayment(pluginWrapper, account, amount, inserted.getId(), insertedTransaction.getId(), properties, callContext);
+                                                                                                                  }
+                                                                                                              }
+            ));
+        } catch (final TimeoutException e) {
+            // TODO PIERRE
+            throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_TIMEOUT, account.getId(), null);
+        } catch (final RuntimeException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, null);
+        }
+    }
+
+    private DirectPayment createDirectPayment(final TransactionType transactionType, final PluginWrapper pluginWrapper, final UUID directPaymentId,
+                                              final Account account, @Nullable final BigDecimal amount,
+                                              final Iterable<PluginProperty> properties, final InternalCallContext callContext) throws PaymentApiException {
+        try {
+            return paymentPluginDispatcher.dispatchWithAccountLock(new CallableWithAccountLock<DirectPayment>(locker,
+                                                                                                              account.getExternalKey(),
+                                                                                                              new WithAccountLockCallback<DirectPayment>() {
+
+                                                                                                                  @Override
+                                                                                                                  public DirectPayment doOperation() throws PaymentApiException {
+                                                                                                                      final DateTime utcNow = clock.getUTCNow();
+                                                                                                                      final DirectPaymentTransactionModelDao ptmd = new DirectPaymentTransactionModelDao(utcNow, utcNow, directPaymentId,
+                                                                                                                                                                                                         transactionType, utcNow, PaymentStatus.UNKNOWN,
+                                                                                                                                                                                                         amount, account.getCurrency(), null, null);
+                                                                                                                      final DirectPaymentTransactionModelDao inserted = paymentDao.updateDirectPaymentWithNewTransaction(directPaymentId, ptmd, callContext);
+
+                                                                                                                      return getDirectPayment(pluginWrapper, account, amount, directPaymentId, inserted.getId(), properties, callContext);
+                                                                                                                  }
+                                                                                                              }
+            ));
+        } catch (final TimeoutException e) {
+            // TODO PIERRE
+            throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_TIMEOUT, account.getId(), null);
+        } catch (final RuntimeException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, null);
+        }
+    }
+
+    private DirectPayment getDirectPayment(final PluginWrapper pluginWrapper, final Account account, @Nullable final BigDecimal amount,
+                                           final UUID directPaymentId, final UUID directPaymentTransactionId, final Iterable<PluginProperty> properties,
+                                           final InternalCallContext callContext) throws PaymentApiException {
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(callContext.getTenantRecordId(), ObjectType.TENANT);
+        final PaymentPluginApi plugin = getPaymentProviderPlugin(account, callContext);
+
+        try {
+            final PaymentInfoPlugin infoPlugin;
+            try {
+                infoPlugin = pluginWrapper.doPluginOperation(plugin, account, amount, directPaymentId, properties, callContext.toCallContext(tenantId));
+            } catch (final RuntimeException e) {
+                // Handle case of plugin RuntimeException to be handled the same as a Plugin failure (PaymentPluginApiException)
+                final String formatError = String.format("Plugin threw RuntimeException for direct payment %s", directPaymentId);
+                throw new PaymentPluginApiException(formatError, e);
+            }
+
+            processPaymentInfoPlugin(infoPlugin, account, amount, directPaymentId, directPaymentTransactionId, callContext);
+        } catch (final PaymentPluginApiException e) {
+            paymentDao.updateDirectPaymentAndTransactionOnCompletion(directPaymentId, PaymentStatus.PAYMENT_FAILURE_ABORTED, amount, account.getCurrency(), directPaymentTransactionId, null, e.getMessage(), callContext);
+        }
+
+        return getPayment(directPaymentId, false, properties, callContext);
+    }
+
+    private PaymentStatus processPaymentInfoPlugin(final PaymentInfoPlugin infoPlugin, final Account account, @Nullable final BigDecimal amount,
+                                                   final UUID directPaymentId, final UUID directPaymentTransactionId, final InternalCallContext callContext) throws PaymentPluginApiException {
+        final PaymentStatus paymentStatus;
+        switch (infoPlugin.getStatus()) {
+            case PROCESSED:
+                paymentStatus = PaymentStatus.SUCCESS;
+                break;
+            case PENDING:
+                paymentStatus = PaymentStatus.PENDING;
+                break;
+            case ERROR:
+                paymentStatus = PaymentStatus.PLUGIN_FAILURE_ABORTED;
+                break;
+            case UNDEFINED:
+            default:
+                final String formatError = String.format("Plugin return status %s for direct payment %s", infoPlugin.getStatus(), directPaymentId);
+                // This will be caught as a retryable Plugin failure
+                throw new PaymentPluginApiException("", formatError);
+        }
+
+        // Update Payment/PaymentAttempt status
+        paymentDao.updateDirectPaymentAndTransactionOnCompletion(directPaymentId, paymentStatus, amount, account.getCurrency(),
+                                                                 directPaymentTransactionId, infoPlugin.getGatewayErrorCode(), infoPlugin.getGatewayError(), callContext);
+
+        return paymentStatus;
+    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index b15f20c..f0d65ad 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -24,16 +26,11 @@ import java.util.UUID;
 
 import javax.inject.Inject;
 
-import org.killbill.billing.ObjectType;
-import org.killbill.billing.payment.api.DirectPaymentTransaction;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.skife.jdbi.v2.IDBI;
-
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.clock.Clock;
 import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.payment.api.DirectPayment;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PaymentStatus;
@@ -48,6 +45,8 @@ import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.clock.Clock;
+import org.skife.jdbi.v2.IDBI;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
@@ -75,6 +74,26 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
+    public Pagination<DirectPaymentModelDao> getDirectPayments(final String pluginName, final Long offset, final Long limit, final InternalTenantContext context) {
+        return paginationHelper.getPagination(DirectPaymentSqlDao.class,
+                                              new PaginationIteratorBuilder<DirectPaymentModelDao, DirectPayment, DirectPaymentSqlDao>() {
+                                                  @Override
+                                                  public Long getCount(final DirectPaymentSqlDao directPaymentSqlDao, final InternalTenantContext context) {
+                                                      return directPaymentSqlDao.getCountByPluginName(pluginName, context);
+                                                  }
+
+                                                  @Override
+                                                  public Iterator<DirectPaymentModelDao> build(final DirectPaymentSqlDao directPaymentSqlDao, final Long limit, final InternalTenantContext context) {
+                                                      return directPaymentSqlDao.getByPluginName(pluginName, offset, limit, context);
+                                                  }
+                                              },
+                                              offset,
+                                              limit,
+                                              context
+                                             );
+    }
+
+    @Override
     public DirectPaymentModelDao insertDirectPaymentWithFirstTransaction(final DirectPaymentModelDao directPayment, final DirectPaymentTransactionModelDao directPaymentTransaction, final InternalCallContext context) {
 
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<DirectPaymentModelDao>() {
@@ -84,7 +103,23 @@ public class DefaultPaymentDao implements PaymentDao {
                 final DirectPaymentSqlDao directPaymentSqlDao = entitySqlDaoWrapperFactory.become(DirectPaymentSqlDao.class);
                 directPaymentSqlDao.create(directPayment, context);
                 entitySqlDaoWrapperFactory.become(DirectTransactionSqlDao.class).create(directPaymentTransaction, context);
-                return  directPaymentSqlDao.getById(directPayment.getId().toString(), context);
+                return directPaymentSqlDao.getById(directPayment.getId().toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public DirectPaymentTransactionModelDao updateDirectPaymentWithNewTransaction(final UUID directPaymentId, final DirectPaymentTransactionModelDao directPaymentTransaction, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<DirectPaymentTransactionModelDao>() {
+            @Override
+            public DirectPaymentTransactionModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final DirectTransactionSqlDao transactional = entitySqlDaoWrapperFactory.become(DirectTransactionSqlDao.class);
+                transactional.create(directPaymentTransaction, context);
+                final DirectPaymentTransactionModelDao directPaymentTransactionModelDao = transactional.getById(directPaymentTransaction.getId().toString(), context);
+
+                entitySqlDaoWrapperFactory.become(DirectPaymentSqlDao.class).updateDirectPaymentForNewTransaction(directPaymentId.toString(), context);
+
+                return directPaymentTransactionModelDao;
             }
         });
     }
@@ -92,7 +127,7 @@ public class DefaultPaymentDao implements PaymentDao {
     @Override
     public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus,
                                                               final BigDecimal processedAmount, final Currency processedCurrency,
-                                                              final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg,  final InternalCallContext context) {
+                                                              final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
 
             @Override
@@ -149,7 +184,6 @@ public class DefaultPaymentDao implements PaymentDao {
         });
     }
 
-
     @Override
     public PaymentModelDao insertPaymentWithFirstAttempt(final PaymentModelDao payment, final PaymentAttemptModelDao attempt, final InternalCallContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
@@ -261,7 +295,8 @@ public class DefaultPaymentDao implements PaymentDao {
                                               },
                                               offset,
                                               limit,
-                                              context);
+                                              context
+                                             );
     }
 
     @Override
@@ -340,7 +375,8 @@ public class DefaultPaymentDao implements PaymentDao {
                                               },
                                               offset,
                                               limit,
-                                              context);
+                                              context
+                                             );
     }
 
     @Override
@@ -410,7 +446,8 @@ public class DefaultPaymentDao implements PaymentDao {
                                               },
                                               offset,
                                               limit,
-                                              context);
+                                              context
+                                             );
     }
 
     @Override
@@ -463,7 +500,8 @@ public class DefaultPaymentDao implements PaymentDao {
                                                                                                          public boolean apply(final PaymentMethodModelDao paymentMethod) {
                                                                                                              return pluginName.equals(paymentMethod.getPluginName());
                                                                                                          }
-                                                                                                     });
+                                                                                                     }
+                                                                                                    );
 
                 for (final PaymentMethodModelDao finalPaymentMethod : newPaymentMethods) {
                     PaymentMethodModelDao foundExistingPaymentMethod = null;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java
index 1714d4f..6813a31 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java
@@ -16,15 +16,11 @@
 
 package org.killbill.billing.payment.dao;
 
-import java.math.BigDecimal;
 import java.util.Iterator;
-import java.util.List;
 
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.api.DirectPayment;
-import org.killbill.billing.payment.api.Refund;
 import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
@@ -38,17 +34,19 @@ import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 @EntitySqlDaoStringTemplate
 public interface DirectPaymentSqlDao extends EntitySqlDao<DirectPaymentModelDao, DirectPayment> {
 
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    void updateDirectPaymentForNewTransaction(@Bind("id") final String directPaymentId,
+                                              @BindBean final InternalCallContext context);
 
-    /*
     @SqlQuery
     @SmartFetchSize(shouldStream = true)
     public Iterator<DirectPaymentModelDao> getByPluginName(@Bind("pluginName") final String pluginName,
-                                                    @Bind("offset") final Long offset,
-                                                    @Bind("rowCount") final Long rowCount,
-                                                    @BindBean final InternalTenantContext context);
+                                                           @Bind("offset") final Long offset,
+                                                           @Bind("rowCount") final Long rowCount,
+                                                           @BindBean final InternalTenantContext context);
 
     @SqlQuery
     public Long getCountByPluginName(@Bind("pluginName") final String pluginName,
                                      @BindBean final InternalTenantContext context);
-                                     */
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
index 12606f9..e13bc91 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -29,11 +29,15 @@ import org.killbill.billing.util.entity.Pagination;
 
 public interface PaymentDao {
 
+    public Pagination<DirectPaymentModelDao> getDirectPayments(String pluginName, Long offset, Long limit, InternalTenantContext context);
+
     public DirectPaymentModelDao insertDirectPaymentWithFirstTransaction(DirectPaymentModelDao directPayment, DirectPaymentTransactionModelDao directPaymentTransaction, InternalCallContext context);
 
+    public DirectPaymentTransactionModelDao updateDirectPaymentWithNewTransaction(UUID dirctPaymentId, DirectPaymentTransactionModelDao directPaymentTransaction, InternalCallContext context);
+
     public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus,
                                                               final BigDecimal processedAmount, final Currency processedCurrency,
-                                                              final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg,  final InternalCallContext context);
+                                                              final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg, final InternalCallContext context);
 
     public DirectPaymentModelDao getDirectPayment(UUID directPaymentId, InternalTenantContext context);
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
index 27ae2d2..3982880 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
@@ -51,11 +51,11 @@ public class DefaultNoOpPaymentMethodPlugin implements PaymentMethodPlugin {
     public DefaultNoOpPaymentMethodPlugin(@Nullable final UUID kbPaymentMethodId,
                                           final String externalId,
                                           final boolean isDefault,
-                                          final Iterable<PluginProperty> props) {
+                                          @Nullable final Iterable<PluginProperty> props) {
         this.kbPaymentMethodId = kbPaymentMethodId;
         this.externalId = externalId;
         this.isDefault = isDefault;
-        this.props = ImmutableList.<PluginProperty>copyOf(props);
+        this.props = props == null ? ImmutableList.<PluginProperty>of() : ImmutableList.<PluginProperty>copyOf(props);
     }
 
     @Override
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg
index 1845370..3767b96 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg
@@ -23,7 +23,7 @@ tableFields(prefix) ::= <<
 >>
 
 tableValues() ::= <<
-:accountId
+  :accountId
 , :paymentMethodId
 , :externalKey
 , :createdBy
@@ -32,4 +32,11 @@ tableValues() ::= <<
 , :updatedDate
 >>
 
-
+updateDirectPaymentForNewTransaction() ::= <<
+update <tableName()>
+set updated_by = :updatedBy
+, updated_date = :createdDate
+where id = :id
+<AND_CHECK_TENANT()>
+;
+>>
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 60282d0..6e98657 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
@@ -41,13 +41,22 @@ public class MockPaymentDao implements PaymentDao {
     private final Map<UUID, PaymentAttemptModelDao> attempts = new HashMap<UUID, PaymentAttemptModelDao>();
 
     @Override
+    public Pagination<DirectPaymentModelDao> getDirectPayments(final String pluginName, final Long offset, final Long limit, final InternalTenantContext context) {
+        return null;
+    }
+
+    @Override
     public DirectPaymentModelDao insertDirectPaymentWithFirstTransaction(final DirectPaymentModelDao directPayment, final DirectPaymentTransactionModelDao directPaymentTransaction, final InternalCallContext context) {
         return null;
     }
 
     @Override
-    public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus, final BigDecimal processedAmount, final Currency processedCurrency, final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg, final InternalCallContext context) {
+    public DirectPaymentTransactionModelDao updateDirectPaymentWithNewTransaction(final UUID dirctPaymentId, final DirectPaymentTransactionModelDao directPaymentTransaction, final InternalCallContext context) {
+        return null;
+    }
 
+    @Override
+    public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus, final BigDecimal processedAmount, final Currency processedCurrency, final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg, final InternalCallContext context) {
     }
 
     @Override

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 392015d..b67ae73 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.7.2</version>
+        <version>0.7.3</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.11.2-SNAPSHOT</version>