killbill-aplcache

Modify Payment API (remove Either)

5/10/2012 4:04:17 PM

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 15c4a94..8d107ef 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -35,6 +35,7 @@ import com.ning.billing.analytics.dao.BusinessAccountDao;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.util.tag.Tag;
@@ -59,14 +60,14 @@ public class BusinessAccountRecorder {
         Account account;
         try {
             account = accountApi.getAccountByKey(data.getExternalKey());
-        final BusinessAccount bac = createBusinessAccountFromAccount(account);
+            final BusinessAccount bac = createBusinessAccountFromAccount(account);
 
-        log.info("ACCOUNT CREATION " + bac);
-        dao.createAccount(bac);
+            log.info("ACCOUNT CREATION " + bac);
+            dao.createAccount(bac);
         } catch (AccountApiException e) {
-           log.warn("Error encountered creating BusinessAccount",e);
+            log.warn("Error encountered creating BusinessAccount",e);
         }
-   }
+    }
 
     /**
      * Notification handler for Account changes
@@ -85,17 +86,19 @@ public class BusinessAccountRecorder {
      * @param paymentInfo payment object (from the payment plugin)
      */
     public void accountUpdated(final PaymentInfoEvent paymentInfo) {
-        final PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(paymentInfo.getPaymentId());
-        if (paymentAttempt == null) {
-            return;
-        }
         try {
+            final PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(paymentInfo.getPaymentId());
+            if (paymentAttempt == null) {
+                return;
+            }
+
             final Account account = accountApi.getAccountById(paymentAttempt.getAccountId());
             accountUpdated(account.getId());
         } catch (AccountApiException e) {
             log.warn("Error encountered creating BusinessAccount",e);
+        } catch (PaymentApiException e) {
+            log.warn("Error encountered creating BusinessAccount",e);            
         }
-
     }
 
     /**
@@ -152,51 +155,56 @@ public class BusinessAccountRecorder {
     }
 
     private void updateBusinessAccountFromAccount(final Account account, final BusinessAccount bac) {
-        DateTime lastInvoiceDate = null;
-        BigDecimal totalInvoiceBalance = BigDecimal.ZERO;
-        String lastPaymentStatus = null;
-        String paymentMethod = null;
-        String creditCardType = null;
-        String billingAddressCountry = null;
-
-        // Retrieve invoices information
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId());
-        if (invoices != null && invoices.size() > 0) {
-            final List<String> invoiceIds = new ArrayList<String>();
-            for (final Invoice invoice : invoices) {
-                invoiceIds.add(invoice.getId().toString());
-                totalInvoiceBalance = totalInvoiceBalance.add(invoice.getBalance());
-
-                if (lastInvoiceDate == null || invoice.getInvoiceDate().isAfter(lastInvoiceDate)) {
-                    lastInvoiceDate = invoice.getInvoiceDate();
+
+        try {
+            DateTime lastInvoiceDate = null;
+            BigDecimal totalInvoiceBalance = BigDecimal.ZERO;
+            String lastPaymentStatus = null;
+            String paymentMethod = null;
+            String creditCardType = null;
+            String billingAddressCountry = null;
+
+            // Retrieve invoices information
+            final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId());
+            if (invoices != null && invoices.size() > 0) {
+                final List<String> invoiceIds = new ArrayList<String>();
+                for (final Invoice invoice : invoices) {
+                    invoiceIds.add(invoice.getId().toString());
+                    totalInvoiceBalance = totalInvoiceBalance.add(invoice.getBalance());
+
+                    if (lastInvoiceDate == null || invoice.getInvoiceDate().isAfter(lastInvoiceDate)) {
+                        lastInvoiceDate = invoice.getInvoiceDate();
+                    }
                 }
-            }
 
-            // Retrieve payments information for these invoices
-            DateTime lastPaymentDate = null;
-            final List<PaymentInfoEvent> payments = paymentApi.getPaymentInfo(invoiceIds);
-            if (payments != null) {
-                for (final PaymentInfoEvent payment : payments) {
-                    // Use the last payment method/type/country as the default one for the account
-                    if (lastPaymentDate == null || payment.getCreatedDate().isAfter(lastPaymentDate)) {
-                        lastPaymentDate = payment.getCreatedDate();
-
-                        lastPaymentStatus = payment.getStatus();
-                        paymentMethod = payment.getPaymentMethod();
-                        creditCardType = payment.getCardType();
-                        billingAddressCountry = payment.getCardCountry();
+                // Retrieve payments information for these invoices
+                DateTime lastPaymentDate = null;
+                final List<PaymentInfoEvent> payments = paymentApi.getPaymentInfo(invoiceIds);
+                if (payments != null) {
+                    for (final PaymentInfoEvent payment : payments) {
+                        // Use the last payment method/type/country as the default one for the account
+                        if (lastPaymentDate == null || payment.getCreatedDate().isAfter(lastPaymentDate)) {
+                            lastPaymentDate = payment.getCreatedDate();
+
+                            lastPaymentStatus = payment.getStatus();
+                            paymentMethod = payment.getPaymentMethod();
+                            creditCardType = payment.getCardType();
+                            billingAddressCountry = payment.getCardCountry();
+                        }
                     }
                 }
             }
-        }
 
-        bac.setLastPaymentStatus(lastPaymentStatus);
-        bac.setPaymentMethod(paymentMethod);
-        bac.setCreditCardType(creditCardType);
-        bac.setBillingAddressCountry(billingAddressCountry);
-        bac.setLastInvoiceDate(lastInvoiceDate);
-        bac.setTotalInvoiceBalance(totalInvoiceBalance);
+            bac.setLastPaymentStatus(lastPaymentStatus);
+            bac.setPaymentMethod(paymentMethod);
+            bac.setCreditCardType(creditCardType);
+            bac.setBillingAddressCountry(billingAddressCountry);
+            bac.setLastInvoiceDate(lastInvoiceDate);
+            bac.setTotalInvoiceBalance(totalInvoiceBalance);
 
-        bac.setBalance(invoiceUserApi.getAccountBalance(account.getId()));
+            bac.setBalance(invoiceUserApi.getAccountBalance(account.getId()));
+        } catch (PaymentApiException e) {
+            log.error("Failed to update Business account", e);
+        }
     }
 }
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index c317465..3aa190b 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -201,7 +201,25 @@ public enum ErrorCode {
     BLOCK_BLOCKED_ACTION(6000, "The action %s is block on this %s with id=%s"),
     BLOCK_TYPE_NOT_SUPPORTED(6001, "The Blockable type '%s' is not supported"),
     
-   /*
+    
+    /*
+     * Range 7000 : Payment
+     */
+    PAYMENT_NO_SUCH_PAYMENT_METHOD(7001, "Payment method for account %s, and paymentId %s does not exist"),
+    PAYMENT_NO_PAYMENT_METHODS(7002, "Payment methods for account %s don't exist"),
+    PAYMENT_UPD_GATEWAY_FAILED(7003, "Failed to update payment gateway for account %s : %s"),
+    PAYMENT_GET_PAYMENT_PROVIDER(7004, "Failed to retrieve payment provider for account %s : %s"),    
+    PAYMENT_ADD_PAYMENT_METHOD(7005, "Failed to add payment method for account %s : %s"),        
+    PAYMENT_DEL_PAYMENT_METHOD(7006, "Failed to delete payment method for account %s : %s"),        
+    PAYMENT_UPD_PAYMENT_METHOD(7007, "Failed to update payment method for account %s : %s"),            
+    PAYMENT_CREATE_PAYMENT(7008, "Failed to create payment for account %s : %s"),                
+    PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT(7009, "Failed to create payment for account %s and attempt %s : %s"),                    
+    PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_WITH_NON_POSITIVE_INV(70010, "Got payment attempt with negative or null invoice for account %s"),                        
+    PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD(7011, "Failed to create payment for attempts %s "),                    
+    PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT(7012, "Failed to create payment provider account for account %s : %s"),                
+    PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT(7013, "Failed to update payment provider account for account %s : %s"),                    
+    PAYMENT_CREATE_REFUND(7014, "Failed to create refund for account %s : %s"),                
+    /*
     *
     * Range 9000: Miscellaneous
     *
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 1679f50..c7b5965 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
@@ -26,36 +26,54 @@ import com.ning.billing.util.callcontext.CallContext;
 
 public interface PaymentApi {
 
-    Either<PaymentErrorEvent, Void> updatePaymentGateway(String accountKey, CallContext context);
+    public void updatePaymentGateway(final String accountKey, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId);
+    public PaymentMethodInfo getPaymentMethod(final String accountKey, final String paymentMethodId)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, List<PaymentMethodInfo>> getPaymentMethods(String accountKey);
+    public List<PaymentMethodInfo> getPaymentMethods(final String accountKey)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod, CallContext context);
+    public String addPaymentMethod(final String accountKey, final PaymentMethodInfo paymentMethod, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context);
+    public PaymentMethodInfo updatePaymentMethod(final String accountKey, final PaymentMethodInfo paymentMethodInfo, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context);
+    public void deletePaymentMethod(final String accountKey, final String paymentMethodId, final CallContext context)
+        throws PaymentApiException;
 
-    List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(String accountKey, List<String> invoiceIds, CallContext context);
-    List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(Account account, List<String> invoiceIds, CallContext context);
-    Either<PaymentErrorEvent, PaymentInfoEvent> createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context);
+    public List<PaymentInfoEvent> createPayment(final String accountKey, final List<String> invoiceIds, final CallContext context)
+        throws PaymentApiException;
+    
+    public List<PaymentInfoEvent> createPayment(final Account account, final List<String> invoiceIds, final CallContext context)
+        throws PaymentApiException;
+    
+    public PaymentInfoEvent createPaymentForPaymentAttempt(final UUID paymentAttemptId, final CallContext context)
+        throws PaymentApiException;
 
-    List<Either<PaymentErrorEvent, PaymentInfoEvent>> createRefund(Account account, List<String> invoiceIds, CallContext context); //TODO
+    public List<PaymentInfoEvent> createRefund(final Account account, final List<String> invoiceIds, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, PaymentProviderAccount> getPaymentProviderAccount(String accountKey);
+    public PaymentProviderAccount getPaymentProviderAccount(final String accountKey)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, String> createPaymentProviderAccount(Account account, CallContext context);
+    public String createPaymentProviderAccount(final Account account, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, Void> updatePaymentProviderAccountContact(String accountKey, CallContext context);
+    public void updatePaymentProviderAccountContact(String accountKey, CallContext context)
+        throws PaymentApiException;
 
-    PaymentAttempt getPaymentAttemptForPaymentId(String id);
+    public PaymentAttempt getPaymentAttemptForPaymentId(final String id)
+        throws PaymentApiException;
 
-    List<PaymentInfoEvent> getPaymentInfo(List<String> invoiceIds);
+    public List<PaymentInfoEvent> getPaymentInfo(final List<String> invoiceIds)
+        throws PaymentApiException;
 
-    List<PaymentAttempt> getPaymentAttemptsForInvoiceId(String invoiceId);
-
-    PaymentInfoEvent getPaymentInfoForPaymentAttemptId(String paymentAttemptId);
+    public List<PaymentAttempt> getPaymentAttemptsForInvoiceId(final String invoiceId)
+        throws PaymentApiException;
 
+    public PaymentInfoEvent getPaymentInfoForPaymentAttemptId(final String paymentAttemptId)
+        throws PaymentApiException;
 }
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
new file mode 100644
index 0000000..ff81b16
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java
@@ -0,0 +1,49 @@
+/* 
+ * 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.api;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.catalog.api.CatalogApiException;
+
+public class PaymentApiException extends BillingExceptionBase {
+    
+    private static final long serialVersionUID = 39445033L;
+
+
+    public PaymentApiException(AccountApiException e) {
+        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);
+    }
+
+    public PaymentApiException(Throwable e, int code, String message) {
+        super(e, code, message);
+    }
+
+    public PaymentApiException(ErrorCode code, Object...args) {
+        super(code, args);
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 9061eac..07502cc 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -67,6 +67,7 @@ import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.jaxrs.util.TagHelper;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -268,6 +269,9 @@ public class AccountResource implements BaseJaxrsResource {
             return Response.status(Status.OK).entity(json).build();
         } catch (AccountApiException e) {
             return Response.status(Status.NO_CONTENT).build();
+        } catch (PaymentApiException e) {
+            log.error(e.getMessage());
+            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
         } catch (EntitlementRepairException e) {
             log.error(e.getMessage());
             return Response.status(Status.INTERNAL_SERVER_ERROR).build();
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 2ed490c..2ac89d2 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,6 +18,7 @@ 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;
 
@@ -29,6 +30,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
@@ -68,9 +70,14 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId) {
+    public PaymentMethodInfo getPaymentMethod(final String accountKey, final String paymentMethodId) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.getPaymentMethodInfo(paymentMethodId);
+        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) {
@@ -100,60 +107,86 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentErrorEvent, List<PaymentMethodInfo>> getPaymentMethods(String accountKey) {
+    public List<PaymentMethodInfo> getPaymentMethods(String accountKey)
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.getPaymentMethods(accountKey);
+        Either<PaymentErrorEvent, List<PaymentMethodInfo>> result =  plugin.getPaymentMethods(accountKey);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_PAYMENT_METHODS, accountKey);
+        }
+        return result.getRight();
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentGateway(String accountKey, CallContext context) {
+    public void updatePaymentGateway(String accountKey, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.updatePaymentGateway(accountKey);
+        Either<PaymentErrorEvent, Void> result =  plugin.updatePaymentGateway(accountKey);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_UPD_GATEWAY_FAILED, accountKey, result.getLeft().getMessage());
+        }
+        return;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentProviderAccount> getPaymentProviderAccount(String accountKey) {
+    public PaymentProviderAccount getPaymentProviderAccount(String accountKey)
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.getPaymentProviderAccount(accountKey);
+        Either<PaymentErrorEvent, PaymentProviderAccount> result = plugin.getPaymentProviderAccount(accountKey);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_PROVIDER, accountKey, result.getLeft().getMessage());
+        }
+        return result.getRight();
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod, CallContext context) {
+    public String addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.addPaymentMethod(accountKey, paymentMethod);
+        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();
     }
 
+
     @Override
-    public Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context) {
+    public void deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.deletePaymentMethod(accountKey, paymentMethodId);
+        Either<PaymentErrorEvent, Void> result =  plugin.deletePaymentMethod(accountKey, paymentMethodId);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
+        }
+        return;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context) {
+    public PaymentMethodInfo updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.updatePaymentMethod(accountKey, paymentMethodInfo);
+        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();
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(String accountKey, List<String> invoiceIds, CallContext context) {
+    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) {
-            log.error("Error getting payment provider plugin.", e);
-            List<Either<PaymentErrorEvent, PaymentInfoEvent>> result = new ArrayList<Either<PaymentErrorEvent, PaymentInfoEvent>>();
-            result.add(new Either.Left<PaymentErrorEvent, PaymentInfoEvent>((PaymentErrorEvent) new DefaultPaymentErrorEvent("createPaymentError", e.getMessage(),
-                    null,
-                    null,
-                    context.getUserToken())));
-            return result;
+            throw new PaymentApiException(e);
         }
-
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentInfoEvent> createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context) {
+    public PaymentInfoEvent createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context) 
+    throws PaymentApiException {
 
         PaymentAttempt paymentAttempt = paymentDao.getPaymentAttemptById(paymentAttemptId);
         if (paymentAttempt != null) {
@@ -165,40 +198,35 @@ public class DefaultPaymentApi implements PaymentApi {
                     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);
-                        return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("invoice_balance_0",
-                                "Invoice balance was 0 or less",
-                                paymentAttempt.getAccountId(),
-                                paymentAttempt.getInvoiceId(),
-                                context.getUserToken()));
-                    }
-                    else {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_WITH_NON_POSITIVE_INV, account.getId());
+
+                    } else {
+                        
                         PaymentAttempt newPaymentAttempt = new PaymentAttempt.Builder(paymentAttempt)
                         .setRetryCount(paymentAttempt.getRetryCount() + 1)
                         .setPaymentAttemptId(UUID.randomUUID())
                         .build();
 
                         paymentDao.createPaymentAttempt(newPaymentAttempt, context);
-                        return processPayment(getPaymentProviderPlugin(account), account, invoice, 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();
+
                     }
                 }
             } catch (AccountApiException e) {
-                log.error("Error creating payment attempt.", e);
-                return new Either.Left<PaymentErrorEvent, PaymentInfoEvent>((PaymentErrorEvent) new DefaultPaymentErrorEvent("createPaymentError", e.getMessage(),
-                        null,
-                        null,
-                        context.getUserToken()));
-
+                throw new PaymentApiException(e);
             }
         }
-        return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("retry_payment_error",
-                "Could not load payment attempt, invoice or account for id " + paymentAttemptId,
-                paymentAttempt.getAccountId(),
-                paymentAttempt.getInvoiceId(),
-                context.getUserToken()));
+        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD, paymentAttemptId);
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(Account account, List<String> invoiceIds, CallContext context) {
+    public List<PaymentInfoEvent> createPayment(Account account, List<String> invoiceIds, CallContext context) 
+        throws PaymentApiException {
+        
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
 
         List<Either<PaymentErrorEvent, PaymentInfoEvent>> processedPaymentsOrErrors = new ArrayList<Either<PaymentErrorEvent, PaymentInfoEvent>>(invoiceIds.size());
@@ -225,7 +253,15 @@ public class DefaultPaymentApi implements PaymentApi {
             }
         }
 
-        return processedPaymentsOrErrors;
+        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());
+            }
+            info.add(cur.getRight());
+        }
+        return info;
     }
 
     private Either<PaymentErrorEvent, PaymentInfoEvent> processPayment(PaymentProviderPlugin plugin, Account account, Invoice invoice,
@@ -271,10 +307,10 @@ public class DefaultPaymentApi implements PaymentApi {
                 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);
+                        paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency(),
+                                paymentAttempt.getPaymentAttemptId(),
+                                paymentAttempt.getPaymentAttemptDate(),
+                                context);
 
         return paymentOrError;
     }
@@ -311,24 +347,29 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> createPaymentProviderAccount(Account account, CallContext context) {
+    public String createPaymentProviderAccount(Account account, CallContext context) 
+        throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
-        return plugin.createPaymentProviderAccount(account);
+        Either<PaymentErrorEvent, String> result =  plugin.createPaymentProviderAccount(account);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT, account.getId(), result.getLeft().getMessage());
+        }
+        return result.getRight();
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountContact(String externalKey, CallContext context) {
+    public void updatePaymentProviderAccountContact(String externalKey, CallContext context) 
+        throws PaymentApiException {
         try {
             Account account = accountUserApi.getAccountByKey(externalKey);
             final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-            return plugin.updatePaymentProviderAccountExistingContact(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;
         } catch (AccountApiException e) {
-            log.error("Error updating payment provider account contact.", e);
-            return new Either.Left<PaymentErrorEvent, Void>((PaymentErrorEvent) new DefaultPaymentErrorEvent("updatePaymentProviderAccountContactError", e.getMessage(),
-                    null,
-                    null,
-                    context.getUserToken()));
-
+            throw new PaymentApiException(e);
         }
     }
 
@@ -338,11 +379,19 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createRefund(Account account,
-            List<String> invoiceIds,
-            CallContext context) {
+    public List<PaymentInfoEvent> createRefund(Account account, List<String> invoiceIds, CallContext context)
+        throws PaymentApiException {
+            
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-        return plugin.processRefund(account);
+        List<Either<PaymentErrorEvent, PaymentInfoEvent>> 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());
+        }
+        return info;
     }
 
     @Override
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 3102e5c..2077c5e 100644
--- a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -18,23 +18,29 @@ package com.ning.billing.payment;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
 
 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;
 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;
@@ -52,39 +58,51 @@ public class RequestProcessor {
 
     @Inject
     public RequestProcessor(Clock clock,
-                            AccountUserApi accountUserApi,
-                            PaymentApi paymentApi,
-                            PaymentProviderPluginRegistry pluginRegistry,
-                            Bus eventBus) {
+            AccountUserApi accountUserApi,
+            PaymentApi paymentApi,
+            PaymentProviderPluginRegistry pluginRegistry,
+            Bus eventBus) {
         this.clock = clock;
         this.accountUserApi = accountUserApi;
         this.paymentApi = paymentApi;
         this.eventBus = eventBus;
     }
+    
+    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) {
+
+
         log.info("Received invoice creation notification for account {} and invoice {}", event.getAccountId(), event.getInvoiceId());
+        PaymentErrorEvent errorEvent = null;
         try {
             final Account account = accountUserApi.getAccountById(event.getAccountId());
-
-            if (account == null) {
-                log.info("could not process invoice payment: could not find a valid account for event {}", event);
-            }
-            else {
+            if (account != null) {
                 CallContext context = new DefaultCallContext("PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
-                List<Either<PaymentErrorEvent, PaymentInfoEvent>> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()), context);
-                if (!results.isEmpty()) {
-                    Either<PaymentErrorEvent, PaymentInfoEvent> result = results.get(0);
-                    try {
-                        eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
-                    } catch (EventBusException e) {
-                        log.error("Failed to post Payment event event for account {} ", account.getId(), e);
-                    }
-                }
+                List<PaymentInfoEvent> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()), context);
+                PaymentInfoEvent infoEvent = (!results.isEmpty()) ?  results.get(0) : null;
+                postPaymentEvent(infoEvent, account.getId());
+                return;
+            } else {
+                errorEvent = new DefaultPaymentErrorEvent(null, "Failed to retrieve account", event.getAccountId(), null, null);
             }
         } catch(AccountApiException e) {
-            log.warn("could not process invoice payment", e);
+            log.error("Failed to process invoice payment", e);
+            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), null, null);            
+        } catch (PaymentApiException e) {
+            log.error("Failed to process invoice payment", e);
+            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), null, null);                        
         }
+        postPaymentEvent(errorEvent, event.getAccountId());
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/RetryService.java b/payment/src/main/java/com/ning/billing/payment/RetryService.java
index 447bb74..abc1fde 100644
--- a/payment/src/main/java/com/ning/billing/payment/RetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/RetryService.java
@@ -24,6 +24,8 @@ import com.ning.billing.util.callcontext.DefaultCallContext;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.config.PaymentConfig;
@@ -31,6 +33,7 @@ import com.ning.billing.lifecycle.KillbillService;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentStatus;
@@ -42,6 +45,9 @@ import com.ning.billing.util.notificationq.NotificationQueueService.Notification
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 
 public class RetryService implements KillbillService {
+    
+    private static final Logger log = LoggerFactory.getLogger(RetryService.class);
+    
     public static final String SERVICE_NAME = "retry-service";
     public static final String QUEUE_NAME = "retry-events";
 
@@ -107,15 +113,14 @@ public class RetryService implements KillbillService {
     }
 
     private void retry(String paymentAttemptId, CallContext context) {
-        PaymentInfoEvent paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
-
-        if (paymentInfo != null && PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
-            // update payment attempt with success and notify invoice api of payment
-            System.out.println("Found processed payment");
-        }
-        else {
-            System.out.println("Creating payment for payment attempt " + paymentAttemptId);
+        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/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index 4d3c630..85aa07b 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,12 +102,11 @@ public abstract class TestPaymentApi {
                                                        new BigDecimal("1.0"),
                                                        Currency.USD));
 
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
+        List<PaymentInfoEvent> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
 
         assertEquals(results.size(), 1);
-        assertTrue(results.get(0).isRight());
 
-        PaymentInfoEvent paymentInfo = results.get(0).getRight();
+        PaymentInfoEvent paymentInfo = results.get(0);
 
         assertNotNull(paymentInfo.getPaymentId());
         assertTrue(paymentInfo.getAmount().compareTo(amount.setScale(2, RoundingMode.HALF_EVEN)) == 0);
@@ -145,7 +144,7 @@ public abstract class TestPaymentApi {
 
     }
 
-    private PaymentProviderAccount setupAccountWithPaypalPaymentMethod() throws AccountApiException, EntityPersistenceException {
+    private PaymentProviderAccount setupAccountWithPaypalPaymentMethod() throws Exception  {
         final Account account = testHelper.createTestPayPalAccount();
         paymentApi.createPaymentProviderAccount(account, context);
 
@@ -156,32 +155,23 @@ public abstract class TestPaymentApi {
                                                                            .setEmail(account.getEmail())
                                                                            .setDefaultMethod(true)
                                                                            .build();
-        Either<PaymentErrorEvent, String> paymentMethodIdOrError = paymentApi.addPaymentMethod(accountKey, paymentMethod, context);
+        String paymentMethodId = paymentApi.addPaymentMethod(accountKey, paymentMethod, context);
 
-        assertTrue(paymentMethodIdOrError.isRight());
-        assertNotNull(paymentMethodIdOrError.getRight());
+        PaymentMethodInfo paymentMethodInfo = paymentApi.getPaymentMethod(accountKey, paymentMethodId);
 
-        Either<PaymentErrorEvent, PaymentMethodInfo> paymentMethodInfoOrError = paymentApi.getPaymentMethod(accountKey, paymentMethodIdOrError.getRight());
-
-        assertTrue(paymentMethodInfoOrError.isRight());
-        assertNotNull(paymentMethodInfoOrError.getRight());
-
-        Either<PaymentErrorEvent, PaymentProviderAccount> accountOrError = paymentApi.getPaymentProviderAccount(accountKey);
-
-        assertTrue(accountOrError.isRight());
-
-        return accountOrError.getRight();
+        PaymentProviderAccount accountResult = paymentApi.getPaymentProviderAccount(accountKey);
+        return accountResult;
     }
 
     @Test(enabled=true)
-    public void testCreatePaypalPaymentMethod() throws AccountApiException, EntityPersistenceException {
+    public void testCreatePaypalPaymentMethod() throws Exception  {
         PaymentProviderAccount account = setupAccountWithPaypalPaymentMethod();
         assertNotNull(account);
         paymentApi.getPaymentMethods(account.getAccountKey());
     }
 
     @Test(enabled=true)
-    public void testUpdatePaymentProviderAccountContact() throws AccountApiException, EntityPersistenceException {
+    public void testUpdatePaymentProviderAccountContact() throws Exception {
         final Account account = testHelper.createTestPayPalAccount();
         paymentApi.createPaymentProviderAccount(account, context);
 
@@ -198,19 +188,12 @@ public abstract class TestPaymentApi {
                                                                   .billingCycleDay(account.getBillCycleDay())
                                                                   .build();
 
-        Either<PaymentErrorEvent, Void> voidOrError = paymentApi.updatePaymentProviderAccountContact(accountToUpdate.getExternalKey(), context);
-        assertTrue(voidOrError.isRight());
+        paymentApi.updatePaymentProviderAccountContact(accountToUpdate.getExternalKey(), context);
     }
 
     @Test(enabled=true)
-    public void testCannotDeleteDefaultPaymentMethod() throws AccountApiException, EntityPersistenceException {
+    public void testCannotDeleteDefaultPaymentMethod() throws Exception  {
         PaymentProviderAccount account = setupAccountWithPaypalPaymentMethod();
-
-        Either<PaymentErrorEvent, Void> errorOrVoid = paymentApi.deletePaymentMethod(account.getAccountKey(), account.getDefaultPaymentMethodId(), context);
-
-        assertTrue(errorOrVoid.isLeft());
+        paymentApi.deletePaymentMethod(account.getAccountKey(), account.getDefaultPaymentMethodId(), context);
     }
-
-   
-
 }
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 5ab2157..9a49780 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -44,6 +44,7 @@ import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.mock.glue.MockJunctionModule;
 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.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
@@ -139,11 +140,13 @@ public class TestRetryService {
                                                        Currency.USD));
 
         mockPaymentProviderPlugin.makeNextInvoiceFail();
-
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
-
-        assertEquals(results.size(), 1);
-        assertTrue(results.get(0).isLeft());
+        boolean failed = false;
+        try {
+            paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
+        } catch (PaymentApiException e) {
+            failed = true;
+        }
+        assertTrue(failed);
 
         List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();