killbill-memoizeit

Changes

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index 057aaf6..0128490 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -19,6 +19,8 @@ package com.ning.billing.account.api.user;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -155,4 +157,19 @@ public class DefaultAccountUserApi implements AccountUserApi {
     public void removeEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
         accountEmailDao.removeEmail(accountId, email, context);
     }
+
+    @Override
+    public void removePaymentMethod(final UUID accountId, final CallContext context) throws AccountApiException {
+        updatePaymentMethod(accountId, null, context);
+    }
+
+
+    @Override
+    public void updatePaymentMethod(final UUID accountId, @Nullable final UUID paymentMethodId, final CallContext context) throws AccountApiException {
+        try {
+            accountDao.updatePaymentMethod(accountId, paymentMethodId, context);
+        }  catch (EntityPersistenceException e) {
+            throw new AccountApiException(e, e.getCode(), e.getMessage());
+        }
+    }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index 2f3cd25..0eb7907 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -20,6 +20,8 @@ import java.util.UUID;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.entity.dao.UpdatableEntityDao;
 
 public interface AccountDao extends UpdatableEntityDao<Account> {
@@ -31,4 +33,11 @@ public interface AccountDao extends UpdatableEntityDao<Account> {
      * @throws AccountApiException when externalKey is null
      */
     public UUID getIdFromKey(String externalKey) throws AccountApiException;
+
+    /**
+     *
+     * @param accountId the id of the account
+     * @param paymentMethodId the is of the current default paymentMethod
+     */
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, final CallContext context) throws EntityPersistenceException;
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index 01b344a..8afc174 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -50,6 +50,9 @@ public interface AccountSqlDao extends UpdatableEntitySqlDao<Account>, Transacti
     @SqlUpdate
     public void update(@AccountBinder Account account, @CallContextBinder final CallContext context);
 
+    @SqlUpdate
+    public void updatePaymentMethod(@Bind("id") String accountId, @Bind("paymentMethodId") String paymentMethodId, @CallContextBinder final CallContext context);
+
     @Override
     @SqlUpdate
     public void insertHistoryFromTransaction(@AccountHistoryBinder final EntityHistory<Account> account,
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
index 9e7faf8..3384230 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
@@ -171,4 +171,48 @@ public class AuditedAccountDao implements AccountDao {
     public void test() {
         accountSqlDao.test();
     }
+
+    @Override
+    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final CallContext context) throws EntityPersistenceException {
+        try {
+            accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+                @Override
+                public Void inTransaction(final AccountSqlDao transactional, final TransactionStatus status) throws EntityPersistenceException, Bus.EventBusException {
+
+                    final Account currentAccount = transactional.getById(accountId.toString());
+                    if (currentAccount == null) {
+                        throw new EntityPersistenceException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
+                    }
+                    final String thePaymentMethodId = paymentMethodId != null ? paymentMethodId.toString() : null;
+                    transactional.updatePaymentMethod(accountId.toString(), thePaymentMethodId, context);
+
+                    final Account account = transactional.getById(accountId.toString());
+
+                    final Long recordId = accountSqlDao.getRecordId(accountId.toString());
+                    final EntityHistory<Account> history = new EntityHistory<Account>(accountId, recordId, account, ChangeType.UPDATE);
+                    accountSqlDao.insertHistoryFromTransaction(history, context);
+
+                    final Long historyRecordId = accountSqlDao.getHistoryRecordId(recordId);
+                    final EntityAudit audit = new EntityAudit(TableName.ACCOUNT_HISTORY, historyRecordId, ChangeType.UPDATE);
+                    accountSqlDao.insertAuditFromTransaction(audit, context);
+
+                    final AccountChangeEvent changeEvent = new DefaultAccountChangeEvent(accountId, context.getUserToken(), currentAccount, account);
+                    if (changeEvent.hasChanges()) {
+                        try {
+                            eventBus.postFromTransaction(changeEvent, transactional);
+                        } catch (EventBusException e) {
+                            log.warn("Failed to post account change event for account " + accountId, e);
+                        }
+                    }
+                    return null;
+                }
+            });
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof EntityPersistenceException) {
+                throw (EntityPersistenceException) re.getCause();
+            } else {
+                throw re;
+            }
+        }
+    }
 }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index c6f3c83..8a8c0ce 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -49,6 +49,16 @@ update() ::= <<
     WHERE id = :id;
 >>
 
+
+updatePaymentMethod() ::= <<
+    UPDATE accounts
+    SET payment_method_id = :paymentMethodId
+    , updated_date = :updatedDate
+    , updated_by = :userName
+    WHERE id = :id;
+>>
+
+
 historyFields() ::= <<
     record_id,
     id,
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index 535dbbc..e525957 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -30,6 +30,7 @@ import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.entity.EntityPersistenceException;
 
 public class MockAccountDao implements AccountDao {
     private final Bus eventBus;
@@ -94,4 +95,11 @@ public class MockAccountDao implements AccountDao {
             }
         }
     }
+
+    @Override
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId,
+            CallContext context) throws EntityPersistenceException {
+        // TODO Auto-generated method stub
+
+    }
 }
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index 3c41f1d..be8abc4 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -54,6 +54,7 @@ import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -224,6 +225,25 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
+    public void testUpdatePaymentMethod() throws Exception {
+        final Account account = createTestAccount(1);
+        accountDao.create(account, context);
+
+        final UUID newPaymentMethodId = UUID.randomUUID();
+        accountDao.updatePaymentMethod(account.getId(), newPaymentMethodId, context);
+
+        final Account newAccount = accountDao.getById(account.getId());
+        assertEquals(newAccount.getPaymentMethodId(), newPaymentMethodId);
+
+        // And then set it to null
+        accountDao.updatePaymentMethod(account.getId(), null, context);
+
+        final Account newAccountWithPMNull = accountDao.getById(account.getId());
+        assertNull(newAccountWithPMNull.getPaymentMethodId());
+
+    }
+
+    @Test(groups = "slow")
     public void testAddingContactInformation() throws Exception {
         final UUID accountId = UUID.randomUUID();
         final DefaultAccount account = new DefaultAccount(accountId, "extKey123456", "myemail123456@glam.com",
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index 85e03cf..7a3868d 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -39,6 +39,10 @@ public interface AccountUserApi {
 
     public void updateAccount(UUID accountId, AccountData accountData, CallContext context) throws AccountApiException;
 
+    public void removePaymentMethod(final UUID accountId, final CallContext context) throws AccountApiException;
+
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, CallContext context) throws AccountApiException;
+
     public Account getAccountByKey(String key) throws AccountApiException;
 
     public Account getAccountById(UUID accountId) throws AccountApiException;
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index bb9751c..e6d853c 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -161,7 +161,7 @@ public enum ErrorCode {
     */
     TAG_DEFINITION_CONFLICTS_WITH_CONTROL_TAG(3900, "The tag definition name conflicts with a reserved %s"),
     TAG_DEFINITION_ALREADY_EXISTS(3901, "The tag definition name already exists %s"),
-    TAG_DEFINITION_DOES_NOT_EXIST(3902, "The tag definition name does not exist %s"),
+    TAG_DEFINITION_DOES_NOT_EXIST(3902, "The tag definition id does not exist %s"),
     TAG_DEFINITION_IN_USE(3903, "The tag definition name is currently in use %s"),
 
     CONTROL_TAG_DOES_NOT_EXIST(3904, "The control tag does not exist %s"),
@@ -172,6 +172,7 @@ public enum ErrorCode {
     *
     */
     TAG_DOES_NOT_EXIST(3950, "The tag does not exist (name: %s)"),
+    TAG_CANNOT_BE_REMOVED(3951, "The tag %s cannot be removed because %s"),
 
     /*
     *
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 bf38550..16c4c5f 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
@@ -131,7 +131,7 @@ public interface PaymentApi {
     public void updatePaymentMethod(final Account account, final UUID paymentMethodId, final PaymentMethodPlugin paymentMetghodInfo)
             throws PaymentApiException;
 
-    public void deletedPaymentMethod(final Account account, final UUID paymentMethodId, final CallContext context)
+    public void deletedPaymentMethod(final Account account, final UUID paymentMethodId, final boolean deleteDefaultPaymentMethodWithAutoPayOff, final CallContext context)
             throws PaymentApiException;
 
     public void setDefaultPaymentMethod(final Account account, final UUID paymentMethodId, final CallContext context)
diff --git a/api/src/main/java/com/ning/billing/util/tag/Tag.java b/api/src/main/java/com/ning/billing/util/tag/Tag.java
index 553ef5f..33584d9 100644
--- a/api/src/main/java/com/ning/billing/util/tag/Tag.java
+++ b/api/src/main/java/com/ning/billing/util/tag/Tag.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.tag;
 
 import java.util.UUID;
 
+
 import com.ning.billing.util.entity.Entity;
 
 public interface Tag extends Entity {
diff --git a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
index 88f9f1a..b00c46b 100644
--- a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
+++ b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.ning.billing.util.entity.Entity;
 
 // TODO: needs to surface created date, created by, isControlTag
+
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface TagDefinition extends Entity {
     String getName();
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagJson.java
new file mode 100644
index 0000000..531fd2b
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagJson.java
@@ -0,0 +1,52 @@
+/*
+ * 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.jaxrs.json;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.tag.TagDefinition;
+
+public class TagJson extends JsonBase {
+
+    private final String tagDefinitionId;
+    private final String tagDefinitionName;
+
+    @JsonCreator
+    public TagJson(@JsonProperty("tagDefinitionId")  final String tagDefinitionId,
+            @JsonProperty("tagDefinitionName") final String tagDefinitionName,
+            @JsonProperty("auditLogs") @Nullable List<AuditLogJson> auditLogs) {
+        super(auditLogs);
+        this.tagDefinitionId = tagDefinitionId;
+        this.tagDefinitionName = tagDefinitionName;
+    }
+
+    public TagJson(final TagDefinition tagDefintion, @Nullable final List<AuditLog> auditLogs) {
+        this(tagDefintion.getId().toString(), tagDefintion.getName(), toAuditLogJson(auditLogs));
+    }
+
+    public String getTagDefinitionId() {
+        return tagDefinitionId;
+    }
+
+    public String getTagDefinitionName() {
+        return tagDefinitionName;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
index 551f0d2..44e26ff 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
@@ -57,7 +57,7 @@ public class PaymentApiExceptionMapper extends ExceptionMapperBase implements Ex
         } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_REFUND.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.PAYMENT_DEL_DEFAULT_PAYMENT_METHOD.getCode()) {
-            return buildInternalErrorResponse(exception, uriInfo);
+            return buildBadRequestResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.PAYMENT_DEL_PAYMENT_METHOD.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.PAYMENT_GET_PAYMENT_METHODS.getCode()) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/TagApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/TagApiExceptionMapper.java
index bcd1220..87010d3 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/TagApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/TagApiExceptionMapper.java
@@ -40,6 +40,8 @@ public class TagApiExceptionMapper extends ExceptionMapperBase implements Except
     public Response toResponse(final TagApiException exception) {
         if (exception.getCode() == ErrorCode.TAG_DOES_NOT_EXIST.getCode()) {
             return buildNotFoundResponse(exception, uriInfo);
+        } else if (exception.getCode() == ErrorCode.TAG_CANNOT_BE_REMOVED.getCode()) {
+            return buildBadRequestResponse(exception, uriInfo);
         } else {
             return buildBadRequestResponse(exception, uriInfo);
         }
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 47e8e64..a8093d6 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
@@ -39,6 +39,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
+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.AccountData;
@@ -79,6 +80,7 @@ import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.tag.ControlTagType;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ArrayListMultimap;
@@ -217,7 +219,7 @@ public class AccountResource extends JaxRsResourceBase {
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TIMELINE)
     @Produces(APPLICATION_JSON)
     public Response getAccountTimeline(@PathParam("accountId") final String accountIdString,
-                                       @QueryParam("audit") @DefaultValue("false") final Boolean withAudit) throws AccountApiException, PaymentApiException, EntitlementRepairException {
+                                       @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws AccountApiException, PaymentApiException, EntitlementRepairException {
         final UUID accountId = UUID.fromString(accountIdString);
         final Account account = accountApi.getAccountById(accountId);
 
@@ -449,8 +451,8 @@ public class AccountResource extends JaxRsResourceBase {
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id));
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String id, @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws TagDefinitionApiException {
+        return super.getTags(UUID.fromString(id), withAudit);
     }
 
     @POST
@@ -474,7 +476,26 @@ public class AccountResource extends JaxRsResourceBase {
                                @QueryParam(QUERY_TAGS) final String tagList,
                                @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                @HeaderParam(HDR_REASON) final String reason,
-                               @HeaderParam(HDR_COMMENT) final String comment) throws TagApiException {
+                               @HeaderParam(HDR_COMMENT) final String comment) throws TagApiException, AccountApiException {
+
+        // Look if there is an AUTO_PAY_OFF for that account and check if the account has a default paymentMethod
+        // If not we can't remove the AUTO_PAY_OFF tag
+        final Collection<UUID> tagDefinitionUUIDs =  getTagDefinitionUUIDs(tagList);
+        boolean isTagAutoPayOff = false;
+        for (UUID cur : tagDefinitionUUIDs) {
+            if (cur.equals(ControlTagType.AUTO_PAY_OFF.getId())) {
+                isTagAutoPayOff = true;
+                break;
+            }
+        }
+        final UUID accountId = UUID.fromString(id);
+        if (isTagAutoPayOff) {
+            final Account account = accountApi.getAccountById(accountId);
+            if (account.getPaymentMethodId() == null) {
+                throw new TagApiException(ErrorCode.TAG_CANNOT_BE_REMOVED, ControlTagType.AUTO_PAY_OFF, " the account does not have a default payment method");
+            }
+        }
+
         return super.deleteTags(UUID.fromString(id), tagList,
                                 context.createContext(createdBy, reason, comment));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 3863737..889795d 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -164,8 +164,8 @@ public class BundleResource extends JaxRsResourceBase {
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAG_URI)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id));
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String id, @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws TagDefinitionApiException {
+        return super.getTags(UUID.fromString(id), withAudit);
     }
 
     @PUT
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index e89e850..816eaf3 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -394,8 +394,8 @@ public class InvoiceResource extends JaxRsResourceBase {
     @GET
     @Path(TAG_URI)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id));
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String id, @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws TagDefinitionApiException {
+        return super.getTags(UUID.fromString(id), withAudit);
     }
 
     @POST
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 790ade4..e52d1c2 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -67,6 +67,10 @@ public interface JaxrsResource {
     public static final String QUERY_BUNDLE_TRANSFER_ADDON = "transferAddOn";
     public static final String QUERY_BUNDLE_TRANSFER_CANCEL_IMM = "cancelImmediately";
 
+    public static final String QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF = "deleteDefaultPmWithAutoPayOff";
+
+    public static final String QUERY_AUDIT = "audit";
+
     public static final String ACCOUNTS = "accounts";
     public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
index 6f2d2f1..7066e05 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -22,6 +22,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -31,7 +32,9 @@ import org.joda.time.format.ISODateTimeFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.json.TagJson;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.util.api.AuditUserApi;
 import com.ning.billing.util.api.CustomFieldUserApi;
@@ -78,7 +81,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         this(uriBuilder, tagUserApi, customFieldUserApi, null);
     }
 
-    protected Response getTags(final UUID id) throws TagDefinitionApiException {
+    protected Response getTags(final UUID id, final boolean withAudit) throws TagDefinitionApiException {
         final Map<String, Tag> tags = tagUserApi.getTags(id, getObjectType());
         final Collection<UUID> tagIdList = (tags.size() == 0) ?
                                            Collections.<UUID>emptyList() :
@@ -89,8 +92,35 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
                                                }
                                            });
 
+        final AtomicReference<TagDefinitionApiException> theException = new AtomicReference<TagDefinitionApiException>();
         final List<TagDefinition> tagDefinitionList = tagUserApi.getTagDefinitions(tagIdList);
-        return Response.status(Response.Status.OK).entity(tagDefinitionList).build();
+        final List<TagJson> result = ImmutableList.<TagJson>copyOf(Collections2.transform(tagIdList, new Function<UUID, TagJson>() {
+            @Override
+            public TagJson apply(UUID input) {
+                try {
+                final TagDefinition tagDefinition = findTagDefinitionFromId(tagDefinitionList, input);
+                    return new TagJson(input.toString(), tagDefinition.getName(), null);
+                } catch (TagDefinitionApiException e) {
+                    theException.set(e);
+                    return null;
+                }
+            }
+        }));
+        // Yackk..
+        if (theException.get() != null) {
+            throw theException.get();
+        }
+
+        return Response.status(Response.Status.OK).entity(result).build();
+    }
+
+    private TagDefinition findTagDefinitionFromId(final List<TagDefinition> tagDefinitionList, final UUID tagDefinitionId) throws TagDefinitionApiException {
+        for (TagDefinition cur : tagDefinitionList) {
+            if (cur.getId().equals(tagDefinitionId)) {
+                return cur;
+            }
+        }
+        throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, tagDefinitionId);
     }
 
     protected Response createTags(final UUID id,
@@ -102,7 +132,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return uriBuilder.buildResponse(this.getClass(), "getTags", id, uriInfo.getBaseUri().toString());
     }
 
-    private Collection<UUID> getTagDefinitionUUIDs(final String tagList) {
+    protected Collection<UUID> getTagDefinitionUUIDs(final String tagList) {
         final String[] tagParts = tagList.split(",\\s*");
         return Collections2.transform(ImmutableList.copyOf(tagParts), new Function<String, UUID>() {
             @Override
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
index 91012ee..15d014b 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
@@ -104,13 +104,14 @@ public class PaymentMethodResource extends JaxRsResourceBase {
     @Produces(APPLICATION_JSON)
     @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
     public Response deletePaymentMethod(@PathParam("paymentMethodId") final String paymentMethodId,
+                                        @QueryParam(QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF) @DefaultValue("false") final Boolean deleteDefaultPaymentMethodWithAutoPayOff,
                                         @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                         @HeaderParam(HDR_REASON) final String reason,
                                         @HeaderParam(HDR_COMMENT) final String comment) throws PaymentApiException, AccountApiException {
         final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId));
         final Account account = accountApi.getAccountById(paymentMethod.getAccountId());
 
-        paymentApi.deletedPaymentMethod(account, UUID.fromString(paymentMethodId), context.createContext(createdBy, reason, comment));
+        paymentApi.deletedPaymentMethod(account, UUID.fromString(paymentMethodId), deleteDefaultPaymentMethodWithAutoPayOff, context.createContext(createdBy, reason, comment));
 
         return Response.status(Status.OK).build();
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 3f49504..e2ce6f4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -25,6 +25,7 @@ import java.util.UUID;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
@@ -170,8 +171,8 @@ public class PaymentResource extends JaxRsResourceBase {
     @GET
     @Path(TAG_URI)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id));
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String id, @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws TagDefinitionApiException {
+        return super.getTags(UUID.fromString(id), withAudit);
     }
 
     @POST
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 03c30d2..69ebb23 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -374,8 +374,8 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @GET
     @Path(TAG_URI)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id));
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String id, @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws TagDefinitionApiException {
+        return super.getTags(UUID.fromString(id), withAudit);
     }
 
     @POST
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
index e568b61..fd7b8eb 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
@@ -105,4 +105,16 @@ public class BlockingAccountUserApi implements AccountUserApi {
     public void removeEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
         userApi.removeEmail(accountId, email, context);
     }
+
+    @Override
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId,
+            CallContext context) throws AccountApiException {
+        userApi.updatePaymentMethod(accountId, paymentMethodId, context);
+    }
+
+    @Override
+    public void removePaymentMethod(UUID accountId, CallContext context)
+            throws AccountApiException {
+        userApi.removePaymentMethod(accountId, context);
+    }
 }
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 a2c1f11..2b10f8a 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
@@ -177,9 +177,9 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public void deletedPaymentMethod(final Account account, final UUID paymentMethodId, final CallContext context)
+    public void deletedPaymentMethod(final Account account, final UUID paymentMethodId, final boolean deleteDefaultPaymentMethodWithAutoPayOff, final CallContext context)
             throws PaymentApiException {
-        methodProcessor.deletedPaymentMethod(account, paymentMethodId);
+        methodProcessor.deletedPaymentMethod(account, paymentMethodId, deleteDefaultPaymentMethodWithAutoPayOff);
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index 83e4146..d1ca916 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -17,14 +17,13 @@ package com.ning.billing.payment.core;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 
-import javax.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
@@ -50,22 +49,34 @@ import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
 import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.globallocker.GlobalLocker;
 
 import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
 
 public class PaymentMethodProcessor extends ProcessorBase {
 
+    private static final Logger log = LoggerFactory.getLogger(PaymentMethodProcessor.class);
+
+    private final Clock clock;
+
     @Inject
     public PaymentMethodProcessor(final PaymentProviderPluginRegistry pluginRegistry,
                                   final AccountUserApi accountUserApi,
                                   final Bus eventBus,
                                   final PaymentDao paymentDao,
+                                  final TagUserApi tagUserApi,
+                                  final Clock clock,
                                   final GlobalLocker locker,
                                   @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
-        super(pluginRegistry, accountUserApi, eventBus, paymentDao, locker, executor);
+        super(pluginRegistry, accountUserApi, eventBus, paymentDao, tagUserApi, locker, executor);
+        this.clock = clock;
     }
 
     public Set<String> getAvailablePlugins() {
@@ -111,9 +122,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     paymentDao.insertPaymentMethod(pmModel, context);
 
                     if (setDefault) {
-                        final MutableAccountData updateAccountData = new DefaultMutableAccountData(account);
-                        updateAccountData.setPaymentMethodId(pm.getId());
-                        accountUserApi.updateAccount(account.getId(), updateAccountData, context);
+                        accountUserApi.updatePaymentMethod(account.getId(), pm.getId(), context);
                     }
                 } catch (PaymentPluginApiException e) {
                     // STEPH all errors should also take a pluginName
@@ -276,7 +285,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
     }
 
 
-    public void deletedPaymentMethod(final Account account, final UUID paymentMethodId)
+    public void deletedPaymentMethod(final Account account, final UUID paymentMethodId, final boolean deleteDefaultPaymentMethodWithAutoPayOff)
             throws PaymentApiException {
 
         new WithAccountLock<Void>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Void>() {
@@ -290,7 +299,17 @@ public class PaymentMethodProcessor extends ProcessorBase {
 
                 try {
                     if (account.getPaymentMethodId().equals(paymentMethodId)) {
-                        throw new PaymentApiException(ErrorCode.PAYMENT_DEL_DEFAULT_PAYMENT_METHOD, account.getId());
+                        if (!deleteDefaultPaymentMethodWithAutoPayOff) {
+                            throw new PaymentApiException(ErrorCode.PAYMENT_DEL_DEFAULT_PAYMENT_METHOD, account.getId());
+                        } else {
+                            final CallContext context = new DefaultCallContext("PaymentMethodProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, null, clock);
+                            final boolean isAccountAutoPayOoff = isAccountAutoPayOff(account.getId());
+                            if (!isAccountAutoPayOoff) {
+                                log.info("Setting account {} to AUTO_PAY_OFF because of default payment method deletion");
+                                setAccountAutoPayOff(account.getId(), context);
+                            }
+                            accountUserApi.removePaymentMethod(account.getId(), context);
+                        }
                     }
                     final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, account.getId());
                     pluginApi.deletePaymentMethod(account.getExternalKey(), paymentMethodModel.getExternalId());
@@ -298,6 +317,8 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     return null;
                 } catch (PaymentPluginApiException e) {
                     throw new PaymentApiException(ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
+                } catch (AccountApiException e) {
+                    throw new PaymentApiException(e);
                 }
             }
         });
@@ -317,10 +338,9 @@ public class PaymentMethodProcessor extends ProcessorBase {
 
                 try {
                     final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, account.getId());
+
                     pluginApi.setDefaultPaymentMethod(account.getExternalKey(), paymentMethodModel.getExternalId());
-                    final MutableAccountData updateAccountData = new DefaultMutableAccountData(account);
-                    updateAccountData.setPaymentMethodId(paymentMethodId);
-                    accountUserApi.updateAccount(account.getId(), updateAccountData, context);
+                    accountUserApi.updatePaymentMethod(account.getId(), paymentMethodId, context);
                     return null;
                 } catch (PaymentPluginApiException e) {
                     throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index 02b6cb4..7509404 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -81,7 +81,6 @@ public class PaymentProcessor extends ProcessorBase {
 
     private final PaymentMethodProcessor paymentMethodProcessor;
     private final InvoicePaymentApi invoicePaymentApi;
-    private final TagUserApi tagUserApi;
     private final FailedPaymentRetryServiceScheduler failedPaymentRetryService;
     private final PluginFailureRetryServiceScheduler pluginFailureRetryService;
     private final AutoPayRetryServiceScheduler autoPayoffRetryService;
@@ -109,10 +108,9 @@ public class PaymentProcessor extends ProcessorBase {
                             final GlobalLocker locker,
                             @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
                             final CallContextFactory factory) {
-        super(pluginRegistry, accountUserApi, eventBus, paymentDao, locker, executor);
+        super(pluginRegistry, accountUserApi, eventBus, paymentDao, tagUserApi, locker, executor);
         this.paymentMethodProcessor = paymentMethodProcessor;
         this.invoicePaymentApi = invoicePaymentApi;
-        this.tagUserApi = tagUserApi;
         this.failedPaymentRetryService = failedPaymentRetryService;
         this.pluginFailureRetryService = pluginFailureRetryService;
         this.autoPayoffRetryService = autoPayoffRetryService;
@@ -272,12 +270,7 @@ public class PaymentProcessor extends ProcessorBase {
                 !isInstantPayment &&
                 !isAccountAutoPayOff) {
             log.warn(String.format("Setting account %s into AUTO_PAY_OFF because of bad payment %s", accountId, lastPaymentForPaymentMethod.getId()));
-            try {
-                tagUserApi.addTag(accountId, ObjectType.ACCOUNT, ControlTagType.AUTO_PAY_OFF.getId(), context);
-            } catch (TagApiException e) {
-                log.error("Failed to add AUTO_PAY_OFF on account " + accountId, e);
-                throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, "Failed to add AUTO_PAY_OFF on account " + accountId);
-            }
+            setAccountAutoPayOff(accountId, context);
         }
     }
 
@@ -312,16 +305,6 @@ public class PaymentProcessor extends ProcessorBase {
         retryFailedPaymentInternal(paymentId, PaymentStatus.PAYMENT_FAILURE);
     }
 
-    private boolean isAccountAutoPayOff(final UUID accountId) {
-        final Map<String, Tag> accountTags = tagUserApi.getTags(accountId, ObjectType.ACCOUNT);
-        for (final Tag cur : accountTags.values()) {
-            if (ControlTagType.AUTO_PAY_OFF.getId().equals(cur.getTagDefinitionId())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
 
     private void retryFailedPaymentInternal(final UUID paymentId, final PaymentStatus... expectedPaymentStates) {
 
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index 33c1431..7d542e9 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -15,6 +15,7 @@
  */
 package com.ning.billing.payment.core;
 
+import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
@@ -31,13 +32,19 @@ import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentMethodModelDao;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
 import com.ning.billing.util.bus.BusEvent;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.globallocker.GlobalLock;
 import com.ning.billing.util.globallocker.GlobalLocker;
 import com.ning.billing.util.globallocker.GlobalLocker.LockerType;
 import com.ning.billing.util.globallocker.LockFailedException;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
 
 public abstract class ProcessorBase {
 
@@ -49,6 +56,7 @@ public abstract class ProcessorBase {
     protected final GlobalLocker locker;
     protected final ExecutorService executor;
     protected final PaymentDao paymentDao;
+    protected final TagUserApi tagUserApi;
 
     private static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
 
@@ -56,6 +64,7 @@ public abstract class ProcessorBase {
                          final AccountUserApi accountUserApi,
                          final Bus eventBus,
                          final PaymentDao paymentDao,
+                         final TagUserApi tagUserApi,
                          final GlobalLocker locker,
                          final ExecutorService executor) {
         this.pluginRegistry = pluginRegistry;
@@ -64,6 +73,27 @@ public abstract class ProcessorBase {
         this.paymentDao = paymentDao;
         this.locker = locker;
         this.executor = executor;
+        this.tagUserApi = tagUserApi;
+    }
+
+    protected boolean isAccountAutoPayOff(final UUID accountId) {
+        final Map<String, Tag> accountTags = tagUserApi.getTags(accountId, ObjectType.ACCOUNT);
+        for (final Tag cur : accountTags.values()) {
+            if (ControlTagType.AUTO_PAY_OFF.getId().equals(cur.getTagDefinitionId())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    protected void setAccountAutoPayOff(UUID accountId, CallContext context) throws PaymentApiException {
+        try {
+            tagUserApi.addTag(accountId, ObjectType.ACCOUNT, ControlTagType.AUTO_PAY_OFF.getId(), context);
+        } catch (TagApiException e) {
+            log.error("Failed to add AUTO_PAY_OFF on account " + accountId, e);
+            throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, "Failed to add AUTO_PAY_OFF on account " + accountId);
+        }
     }
 
     protected PaymentPluginApi getPaymentProviderPlugin(final UUID paymentMethodId) throws PaymentApiException {
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index 63706ba..a254ff2 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -48,6 +48,7 @@ import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
@@ -77,10 +78,11 @@ public class RefundProcessor extends ProcessorBase {
                            final InvoicePaymentApi invoicePaymentApi,
                            final Bus eventBus,
                            final CallContextFactory factory,
+                           final TagUserApi tagUserApi,
                            final PaymentDao paymentDao,
                            final GlobalLocker locker,
                            @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
-        super(pluginRegistry, accountUserApi, eventBus, paymentDao, locker, executor);
+        super(pluginRegistry, accountUserApi, eventBus, paymentDao, tagUserApi, locker, executor);
         this.invoicePaymentApi = invoicePaymentApi;
         this.factory = factory;
     }
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 88369bf..f3a57e3 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
@@ -90,11 +90,13 @@ public class TestPaymentApi extends PaymentTestSuite {
 
     @BeforeClass(groups = "fast")
     public void setupClass() throws Exception {
-        account = testHelper.createTestAccount("yoyo.yahoo.com");
+        account = testHelper.createTestAccount("yoyo.yahoo.com", false);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setUp() throws EventBusException {
+    public void setUp() throws Exception {
+        final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
+        testHelper.addTestPaymentMethod(account, paymentMethodInfo);
         eventBus.start();
     }
 
@@ -201,14 +203,24 @@ public class TestPaymentApi extends PaymentTestSuite {
 
         boolean failed = false;
         try {
-            paymentApi.deletedPaymentMethod(account, newPaymentMethodId, context);
+            paymentApi.deletedPaymentMethod(account, newPaymentMethodId, false, context);
         } catch (PaymentApiException e) {
             failed = true;
         }
         assertTrue(failed);
 
-        paymentApi.deletedPaymentMethod(account, initDefaultMethod.getId(), context);
+        paymentApi.deletedPaymentMethod(account, initDefaultMethod.getId(), true,  context);
         methods = paymentApi.getPaymentMethods(account, false);
         assertEquals(methods.size(), 1);
+
+        // NOW retry with default payment method with special flag
+        paymentApi.deletedPaymentMethod(account, newPaymentMethodId, true, context);
+
+        methods = paymentApi.getPaymentMethods(account, false);
+        assertEquals(methods.size(), 0);
+
+
+
     }
+
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java
index a349629..68f95b9 100644
--- a/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java
+++ b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java
@@ -33,9 +33,12 @@ import com.ning.billing.payment.api.PaymentMethod;
 import com.ning.billing.payment.dao.MockPaymentDao;
 import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
 import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
+import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.globallocker.GlobalLocker;
 
 public class TestPaymentMethodProcessor extends PaymentTestSuite {
@@ -47,12 +50,14 @@ public class TestPaymentMethodProcessor extends PaymentTestSuite {
         final DefaultPaymentProviderPluginRegistry pluginRegistry = new DefaultPaymentProviderPluginRegistry(Mockito.mock(PaymentConfig.class));
         pluginRegistry.register(new ExternalPaymentProviderPlugin(new ClockMock()), ExternalPaymentProviderPlugin.PLUGIN_NAME);
 
+        final Clock clock = new DefaultClock();
         final AccountUserApi accountUserApi = Mockito.mock(AccountUserApi.class);
         final Bus bus = Mockito.mock(Bus.class);
         final MockPaymentDao paymentDao = new MockPaymentDao();
         final GlobalLocker globalLocker = Mockito.mock(GlobalLocker.class);
         final ExecutorService executorService = Mockito.mock(ExecutorService.class);
-        processor = new PaymentMethodProcessor(pluginRegistry, accountUserApi, bus, paymentDao, globalLocker, executorService);
+        final TagUserApi tagUserApi =  Mockito.mock(TagUserApi.class);
+        processor = new PaymentMethodProcessor(pluginRegistry, accountUserApi, bus, paymentDao, tagUserApi, clock, globalLocker, executorService);
     }
 
     @Test(groups = "fast")
diff --git a/payment/src/test/java/com/ning/billing/payment/TestHelper.java b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
index af509ab..ed4c905 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -95,7 +95,7 @@ public class TestHelper {
         return invoice;
     }
 
-    public Account createTestAccount(final String email) throws Exception {
+    public Account createTestAccount(final String email, final boolean addPaymentMethod) throws Exception {
         final String name = "First" + UUID.randomUUID().toString() + " " + "Last" + UUID.randomUUID().toString();
         final String externalKey = UUID.randomUUID().toString();
 
@@ -114,12 +114,14 @@ public class TestHelper {
         Mockito.when(accountUserApi.getAccountById(Mockito.<UUID>any())).thenReturn(account);
         Mockito.when(accountUserApi.getAccountByKey(Mockito.anyString())).thenReturn(account);
 
-        final PaymentMethodPlugin pm = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
-        addTestPaymentMethod(account, pm);
+        if (addPaymentMethod) {
+            final PaymentMethodPlugin pm = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
+            addTestPaymentMethod(account, pm);
+        }
         return account;
     }
 
-    private void addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo) throws Exception {
+    public void addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo) throws Exception {
         final UUID paymentMethodId = paymentApi.addPaymentMethod(PaymentTestModuleWithMocks.PLUGIN_TEST_NAME, account, true, paymentMethodInfo, context);
         Mockito.when(account.getPaymentMethodId()).thenReturn(paymentMethodId);
     }
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 0c021a6..9deb279 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -151,7 +151,7 @@ public class TestRetryService extends PaymentTestSuite {
 
     private void testSchedulesRetryInternal(final int maxTries, final FailureType failureType) throws Exception {
 
-        final Account account = testHelper.createTestAccount("yiyi.gmail.com");
+        final Account account = testHelper.createTestAccount("yiyi.gmail.com", true);
         final Invoice invoice = testHelper.createTestInvoice(account, clock.getUTCToday(), Currency.USD);
         final BigDecimal amount = new BigDecimal("10.00");
         final UUID subscriptionId = UUID.randomUUID();
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index 9d88abb..fab5627 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -42,6 +42,7 @@ import com.ning.billing.jaxrs.json.PaymentMethodJson;
 import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.json.TagDefinitionJson;
+import com.ning.billing.jaxrs.json.TagJson;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.http.client.Response;
 
@@ -226,6 +227,55 @@ public class TestAccount extends TestJaxrsBase {
         baseJson = response.getResponseBody();
         paymentMethods = mapper.readValue(baseJson, new TypeReference<List<PaymentMethodJson>>() {});
         assertEquals(paymentMethods.size(), 1);
+
+
+        //
+        // DELETE DEFAULT PAYMENT METHOD (without special flag first)
+        //
+        uri = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodPP.getPaymentMethodId() ;
+        response = doDelete(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.BAD_REQUEST.getStatusCode());
+
+        //
+        // RETRY TO DELETE DEFAULT PAYMENT METHOD (with special flag this time)
+        //
+        uri = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodPP.getPaymentMethodId() ;
+        queryParams = new HashMap<String, String>();
+        queryParams.put(JaxrsResource.QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF, "true");
+
+        response = doDelete(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        // CHECK ACCOUNT IS NOW AUTO_PAY_OFF
+
+        uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.TAGS;
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        baseJson = response.getResponseBody();
+        List<TagJson> tagsJson = mapper.readValue(baseJson, new TypeReference<List<TagJson>>() {});
+        Assert.assertEquals(tagsJson.size(), 1);
+        TagJson tagJson = tagsJson.get(0);
+        Assert.assertEquals(tagJson.getTagDefinitionName(), "AUTO_PAY_OFF");
+        Assert.assertEquals(tagJson.getTagDefinitionId(), new UUID(0, 1).toString());
+
+        // FETCH ACCOUNT AGAIN AND CHECK THERE IS NO DEFAULT PAYMENT METHOD SET
+        uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId();
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+
+        AccountJson updatedAccountJson = mapper.readValue(response.getResponseBody(), AccountJson.class);
+        Assert.assertEquals(updatedAccountJson.getAccountId(), accountJson.getAccountId());
+        Assert.assertNull(updatedAccountJson.getPaymentMethodId());
+
+        //
+        // FINALLY TRY TO REMOVE AUTO_PAY_OFF WITH NO DEFAULT PAYMENT METHOD ON ACCOUNT
+        //
+        uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.TAGS;
+        queryParams = new HashMap<String, String>();
+        queryParams.put(JaxrsResource.QUERY_TAGS, new UUID(0, 1).toString());
+        response = doDelete(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.BAD_REQUEST.getStatusCode());
+
     }
 
     @Test(groups = "slow")
diff --git a/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java
index 53d0596..d0210c2 100644
--- a/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java
@@ -165,4 +165,17 @@ public class MockAccountUserApi implements AccountUserApi {
             throws AccountApiException {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId,
+            CallContext context) throws AccountApiException {
+        throw new UnsupportedOperationException();
+
+    }
+
+    @Override
+    public void removePaymentMethod(UUID accountId, CallContext context)
+            throws AccountApiException {
+        throw new UnsupportedOperationException();
+    }
 }