killbill-memoizeit

Payment work (ongoing)

5/15/2012 9:06:52 PM

Changes

payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java 47(+0 -47)

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
index 8d107ef..9ba2976 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -167,9 +167,9 @@ public class BusinessAccountRecorder {
             // Retrieve invoices information
             final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId());
             if (invoices != null && invoices.size() > 0) {
-                final List<String> invoiceIds = new ArrayList<String>();
+                final List<UUID> invoiceIds = new ArrayList<UUID>();
                 for (final Invoice invoice : invoices) {
-                    invoiceIds.add(invoice.getId().toString());
+                    invoiceIds.add(invoice.getId());
                     totalInvoiceBalance = totalInvoiceBalance.add(invoice.getBalance());
 
                     if (lastInvoiceDate == null || invoice.getInvoiceDate().isAfter(lastInvoiceDate)) {
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index b567464..dac4a3a 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -83,6 +83,7 @@ import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.util.bus.Bus;
@@ -280,8 +281,8 @@ public class TestAnalyticsService {
 
         paymentInfoNotification = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString()).setPaymentMethod(PAYMENT_METHOD).setCardCountry(CARD_COUNTRY).build();
         final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice.getId(), account.getId(), BigDecimal.TEN,
-                ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getPaymentId(), 1);
-        paymentDao.createPaymentAttempt(paymentAttempt, context);
+                ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getPaymentId(), 1, PaymentAttemptStatus.COMPLETED_SUCCESS);
+        paymentDao.createPaymentAttempt(paymentAttempt, PaymentAttemptStatus.COMPLETED_SUCCESS, context);
         paymentDao.savePaymentInfo(paymentInfoNotification, context);
         Assert.assertEquals(paymentDao.getPaymentInfo(Arrays.asList(invoice.getId().toString())).size(), 1);
     }
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 c7b5965..00496d2 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
@@ -44,16 +44,16 @@ public interface PaymentApi {
     public void deletePaymentMethod(final String accountKey, final String paymentMethodId, final CallContext context)
         throws PaymentApiException;
 
-    public List<PaymentInfoEvent> createPayment(final String accountKey, final List<String> invoiceIds, final CallContext context)
+    public PaymentInfoEvent createPayment(final String accountKey, final UUID invoiceId, final CallContext context)
         throws PaymentApiException;
     
-    public List<PaymentInfoEvent> createPayment(final Account account, final List<String> invoiceIds, final CallContext context)
+    public PaymentInfoEvent createPayment(final Account account, final UUID invoiceId, final CallContext context)
         throws PaymentApiException;
     
-    public PaymentInfoEvent createPaymentForPaymentAttempt(final UUID paymentAttemptId, final CallContext context)
+    public PaymentInfoEvent createPaymentForPaymentAttempt(final String accountKey, final UUID paymentAttemptId, final CallContext context)
         throws PaymentApiException;
 
-    public List<PaymentInfoEvent> createRefund(final Account account, final List<String> invoiceIds, final CallContext context)
+    public PaymentInfoEvent createRefund(final Account account, final UUID paymentId, final CallContext context)
         throws PaymentApiException;
 
     public PaymentProviderAccount getPaymentProviderAccount(final String accountKey)
@@ -68,7 +68,7 @@ public interface PaymentApi {
     public PaymentAttempt getPaymentAttemptForPaymentId(final String id)
         throws PaymentApiException;
 
-    public List<PaymentInfoEvent> getPaymentInfo(final List<String> invoiceIds)
+    public List<PaymentInfoEvent> getPaymentInfo(final List<UUID> invoiceIds)
         throws PaymentApiException;
 
     public List<PaymentAttempt> getPaymentAttemptsForInvoiceId(final String invoiceId)
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java
index ff81b16..90a843f 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java
@@ -19,6 +19,7 @@ import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 
 public class PaymentApiException extends BillingExceptionBase {
     
@@ -29,12 +30,6 @@ public class PaymentApiException extends BillingExceptionBase {
         super(e, e.getCode(), e.getMessage());
     }
 
-    /*
-    public PaymentApiException(CatalogApiException e) {
-        super(e, e.getCode(), e.getMessage());
-    }
-    */
-    
     public PaymentApiException(Throwable e, ErrorCode code, Object...args) {
         super(e, code, args);
     }
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
index 15ac4ee..54c8ba7 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
@@ -38,6 +38,7 @@ public class PaymentAttempt {
     private final Integer retryCount;
     private final DateTime createdDate;
     private final DateTime updatedDate;
+    private final PaymentAttemptStatus paymentAttemptStatus;
 
     public PaymentAttempt(UUID paymentAttemptId,
                           UUID invoiceId,
@@ -48,6 +49,7 @@ public class PaymentAttempt {
                           DateTime paymentAttemptDate,
                           String paymentId,
                           Integer retryCount,
+                          PaymentAttemptStatus paymentAttemptStatus,
                           DateTime createdDate,
                           DateTime updatedDate) {
         this.paymentAttemptId = paymentAttemptId;
@@ -59,6 +61,7 @@ public class PaymentAttempt {
         this.paymentAttemptDate = paymentAttemptDate == null ? new DateTime(DateTimeZone.UTC) : paymentAttemptDate;
         this.paymentId = paymentId;
         this.retryCount = retryCount == null ? 0 : retryCount;
+        this.paymentAttemptStatus = paymentAttemptStatus;
         this.createdDate = createdDate;
         this.updatedDate = updatedDate;
     }
@@ -71,7 +74,8 @@ public class PaymentAttempt {
                           DateTime invoiceDate,
                           DateTime paymentAttemptDate,
                           String paymentId,
-                          Integer retryCount) {
+                          Integer retryCount,
+                          PaymentAttemptStatus paymentAttemptStatus) {
         this(paymentAttemptId,
              invoiceId,
              accountId,
@@ -81,10 +85,12 @@ public class PaymentAttempt {
              paymentAttemptDate,
              paymentId,
              retryCount,
+             paymentAttemptStatus,
              null,
              null);
     }
 
+    /*
     public PaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, BigDecimal amount, Currency currency, DateTime invoiceDate, DateTime paymentAttemptDate) {
         this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, null, null);
     }
@@ -93,8 +99,17 @@ public class PaymentAttempt {
         this(paymentAttemptId, invoiceId, accountId, null, null, invoiceDate, paymentAttemptDate, null, null);
     }
 
-    public PaymentAttempt(UUID paymentAttemptId, Invoice invoice) {
-        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null, null, null);
+*/
+    public PaymentAttempt(UUID paymentAttemptId, Invoice invoice, PaymentAttemptStatus paymentAttemptStatus) {
+        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null, null, null, paymentAttemptStatus);
+    }
+    
+    public enum PaymentAttemptStatus {
+        IN_PROCESSING,
+        COMPLETED_SUCCESS,
+        COMPLETED_FAILED,
+        COMPLETED_ABORTED,
+        PENDING
     }
 
     public DateTime getInvoiceDate() {
@@ -140,10 +155,15 @@ public class PaymentAttempt {
     public Integer getRetryCount() {
         return retryCount;
     }
+    
+    public PaymentAttemptStatus getPaymentAttemptStatus() {
+        return paymentAttemptStatus;
+    }
 
     @Override
     public String toString() {
-        return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", invoiceId=" + invoiceId + ", accountId=" + accountId + ", amount=" + amount + ", currency=" + currency + ", paymentId=" + paymentId + ", invoiceDate=" + invoiceDate + ", paymentAttemptDate=" + paymentAttemptDate + ", retryCount=" + retryCount + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
+        return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", invoiceId=" + invoiceId + ", accountId=" + accountId + ", amount=" + amount + ", currency=" + currency + ", paymentId=" + paymentId + ", invoiceDate=" + invoiceDate + ", paymentAttemptDate=" + paymentAttemptDate +
+        ", retryCount=" + retryCount + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
     }
 
     public Builder cloner() {
@@ -162,7 +182,8 @@ public class PaymentAttempt {
         private Integer retryCount;
         private DateTime createdDate;
         private DateTime updatedDate;
-
+        private PaymentAttemptStatus paymentAttemptStatus;
+        
         public Builder() {
         }
 
@@ -176,6 +197,7 @@ public class PaymentAttempt {
             this.paymentAttemptDate = src.paymentAttemptDate;
             this.paymentId = src.paymentId;
             this.retryCount = src.retryCount;
+            this.paymentAttemptStatus = paymentAttemptStatus;
             this.createdDate = src.createdDate;
             this.updatedDate = src.updatedDate;
         }
@@ -235,6 +257,11 @@ public class PaymentAttempt {
             return this;
         }
 
+        public Builder setPaymentAttemptStatus(PaymentAttemptStatus status) {
+         this.paymentAttemptStatus = paymentAttemptStatus; 
+         return this;
+        }
+        
         public PaymentAttempt build() {
             return new PaymentAttempt(paymentAttemptId,
                                       invoiceId,
@@ -245,6 +272,7 @@ public class PaymentAttempt {
                                       paymentAttemptDate,
                                       paymentId,
                                       retryCount,
+                                      paymentAttemptStatus,
                                       createdDate,
                                       updatedDate);
         }
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java b/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
index a93b3bb..5883275 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
@@ -16,12 +16,18 @@
 package com.ning.billing.payment.api;
 
 import java.math.BigDecimal;
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.util.bus.BusEvent;
 
 public interface PaymentInfoEvent extends BusEvent {
 
+    public UUID getInvoiceId();
+
+    public UUID getAccountId();
+    
     public String getPaymentId();
 
     public BigDecimal getAmount();
@@ -51,4 +57,5 @@ public interface PaymentInfoEvent extends BusEvent {
     public String getType();
 
     public DateTime getUpdatedDate();
+    
 }
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
new file mode 100644
index 0000000..482c2df
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
@@ -0,0 +1,53 @@
+/* 
+ * Copyright 2010-2011 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.math.BigDecimal;
+
+import org.joda.time.DateTime;
+
+public interface PaymentInfoPlugin {
+    
+    public String getPaymentId();
+
+    public BigDecimal getAmount();
+
+    public String getBankIdentificationNumber();
+
+    public DateTime getCreatedDate();
+
+    public DateTime getEffectiveDate();
+
+    public String getPaymentNumber();
+
+    public String getPaymentMethod();
+
+    public String getCardType();
+
+    public String getCardCountry();
+
+    public String getReferenceId();
+
+    public String getPaymentMethodId();
+
+    public BigDecimal getRefundAmount();
+
+    public String getStatus();
+
+    public String getType();
+
+    public DateTime getUpdatedDate();
+}
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java
new file mode 100644
index 0000000..5df657e
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java
@@ -0,0 +1,37 @@
+/* 
+ * Copyright 2010-2011 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;
+
+public class PaymentPluginApiException extends Exception {
+
+    private static final long serialVersionUID = 15642965L;
+    
+    private final String errorType;
+    private final String errorMessage;
+    
+    public PaymentPluginApiException(final String errorType, final String errorMessage) {
+        this.errorMessage = errorMessage;
+        this.errorType = errorType;
+    }
+
+    public String getErrorType() {
+        return errorType;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentProviderPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentProviderPlugin.java
new file mode 100644
index 0000000..93192b0
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentProviderPlugin.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2011 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.List;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.PaymentMethodInfo;
+import com.ning.billing.payment.api.PaymentProviderAccount;
+
+public interface PaymentProviderPlugin {
+    
+    public PaymentInfoPlugin processInvoice(Account account, Invoice invoice)
+    throws PaymentPluginApiException;
+
+    public String createPaymentProviderAccount(Account account)
+    throws PaymentPluginApiException;
+
+    public PaymentInfoPlugin getPaymentInfo(String paymentId)
+    throws PaymentPluginApiException;
+
+    public PaymentProviderAccount getPaymentProviderAccount(String accountKey)
+    throws PaymentPluginApiException;
+
+    public void updatePaymentGateway(String accountKey)
+    throws PaymentPluginApiException;    
+
+    public PaymentMethodInfo getPaymentMethodInfo(String paymentMethodId) 
+    throws PaymentPluginApiException;
+
+    public List<PaymentMethodInfo> getPaymentMethods(String accountKey)
+    throws PaymentPluginApiException;
+
+    public String addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod)
+    throws PaymentPluginApiException;
+
+    public PaymentMethodInfo updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo)
+    throws PaymentPluginApiException;
+
+    public void deletePaymentMethod(String accountKey, String paymentMethodId)
+    throws PaymentPluginApiException;    
+
+
+    public void updatePaymentProviderAccountExistingContact(Account account)
+    throws PaymentPluginApiException;
+
+    public void updatePaymentProviderAccountWithNewContact(Account account)
+    throws PaymentPluginApiException;
+
+    public List<PaymentInfoPlugin> processRefund(Account account)
+    throws PaymentPluginApiException;
+}
diff --git a/api/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.java b/api/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.java
new file mode 100644
index 0000000..bd76f30
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.java
@@ -0,0 +1,25 @@
+/* 
+ * Copyright 2010-2011 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 com.ning.billing.payment.plugin.api.PaymentProviderPlugin;
+
+public interface PaymentProviderPluginRegistry {
+
+    public void register(final PaymentProviderPlugin plugin, final String name);
+
+    public PaymentProviderPlugin getPlugin(final String name);
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestNotifyInvoicePaymentApi.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestNotifyInvoicePaymentApi.java
index d7febde..c2c7932 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestNotifyInvoicePaymentApi.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestNotifyInvoicePaymentApi.java
@@ -40,6 +40,7 @@ import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.payment.RequestProcessor;
 import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
@@ -85,7 +86,7 @@ public class TestNotifyInvoicePaymentApi {
         final Account account = testHelper.createTestCreditCardAccount();
         final Invoice invoice = testHelper.createTestInvoice(account);
 
-        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice, PaymentAttemptStatus.COMPLETED_SUCCESS);
 
         invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
                                      invoice.getBalance(),
@@ -104,7 +105,7 @@ public class TestNotifyInvoicePaymentApi {
         final Account account = testHelper.createTestCreditCardAccount();
         final Invoice invoice = testHelper.createTestInvoice(account);
 
-        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice, PaymentAttemptStatus.COMPLETED_SUCCESS);
         invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
                                                  paymentAttempt.getPaymentAttemptId(),
                                                  paymentAttempt.getPaymentAttemptDate(),
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 2ac89d2..1333925 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
@@ -18,13 +18,10 @@ package com.ning.billing.payment.api;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.LinkedList;
+
 import java.util.List;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
-import org.apache.commons.lang.StringUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,285 +34,299 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
-import com.ning.billing.payment.RetryService;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.dao.PaymentDao;
-import com.ning.billing.payment.provider.PaymentProviderPlugin;
+
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.PaymentProviderPlugin;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.retry.FailedPaymentRetryService;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.globallocker.GlobalLock;
+import com.ning.billing.util.globallocker.GlobalLocker;
+import com.ning.billing.util.globallocker.LockFailedException;
+import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
 
 public class DefaultPaymentApi implements PaymentApi {
+    
+    private final static int NB_LOCK_TRY = 5;
+    
     private final PaymentProviderPluginRegistry pluginRegistry;
     private final AccountUserApi accountUserApi;
     private final InvoicePaymentApi invoicePaymentApi;
-    private final RetryService retryService;
+    private final FailedPaymentRetryService retryService;
     private final PaymentDao paymentDao;
     private final PaymentConfig config;
-
-
+    private final GlobalLocker locker;
+    
     private static final Logger log = LoggerFactory.getLogger(DefaultPaymentApi.class);
 
     @Inject
-    public DefaultPaymentApi(PaymentProviderPluginRegistry pluginRegistry,
-            AccountUserApi accountUserApi,
-            InvoicePaymentApi invoicePaymentApi,
-            RetryService retryService,
-            PaymentDao paymentDao,
-            PaymentConfig config) {
+    public DefaultPaymentApi(final PaymentProviderPluginRegistry pluginRegistry,
+            final AccountUserApi accountUserApi,
+            final InvoicePaymentApi invoicePaymentApi,
+            final FailedPaymentRetryService retryService,
+            final PaymentDao paymentDao,
+            final PaymentConfig config,
+            final GlobalLocker locker) {
         this.pluginRegistry = pluginRegistry;
         this.accountUserApi = accountUserApi;
         this.invoicePaymentApi = invoicePaymentApi;
         this.retryService = retryService;
         this.paymentDao = paymentDao;
         this.config = config;
+        this.locker = locker;
     }
 
     @Override
     public PaymentMethodInfo getPaymentMethod(final String accountKey, final String paymentMethodId) 
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, PaymentMethodInfo> result = plugin.getPaymentMethodInfo(paymentMethodId);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, accountKey, paymentMethodId);
-        }
-        return result.getRight();
-    }
-
-    private PaymentProviderPlugin getPaymentProviderPlugin(String accountKey) {
-        String paymentProviderName = null;
-
-        if (accountKey != null) {
-            Account account;
-            try {
-                account = accountUserApi.getAccountByKey(accountKey);
-                return getPaymentProviderPlugin(account);
-            } catch (AccountApiException e) {
-                log.error("Error getting payment provider plugin.", e);
-            }
-        }
-
-        return pluginRegistry.getPlugin(paymentProviderName);
-    }
-
-    private PaymentProviderPlugin getPaymentProviderPlugin(Account account) {
-        String paymentProviderName = null;
-
-        if (account != null) {
-            paymentProviderName = account.getPaymentProviderName();
+        try {
+            final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+            return plugin.getPaymentMethodInfo(paymentMethodId);
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(e, ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, accountKey, paymentMethodId);            
         }
-
-        return pluginRegistry.getPlugin(paymentProviderName);
     }
 
     @Override
     public List<PaymentMethodInfo> getPaymentMethods(String accountKey)
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, List<PaymentMethodInfo>> result =  plugin.getPaymentMethods(accountKey);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_NO_PAYMENT_METHODS, accountKey);
+        try {
+            final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+            return plugin.getPaymentMethods(accountKey);
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(e, ErrorCode.PAYMENT_NO_PAYMENT_METHODS, accountKey);
         }
-        return result.getRight();
     }
 
     @Override
-    public void updatePaymentGateway(String accountKey, CallContext context) 
+    public void updatePaymentGateway(final String accountKey, final CallContext context) 
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, Void> result =  plugin.updatePaymentGateway(accountKey);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_UPD_GATEWAY_FAILED, accountKey, result.getLeft().getMessage());
-        }
-        return;
+
+        new WithAccountLock<Void>().processAccountWithLock(locker, accountKey, new WithAccountLockCallback<Void>() {
+            @Override
+            public Void doOperation() throws PaymentApiException {
+
+                try {
+                    final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+                    plugin.updatePaymentGateway(accountKey);
+                    return null;
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(e, ErrorCode.PAYMENT_UPD_GATEWAY_FAILED, accountKey, e.getMessage());
+                }
+            }
+        });
     }
 
     @Override
     public PaymentProviderAccount getPaymentProviderAccount(String accountKey)
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, PaymentProviderAccount> result = plugin.getPaymentProviderAccount(accountKey);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_PROVIDER, accountKey, result.getLeft().getMessage());
+        try {
+            final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+            return plugin.getPaymentProviderAccount(accountKey);
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(e, ErrorCode.PAYMENT_GET_PAYMENT_PROVIDER, accountKey, e.getMessage());
         }
-        return result.getRight();
     }
 
     @Override
-    public String addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod, CallContext context) 
+    public String addPaymentMethod(final String accountKey, final PaymentMethodInfo paymentMethod, final CallContext context) 
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, String> result =  plugin.addPaymentMethod(accountKey, paymentMethod);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
-        }
-        return result.getRight();
+        
+        return new WithAccountLock<String>().processAccountWithLock(locker, accountKey, new WithAccountLockCallback<String>() {
+
+            @Override
+            public String doOperation() throws PaymentApiException {
+                try {
+                final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+                return plugin.addPaymentMethod(accountKey, paymentMethod);
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(e, ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, accountKey, e.getMessage());
+                }
+            }
+        });
     }
 
 
     @Override
-    public void deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context) 
+    public void deletePaymentMethod(final String accountKey, final String paymentMethodId, final CallContext context) 
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, Void> result =  plugin.deletePaymentMethod(accountKey, paymentMethodId);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
-        }
-        return;
+        
+        new WithAccountLock<Void>().processAccountWithLock(locker, accountKey, new WithAccountLockCallback<Void>() {
+
+            @Override
+            public Void doOperation() throws PaymentApiException {
+                try {
+                final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+                plugin.deletePaymentMethod(accountKey, paymentMethodId);
+                return null;
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(e, ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, accountKey, e.getMessage());
+                }
+            }
+        });
     }
 
     @Override
-    public PaymentMethodInfo updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context) 
+    public PaymentMethodInfo updatePaymentMethod(final String accountKey, final PaymentMethodInfo paymentMethodInfo, final CallContext context) 
     throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        Either<PaymentErrorEvent, PaymentMethodInfo> result = plugin.updatePaymentMethod(accountKey, paymentMethodInfo);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
-        }
-        return result.getRight();
+
+        return new WithAccountLock<PaymentMethodInfo>().processAccountWithLock(locker, accountKey, new WithAccountLockCallback<PaymentMethodInfo>() {
+
+            @Override
+            public PaymentMethodInfo doOperation() throws PaymentApiException {
+                try {
+                    final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+                    return plugin.updatePaymentMethod(accountKey, paymentMethodInfo);
+                }  catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(e, ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, accountKey, e.getMessage());
+                }
+            }
+        });
     }
 
     @Override
-    public List<PaymentInfoEvent> createPayment(String accountKey, List<String> invoiceIds, CallContext context) 
-    throws PaymentApiException {
-        try {
-            final Account account = accountUserApi.getAccountByKey(accountKey);
-            return createPayment(account, invoiceIds, context);
-        } catch (AccountApiException e) {
-            throw new PaymentApiException(e);
-        }
+    public PaymentInfoEvent createPayment(final String accountKey, final UUID invoiceId, final CallContext context) 
+        throws PaymentApiException {
+
+        return new WithAccountLock<PaymentInfoEvent>().processAccountWithLock(locker, accountKey, new WithAccountLockCallback<PaymentInfoEvent>() {
+
+            @Override
+            public PaymentInfoEvent doOperation() throws PaymentApiException {
+                try {
+                    final Account account = accountUserApi.getAccountByKey(accountKey);
+                    return createPayment(account, invoiceId, context);
+                } catch (AccountApiException e) {
+                    throw new PaymentApiException(e);
+                }
+            }
+        });
     }
 
     @Override
-    public PaymentInfoEvent createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context) 
+    public PaymentInfoEvent createPaymentForPaymentAttempt(final String accountKey, final UUID paymentAttemptId, final CallContext context) 
     throws PaymentApiException {
 
-        PaymentAttempt paymentAttempt = paymentDao.getPaymentAttemptById(paymentAttemptId);
-        if (paymentAttempt != null) {
-            try {
-                Invoice invoice = invoicePaymentApi.getInvoice(paymentAttempt.getInvoiceId());
-                Account account = accountUserApi.getAccountById(paymentAttempt.getAccountId());
+        return new WithAccountLock<PaymentInfoEvent>().processAccountWithLock(locker, accountKey, new WithAccountLockCallback<PaymentInfoEvent>() {
+
+            @Override
+            public PaymentInfoEvent doOperation() throws PaymentApiException {
+                PaymentAttempt paymentAttempt = paymentDao.getPaymentAttemptById(paymentAttemptId);
+                try {
+
+                    Invoice invoice = paymentAttempt != null ? invoicePaymentApi.getInvoice(paymentAttempt.getInvoiceId()) : null;
+                    Account account = paymentAttempt != null ? accountUserApi.getAccountById(paymentAttempt.getAccountId()) : null;
+                    if (invoice == null || account == null) {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD, paymentAttemptId);                            
+                    }
 
-                if (invoice != null && account != null) {
                     if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0 ) {
-                        // TODO: send a notification that invoice was ignored?
                         log.info("Received invoice for payment with outstanding amount of 0 {} ", invoice);
                         throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_WITH_NON_POSITIVE_INV, account.getId());
+                    }
 
-                    } else {
-                        
+                    try {
                         PaymentAttempt newPaymentAttempt = new PaymentAttempt.Builder(paymentAttempt)
                         .setRetryCount(paymentAttempt.getRetryCount() + 1)
                         .setPaymentAttemptId(UUID.randomUUID())
                         .build();
 
-                        paymentDao.createPaymentAttempt(newPaymentAttempt, context);
-                        Either<PaymentErrorEvent, PaymentInfoEvent> result =  processPayment(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt, context);
-                        if (result.isLeft()) {
-                            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT, account.getId(),  paymentAttemptId, result.getLeft().getMessage());                            
-                        }
-                        return result.getRight();
+                        paymentDao.createPaymentAttempt(newPaymentAttempt, PaymentAttemptStatus.IN_PROCESSING, context);
+                        PaymentInfoEvent result = processPaymentWithAccountLocked(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt, context);
 
+                        return result;
+                    } catch (PaymentPluginApiException e) {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT, account.getId(),  paymentAttemptId, e.getMessage());                            
                     }
+                } catch (AccountApiException e) {
+                    throw new PaymentApiException(e);
                 }
-            } catch (AccountApiException e) {
-                throw new PaymentApiException(e);
             }
-        }
-        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD, paymentAttemptId);
+        });
     }
 
     @Override
-    public List<PaymentInfoEvent> createPayment(Account account, List<String> invoiceIds, CallContext context) 
-        throws PaymentApiException {
-        
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
+    public PaymentInfoEvent createPayment(final Account account, final UUID invoiceId, final CallContext context) 
+    throws PaymentApiException {
 
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> processedPaymentsOrErrors = new ArrayList<Either<PaymentErrorEvent, PaymentInfoEvent>>(invoiceIds.size());
+        return new WithAccountLock<PaymentInfoEvent>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<PaymentInfoEvent>() {
 
-        for (String invoiceId : invoiceIds) {
-            Invoice invoice = invoicePaymentApi.getInvoice(UUID.fromString(invoiceId));
+            @Override
+            public PaymentInfoEvent doOperation()
+            throws PaymentApiException {
+
+                try {
+                    final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
 
-            if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0 ) {
-                log.debug("Received invoice for payment with balance of 0 {} ", invoice);
-            }
-            else if (invoice.isMigrationInvoice()) {
-                log.info("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
-                Either<PaymentErrorEvent, PaymentInfoEvent> result = Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("migration invoice",
-                        "Invoice balance was a migration invoice",
-                        account.getId(),
-                        UUID.fromString(invoiceId),
-                        context.getUserToken()));
-                processedPaymentsOrErrors.add(result);
-            }
-            else {
-                PaymentAttempt paymentAttempt = paymentDao.createPaymentAttempt(invoice, context);
 
-                processedPaymentsOrErrors.add(processPayment(plugin, account, invoice, paymentAttempt, context));
-            }
-        }
 
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> result =  processedPaymentsOrErrors;
-        List<PaymentInfoEvent> info = new LinkedList<PaymentInfoEvent>();
-        for (Either<PaymentErrorEvent, PaymentInfoEvent> cur : result) {
-            if (cur.isLeft()) {
-                throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), cur.getLeft().getMessage());
+                    Invoice invoice = invoicePaymentApi.getInvoice(invoiceId);
+
+                        if (invoice.isMigrationInvoice()) {
+                            log.error("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
+                            return null;
+                        }
+
+                        PaymentInfoEvent result = null;
+                        if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0 ) {
+                            // STEPH null
+                            PaymentAttempt paymentAttempt = paymentDao.createPaymentAttempt(invoice, null, context);
+                            result = processPaymentWithAccountLocked(plugin, account, invoice, paymentAttempt, context);
+                        }
+
+                    return result;
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(e, ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), e.getMessage());
+                }
             }
-            info.add(cur.getRight());
-        }
-        return info;
+        });
     }
 
-    private Either<PaymentErrorEvent, PaymentInfoEvent> processPayment(PaymentProviderPlugin plugin, Account account, Invoice invoice,
-            PaymentAttempt paymentAttempt, CallContext context) {
-        Either<PaymentErrorEvent, PaymentInfoEvent> paymentOrError = plugin.processInvoice(account, invoice);
-        PaymentInfoEvent paymentInfo = null;
 
-        if (paymentOrError.isLeft()) {
-            String error = StringUtils.substring(paymentOrError.getLeft().getMessage() + paymentOrError.getLeft().getBusEventType(), 0, 100);
-            log.info("Could not process a payment for " + paymentAttempt + " error was " + error);
+    private PaymentInfoEvent processPaymentWithAccountLocked(PaymentProviderPlugin plugin, Account account, Invoice invoice,
+            PaymentAttempt paymentAttempt, CallContext context) throws PaymentPluginApiException {
 
-            scheduleRetry(paymentAttempt, error);
+        PaymentInfoEvent paymentInfo = null;
+        try {
+            paymentInfo = new DefaultPaymentInfoEvent(plugin.processInvoice(account, invoice), account.getId(), invoice.getId());
+        } catch (PaymentPluginApiException e) {
+            log.info("Could not process a payment for " + paymentAttempt + ", error was " + e.getMessage());
+            scheduleRetry(paymentAttempt);
+            throw e;
         }
-        else {
-            paymentInfo = paymentOrError.getRight();
-            paymentDao.savePaymentInfo(paymentInfo, context);
 
-            final String paymentMethodId = paymentInfo.getPaymentMethodId();
-            log.debug("Fetching payment method info for payment method id " + ((paymentMethodId == null) ? "null" : paymentMethodId));
-            Either<PaymentErrorEvent, PaymentMethodInfo> paymentMethodInfoOrError = plugin.getPaymentMethodInfo(paymentMethodId);
+        paymentDao.savePaymentInfo(paymentInfo, context);
 
-            if (paymentMethodInfoOrError.isRight()) {
-                PaymentMethodInfo paymentMethodInfo = paymentMethodInfoOrError.getRight();
+        final String paymentMethodId = paymentInfo.getPaymentMethodId();
+        log.debug("Fetching payment method info for payment method id " + ((paymentMethodId == null) ? "null" : paymentMethodId));
+        PaymentMethodInfo paymentMethodInfo = plugin.getPaymentMethodInfo(paymentMethodId);
 
-                if (paymentMethodInfo instanceof CreditCardPaymentMethodInfo) {
-                    CreditCardPaymentMethodInfo ccPaymentMethod = (CreditCardPaymentMethodInfo)paymentMethodInfo;
-                    paymentDao.updatePaymentInfo(ccPaymentMethod.getType(), paymentInfo.getPaymentId(), ccPaymentMethod.getCardType(), ccPaymentMethod.getCardCountry(), context);
-                }
-                else if (paymentMethodInfo instanceof PaypalPaymentMethodInfo) {
-                    PaypalPaymentMethodInfo paypalPaymentMethodInfo = (PaypalPaymentMethodInfo)paymentMethodInfo;
-                    paymentDao.updatePaymentInfo(paypalPaymentMethodInfo.getType(), paymentInfo.getPaymentId(), null, null, context);
-                }
-            } else {
-                log.info(paymentMethodInfoOrError.getLeft().getMessage());
-            }
+        if (paymentMethodInfo instanceof CreditCardPaymentMethodInfo) {
+            CreditCardPaymentMethodInfo ccPaymentMethod = (CreditCardPaymentMethodInfo)paymentMethodInfo;
+            paymentDao.updatePaymentInfo(ccPaymentMethod.getType(), paymentInfo.getPaymentId(), ccPaymentMethod.getCardType(), ccPaymentMethod.getCardCountry(), context);
 
-            if (paymentInfo.getPaymentId() != null) {
-                paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getPaymentAttemptId(), paymentInfo.getPaymentId(), context);
-            }
+        } else if (paymentMethodInfo instanceof PaypalPaymentMethodInfo) {
+            PaypalPaymentMethodInfo paypalPaymentMethodInfo = (PaypalPaymentMethodInfo)paymentMethodInfo;
+            paymentDao.updatePaymentInfo(paypalPaymentMethodInfo.getType(), paymentInfo.getPaymentId(), null, null, context);
         }
+        if (paymentInfo.getPaymentId() != null) {
+            paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getPaymentAttemptId(), paymentInfo.getPaymentId(), context);
+        }
+
+        invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
+                    paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : paymentInfo.getAmount(),
+                      /*paymentInfo.getRefundAmount(), */
+                      paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency(),
+                        paymentAttempt.getPaymentAttemptId(),
+                          paymentAttempt.getPaymentAttemptDate(),
+                            context);
 
-        invoicePaymentApi.notifyOfPaymentAttempt(
-                invoice.getId(),
-                paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : paymentInfo.getAmount(),
-                        //                                                                         paymentInfo.getRefundAmount(), TODO
-                        paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency(),
-                                paymentAttempt.getPaymentAttemptId(),
-                                paymentAttempt.getPaymentAttemptDate(),
-                                context);
+        return paymentInfo;
 
-        return paymentOrError;
     }
 
-    private void scheduleRetry(PaymentAttempt paymentAttempt, String error) {
+    private void scheduleRetry(PaymentAttempt paymentAttempt) {
         final List<Integer> retryDays = config.getPaymentRetryDays();
 
         int retryCount = 0;
@@ -348,28 +359,28 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public String createPaymentProviderAccount(Account account, CallContext context) 
-        throws PaymentApiException {
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
-        Either<PaymentErrorEvent, String> result =  plugin.createPaymentProviderAccount(account);
-        if (result.isLeft()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT, account.getId(), result.getLeft().getMessage());
+    throws PaymentApiException {
+        try {
+            final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
+            return plugin.createPaymentProviderAccount(account);
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT, account.getId(), e.getMessage());
         }
-        return result.getRight();
     }
 
     @Override
     public void updatePaymentProviderAccountContact(String externalKey, CallContext context) 
         throws PaymentApiException {
+        
+        Account account = null;
         try {
-            Account account = accountUserApi.getAccountByKey(externalKey);
+            account = accountUserApi.getAccountByKey(externalKey);
             final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-            Either<PaymentErrorEvent, Void> result = plugin.updatePaymentProviderAccountExistingContact(account);
-            if (result.isLeft()) {
-                throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT, account.getId(), result.getLeft().getMessage());
-            }
-            return;
+            plugin.updatePaymentProviderAccountExistingContact(account);
         } catch (AccountApiException e) {
             throw new PaymentApiException(e);
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT, account.getId(), e.getMessage());
         }
     }
 
@@ -379,24 +390,37 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public List<PaymentInfoEvent> createRefund(Account account, List<String> invoiceIds, CallContext context)
+    public PaymentInfoEvent createRefund(Account account, UUID paymentId, CallContext context)
         throws PaymentApiException {
+
+        /*
+        try {
             
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> result = plugin.processRefund(account);
+        List<PaymentInfoPlugin> result = plugin.processRefund(account);
         List<PaymentInfoEvent> info =  new LinkedList<PaymentInfoEvent>();
-        for (Either<PaymentErrorEvent, PaymentInfoEvent> cur : result) {
-            if (cur.isLeft()) {
-                throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), cur.getLeft().getMessage());
-            }
-            info.add(cur.getRight());
+        int i = 0;
+        for (PaymentInfoPlugin cur : result) {
+            // STEPH
+            //info.add(new DefaultPaymentInfoEvent(cur, account.getId(), invoiceIds.get(i)));
         }
         return info;
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), e.getMessage());
+        }
+        */
+        // STEPH
+        return null;
     }
 
     @Override
-    public List<PaymentInfoEvent> getPaymentInfo(List<String> invoiceIds) {
-        return paymentDao.getPaymentInfo(invoiceIds);
+    public List<PaymentInfoEvent> getPaymentInfo(List<UUID> invoiceIds) {
+        // STEPH until DAO is changed:
+        List<String> invoiceIdStrings = new ArrayList<String>();
+        for (UUID cur : invoiceIds) {
+            invoiceIdStrings.add(cur.toString());
+        }
+        return paymentDao.getPaymentInfo(invoiceIdStrings);
     }
 
     @Override
@@ -409,4 +433,59 @@ public class DefaultPaymentApi implements PaymentApi {
         return paymentDao.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
     }
 
+    
+
+    private PaymentProviderPlugin getPaymentProviderPlugin(String accountKey) {
+
+        String paymentProviderName = null;
+        if (accountKey != null) {
+            Account account;
+            try {
+                account = accountUserApi.getAccountByKey(accountKey);
+                return getPaymentProviderPlugin(account);
+            } catch (AccountApiException e) {
+                log.error("Error getting payment provider plugin.", e);
+            }
+        }
+        return pluginRegistry.getPlugin(paymentProviderName);
+    }
+    
+    private PaymentProviderPlugin getPaymentProviderPlugin(Account account) {
+        String paymentProviderName = null;
+
+        if (account != null) {
+            paymentProviderName = account.getPaymentProviderName();
+        }
+
+        return pluginRegistry.getPlugin(paymentProviderName);
+    }
+
+
+
+    public interface WithAccountLockCallback<T> {
+        public T doOperation() throws PaymentApiException;
+    }
+    
+    public static class WithAccountLock<T> {
+        public T processAccountWithLock(final GlobalLocker locker, final String accountExternalKey, final WithAccountLockCallback<T> callback)
+         throws PaymentApiException {
+            GlobalLock lock = null;
+            try {
+                lock = locker.lockWithNumberOfTries(LockerService.PAYMENT, accountExternalKey, NB_LOCK_TRY);
+                return callback.doOperation();
+            } catch (LockFailedException e) {
+                // Not good!
+                log.error(String.format("Failed to lock account %s",
+                        accountExternalKey), e);
+                // STEPH or throws
+                return null;
+            } finally {
+                if (lock != null) {
+                    lock.release();
+                }        
+            }
+        }
+    }
+    
+    
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
index e592294..ac0f3bb 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
@@ -26,12 +26,15 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
 import com.google.common.base.Objects;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
 import com.ning.billing.util.bus.BusEvent;
 import com.ning.billing.util.bus.BusEvent.BusEventType;
 
 public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
 	
 
+    private final UUID invoiceId;
+    private final UUID accountId;
     private final String paymentId;
     private final BigDecimal amount;
     private final BigDecimal refundAmount;
@@ -50,22 +53,27 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     private final DateTime updatedDate;
 
     @JsonCreator
-    public DefaultPaymentInfoEvent(@JsonProperty("paymentId") String paymentId,
-                       @JsonProperty("amount") BigDecimal amount,
-                       @JsonProperty("refundAmount") BigDecimal refundAmount,
-                       @JsonProperty("bankIdentificationNumber") String bankIdentificationNumber,
-                       @JsonProperty("paymentNumber") String paymentNumber,
-                       @JsonProperty("status") String status,
-                       @JsonProperty("type") String type,
-                       @JsonProperty("referenceId") String referenceId,
-                       @JsonProperty("paymentMethodId") String paymentMethodId,
-                       @JsonProperty("paymentMethod") String paymentMethod,
-                       @JsonProperty("cardType") String cardType,
-                       @JsonProperty("cardCountry") String cardCountry,
-                       @JsonProperty("userToken") UUID userToken,
-                       @JsonProperty("effectiveDate") DateTime effectiveDate,
-                       @JsonProperty("createdDate") DateTime createdDate,
-                       @JsonProperty("updatedDate") DateTime updatedDate) {
+    public DefaultPaymentInfoEvent(@JsonProperty("accountId") UUID accountId,
+            @JsonProperty("invoiceId") UUID invoiceId,            
+            @JsonProperty("paymentId") String paymentId,
+            @JsonProperty("amount") BigDecimal amount,
+            @JsonProperty("refundAmount") BigDecimal refundAmount,
+            @JsonProperty("bankIdentificationNumber") String bankIdentificationNumber,
+            @JsonProperty("paymentNumber") String paymentNumber,
+            @JsonProperty("status") String status,
+            @JsonProperty("type") String type,
+            @JsonProperty("referenceId") String referenceId,
+            @JsonProperty("paymentMethodId") String paymentMethodId,
+            @JsonProperty("paymentMethod") String paymentMethod,
+            @JsonProperty("cardType") String cardType,
+            @JsonProperty("cardCountry") String cardCountry,
+            @JsonProperty("userToken") UUID userToken,
+            @JsonProperty("effectiveDate") DateTime effectiveDate,
+            @JsonProperty("createdDate") DateTime createdDate,
+            @JsonProperty("updatedDate") DateTime updatedDate) {
+
+        this.accountId = accountId;
+        this.invoiceId = invoiceId;
         this.paymentId = paymentId;
         this.amount = amount;
         this.refundAmount = refundAmount;
@@ -85,22 +93,30 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     }
 
     public DefaultPaymentInfoEvent(DefaultPaymentInfoEvent src) {
-        this(src.paymentId,
-             src.amount,
-             src.refundAmount,
-             src.bankIdentificationNumber,
-             src.paymentNumber,
-             src.status,
-             src.type,
-             src.referenceId,
-             src.paymentMethodId,
-             src.paymentMethod,
-             src.cardType,
-             src.cardCountry,
-             src.userToken,
-             src.effectiveDate,
-             src.createdDate,
-             src.updatedDate);
+        this(src.accountId,
+                src.invoiceId,
+                src.paymentId,
+                src.amount,
+                src.refundAmount,
+                src.bankIdentificationNumber,
+                src.paymentNumber,
+                src.status,
+                src.type,
+                src.referenceId,
+                src.paymentMethodId,
+                src.paymentMethod,
+                src.cardType,
+                src.cardCountry,
+                src.userToken,
+                src.effectiveDate,
+                src.createdDate,
+                src.updatedDate);
+    }
+    
+    public DefaultPaymentInfoEvent(PaymentInfoPlugin info, UUID accountId, UUID invoiceId) {
+        this(accountId, invoiceId, info.getPaymentId(), info.getAmount(), info.getRefundAmount(), info.getBankIdentificationNumber(), info.getPaymentNumber(),
+                info.getStatus(), info.getCardType(), info.getReferenceId(), info.getPaymentMethodId(), info.getPaymentMethod(), info.getCardType(), info.getCardCountry(),
+                null, info.getEffectiveDate(), info.getCreatedDate(), info.getUpdatedDate());
     }
     
     @JsonIgnore
@@ -124,6 +140,16 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     }
 
     @Override
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
     public BigDecimal getAmount() {
         return amount;
     }
@@ -194,6 +220,9 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     }
 
     public static class Builder {
+        
+        private UUID invoiceId;
+        private UUID accountId;
         private String paymentId;
         private BigDecimal amount;
         private BigDecimal refundAmount;
@@ -215,6 +244,8 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
         }
 
         public Builder(DefaultPaymentInfoEvent src) {
+            this.accountId = src.accountId;
+            this.invoiceId = src.invoiceId;
             this.paymentId = src.paymentId;
             this.amount = src.amount;
             this.refundAmount = src.refundAmount;
@@ -233,6 +264,17 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
             this.updatedDate = src.updatedDate;
         }
 
+
+        public Builder setAccountId(UUID accountId) {
+            this.accountId = accountId;
+            return this;
+        }
+
+        public Builder setInvoiceId(UUID invoiceId) {
+            this.invoiceId = invoiceId;
+            return this;
+        }
+        
         public Builder setPaymentId(String paymentId) {
             this.paymentId = paymentId;
             return this;
@@ -314,22 +356,24 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
         }
 
         public PaymentInfoEvent build() {
-            return new DefaultPaymentInfoEvent(paymentId,
-                                   amount,
-                                   refundAmount,
-                                   bankIdentificationNumber,
-                                   paymentNumber,
-                                   status,
-                                   type,
-                                   referenceId,
-                                   paymentMethodId,
-                                   paymentMethod,
-                                   cardType,
-                                   cardCountry,
-                                   userToken,
-                                   effectiveDate,
-                                   createdDate,
-                                   updatedDate);
+            return new DefaultPaymentInfoEvent(accountId,
+                    invoiceId,
+                    paymentId,
+                    amount,
+                    refundAmount,
+                    bankIdentificationNumber,
+                    paymentNumber,
+                    status,
+                    type,
+                    referenceId,
+                    paymentMethodId,
+                    paymentMethod,
+                    cardType,
+                    cardCountry,
+                    userToken,
+                    effectiveDate,
+                    createdDate,
+                    updatedDate);
         }
     }
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index a21a90c..8b0e163 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -52,7 +53,7 @@ public class AuditedPaymentDao implements PaymentDao {
     }
 
     @Override
-    public PaymentAttempt createPaymentAttempt(final PaymentAttempt paymentAttempt, final CallContext context) {
+    public PaymentAttempt createPaymentAttempt(final PaymentAttempt paymentAttempt, final PaymentAttemptStatus paymentAttemptStatus, final CallContext context) {
         return sqlDao.inTransaction(new Transaction<PaymentAttempt, PaymentSqlDao>() {
             @Override
             public PaymentAttempt inTransaction(PaymentSqlDao transactional, TransactionStatus status) throws Exception {
@@ -69,11 +70,11 @@ public class AuditedPaymentDao implements PaymentDao {
     }
 
     @Override
-    public PaymentAttempt createPaymentAttempt(final Invoice invoice, final CallContext context) {
+    public PaymentAttempt createPaymentAttempt(final Invoice invoice, final PaymentAttemptStatus paymentAttemptStatus, final CallContext context) {
         return sqlDao.inTransaction(new Transaction<PaymentAttempt, PaymentSqlDao>() {
             @Override
             public PaymentAttempt inTransaction(PaymentSqlDao transactional, TransactionStatus status) throws Exception {
-                final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+                final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice, paymentAttemptStatus);
                 transactional.insertPaymentAttempt(paymentAttempt, context);
                 UUID historyRecordId = UUID.randomUUID();
                 transactional.insertPaymentAttemptHistory(historyRecordId.toString(), paymentAttempt, 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 8c07824..f289d5c 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
@@ -22,12 +22,13 @@ import java.util.UUID;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.util.callcontext.CallContext;
 
 public interface PaymentDao {
 
-    PaymentAttempt createPaymentAttempt(Invoice invoice, CallContext context);
-    PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt, CallContext context);
+    PaymentAttempt createPaymentAttempt(Invoice invoice, PaymentAttemptStatus status, CallContext context);
+    PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt, PaymentAttemptStatus status, CallContext context);
 
     void savePaymentInfo(PaymentInfoEvent right, CallContext context);
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index b7b861a..9b27f23 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -44,6 +44,7 @@ import org.skife.jdbi.v2.unstable.BindIn;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
 @ExternalizedSqlViaStringTemplate3()
@@ -117,6 +118,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
             stmt.bind("payment_attempt_dt", getDate(paymentAttempt.getPaymentAttemptDate()));
             stmt.bind("payment_id", paymentAttempt.getPaymentId());
             stmt.bind("retry_count", paymentAttempt.getRetryCount());
+            stmt.bind("processing_status", paymentAttempt.getPaymentAttemptStatus().toString());
             stmt.bind("created_dt", getDate(paymentAttempt.getCreatedDate()));
             stmt.bind("updated_dt", getDate(paymentAttempt.getUpdatedDate()));
         }
@@ -135,6 +137,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
             DateTime paymentAttemptDate = getDate(rs, "payment_attempt_dt");
             String paymentId = rs.getString("payment_id");
             Integer retryCount = rs.getInt("retry_count");
+            PaymentAttemptStatus paymentAttemptStatus = PaymentAttemptStatus.valueOf(rs.getString("processing_status"));
             DateTime createdDate = getDate(rs, "created_dt");
             DateTime updatedDate = getDate(rs, "updated_dt");
 
@@ -147,6 +150,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
                                       paymentAttemptDate,
                                       paymentId,
                                       retryCount,
+                                      paymentAttemptStatus,
                                       createdDate,
                                       updatedDate);
         }
@@ -177,6 +181,8 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
         @Override
         public PaymentInfoEvent map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
 
+            UUID accountId = null;
+            UUID invoiceId = null;
             String paymentId = rs.getString("payment_id");
             BigDecimal amount = rs.getBigDecimal("amount");
             BigDecimal refundAmount = rs.getBigDecimal("refund_amount");
@@ -193,24 +199,26 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
             DateTime createdDate = getDate(rs, "created_dt");
             DateTime updatedDate = getDate(rs, "updated_dt");
 
-            UUID userToken = null; //rs.getString("user_token") != null ? UUID.fromString(rs.getString("user_token")) : null;
+            UUID userToken = rs.getString("user_token") != null ? UUID.fromString(rs.getString("user_token")) : null;
             
-            return new DefaultPaymentInfoEvent(paymentId,
-                                   amount,
-                                   refundAmount,
-                                   bankIdentificationNumber,
-                                   paymentNumber,
-                                   status,
-                                   type,
-                                   referenceId,
-                                   paymentMethodId,
-                                   paymentMethod,
-                                   cardType,
-                                   cardCountry,
-                                   userToken,
-                                   effectiveDate,
-                                   createdDate,
-                                   updatedDate);
+            return new DefaultPaymentInfoEvent(accountId,
+                    invoiceId,
+                    paymentId,
+                    amount,
+                    refundAmount,
+                    bankIdentificationNumber,
+                    paymentNumber,
+                    status,
+                    type,
+                    referenceId,
+                    paymentMethodId,
+                    paymentMethod,
+                    cardType,
+                    cardCountry,
+                    userToken,
+                    effectiveDate,
+                    createdDate,
+                    updatedDate);
         }
     }
 
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index 62785c9..98e9e59 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -16,7 +16,7 @@
 
 package com.ning.billing.payment.provider;
 
-import java.util.Arrays;
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
@@ -25,94 +25,152 @@ import org.joda.time.DateTimeZone;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.payment.api.DefaultPaymentErrorEvent;
-import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
-import com.ning.billing.payment.api.Either;
-import com.ning.billing.payment.api.PaymentErrorEvent;
-import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentMethodInfo;
 import com.ning.billing.payment.api.PaymentProviderAccount;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.PaymentProviderPlugin;
 
 public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
 
     @Override
-    public Either<PaymentErrorEvent, PaymentInfoEvent> processInvoice(Account account, Invoice invoice) {
-        PaymentInfoEvent payment = new DefaultPaymentInfoEvent.Builder()
-                                             .setPaymentId(UUID.randomUUID().toString())
-                                             .setAmount(invoice.getBalance())
-                                             .setStatus("Processed")
-                                             .setCreatedDate(new DateTime(DateTimeZone.UTC))
-                                             .setEffectiveDate(new DateTime(DateTimeZone.UTC))
-                                             .setType("Electronic")
-                                             .build();
-        return Either.right(payment);
+    public PaymentInfoPlugin processInvoice(final Account account, final Invoice invoice)
+            throws PaymentPluginApiException {
+        PaymentInfoPlugin payment = new PaymentInfoPlugin() {
+            @Override
+            public DateTime getUpdatedDate() {
+                return new DateTime(DateTimeZone.UTC);
+            }
+            @Override
+            public String getType() {
+                return "Electronic";
+            }
+            @Override
+            public String getStatus() {
+                return "Processed";
+            }
+            @Override
+            public BigDecimal getRefundAmount() {
+                return null;
+            }
+            @Override
+            public String getReferenceId() {
+                return null;
+            }
+            @Override
+            public String getPaymentNumber() {
+                return null;
+            }
+            @Override
+            public String getPaymentMethodId() {
+                return null;
+            }
+            @Override
+            public String getPaymentMethod() {
+                return null;
+            }
+            @Override
+            public String getPaymentId() {
+                return UUID.randomUUID().toString();
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return null;
+            }
+            @Override
+            public DateTime getCreatedDate() {
+                return new DateTime(DateTimeZone.UTC);
+            }
+            @Override
+            public String getCardType() {
+                return null;
+            }
+            @Override
+            public String getCardCountry() {
+                return null;
+            }
+            @Override
+            public String getBankIdentificationNumber() {
+                return null;
+            }
+            @Override
+            public BigDecimal getAmount() {
+                return invoice.getBalance();
+            }
+        };
+        return payment;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentInfoEvent> getPaymentInfo(String paymentId) {
-        return Either.right(null);
+    public String createPaymentProviderAccount(Account account)
+            throws PaymentPluginApiException {
+        
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> createPaymentProviderAccount(Account account) {
-        return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unsupported",
-                                            "Account creation not supported in this plugin",
-                                            account.getId(),
-                                            null, null));
+    public PaymentInfoPlugin getPaymentInfo(String paymentId)
+            throws PaymentPluginApiException {
+        
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentProviderAccount> getPaymentProviderAccount(String accountKey) {
-        return Either.right(null);
+    public PaymentProviderAccount getPaymentProviderAccount(String accountKey)
+            throws PaymentPluginApiException {
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod) {
-        return Either.right(null);
-    }
-
-    public void setDefaultPaymentMethodOnAccount(PaymentProviderAccount account, String paymentMethodId) {
-        // NO-OP
+    public void updatePaymentGateway(String accountKey)
+            throws PaymentPluginApiException {
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethod) {
-        return Either.right(paymentMethod);
+    public PaymentMethodInfo getPaymentMethodInfo(String paymentMethodId)
+            throws PaymentPluginApiException {
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId) {
-        return Either.right(null);
+    public List<PaymentMethodInfo> getPaymentMethods(String accountKey)
+            throws PaymentPluginApiException {
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> getPaymentMethodInfo(String paymentMethodId) {
-        return Either.right(null);
+    public String addPaymentMethod(String accountKey,
+            PaymentMethodInfo paymentMethod) throws PaymentPluginApiException {
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, List<PaymentMethodInfo>> getPaymentMethods(final String accountKey) {
-        return Either.right(Arrays.<PaymentMethodInfo>asList());
+    public PaymentMethodInfo updatePaymentMethod(String accountKey,
+            PaymentMethodInfo paymentMethodInfo)
+            throws PaymentPluginApiException {
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentGateway(String accountKey) {
-        return Either.right(null);
+    public void deletePaymentMethod(String accountKey, String paymentMethodId)
+    throws PaymentPluginApiException {
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountExistingContact(Account account) {
-        return Either.right(null);
+    public void updatePaymentProviderAccountExistingContact(Account account)
+            throws PaymentPluginApiException {
+
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountWithNewContact(Account account) {
-        return Either.right(null);
+    public void updatePaymentProviderAccountWithNewContact(Account account)
+            throws PaymentPluginApiException {
+
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> processRefund(Account account) {
-        // TODO Auto-generated method stub
+    public List<PaymentInfoPlugin> processRefund(Account account)
+            throws PaymentPluginApiException {
         return null;
     }
 
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java
index 5ba98b7..ceb36f9 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java
@@ -20,6 +20,7 @@ import com.google.inject.Inject;
 import com.google.inject.Provider;
 
 public class NoOpPaymentProviderPluginProvider implements Provider<NoOpPaymentProviderPlugin> {
+
     private PaymentProviderPluginRegistry registry;
     private final String instanceName;
 
diff --git a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
index 2077c5e..d11a70c 100644
--- a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -19,11 +19,13 @@ package com.ning.billing.payment;
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
@@ -31,57 +33,64 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.payment.api.DefaultPaymentErrorEvent;
-import com.ning.billing.payment.api.Either;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
-import com.ning.billing.util.bus.BusEvent.BusEventType;
 import com.ning.billing.util.bus.BusEvent;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.DefaultCallContext;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.globallocker.GlobalLock;
+import com.ning.billing.util.globallocker.GlobalLocker;
+import com.ning.billing.util.globallocker.LockFailedException;
+import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
 
 public class RequestProcessor {
+    
     public static final String PAYMENT_PROVIDER_KEY = "paymentProvider";
+    
+    private final static int NB_PAYMENT_THREADS = 3; // STEPH
+    private final static String PAYMENT_GROUP_NAME = "payment-grp";
+    private final static String PAYMENT_TH_NAME = "payment-th";
+
+    
     private final AccountUserApi accountUserApi;
     private final PaymentApi paymentApi;
     private final Bus eventBus;
     private final Clock clock;
-
+    private final ExecutorService executor;
+    
     private static final Logger log = LoggerFactory.getLogger(RequestProcessor.class);
 
     @Inject
-    public RequestProcessor(Clock clock,
-            AccountUserApi accountUserApi,
-            PaymentApi paymentApi,
-            PaymentProviderPluginRegistry pluginRegistry,
-            Bus eventBus) {
+    public RequestProcessor(final Clock clock,
+            final AccountUserApi accountUserApi,
+            final PaymentApi paymentApi,
+            final Bus eventBus,
+            final GlobalLocker locker) {
         this.clock = clock;
         this.accountUserApi = accountUserApi;
         this.paymentApi = paymentApi;
         this.eventBus = eventBus;
+        this.executor = Executors.newFixedThreadPool(NB_PAYMENT_THREADS, new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                return new Thread(new ThreadGroup(PAYMENT_GROUP_NAME),
+                        r,
+                        PAYMENT_TH_NAME);
+            }
+        });
     }
     
-    private void postPaymentEvent(BusEvent ev, UUID accountId) {
-        if (ev == null) {
-            return;
-        }
-        try {
-            eventBus.post(ev);
-        } catch (EventBusException e) {
-            log.error("Failed to post Payment event event for account {} ", accountId, e);
-        }
-    }
 
     @Subscribe
-    public void receiveInvoice(InvoiceCreationEvent event) {
-
+    public void processInvoiceEvent(InvoiceCreationEvent event) {
 
         log.info("Received invoice creation notification for account {} and invoice {}", event.getAccountId(), event.getInvoiceId());
         PaymentErrorEvent errorEvent = null;
@@ -89,9 +98,8 @@ public class RequestProcessor {
             final Account account = accountUserApi.getAccountById(event.getAccountId());
             if (account != null) {
                 CallContext context = new DefaultCallContext("PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
-                List<PaymentInfoEvent> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()), context);
-                PaymentInfoEvent infoEvent = (!results.isEmpty()) ?  results.get(0) : null;
-                postPaymentEvent(infoEvent, account.getId());
+                PaymentInfoEvent result = paymentApi.createPayment(account, event.getInvoiceId(), context);
+                postPaymentEvent(result, account.getId());
                 return;
             } else {
                 errorEvent = new DefaultPaymentErrorEvent(null, "Failed to retrieve account", event.getAccountId(), null, null);
@@ -105,4 +113,16 @@ public class RequestProcessor {
         }
         postPaymentEvent(errorEvent, event.getAccountId());
     }
+    
+    private void postPaymentEvent(BusEvent ev, UUID accountId) {
+        if (ev == null) {
+            return;
+        }
+        try {
+            eventBus.post(ev);
+        } catch (EventBusException e) {
+            log.error("Failed to post Payment event event for account {} ", accountId, e);
+        }
+    }
+
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/retry/RetryService.java b/payment/src/main/java/com/ning/billing/payment/retry/RetryService.java
new file mode 100644
index 0000000..6809514
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/retry/RetryService.java
@@ -0,0 +1,29 @@
+/* 
+ * Copyright 2010-2011 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.retry;
+
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
+
+public interface RetryService {
+    
+    public void initialize(final String svcName)
+        throws NotificationQueueAlreadyExists;
+    
+    public void start();
+    
+    public void stop();
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/retry/TimedoutPaymentRetryService.java b/payment/src/main/java/com/ning/billing/payment/retry/TimedoutPaymentRetryService.java
new file mode 100644
index 0000000..3a1647f
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/retry/TimedoutPaymentRetryService.java
@@ -0,0 +1,118 @@
+/* 
+ * Copyright 2010-2011 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.retry;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.ning.billing.config.PaymentConfig;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationQueue;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+
+public class TimedoutPaymentRetryService implements RetryService {
+
+    
+    
+  private static final Logger log = LoggerFactory.getLogger(TimedoutPaymentRetryService.class);
+    
+    public static final String QUEUE_NAME = "timedout-retry";
+
+    private final Clock clock;
+    private final NotificationQueueService notificationQueueService;
+    private final PaymentConfig config;
+    private final PaymentApi paymentApi;
+    private NotificationQueue retryQueue;
+
+    @Inject
+    public TimedoutPaymentRetryService(Clock clock,
+                        NotificationQueueService notificationQueueService,
+                        PaymentConfig config,
+                        PaymentApi paymentApi) {
+        this.clock = clock;
+        this.notificationQueueService = notificationQueueService;
+        this.paymentApi = paymentApi;
+        this.config = config;
+    }
+
+    @Override
+    public void initialize(final String svcName) throws NotificationQueueAlreadyExists {
+        retryQueue = notificationQueueService.createNotificationQueue(svcName, QUEUE_NAME, new NotificationQueueHandler() {
+            @Override
+            public void handleReadyNotification(String notificationKey, DateTime eventDateTime) {
+                CallContext context = new DefaultCallContext("TimedoutRetryService", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
+                retry(notificationKey, context);
+            }
+        },
+        config);
+    }
+
+    @Override
+    public void start() {
+        retryQueue.startQueue();
+    }
+
+    @Override
+    public void stop() {
+        if (retryQueue != null) {
+            retryQueue.stopQueue();
+         }
+    }
+
+    public void scheduleRetry(PaymentAttempt paymentAttempt, DateTime timeOfRetry) {
+        /*
+        final String id = paymentAttempt.getPaymentAttemptId().toString();
+
+        NotificationKey key = new NotificationKey() {
+            @Override
+            public String toString() {
+                return id;
+            }
+        };
+
+        if (retryQueue != null) {
+            retryQueue.recordFutureNotification(timeOfRetry, key);
+        }
+        */
+    }
+
+    private void retry(String paymentAttemptId, CallContext context) {
+        
+        /*
+        try {
+            PaymentInfoEvent paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
+            if (paymentInfo != null && PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
+                return;
+            }
+            paymentApi.createPaymentForPaymentAttempt(UUID.fromString(paymentAttemptId), context);
+        } catch (PaymentApiException e) {
+            log.error(String.format("Failed to retry payment for %s"), e);
+        }
+        */
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/setup/DefaultPaymentService.java b/payment/src/main/java/com/ning/billing/payment/setup/DefaultPaymentService.java
index eae1baa..4bc3ed3 100644
--- a/payment/src/main/java/com/ning/billing/payment/setup/DefaultPaymentService.java
+++ b/payment/src/main/java/com/ning/billing/payment/setup/DefaultPaymentService.java
@@ -16,16 +16,26 @@
 
 package com.ning.billing.payment.setup;
 
+import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.lifecycle.KillbillService;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
+import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
 import com.ning.billing.payment.RequestProcessor;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentService;
+import com.ning.billing.payment.retry.FailedPaymentRetryService;
+import com.ning.billing.payment.retry.TimedoutPaymentRetryService;
 import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 
 public class DefaultPaymentService implements PaymentService {
     private static final Logger log = LoggerFactory.getLogger(DefaultPaymentService.class);
@@ -35,12 +45,17 @@ public class DefaultPaymentService implements PaymentService {
     private final RequestProcessor requestProcessor;
     private final Bus eventBus;
     private final PaymentApi api;
+    private final FailedPaymentRetryService failedRetryService;
+    private final TimedoutPaymentRetryService timedoutRetryService;
 
     @Inject
-    public DefaultPaymentService(final RequestProcessor requestProcessor, final PaymentApi api, final Bus eventBus) {
+    public DefaultPaymentService(final RequestProcessor requestProcessor, final PaymentApi api, final Bus eventBus,
+            final FailedPaymentRetryService failedRetryService, final TimedoutPaymentRetryService timedoutRetryService) {
         this.requestProcessor = requestProcessor;
         this.eventBus = eventBus;
         this.api = api;
+        this.failedRetryService = failedRetryService;
+        this.timedoutRetryService = timedoutRetryService;
     }
 
     @Override
@@ -48,6 +63,12 @@ public class DefaultPaymentService implements PaymentService {
         return SERVICE_NAME;
     }
 
+    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    public void initialize() throws NotificationQueueAlreadyExists {
+        failedRetryService.initialize(SERVICE_NAME);
+        timedoutRetryService.initialize(SERVICE_NAME);
+    }
+    
     @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.REGISTER_EVENTS)
     public void registerForNotifications() {
         try {
@@ -57,10 +78,21 @@ public class DefaultPaymentService implements PaymentService {
             log.error("Unable to register with the EventBus!", e);
         }
     }
+    
+    @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
+    public void start() {
+        failedRetryService.start();
+        timedoutRetryService.start();
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
+    public void stop() {
+        failedRetryService.stop();
+        timedoutRetryService.stop();
+    }
 
     @Override
     public PaymentApi getPaymentApi() {
         return api;
     }
-
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
index de4bf3c..9475f7c 100644
--- a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
@@ -23,13 +23,14 @@ import org.skife.config.ConfigurationObjectFactory;
 import com.google.inject.AbstractModule;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.payment.RequestProcessor;
-import com.ning.billing.payment.RetryService;
 import com.ning.billing.payment.api.DefaultPaymentApi;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentService;
 import com.ning.billing.payment.dao.AuditedPaymentDao;
 import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.retry.FailedPaymentRetryService;
 
 public class PaymentModule extends AbstractModule {
     private final Properties props;
@@ -50,7 +51,7 @@ public class PaymentModule extends AbstractModule {
     }
 
     protected void installRetryEngine() {
-        bind(RetryService.class).asEagerSingleton();
+        bind(FailedPaymentRetryService.class).asEagerSingleton();
     }
 
     @Override
@@ -59,7 +60,7 @@ public class PaymentModule extends AbstractModule {
         final PaymentConfig paymentConfig = factory.build(PaymentConfig.class);
 
         bind(PaymentConfig.class).toInstance(paymentConfig);
-        bind(PaymentProviderPluginRegistry.class).asEagerSingleton();
+        bind(PaymentProviderPluginRegistry.class).to(DefaultPaymentProviderPluginRegistry.class).asEagerSingleton();
         bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
         bind(RequestProcessor.class).asEagerSingleton();
         bind(PaymentService.class).to(DefaultPaymentService.class).asEagerSingleton();
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index 87c8d8f..cb9effa 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -10,6 +10,7 @@ paymentAttemptFields(prefix) ::= <<
     <prefix>payment_attempt_dt,
     <prefix>invoice_dt,
     <prefix>retry_count,
+    <prefix>processing_status,
     <prefix>created_by,
     <prefix>created_dt,
     <prefix>updated_by,
@@ -39,13 +40,13 @@ paymentInfoFields(prefix) ::= <<
 insertPaymentAttempt() ::= <<
     INSERT INTO payment_attempts (<paymentAttemptFields()>)
     VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id,
-            :payment_attempt_dt, :invoice_dt, :retry_count, :userName, :createdDate, :userName, :createdDate);
+            :payment_attempt_dt, :invoice_dt, :retry_count, :processing_status, :userName, :createdDate, :userName, :createdDate);
 >>
 
 insertPaymentAttemptHistory() ::= <<
     INSERT INTO payment_attempt_history (history_record_id, <paymentAttemptFields()>)
     VALUES (:historyRecordId, :payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id,
-            :payment_attempt_dt, :invoice_dt, :retry_count, :userName, :createdDate, :userName, :createdDate);
+            :payment_attempt_dt, :invoice_dt, :retry_count, :processing_status, :userName, :createdDate, :userName, :createdDate);
 >>
 
 getPaymentAttemptForPaymentId() ::= <<
@@ -112,7 +113,7 @@ getPaymentInfos(invoiceIds) ::= <<
 >>
 
 getPaymentInfoForPaymentAttemptId() ::= <<
-    SELECT <paymentInfoFields("p.")>
+    SELECT <paymentInfoFields("p.")>,
       FROM payments p, payment_attempts pa
      WHERE pa.payment_attempt_id = :payment_attempt_id
        AND pa.payment_id = p.payment_id
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
index 3a344f9..f374569 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -8,6 +8,7 @@ CREATE TABLE payment_attempts (
       payment_attempt_dt datetime NOT NULL,
       payment_id varchar(36) COLLATE utf8_bin,
       retry_count tinyint,
+      processing_status varchar(20),
       invoice_dt datetime NOT NULL,
       created_by varchar(50) NOT NULL,
       created_dt datetime NOT NULL,
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
index 14a5dba..0725dce 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
@@ -49,7 +49,7 @@ public class TestEventJson {
     
     @Test(groups= {"fast"})
     public void testPaymentInfoEvent() throws Exception {
-        PaymentInfoEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID().toString(), new BigDecimal(12), new BigDecimal(12.9), "BNP", "eeert", "success",
+        PaymentInfoEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID().toString(), new BigDecimal(12), new BigDecimal(12.9), "BNP", "eeert", "success",
                 "credit", "ref", "paypal", "paypal", "", "", UUID.randomUUID(), new DateTime(), new DateTime(), new DateTime());
         
         String json = mapper.writeValueAsString(e);
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index 85aa07b..4dea9c5 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -102,11 +102,7 @@ public abstract class TestPaymentApi {
                                                        new BigDecimal("1.0"),
                                                        Currency.USD));
 
-        List<PaymentInfoEvent> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
-
-        assertEquals(results.size(), 1);
-
-        PaymentInfoEvent paymentInfo = results.get(0);
+        PaymentInfoEvent paymentInfo = paymentApi.createPayment(account.getExternalKey(), invoice.getId(), context);
 
         assertNotNull(paymentInfo.getPaymentId());
         assertTrue(paymentInfo.getAmount().compareTo(amount.setScale(2, RoundingMode.HALF_EVEN)) == 0);
@@ -124,7 +120,7 @@ public abstract class TestPaymentApi {
         DateTime paymentAttemptDateTruncated = paymentAttempt.getPaymentAttemptDate().withMillisOfSecond(0).withSecondOfMinute(0);
         assertEquals(paymentAttemptDateTruncated.compareTo(nowTruncated), 0);
 
-        List<PaymentInfoEvent> paymentInfos = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId().toString()));
+        List<PaymentInfoEvent> paymentInfos = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId()));
         assertNotNull(paymentInfos);
         assertTrue(paymentInfos.size() > 0);
 
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 cad55f0..e9c6545 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
@@ -31,6 +31,7 @@ import com.google.common.collect.Collections2;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
 public class MockPaymentDao implements PaymentDao {
@@ -48,22 +49,23 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public PaymentAttempt createPaymentAttempt(Invoice invoice, CallContext context) {
+    public PaymentAttempt createPaymentAttempt(Invoice invoice, PaymentAttemptStatus paymentAttemptStatus, CallContext context) {
         PaymentAttempt updatedPaymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice.getId(), invoice.getAccountId(),
                 invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(),
-                null, null, null, context.getCreatedDate(), context.getUpdatedDate());
+                null, null, null, paymentAttemptStatus, context.getCreatedDate(), context.getUpdatedDate());
 
         paymentAttempts.put(updatedPaymentAttempt.getPaymentAttemptId(), updatedPaymentAttempt);
         return updatedPaymentAttempt;
     }
 
     @Override
-    public PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt, CallContext context) {
+    public PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt, PaymentAttemptStatus paymentAttemptStatus, CallContext context) {
         PaymentAttempt updatedPaymentAttempt = new PaymentAttempt(paymentAttempt.getPaymentAttemptId(),
                 paymentAttempt.getInvoiceId(),
                 paymentAttempt.getAccountId(), paymentAttempt.getAmount(), paymentAttempt.getCurrency(),
                 paymentAttempt.getInvoiceDate(), paymentAttempt.getPaymentAttemptDate(),
                 paymentAttempt.getPaymentId(), paymentAttempt.getRetryCount(),
+                paymentAttemptStatus,
                 context.getCreatedDate(), context.getUpdatedDate());
 
         paymentAttempts.put(updatedPaymentAttempt.getPaymentAttemptId(), updatedPaymentAttempt);
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 902db05..8baaec0 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -36,6 +36,7 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
 public abstract class TestPaymentDao {
@@ -89,7 +90,7 @@ public abstract class TestPaymentDao {
                 .setInvoiceDate(context.getCreatedDate())
                 .build();
 
-        paymentDao.createPaymentAttempt(paymentAttempt, context);
+        paymentDao.createPaymentAttempt(paymentAttempt, PaymentAttemptStatus.IN_PROCESSING, context);
     }
 
     @Test
@@ -104,8 +105,8 @@ public abstract class TestPaymentDao {
         ClockMock clock = new ClockMock();
         CallContext thisContext = new DefaultCallContext("Payment Tests", CallOrigin.TEST, UserType.TEST, clock);
 
-        PaymentAttempt originalPaymentAttempt = new PaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, clock.getUTCNow(), clock.getUTCNow(), paymentId, 0);
-        PaymentAttempt attempt = paymentDao.createPaymentAttempt(originalPaymentAttempt, thisContext);
+        PaymentAttempt originalPaymentAttempt = new PaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, clock.getUTCNow(), clock.getUTCNow(), paymentId, 0, PaymentAttemptStatus.IN_PROCESSING);
+        PaymentAttempt attempt = paymentDao.createPaymentAttempt(originalPaymentAttempt, PaymentAttemptStatus.IN_PROCESSING, thisContext);
 
         List<PaymentAttempt> attemptsFromGet = paymentDao.getPaymentAttemptsForInvoiceId(invoiceId.toString());
 
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 492bf30..ba767c3 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -40,9 +40,13 @@ import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentMethodInfo;
 import com.ning.billing.payment.api.PaymentProviderAccount;
 import com.ning.billing.payment.api.PaypalPaymentMethodInfo;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.PaymentProviderPlugin;
 import com.ning.billing.util.clock.Clock;
 
 public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
+    
     private final AtomicBoolean makeNextInvoiceFail = new AtomicBoolean(false);
     private final Map<String, PaymentInfoEvent> payments = new ConcurrentHashMap<String, PaymentInfoEvent>();
     private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
@@ -59,66 +63,63 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentInfoEvent> processInvoice(Account account, Invoice invoice) {
+    public PaymentInfoPlugin processInvoice(Account account, Invoice invoice) throws PaymentPluginApiException {
         if (makeNextInvoiceFail.getAndSet(false)) {
-            return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unknown", "test error", account.getId(), invoice.getId(), null));
-        }
-        else {
-            PaymentInfoEvent payment = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString())
-                                                 .setAmount(invoice.getBalance())
-                                                 .setStatus("Processed")
-                                                 .setBankIdentificationNumber("1234")
-                                                 .setCreatedDate(clock.getUTCNow())
-                                                 .setEffectiveDate(clock.getUTCNow())
-                                                 .setPaymentNumber("12345")
-                                                 .setReferenceId("12345")
-                                                 .setType("Electronic")
-                                                 .build();
-            payments.put(payment.getPaymentId(), payment);
-            return Either.right(payment);
+            throw new PaymentPluginApiException("", "test error");
         }
+        PaymentInfoEvent payment = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString())
+        .setAmount(invoice.getBalance())
+        .setStatus("Processed")
+        .setBankIdentificationNumber("1234")
+        .setCreatedDate(clock.getUTCNow())
+        .setEffectiveDate(clock.getUTCNow())
+        .setPaymentNumber("12345")
+        .setReferenceId("12345")
+        .setType("Electronic")
+        .build();
+        // STEPH
+        //return payment;
+        return null;
     }
 
+
     @Override
-    public Either<PaymentErrorEvent, PaymentInfoEvent> getPaymentInfo(String paymentId) {
+    public PaymentInfoPlugin getPaymentInfo(String paymentId) throws PaymentPluginApiException {
         PaymentInfoEvent payment = payments.get(paymentId);
-
         if (payment == null) {
-            return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("notfound", "No payment found for id " + paymentId, null, null, null));
-        }
-        else {
-            return Either.right(payment);
+            throw new PaymentPluginApiException("", "No payment found for id " + paymentId);
         }
+        // STEPH
+        return null;
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> createPaymentProviderAccount(Account account) {
+    public String createPaymentProviderAccount(Account account)  throws PaymentPluginApiException {
         if (account != null) {
             String id = String.valueOf(RandomStringUtils.randomAlphanumeric(10));
             accounts.put(account.getExternalKey(),
                          new PaymentProviderAccount.Builder().setAccountKey(account.getExternalKey())
                                                              .setId(id)
                                                              .build());
-
-            return Either.right(id);
+            return id;
         }
         else {
-            return Either.left((PaymentErrorEvent)  new DefaultPaymentErrorEvent("unknown", "Did not get account to create payment provider account", null, null, null));
+            throw new PaymentPluginApiException("", "Did not get account to create payment provider account");
         }
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentProviderAccount> getPaymentProviderAccount(String accountKey) {
+    public PaymentProviderAccount getPaymentProviderAccount(String accountKey)  throws PaymentPluginApiException {
         if (accountKey != null) {
-            return Either.right(accounts.get(accountKey));
+            return accounts.get(accountKey);
         }
         else {
-            return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unknown", "Did not get account for accountKey " + accountKey, null, null, null));
+            throw new PaymentPluginApiException("", "Did not get account for accountKey " + accountKey);
         }
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod) {
+    public String addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod)  throws PaymentPluginApiException {
         if (paymentMethod != null) {
             PaymentProviderAccount account = accounts.get(accountKey);
 
@@ -133,34 +134,34 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
                     PaypalPaymentMethodInfo paypalPaymentMethod = (PaypalPaymentMethodInfo)paymentMethod;
 
                     realPaymentMethod = new PaypalPaymentMethodInfo.Builder(paypalPaymentMethod)
-                                                                   .setId(paymentMethodId)
-                                                                   .setAccountId(accountKey)
-                                                                   .setDefaultMethod(shouldBeDefault)
-                                                                   .setBaid(paypalPaymentMethod.getBaid())
-                                                                   .setEmail(paypalPaymentMethod.getEmail())
-                                                                   .build();
+                    .setId(paymentMethodId)
+                    .setAccountId(accountKey)
+                    .setDefaultMethod(shouldBeDefault)
+                    .setBaid(paypalPaymentMethod.getBaid())
+                    .setEmail(paypalPaymentMethod.getEmail())
+                    .build();
                 }
                 else if (paymentMethod instanceof CreditCardPaymentMethodInfo) {
                     CreditCardPaymentMethodInfo ccPaymentMethod = (CreditCardPaymentMethodInfo)paymentMethod;
                     realPaymentMethod = new CreditCardPaymentMethodInfo.Builder(ccPaymentMethod).setId(paymentMethodId).build();
                 }
                 if (realPaymentMethod == null) {
-                    return Either.left((PaymentErrorEvent)  new DefaultPaymentErrorEvent("unsupported", "Payment method " + paymentMethod.getType() + " not supported by the plugin", null, null, null));
+                    throw new PaymentPluginApiException("", "Payment method " + paymentMethod.getType() + " not supported by the plugin");                    
                 }
                 else {
                     if (shouldBeDefault) {
                         setDefaultPaymentMethodOnAccount(account, paymentMethodId);
                     }
                     paymentMethods.put(paymentMethodId, realPaymentMethod);
-                    return Either.right(paymentMethodId);
+                    return paymentMethodId;
                 }
             }
-                else {
-                    return Either.left((PaymentErrorEvent)  new DefaultPaymentErrorEvent("noaccount", "Could not retrieve account for accountKey " + accountKey, null, null, null));
-                }
+            else {
+                throw new PaymentPluginApiException("", "Could not retrieve account for accountKey " + accountKey);                    
+            }
         }
         else {
-            return Either.left((PaymentErrorEvent)  new DefaultPaymentErrorEvent("unknown", "Could not create add payment method " + paymentMethod + " for " + accountKey, null, null, null));
+            throw new PaymentPluginApiException("", "Could not create add payment method " + paymentMethod + " for " + accountKey);
         }
     }
 
@@ -191,7 +192,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethod) {
+    public PaymentMethodInfo updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethod)  throws PaymentPluginApiException {
         if (paymentMethod != null) {
             PaymentMethodInfo realPaymentMethod = null;
 
@@ -204,45 +205,43 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
                 realPaymentMethod = new CreditCardPaymentMethodInfo.Builder(ccPaymentMethod).build();
             }
             if (realPaymentMethod == null) {
-                return Either.left((PaymentErrorEvent)  new DefaultPaymentErrorEvent("unsupported", "Payment method " + paymentMethod.getType() + " not supported by the plugin", null, null, null));
+                throw new PaymentPluginApiException("", "Payment method " + paymentMethod.getType() + " not supported by the plugin");
             }
             else {
                 paymentMethods.put(paymentMethod.getId(), paymentMethod);
-                return Either.right(realPaymentMethod);
+                return realPaymentMethod;
             }
         }
         else {
-            return Either.left((PaymentErrorEvent)  new DefaultPaymentErrorEvent("unknown", "Could not create add payment method " + paymentMethod + " for " + accountKey, null, null, null));
+            throw new PaymentPluginApiException("", "Could not create add payment method " + paymentMethod + " for " + accountKey);            
         }
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId) {
+    public void deletePaymentMethod(String accountKey, String paymentMethodId)  throws PaymentPluginApiException {
         PaymentMethodInfo paymentMethodInfo = paymentMethods.get(paymentMethodId);
         if (paymentMethodInfo != null) {
             if (Boolean.FALSE.equals(paymentMethodInfo.getDefaultMethod()) || paymentMethodInfo.getDefaultMethod() == null) {
                 if (paymentMethods.remove(paymentMethodId) == null) {
-                    return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unknown", "Did not get any result back", null, null, null));
+                    throw new PaymentPluginApiException("", "Did not get any result back");
                 }
-            }
-            else {
-                return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("error", "Cannot delete default payment method", null, null, null));
+        } else {
+                throw new PaymentPluginApiException("", "Cannot delete default payment method");                
             }
         }
-        return Either.right(null);
+        return;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> getPaymentMethodInfo(String paymentMethodId) {
+    public PaymentMethodInfo getPaymentMethodInfo(String paymentMethodId)  throws PaymentPluginApiException {
         if (paymentMethodId == null) {
-            return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unknown", "Could not retrieve payment method for paymentMethodId " + paymentMethodId, null, null, null));
+            throw new PaymentPluginApiException("", "Could not retrieve payment method for paymentMethodId " + paymentMethodId);
         }
-
-        return Either.right(paymentMethods.get(paymentMethodId));
+        return paymentMethods.get(paymentMethodId);
     }
 
     @Override
-    public Either<PaymentErrorEvent, List<PaymentMethodInfo>> getPaymentMethods(final String accountKey) {
+    public List<PaymentMethodInfo> getPaymentMethods(final String accountKey) throws PaymentPluginApiException {
 
         Collection<PaymentMethodInfo> filteredPaymentMethods = Collections2.filter(paymentMethods.values(), new Predicate<PaymentMethodInfo>() {
             @Override
@@ -251,27 +250,23 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
             }
         });
         List<PaymentMethodInfo> result = new ArrayList<PaymentMethodInfo>(filteredPaymentMethods);
-        return Either.right(result);
+        return result;
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentGateway(String accountKey) {
-        return Either.right(null);
+    public void updatePaymentGateway(String accountKey)  throws PaymentPluginApiException {
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountExistingContact(Account account) {
-        // nothing to do here
-        return Either.right(null);
+    public void updatePaymentProviderAccountExistingContact(Account account)  throws PaymentPluginApiException {
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountWithNewContact(Account account) {
-        return Either.right(null);
+    public void updatePaymentProviderAccountWithNewContact(Account account)  throws PaymentPluginApiException {
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> processRefund(Account account) {
+    public List<PaymentInfoPlugin> processRefund(Account account)  throws PaymentPluginApiException {
         return null;
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
index 05bba03..fa778e9 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
@@ -21,6 +21,7 @@ import com.google.inject.Provider;
 import com.ning.billing.util.clock.Clock;
 
 public class MockPaymentProviderPluginProvider implements Provider<MockPaymentProviderPlugin> {
+
     private PaymentProviderPluginRegistry registry;
     private final String instanceName;
 
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index 9a49780..77d344a 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -49,9 +49,12 @@ import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.api.PaymentAttempt.PaymentAttemptStatus;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.retry.FailedPaymentRetryService;
 import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
@@ -84,7 +87,7 @@ public class TestRetryService {
     @Inject
     private PaymentDao paymentDao;
     @Inject
-    private RetryService retryService;
+    private FailedPaymentRetryService retryService;
     @Inject
     private NotificationQueueService notificationQueueService;
 
@@ -97,7 +100,7 @@ public class TestRetryService {
 
     @BeforeClass(alwaysRun = true)
     public void initialize() throws Exception {
-        retryService.initialize();
+        retryService.initialize("payment-service");
     }
 
     @BeforeMethod(alwaysRun = true)
@@ -106,7 +109,7 @@ public class TestRetryService {
         retryService.start();
 
         mockPaymentProviderPlugin = (MockPaymentProviderPlugin)registry.getPlugin(null);
-        mockNotificationQueue = (MockNotificationQueue)notificationQueueService.getNotificationQueue(RetryService.SERVICE_NAME, RetryService.QUEUE_NAME);
+        mockNotificationQueue = (MockNotificationQueue)notificationQueueService.getNotificationQueue("payment-service", FailedPaymentRetryService.QUEUE_NAME);
         context = new DefaultCallContext("RetryServiceTests", CallOrigin.INTERNAL, UserType.TEST, clock);
         ((ZombieControl)invoicePaymentApi).addResult("notifyOfPaymentAttempt", BrainDeadProxyFactory.ZOMBIE_VOID);
 
@@ -142,7 +145,7 @@ public class TestRetryService {
         mockPaymentProviderPlugin.makeNextInvoiceFail();
         boolean failed = false;
         try {
-            paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
+            paymentApi.createPayment(account.getExternalKey(), invoice.getId(), context);
         } catch (PaymentApiException e) {
             failed = true;
         }
@@ -186,12 +189,12 @@ public class TestRetryService {
 
         int numberOfDays = paymentConfig.getPaymentRetryDays().get(0);
         DateTime nextRetryDate = now.plusDays(numberOfDays);
-        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice).cloner()
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice, PaymentAttemptStatus.COMPLETED_FAILED).cloner()
                                                                                       .setRetryCount(1)
                                                                                       .setPaymentAttemptDate(now)
                                                                                       .build();
 
-        paymentDao.createPaymentAttempt(paymentAttempt, context);
+        paymentDao.createPaymentAttempt(paymentAttempt, PaymentAttemptStatus.COMPLETED_FAILED,  context);
         retryService.scheduleRetry(paymentAttempt, nextRetryDate);
         ((ClockMock)clock).setDeltaFromReality(Days.days(numberOfDays).toStandardSeconds().getSeconds() * 1000);
         Thread.sleep(2000);
@@ -199,7 +202,7 @@ public class TestRetryService {
         List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();
         assertEquals(pendingNotifications.size(), 0);
 
-        List<PaymentInfoEvent> paymentInfoList = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId().toString()));
+        List<PaymentInfoEvent> paymentInfoList = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId()));
         assertEquals(paymentInfoList.size(), 1);
 
         PaymentInfoEvent paymentInfo = paymentInfoList.get(0);
diff --git a/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java b/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java
index 5312e09..8e30d52 100644
--- a/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java
@@ -23,8 +23,8 @@ public interface GlobalLocker {
 
     public enum LockerService {
 
-        // Only service needing global lock
-        INVOICE("invoice");
+        INVOICE("invoice"),
+        PAYMENT("payment");        
 
         private final String svcName;