killbill-uncached

Implement JAXRS chargeback APIs Fix profile/killbill jaxrs

7/2/2014 10:53:42 PM

Changes

jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ChargebackJson.java 173(+0 -173)

jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ChargebackResource.java 99(+0 -99)

jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestChargebackJson.java 59(+0 -59)

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index e232594..cf3a7ad 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -47,6 +47,7 @@ import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -790,8 +791,9 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
         // Now, create a chargeback for the second (first non-zero dollar) invoice
-        final InvoicePayment payment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
-        createChargeBackAndCheckForCompletion(payment, NextEvent.BLOCK, NextEvent.INVOICE_ADJUSTMENT);
+        final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
+        final DirectPayment payment = paymentApi.getPayment(invoicePayment.getPaymentId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        createChargeBackAndCheckForCompletion(account, payment, NextEvent.BLOCK, NextEvent.INVOICE_ADJUSTMENT);
         // We should now be in OD1
         checkODState("OD1");
         checkChangePlanWithOverdueState(baseEntitlement, true, true);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 51a6d69..ceb3cb0 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -517,18 +517,17 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         }, events);
     }
 
-    protected void createChargeBackAndCheckForCompletion(final InvoicePayment payment, final NextEvent... events) {
+    protected void createChargeBackAndCheckForCompletion(final Account account, final DirectPayment payment, final NextEvent... events) {
         doCallAndCheckForCompletion(new Function<Void, Void>() {
             @Override
             public Void apply(@Nullable final Void input) {
-                /*
                 try {
-                    // STEPH to be fixed with chargeback code
-                    //invoicePaymentApi.createChargeback(payment.getId(), payment.getAmount(), callContext);
-                } catch (final InvoiceApiException e) {
+                    paymentApi.createChargebackWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                                                                  PAYMENT_OPTIONS, callContext);
+                } catch (PaymentApiException e) {
                     fail(e.toString());
+                    return null;
                 }
-                */
                 return null;
             }
         }, events);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 9de0719..bb0b051 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -64,7 +64,6 @@ import org.killbill.billing.jaxrs.json.AccountEmailJson;
 import org.killbill.billing.jaxrs.json.AccountJson;
 import org.killbill.billing.jaxrs.json.AccountTimelineJson;
 import org.killbill.billing.jaxrs.json.BundleJson;
-import org.killbill.billing.jaxrs.json.ChargebackJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.DirectPaymentJson;
 import org.killbill.billing.jaxrs.json.DirectTransactionJson;
@@ -330,20 +329,6 @@ public class AccountResource extends JaxRsResourceBase {
         // Get the payments
         final List<DirectPayment> payments = paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), tenantContext);
 
-        // Get the refunds
-        final Iterable<DirectPaymentTransaction> refunds = getDirectPaymentTransactions(payments, TransactionType.REFUND);
-        final Multimap<UUID, DirectPaymentTransaction> refundsByPayment = ArrayListMultimap.<UUID, DirectPaymentTransaction>create();
-        for (final DirectPaymentTransaction refund : refunds) {
-            refundsByPayment.put(refund.getDirectPaymentId(), refund);
-        }
-
-        // Get the chargebacks
-        final Iterable<DirectPaymentTransaction> chargebacks = getDirectPaymentTransactions(payments, TransactionType.CHARGEBACK);
-        final Multimap<UUID, DirectPaymentTransaction> chargebacksByPayment = ArrayListMultimap.<UUID, DirectPaymentTransaction>create();
-        for (final DirectPaymentTransaction chargeback : chargebacks) {
-            chargebacksByPayment.put(chargeback.getDirectPaymentId(), chargeback);
-        }
-
         // Get the bundles
         final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), tenantContext);
 
@@ -457,10 +442,11 @@ public class AccountResource extends JaxRsResourceBase {
     public Response getInvoicePayments(@PathParam("accountId") final String accountIdStr,
                                        @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                        @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
-        final TenantContext tenantContext = context.createContext(request);
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
         final UUID accountId = UUID.fromString(accountIdStr);
-        final List<DirectPayment> payments = paymentApi.getAccountPayments(accountId, withPluginInfo, ImmutableList.<PluginProperty>of(), tenantContext);
+        final TenantContext tenantContext = context.createContext(request);
+        final Account account = accountUserApi.getAccountById(accountId, tenantContext);
+        final List<DirectPayment> payments = paymentApi.getAccountPayments(account.getId(), withPluginInfo, ImmutableList.<PluginProperty>of(), tenantContext);
         final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePaymentsByAccount(accountId, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
         final List<InvoicePaymentJson> result = new ArrayList<InvoicePaymentJson>(payments.size());
@@ -545,7 +531,7 @@ public class AccountResource extends JaxRsResourceBase {
             return Response.status(Status.BAD_REQUEST).build();
         }
 
-        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, data.getPluginName(), data.getExternalKey(),isDefault, data.getPluginDetail(), pluginProperties, callContext);
+        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, data.getExternalKey(), data.getPluginName(), isDefault, data.getPluginDetail(), pluginProperties, callContext);
         if (payAllUnpaidInvoices && unpaidInvoices.size() > 0) {
             for (final Invoice invoice : unpaidInvoices) {
                 createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), false, callContext);
@@ -674,27 +660,6 @@ public class AccountResource extends JaxRsResourceBase {
     }
 
     /*
-     * ************************** CHARGEBACKS ********************************
-     */
-    @GET
-    @Path("/{accountId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
-    @Produces(APPLICATION_JSON)
-    public Response getChargebacksForAccount(@PathParam("accountId") final String accountIdStr,
-                                             @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
-
-        final UUID accountId = UUID.fromString(accountIdStr);
-        final TenantContext tenantContext = context.createContext(request);
-
-        final List<DirectPayment> payments = paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), tenantContext);
-        final Iterable<DirectPaymentTransaction> transactions = getDirectPaymentTransactions(payments, TransactionType.CHARGEBACK);
-        final List<ChargebackJson> chargebacksJson = new ArrayList<ChargebackJson>();
-        for (final DirectPaymentTransaction chargeback : transactions) {
-            chargebacksJson.add(new ChargebackJson(accountId, chargeback));
-        }
-        return Response.status(Response.Status.OK).entity(chargebacksJson).build();
-    }
-
-    /*
      * ************************** OVERDUE ********************************
      */
     @GET
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/DirectPaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/DirectPaymentResource.java
index cbae67b..a538348 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/DirectPaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/DirectPaymentResource.java
@@ -63,6 +63,7 @@ import org.killbill.clock.Clock;
 
 import com.google.common.base.Function;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -83,9 +84,9 @@ public class DirectPaymentResource extends JaxRsResourceBase {
     }
 
     @GET
-    @Path("/{directPaymentId:" + UUID_PATTERN + "}/")
+    @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
-    public Response getDirectPayment(@PathParam("directPaymentId") final String directPaymentIdStr,
+    public Response getDirectPayment(@PathParam("paymentId") final String directPaymentIdStr,
                                      @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
                                      @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                      @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@@ -184,11 +185,11 @@ public class DirectPaymentResource extends JaxRsResourceBase {
     }
 
     @POST
-    @Path("/{directPaymentId:" + UUID_PATTERN + "}/")
+    @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response captureAuthorization(final DirectTransactionJson json,
-                                         @PathParam("directPaymentId") final String directPaymentIdStr,
+                                         @PathParam("paymentId") final String directPaymentIdStr,
                                          @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                          @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                          @HeaderParam(HDR_REASON) final String reason,
@@ -209,11 +210,11 @@ public class DirectPaymentResource extends JaxRsResourceBase {
     }
 
     @POST
-    @Path("/{directPaymentId:" + UUID_PATTERN + "}/" + REFUNDS)
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + REFUNDS)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response refundPayment(final DirectTransactionJson json,
-                                  @PathParam("directPaymentId") final String directPaymentIdStr,
+                                  @PathParam("paymentId") final String directPaymentIdStr,
                                   @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                   @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                   @HeaderParam(HDR_REASON) final String reason,
@@ -234,11 +235,11 @@ public class DirectPaymentResource extends JaxRsResourceBase {
     }
 
     @DELETE
-    @Path("/{directPaymentId:" + UUID_PATTERN + "}/")
+    @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response voidPayment(final DirectTransactionJson json,
-                                @PathParam("directPaymentId") final String directPaymentIdStr,
+                                @PathParam("paymentId") final String directPaymentIdStr,
                                 @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                 @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                 @HeaderParam(HDR_REASON) final String reason,
@@ -256,6 +257,30 @@ public class DirectPaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, DirectPaymentResource.class, "getDirectPayment", payment.getId());
     }
 
+    @POST
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response chargebackPayment(final DirectTransactionJson json,
+                                  @PathParam("paymentId") final String directPaymentIdStr,
+                                  @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                  @HeaderParam(HDR_REASON) final String reason,
+                                  @HeaderParam(HDR_COMMENT) final String comment,
+                                  @javax.ws.rs.core.Context final UriInfo uriInfo,
+                                  @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID directPaymentId = UUID.fromString(directPaymentIdStr);
+        final DirectPayment initialPayment = paymentApi.getPayment(directPaymentId, false, ImmutableList.<PluginProperty>of(), callContext);
+
+        final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
+        final Currency currency = json.getCurrency() == null ? account.getCurrency() : Currency.valueOf(json.getCurrency());
+
+        final DirectPayment payment = paymentApi.createChargeback(account, directPaymentId, json.getAmount(), currency,
+                                                                  json.getTransactionExternalKey(), callContext);
+        return uriBuilder.buildResponse(uriInfo, DirectPaymentResource.class, "getDirectPayment", payment.getId());
+    }
+
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.PAYMENT;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index e256dbc..95cfe9c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -141,7 +141,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
 
         final Iterable<PluginProperty> pluginProperties;
-        final String transactionExternalKey = UUID.randomUUID().toString();
+        final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUID.randomUUID().toString();
         if (json.isAdjusted() != null && json.isAdjusted()) {
             if (json.getAdjustments() != null && json.getAdjustments().size() > 0) {
                 final Map<UUID, BigDecimal> adjustments = new HashMap<UUID, BigDecimal>();
@@ -164,6 +164,29 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(InvoicePaymentResource.class, "getInvoicePayment", result.getId(), uriInfo.getBaseUri().toString());
     }
 
+    @POST
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createChargeback(final InvoicePaymentTransactionJson json,
+                                     @PathParam("paymentId") final String paymentId,
+                                     @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                     @HeaderParam(HDR_REASON) final String reason,
+                                     @HeaderParam(HDR_COMMENT) final String comment,
+                                     @javax.ws.rs.core.Context final UriInfo uriInfo,
+                                     @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID paymentUuid = UUID.fromString(paymentId);
+        final DirectPayment payment = paymentApi.getPayment(paymentUuid, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
+        final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUID.randomUUID().toString();
+
+        final DirectPayment result = paymentApi.createChargebackWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(),
+                                                                                   transactionExternalKey, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+        return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId());
+    }
+
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentApi.java
index f6eb9c5..a79efe9 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentApi.java
@@ -208,37 +208,37 @@ public class DefaultDirectPaymentApi implements DirectPaymentApi {
     }
 
     @Override
-    public void notifyPendingTransactionOfStateChangedWithPaymentControl(Account account, UUID directPaymentTransactionId, boolean isSuccess, PaymentOptions paymentOptions, CallContext context) throws PaymentApiException {
+    public void notifyPendingTransactionOfStateChangedWithPaymentControl(final Account account, final UUID directPaymentTransactionId, final boolean isSuccess, final PaymentOptions paymentOptions, final CallContext context) throws PaymentApiException {
 
     }
 
 
     @Override
-    public DirectPayment notifyChargeback(final Account account, final UUID directPaymentTransactionId, @Nullable final String chargebackTransactionExternalKey, final BigDecimal amount, final Currency currency,
-                                          final CallContext callContext) throws PaymentApiException {
-
+    public DirectPayment createChargeback(final Account account, final UUID directPaymentId, final BigDecimal amount, final Currency currency, final String directPaymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(amount, "amount");
         checkNotNullParameter(currency, "currency");
-        checkNotNullParameter(directPaymentTransactionId, "paymentTransactionId");
+        checkNotNullParameter(directPaymentId, "paymentId");
         checkPositiveAmount(amount);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return directPaymentProcessor.notifyChargeback(IS_API_PAYMENT, account, null, directPaymentTransactionId, chargebackTransactionExternalKey, amount, currency, true,
+        return directPaymentProcessor.createChargeback(IS_API_PAYMENT, account, directPaymentId, directPaymentTransactionExternalKey, amount, currency, true,
                                                        callContext, internalCallContext);
+
     }
 
+
     @Override
-    public DirectPayment notifyChargebackWithPaymentControl(Account account, UUID directPaymentTransactionId,@Nullable String chargebackTransactionExternalKey, BigDecimal amount, Currency currency, final PaymentOptions paymentOptions, CallContext callContext) throws PaymentApiException {
+    public DirectPayment createChargebackWithPaymentControl(final Account account, final UUID directPaymentId, final BigDecimal amount, final Currency currency, final String directPaymentTransactionExternalKey, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(amount, "amount");
         checkNotNullParameter(currency, "currency");
-        checkNotNullParameter(directPaymentTransactionId, "paymentTransactionId");
+        checkNotNullParameter(directPaymentId, "paymentId");
         checkPositiveAmount(amount);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlledPaymentProcessor.notifyPaymentPaymentOfChargeback(account, directPaymentTransactionId, chargebackTransactionExternalKey, amount, currency,
-                                                                                 paymentOptions.getPaymentControlPluginName(), callContext, internalCallContext);
+        return pluginControlledPaymentProcessor.createChargeback(account, directPaymentId, directPaymentTransactionExternalKey, amount, currency,
+                                                                 paymentOptions.getPaymentControlPluginName(), callContext, internalCallContext);
     }
 
     @Override
@@ -308,7 +308,7 @@ public class DefaultDirectPaymentApi implements DirectPaymentApi {
     }
 
     @Override
-    public PaymentMethod getPaymentMethodByExternalKey(String paymentMethodExternalKey, boolean includedInactive, boolean withPluginInfo, Iterable<PluginProperty> properties, TenantContext context)
+    public PaymentMethod getPaymentMethodByExternalKey(final String paymentMethodExternalKey, final boolean includedInactive, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context)
             throws PaymentApiException {
         return paymentMethodProcessor.getPaymentMethodByExternalKey(paymentMethodExternalKey, includedInactive, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
index fb48cd7..cc4fa4d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
@@ -144,13 +144,9 @@ public class DirectPaymentProcessor extends ProcessorBase {
         return performOperation(isApiPayment, TransactionType.CREDIT, account, paymentMethodId, directPaymentId, amount, currency, directPaymentExternalKey, directPaymentTransactionExternalKey, shouldLockAccountAndDispatch, properties, callContext, internalCallContext);
     }
 
-    public DirectPayment notifyChargeback(final boolean isApiPayment, final Account account, @Nullable final UUID paymentId, @Nullable final UUID transactionId, final String directPaymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
+    public DirectPayment createChargeback(final boolean isApiPayment, final Account account, final UUID paymentId, final String directPaymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
                                           final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-
-        // We need to have something...
-        Preconditions.checkState(paymentId != null || transactionId != null);
-        final UUID nonNullPaymentId = retrieveNonNullPaymentIdFromArguments(paymentId, transactionId, internalCallContext);
-        return performOperation(isApiPayment, TransactionType.CHARGEBACK, account, null, nonNullPaymentId, amount, currency, null, directPaymentTransactionExternalKey, shouldLockAccountAndDispatch, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
+        return performOperation(isApiPayment, TransactionType.CHARGEBACK, account, null, paymentId, amount, currency, null, directPaymentTransactionExternalKey, shouldLockAccountAndDispatch, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
     }
 
     public List<DirectPayment> getAccountPayments(final UUID accountId, final InternalTenantContext tenantContext) throws PaymentApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
index 230dd4a..7c58873 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
@@ -182,15 +182,13 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
                                                                 callContext, internalCallContext);
     }
 
-    public DirectPayment notifyPaymentPaymentOfChargeback(final Account account, final UUID transactionId, final String transactionExternalKey, final BigDecimal amount, final Currency currency,
-                                                          final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        // We need to have something...
-        final UUID nonNullPaymentId = retrieveNonNullPaymentIdFromArguments(null, transactionId, internalCallContext);
+    public DirectPayment createChargeback(final Account account, final UUID paymentId, final String transactionExternalKey, final BigDecimal amount, final Currency currency,
+                                          final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledDirectPaymentAutomatonRunner.run(true,
                                                                 TransactionType.CHARGEBACK,
                                                                 account,
                                                                 null,
-                                                                nonNullPaymentId,
+                                                                paymentId,
                                                                 null,
                                                                 transactionExternalKey,
                                                                 amount,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index f0689da..47a8ba0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -171,20 +171,6 @@ public abstract class ProcessorBase {
         }
     }
 
-    protected UUID retrieveNonNullPaymentIdFromArguments(@Nullable final UUID paymentId, @Nullable final UUID transactionId, final InternalCallContext internalCallContext) throws PaymentApiException {
-        final UUID nonNullPaymentId;
-        if (paymentId == null) {
-            final PaymentTransactionModelDao transactionModelDao = paymentDao.getDirectPaymentTransaction(transactionId, internalCallContext);
-            if (transactionModelDao == null) {
-                throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, transactionId, "need to specify a valid transactionId");
-            }
-            nonNullPaymentId = transactionModelDao.getPaymentId();
-        } else {
-            nonNullPaymentId = paymentId;
-        }
-        return nonNullPaymentId;
-    }
-
     protected TenantContext buildTenantContext(final InternalTenantContext context) {
         return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT));
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java
index c3d0235..5955c18 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/ChargebackOperation.java
@@ -17,9 +17,14 @@
 
 package org.killbill.billing.payment.core.sm;
 
+import java.math.BigDecimal;
+
+import javax.annotation.Nullable;
+
 import org.killbill.automaton.OperationResult;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
@@ -29,6 +34,9 @@ import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 public class ChargebackOperation extends DirectPaymentOperation {
 
     private final Logger logger = LoggerFactory.getLogger(ChargebackOperation.class);
@@ -42,14 +50,36 @@ public class ChargebackOperation extends DirectPaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting CHARGEBACK for payment {} ({} {})", directPaymentStateContext.getDirectPaymentId(), directPaymentStateContext.getAmount(), directPaymentStateContext.getCurrency());
-        return new DefaultNoOpPaymentInfoPlugin( directPaymentStateContext.getDirectPaymentId(),
+
+        final PaymentPluginStatus status;
+        if (!directPaymentStateContext.getOnLeavingStateExistingTransactions().isEmpty()) {
+            final Iterable<PaymentTransactionModelDao> purchaseTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.PURCHASE);
+            final Iterable<PaymentTransactionModelDao> captureTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.CAPTURE);
+            final Iterable<PaymentTransactionModelDao> refundTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.REFUND);
+            final Iterable<PaymentTransactionModelDao> chargebackTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.CHARGEBACK);
+
+            final BigDecimal purchasedAmount = getSumAmount(purchaseTransactions);
+            final BigDecimal capturedAmount = getSumAmount(captureTransactions);
+            final BigDecimal refundedAmount = getSumAmount(refundTransactions);
+            final BigDecimal chargebackAmount = getSumAmount(chargebackTransactions);
+            final BigDecimal chargebackAvailableAmount = purchasedAmount.add(capturedAmount).subtract(refundedAmount.add(chargebackAmount));
+
+            if (directPaymentStateContext.getAmount().compareTo(chargebackAvailableAmount) > 0) {
+                status = PaymentPluginStatus.ERROR;
+            } else {
+                status = PaymentPluginStatus.PROCESSED;
+            }
+        } else {
+            status = PaymentPluginStatus.PROCESSED;
+        }
+        return new DefaultNoOpPaymentInfoPlugin(directPaymentStateContext.getDirectPaymentId(),
                                                  directPaymentStateContext.getTransactionPaymentId(),
                                                  TransactionType.CHARGEBACK,
                                                  directPaymentStateContext.getAmount(),
                                                  directPaymentStateContext.getCurrency(),
                                                  null,
                                                  null,
-                                                 PaymentPluginStatus.PROCESSED,
+                                                 status,
                                                  null);
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentAutomatonDAOHelper.java
index c5f9e04..dc6e161 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentAutomatonDAOHelper.java
@@ -37,6 +37,8 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 
+import com.google.common.collect.ImmutableList;
+
 public class DirectPaymentAutomatonDAOHelper {
 
     protected final DirectPaymentStateContext directPaymentStateContext;
@@ -61,16 +63,18 @@ public class DirectPaymentAutomatonDAOHelper {
 
     public void createNewDirectPaymentTransaction() throws PaymentApiException {
 
-
         final PaymentTransactionModelDao paymentTransactionModelDao;
+        final List<PaymentTransactionModelDao> existingTransactions;
         if (directPaymentStateContext.getDirectPaymentId() == null) {
             final PaymentModelDao newPaymentModelDao = buildNewDirectPaymentModelDao();
             final PaymentTransactionModelDao newPaymentTransactionModelDao = buildNewDirectPaymentTransactionModelDao(newPaymentModelDao.getId());
 
+            existingTransactions = ImmutableList.of();
             final PaymentModelDao paymentModelDao = paymentDao.insertDirectPaymentWithFirstTransaction(newPaymentModelDao, newPaymentTransactionModelDao, internalCallContext);
             paymentTransactionModelDao = paymentDao.getDirectTransactionsForDirectPayment(paymentModelDao.getId(), internalCallContext).get(0);
+
         } else {
-            final List<PaymentTransactionModelDao> existingTransactions = paymentDao.getDirectTransactionsForDirectPayment(directPaymentStateContext.getDirectPaymentId(), internalCallContext);
+            existingTransactions = paymentDao.getDirectTransactionsForDirectPayment(directPaymentStateContext.getDirectPaymentId(), internalCallContext);
             if (existingTransactions.isEmpty()) {
                 throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, directPaymentStateContext.getDirectPaymentId());
             }
@@ -83,7 +87,7 @@ public class DirectPaymentAutomatonDAOHelper {
         }
         // Update the context
         directPaymentStateContext.setDirectPaymentTransactionModelDao(paymentTransactionModelDao);
-
+        directPaymentStateContext.setOnLeavingStateExistingTransactions(existingTransactions);
     }
 
     public void processPaymentInfoPlugin(final TransactionStatus paymentStatus, @Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentOperation.java
index c519331..cf6e6c5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentOperation.java
@@ -17,6 +17,8 @@
 
 package org.killbill.billing.payment.core.sm;
 
+import java.math.BigDecimal;
+import java.util.Iterator;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -25,7 +27,10 @@ import org.killbill.automaton.OperationException;
 import org.killbill.automaton.OperationResult;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
@@ -36,16 +41,21 @@ import org.killbill.commons.locker.LockFailedException;
 import com.google.common.base.Objects;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 // Encapsulates the payment specific logic
 public abstract class DirectPaymentOperation extends OperationCallbackBase implements OperationCallback {
 
     protected final PaymentPluginApi plugin;
+    protected final DirectPaymentAutomatonDAOHelper daoHelper;
 
     protected DirectPaymentOperation(final DirectPaymentAutomatonDAOHelper daoHelper, final GlobalLocker locker,
                                      final PluginDispatcher<OperationResult> paymentPluginDispatcher,
                                      final DirectPaymentStateContext directPaymentStateContext) throws PaymentApiException {
         super(locker, paymentPluginDispatcher, directPaymentStateContext);
+        this.daoHelper = daoHelper;
         this.plugin = daoHelper.getPaymentProviderPlugin();
     }
 
@@ -92,6 +102,27 @@ public abstract class DirectPaymentOperation extends OperationCallbackBase imple
     @Override
     protected abstract PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException;
 
+    protected Iterable<PaymentTransactionModelDao> getOnLeavingStateExistingTransactionsForType(final TransactionType transactionType) {
+        if (directPaymentStateContext.getOnLeavingStateExistingTransactions() == null || directPaymentStateContext.getOnLeavingStateExistingTransactions().isEmpty()) {
+            return ImmutableList.of();
+        }
+        return Iterables.filter(directPaymentStateContext.getOnLeavingStateExistingTransactions(), new Predicate<PaymentTransactionModelDao>() {
+            @Override
+            public boolean apply(final PaymentTransactionModelDao input) {
+                return input.getTransactionStatus() == TransactionStatus.SUCCESS && input.getTransactionType() == transactionType;
+            }
+        });
+    }
+
+    protected BigDecimal getSumAmount(final Iterable<PaymentTransactionModelDao> transactions) {
+        BigDecimal result = BigDecimal.ZERO;
+        Iterator<PaymentTransactionModelDao> iterator = transactions.iterator();
+        while (iterator.hasNext()) {
+            result = result.add(iterator.next().getAmount());
+        }
+        return result;
+    }
+
     private OperationResult doOperationCallbackWithDispatchAndAccountLock() throws OperationException {
         return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<OperationResult, OperationException>() {
             @Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentStateContext.java
index d8019dc..dfeeaa6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/DirectPaymentStateContext.java
@@ -18,6 +18,7 @@
 package org.killbill.billing.payment.core.sm;
 
 import java.math.BigDecimal;
+import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -31,12 +32,15 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.util.callcontext.CallContext;
 
+import com.google.common.collect.ImmutableList;
+
 public class DirectPaymentStateContext {
 
     // HACK
     protected UUID paymentMethodId;
 
     // Stateful objects created by the callbacks and passed to the other following callbacks in the automaton
+    protected List<PaymentTransactionModelDao> onLeavingStateExistingTransactions;
     protected PaymentTransactionModelDao directPaymentTransactionModelDao;
     protected PaymentTransactionInfoPlugin paymentInfoPlugin;
     protected BigDecimal amount;
@@ -81,6 +85,7 @@ public class DirectPaymentStateContext {
         this.properties = properties;
         this.internalCallContext = internalCallContext;
         this.callContext = callContext;
+        this.onLeavingStateExistingTransactions = ImmutableList.of();
     }
 
     public void setPaymentMethodId(final UUID paymentMethodId) {
@@ -95,6 +100,14 @@ public class DirectPaymentStateContext {
         this.directPaymentTransactionModelDao = directPaymentTransactionModelDao;
     }
 
+    public List<PaymentTransactionModelDao> getOnLeavingStateExistingTransactions() {
+        return onLeavingStateExistingTransactions;
+    }
+
+    public void setOnLeavingStateExistingTransactions(final List<PaymentTransactionModelDao> onLeavingStateExistingTransactions) {
+        this.onLeavingStateExistingTransactions = onLeavingStateExistingTransactions;
+    }
+
     public PaymentTransactionInfoPlugin getPaymentInfoPlugin() {
         return paymentInfoPlugin;
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryChargebackOperationCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryChargebackOperationCallback.java
index f644088..d442ceb 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryChargebackOperationCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryChargebackOperationCallback.java
@@ -34,15 +34,14 @@ public class RetryChargebackOperationCallback extends RetryOperationCallback {
 
     @Override
     protected DirectPayment doCallSpecificOperationCallback() throws PaymentApiException {
-        return directPaymentProcessor.notifyChargeback(retryableDirectPaymentStateContext.isApiPayment(),
-                                                    retryableDirectPaymentStateContext.getAccount(),
-                                                    retryableDirectPaymentStateContext.getDirectPaymentId(),
-                                                    null,
-                                                    retryableDirectPaymentStateContext.getDirectPaymentTransactionExternalKey(),
-                                                    retryableDirectPaymentStateContext.getAmount(),
-                                                    retryableDirectPaymentStateContext.getCurrency(),
-                                                    false,
-                                                    retryableDirectPaymentStateContext.getCallContext(),
-                                                    retryableDirectPaymentStateContext.getInternalCallContext());
+        return directPaymentProcessor.createChargeback(retryableDirectPaymentStateContext.isApiPayment(),
+                                                       retryableDirectPaymentStateContext.getAccount(),
+                                                       retryableDirectPaymentStateContext.getDirectPaymentId(),
+                                                       retryableDirectPaymentStateContext.getDirectPaymentTransactionExternalKey(),
+                                                       retryableDirectPaymentStateContext.getAmount(),
+                                                       retryableDirectPaymentStateContext.getCurrency(),
+                                                       false,
+                                                       retryableDirectPaymentStateContext.getCallContext(),
+                                                       retryableDirectPaymentStateContext.getInternalCallContext());
     }
 }
diff --git a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
index 990920d..b487b9c 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
+++ b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
@@ -235,21 +235,32 @@
                 <operation name="OP_VOID"/>
             </operations>
         </stateMachine>
-        <!-- The transition to the state CHARGEBACK_SUCCESS occurs outside of the state machine engine (at least for now).
-             It needs to exist but there is no need for a linkStateMachine Transition to reach that state.
-         -->
         <stateMachine name="CHARGEBACK">
         <states>
             <state name="CHARGEBACK_INIT"/>
             <state name="CHARGEBACK_SUCCESS"/>
+            <state name="CHARGEBACK_FAILED"/>
+            <state name="CHARGEBACK_ERRORED"/>
         </states>
             <transitions>
-            <transition>
-                <initialState>CHARGEBACK_INIT</initialState>
-                <operation>OP_CHARGEBACK</operation>
-                <operationResult>SUCCESS</operationResult>
-                <finalState>CHARGEBACK_SUCCESS</finalState>
-            </transition>
+                <transition>
+                    <initialState>CHARGEBACK_INIT</initialState>
+                    <operation>OP_CHARGEBACK</operation>
+                    <operationResult>SUCCESS</operationResult>
+                    <finalState>CHARGEBACK_SUCCESS</finalState>
+                </transition>
+                <transition>
+                    <initialState>CHARGEBACK_INIT</initialState>
+                    <operation>OP_CHARGEBACK</operation>
+                    <operationResult>FAILURE</operationResult>
+                    <finalState>CHARGEBACK_FAILED</finalState>
+                </transition>
+                <transition>
+                    <initialState>CHARGEBACK_INIT</initialState>
+                    <operation>OP_CHARGEBACK</operation>
+                    <operationResult>EXCEPTION</operationResult>
+                    <finalState>CHARGEBACK_ERRORED</finalState>
+                </transition>
             </transitions>
             <operations>
                 <operation name="OP_CHARGEBACK"/>
@@ -308,17 +319,29 @@
         </linkStateMachine>
         <linkStateMachine>
             <initialStateMachine>REFUND</initialStateMachine>
-            <initialState>REFUND_FAILED</initialState>
+            <initialState>REFUND_SUCCESS</initialState>
             <finalStateMachine>REFUND</finalStateMachine>
             <finalState>REFUND_INIT</finalState>
         </linkStateMachine>
         <linkStateMachine>
             <initialStateMachine>REFUND</initialStateMachine>
-            <initialState>REFUND_SUCCESS</initialState>
+            <initialState>REFUND_FAILED</initialState>
             <finalStateMachine>REFUND</finalStateMachine>
             <finalState>REFUND_INIT</finalState>
         </linkStateMachine>
         <linkStateMachine>
+            <initialStateMachine>REFUND</initialStateMachine>
+            <initialState>REFUND_SUCCESS</initialState>
+            <finalStateMachine>CHARGEBACK</finalStateMachine>
+            <finalState>CHARGEBACK_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
+            <initialStateMachine>REFUND</initialStateMachine>
+            <initialState>REFUND_FAILED</initialState>
+            <finalStateMachine>CHARGEBACK</finalStateMachine>
+            <finalState>CHARGEBACK_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
             <initialStateMachine>PURCHASE</initialStateMachine>
             <initialState>PURCHASE_SUCCESS</initialState>
             <finalStateMachine>REFUND</finalStateMachine>
@@ -330,5 +353,23 @@
             <finalStateMachine>CHARGEBACK</finalStateMachine>
             <finalState>CHARGEBACK_INIT</finalState>
         </linkStateMachine>
+        <linkStateMachine>
+            <initialStateMachine>CHARGEBACK</initialStateMachine>
+            <initialState>CHARGEBACK_SUCCESS</initialState>
+            <finalStateMachine>CHARGEBACK</finalStateMachine>
+            <finalState>CHARGEBACK_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
+            <initialStateMachine>CHARGEBACK</initialStateMachine>
+            <initialState>CHARGEBACK_FAILED</initialState>
+            <finalStateMachine>CHARGEBACK</finalStateMachine>
+            <finalState>CHARGEBACK_INIT</finalState>
+        </linkStateMachine>
+        <linkStateMachine>
+            <initialStateMachine>CHARGEBACK</initialStateMachine>
+            <initialState>CHARGEBACK_ERRORED</initialState>
+            <finalStateMachine>CHARGEBACK</finalStateMachine>
+            <finalState>CHARGEBACK_INIT</finalState>
+        </linkStateMachine>
     </linkStateMachines>
 </stateMachineConfig>
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index cc5e346..f47842b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -440,7 +440,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         final DirectPayment payment = paymentApi.createPurchase(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
                                                                 ImmutableList.<PluginProperty>of(), callContext);
 
-        paymentApi.notifyChargeback(account, payment.getTransactions().get(0).getId(), transactionExternalKey2, requestedAmount, Currency.AED, callContext);
+        paymentApi.createChargeback(account, payment.getId(), requestedAmount, Currency.AED, transactionExternalKey2,  callContext);
         final DirectPayment payment2 = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
 
         assertEquals(payment2.getExternalKey(), paymentExternalKey);
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index 4779159..57ccdce 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -345,14 +345,11 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
     private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, final Currency currency) throws PaymentPluginApiException {
 
         if (makeNextInvoiceFailWithException.getAndSet(false)) {
-            System.out.println("################## (STEPH) MockPaymentProviderPlugin getPaymentTransactionInfoPluginResult makeNextInvoiceFailWithException  => THROW");
             throw new PaymentPluginApiException("", "test error");
         }
 
         final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
 
-        System.out.println("################## (STEPH) MockPaymentProviderPlugin getPaymentTransactionInfoPluginResult makeNextInvoiceFailWithError => status = " + status);
-
         InternalPaymentInfo info = payments.get(kbPaymentId.toString());
         if (info == null) {
             info = new InternalPaymentInfo();
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
index 9f604ed..bfcd862 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
@@ -40,9 +40,7 @@ import static org.testng.Assert.assertNotNull;
 
 public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
-    //private final int DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC = 5;
-    // STEPH fix timeout value after debug
-    private final int DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC = 5000;
+    private final int DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC = 5;
 
     protected static final String PLUGIN_NAME = "noop";
 
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
index 3b507a5..35d2e89 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
@@ -29,7 +29,6 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.AccountTimeline;
 import org.killbill.billing.client.model.AuditLog;
-import org.killbill.billing.client.model.Chargeback;
 import org.killbill.billing.client.model.Credit;
 import org.killbill.billing.client.model.EventSubscription;
 import org.killbill.billing.client.model.Invoice;
@@ -43,6 +42,10 @@ import org.killbill.billing.util.audit.ChangeType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+
 public class TestAccountTimeline extends TestJaxrsBase {
 
     private static final String PAYMENT_REQUEST_PROCESSOR = "PaymentRequestProcessor";
@@ -93,15 +96,11 @@ public class TestAccountTimeline extends TestJaxrsBase {
         killBillClient.createInvoicePaymentRefund(refund, createdBy, reason, comment);
 
         // Add chargeback
-        /*
-        // STEPH disable until chargeback has been fixed
         final BigDecimal chargebackAmount = BigDecimal.ONE;
-        final Chargeback chargeback = new Chargeback();
+        final InvoicePaymentTransaction chargeback = new InvoicePaymentTransaction();
         chargeback.setPaymentId(postedPayment.getPaymentId());
         chargeback.setAmount(chargebackAmount);
-        killBillClient.createChargeBack(chargeback, createdBy, reason, comment);
-        */
-        final BigDecimal chargebackAmount = BigDecimal.ZERO;
+        killBillClient.createInvoicePaymentChargeback(chargeback, createdBy, reason, comment);
 
         // Verify payments
         verifyPayments(accountJson.getAccountId(), startTime, endTime, refundAmount, chargebackAmount);
@@ -136,19 +135,16 @@ public class TestAccountTimeline extends TestJaxrsBase {
             Assert.assertEquals(refundTransaction.getPaymentId(), payment.getPaymentId());
             Assert.assertEquals(refundTransaction.getAmount().compareTo(refundAmount), 0);
 
-            // Verify chargebacks
-            /*
-STEPH
             final List<PaymentTransaction> chargebackTransactions = getDirectPaymentTransactions(timeline.getPayments(), TransactionType.CHARGEBACK.toString());
             Assert.assertEquals(chargebackTransactions.size(), 1);
             final PaymentTransaction chargebackTransaction = chargebackTransactions.get(0);
             Assert.assertEquals(chargebackTransaction.getPaymentId(), payment.getPaymentId());
             Assert.assertEquals(chargebackTransaction.getAmount().compareTo(chargebackAmount), 0);
-*/
+
             // Verify audits
             final List<AuditLog> paymentAuditLogs = purchaseTransaction.getAuditLogs();
             final List<AuditLog> refundAuditLogs = refundTransaction.getAuditLogs();
-            //final List<AuditLog> chargebackAuditLogs = chargebackTransaction.getAuditLogs(); STEPH
+            final List<AuditLog> chargebackAuditLogs = chargebackTransaction.getAuditLogs();
 
             if (AuditLevel.NONE.equals(auditLevel)) {
                 // Audits for payments
@@ -158,7 +154,7 @@ STEPH
                 Assert.assertEquals(refundAuditLogs.size(), 0);
 
                 // Audits for chargebacks
-                //Assert.assertEquals(chargebackAuditLogs.size(), 0); STEPH
+                Assert.assertEquals(chargebackAuditLogs.size(), 0);
             } else if (AuditLevel.MINIMAL.equals(auditLevel)) {
                 // Audits for payments
                 Assert.assertEquals(paymentAuditLogs.size(), 1);
@@ -169,8 +165,8 @@ STEPH
                 verifyAuditLog(refundAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
 
                 // Audits for chargebacks
-                //Assert.assertEquals(chargebackAuditLogs.size(), 1); STEPH
-                //verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime); STEPH
+                Assert.assertEquals(chargebackAuditLogs.size(), 1);
+                verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
             } else {
                 // Audits for payments
                 Assert.assertEquals(paymentAuditLogs.size(), 2);
@@ -183,8 +179,9 @@ STEPH
                 verifyAuditLog(refundAuditLogs.get(1), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
 
                 // Audits for chargebacks
-                //Assert.assertEquals(chargebackAuditLogs.size(), 1); STEPH
-                // verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime); STEPH
+                Assert.assertEquals(chargebackAuditLogs.size(), 2);
+                verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+                verifyAuditLog(chargebackAuditLogs.get(1), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
             }
         }
     }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java
index f8b3196..d5929de 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java
@@ -26,13 +26,19 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
-import org.killbill.billing.client.model.Chargeback;
 import org.killbill.billing.client.model.Invoice;
+import org.killbill.billing.client.model.InvoicePayment;
+import org.killbill.billing.client.model.InvoicePaymentTransaction;
+import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Payment;
+import org.killbill.billing.client.model.PaymentTransaction;
 import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.payment.api.TransactionType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
@@ -41,48 +47,50 @@ import static org.testng.Assert.fail;
 
 public class TestChargeback extends TestJaxrsBase {
 
-    // STEPH disable all chargeback tests until chargeback gets correctly implemented in payment (part of TODO list)
-    @Test(groups = "slow", description = "Can create a chargeback", enabled=false)
+    @Test(groups = "slow", description = "Can create a chargeback")
     public void testAddChargeback() throws Exception {
         final Payment payment = createAccountWithInvoiceAndPayment();
         createAndVerifyChargeback(payment);
     }
 
-    @Test(groups = "slow", description = "Can create multiple chargebacks", enabled=false)
+    @Test(groups = "slow", description = "Can create multiple chargebacks")
     public void testMultipleChargeback() throws Exception {
         final Payment payment = createAccountWithInvoiceAndPayment();
 
         // We get a 249.95 payment so we do 4 chargeback and then the fifth should fail
-        final Chargeback input = new Chargeback();
-        input.setAmount(new BigDecimal("50.00"));
+        final InvoicePaymentTransaction input = new InvoicePaymentTransaction();
         input.setPaymentId(payment.getPaymentId());
-
+        input.setAmount(new BigDecimal("50.00"));
         int count = 4;
         while (count-- > 0) {
-            assertNotNull(killBillClient.createChargeBack(input, createdBy, reason, comment));
+            assertNotNull(killBillClient.createInvoicePaymentChargeback(input, createdBy, reason, comment));
         }
 
         // Last attempt should fail because this is more than the Payment
         try {
-            killBillClient.createChargeBack(input, createdBy, reason, comment);
+            killBillClient.createInvoicePaymentChargeback(input, createdBy, reason, comment);
             fail();
         } catch (final KillBillClientException e) {
         }
 
-        // Find the chargeback by account
-        List<Chargeback> chargebacks = killBillClient.getChargebacksForAccount(payment.getAccountId());
-        assertEquals(chargebacks.size(), 4);
-        for (final Chargeback chargeBack : chargebacks) {
-            assertTrue(chargeBack.getAmount().compareTo(input.getAmount()) == 0);
-            assertEquals(chargeBack.getPaymentId(), input.getPaymentId());
+        final List<InvoicePayment> payments = killBillClient.getInvoicePaymentsForAccount(payment.getAccountId());
+        final List<PaymentTransaction> transactions = getDirectPaymentTransactions(payments, TransactionType.CHARGEBACK.toString());
+        Assert.assertEquals(transactions.size(), 5);
+        int found = 0;
+        for (final PaymentTransaction transaction : transactions) {
+            if (transaction.getStatus().equals("SUCCESS")) {
+                assertTrue(transaction.getAmount().compareTo(input.getAmount()) == 0);
+                assertEquals(transaction.getPaymentId(), input.getPaymentId());
+                found++;
+            } else {
+                assertEquals(transaction.getStatus(), "PAYMENT_FAILURE");
+                found++;
+            }
         }
-
-        // Find the chargeback by payment
-        chargebacks = killBillClient.getChargebacksForPayment(payment.getPaymentId());
-        assertEquals(chargebacks.size(), 4);
+        assertEquals(found, 5);
     }
 
-    @Test(groups = "slow", description = "Can add a chargeback for deleted payment methods", enabled=false)
+    @Test(groups = "slow", description = "Can add a chargeback for deleted payment methods")
     public void testAddChargebackForDeletedPaymentMethod() throws Exception {
         final Payment payment = createAccountWithInvoiceAndPayment();
 
@@ -99,59 +107,62 @@ public class TestChargeback extends TestJaxrsBase {
         createAndVerifyChargeback(payment);
     }
 
-    @Test(groups = "slow", description = "Cannot add a chargeback for non existent payment", enabled=false)
-    public void testInvoicePaymentDoesNotExist() throws Exception {
-        final Chargeback input = new Chargeback();
+    @Test(groups = "slow", description = "Cannot add a chargeback for non existent payment")
+    public void testInvoicePaymentDoesNotExist() {
+
+        final InvoicePaymentTransaction input = new InvoicePaymentTransaction();
+        input.setPaymentId(input.getPaymentId());
         input.setAmount(BigDecimal.TEN);
-        input.setPaymentId(UUID.randomUUID());
-        assertNull(killBillClient.createChargeBack(input, createdBy, reason, comment));
+        try {
+            killBillClient.createInvoicePaymentChargeback(input, createdBy, reason, comment);
+            fail();
+        } catch (NullPointerException e) {
+        } catch (KillBillClientException e) {
+            fail();
+        }
     }
 
-    @Test(groups = "slow", description = "Cannot add a badly formatted chargeback", enabled=false)
+    @Test(groups = "slow", description = "Cannot add a badly formatted chargeback")
     public void testBadRequest() throws Exception {
         final Payment payment = createAccountWithInvoiceAndPayment();
 
-        final Chargeback input = new Chargeback();
-        input.setAmount(BigDecimal.TEN.negate());
+        final InvoicePaymentTransaction input = new InvoicePaymentTransaction();
         input.setPaymentId(payment.getPaymentId());
+        input.setAmount(BigDecimal.TEN.negate());
 
         try {
-            killBillClient.createChargeBack(input, createdBy, reason, comment);
+            killBillClient.createInvoicePaymentChargeback(input, createdBy, reason, comment);
             fail();
         } catch (final KillBillClientException e) {
         }
     }
 
-    @Test(groups = "slow", description = "Accounts can have zero chargeback", enabled=false)
+    @Test(groups = "slow", description = "Accounts can have zero chargeback")
     public void testNoChargebackForAccount() throws Exception {
-        Assert.assertEquals(killBillClient.getChargebacksForAccount(UUID.randomUUID()).size(), 0);
-    }
-
-    @Test(groups = "slow", description = "Payments can have zero chargeback", enabled=false)
-    public void testNoChargebackForPayment() throws Exception {
-        Assert.assertEquals(killBillClient.getChargebacksForPayment(UUID.randomUUID()).size(), 0);
+        final List<InvoicePayment> payments = killBillClient.getInvoicePaymentsForAccount(UUID.randomUUID());
+        final List<PaymentTransaction> transactions = getDirectPaymentTransactions(payments, TransactionType.CHARGEBACK.toString());
+        Assert.assertEquals(transactions.size(), 0);
     }
 
     private void createAndVerifyChargeback(final Payment payment) throws KillBillClientException {
         // Create the chargeback
-        final Chargeback chargeback = new Chargeback();
+        final InvoicePaymentTransaction chargeback = new InvoicePaymentTransaction();
         chargeback.setPaymentId(payment.getPaymentId());
         chargeback.setAmount(BigDecimal.TEN);
-        final Chargeback chargebackJson = killBillClient.createChargeBack(chargeback, createdBy, reason, comment);
-        assertEquals(chargebackJson.getAmount().compareTo(chargeback.getAmount()), 0);
-        assertEquals(chargebackJson.getPaymentId(), chargeback.getPaymentId());
+
+        final InvoicePayment chargebackJson = killBillClient.createInvoicePaymentChargeback(chargeback, createdBy, reason, comment);
+        final List<PaymentTransaction> chargebackTransactions = getDirectPaymentTransactions(ImmutableList.of(chargebackJson), TransactionType.CHARGEBACK.toString());
+        assertEquals(chargebackTransactions.size(), 1);
+
+        assertEquals(chargebackTransactions.get(0).getAmount().compareTo(chargeback.getAmount()), 0);
+        assertEquals(chargebackTransactions.get(0).getPaymentId(), chargeback.getPaymentId());
 
         // Find the chargeback by account
-        List<Chargeback> chargebacks = killBillClient.getChargebacksForAccount(payment.getAccountId());
-        assertEquals(chargebacks.size(), 1);
-        assertEquals(chargebacks.get(0).getAmount().compareTo(chargeback.getAmount()), 0);
-        assertEquals(chargebacks.get(0).getPaymentId(), chargeback.getPaymentId());
-
-        // Find the chargeback by payment
-        chargebacks = killBillClient.getChargebacksForPayment(payment.getPaymentId());
-        assertEquals(chargebacks.size(), 1);
-        assertEquals(chargebacks.get(0).getAmount().compareTo(chargeback.getAmount()), 0);
-        assertEquals(chargebacks.get(0).getPaymentId(), chargeback.getPaymentId());
+        final List<InvoicePayment> payments = killBillClient.getInvoicePaymentsForAccount(payment.getAccountId());
+        final List<PaymentTransaction> transactions = getDirectPaymentTransactions(payments, TransactionType.CHARGEBACK.toString());
+        Assert.assertEquals(transactions.size(), 1);
+        assertEquals(transactions.get(0).getAmount().compareTo(chargeback.getAmount()), 0);
+        assertEquals(transactions.get(0).getPaymentId(), chargeback.getPaymentId());
     }
 
     private Payment createAccountWithInvoiceAndPayment() throws Exception {
@@ -182,7 +193,7 @@ public class TestChargeback extends TestJaxrsBase {
     }
 
     private Payment getPayment(final Invoice invoice) throws KillBillClientException {
-        final List<Payment> payments = null; //killBillClient.getPaymentsForInvoice(invoice.getInvoiceId());
+        final InvoicePayments payments = killBillClient.getInvoicePayment(invoice.getInvoiceId());
         assertNotNull(payments);
         assertEquals(payments.size(), 1);
         return payments.get(0);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExceptions.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExceptions.java
index 8e68e97..fa7c31f 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExceptions.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExceptions.java
@@ -21,32 +21,29 @@ package org.killbill.billing.jaxrs;
 import java.math.BigDecimal;
 import java.util.List;
 
-import org.killbill.billing.client.model.InvoicePayment;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
-import org.killbill.billing.client.model.Chargeback;
-import org.killbill.billing.client.model.Payment;
+import org.killbill.billing.client.model.InvoicePayment;
+import org.killbill.billing.client.model.InvoicePaymentTransaction;
 import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 import static org.testng.Assert.fail;
 
 public class TestExceptions extends TestJaxrsBase {
 
-    // STEPH disable all chargeback tests until chargeback gets correctly implemented in payment (part of TODO list)
-    @Test(groups = "slow", enabled=false)
+    @Test(groups = "slow", enabled = false)
     public void testExceptionMapping() throws Exception {
         final Account account = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
         final List<InvoicePayment> payments = killBillClient.getInvoicePaymentsForAccount(account.getAccountId());
-        final Chargeback input = new Chargeback();
-        input.setAmount(BigDecimal.TEN.negate());
-        input.setPaymentId(payments.get(0).getPaymentId());
 
+        final InvoicePaymentTransaction input = new InvoicePaymentTransaction();
+        input.setPaymentId(payments.get(0).getPaymentId());
+        input.setAmount(BigDecimal.TEN.negate());
         try {
-            killBillClient.createChargeBack(input, createdBy, reason, comment);
+            killBillClient.createInvoicePaymentChargeback(input, createdBy, reason, comment);
             fail();
         } catch (final KillBillClientException e) {
             Assert.assertEquals(e.getBillingException().getClassName(), InvoiceApiException.class.getName());