killbill-uncached

payment: update payment attempt row with payment method id This

5/3/2017 5:46:40 PM

Details

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 06e6817..2969715 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
@@ -435,7 +435,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                 .isNotifiedForInvoices(false)
                 .externalKey(UUID.randomUUID().toString().substring(1, 8))
                 .currency(Currency.USD)
-                .paymentMethodId(UUID.randomUUID())
                 .timeZone(DateTimeZone.UTC);
         if (billingDay != null) {
             builder.billingCycleDayLocal(billingDay);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
index f5b74e6..7879aad 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -39,6 +39,7 @@ import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
 import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
@@ -108,6 +109,54 @@ public class TestPaymentWithControl extends TestIntegrationBase {
     }
 
     @Test(groups = "slow")
+    public void testAuthSuccessWithPaymentControlNullPaymentMethodId() throws Exception {
+        final AccountData accountData = getAccountData(1);
+        final Account account = accountUserApi.createAccount(accountData, callContext);
+
+        // Add non-default payment method
+        final PaymentMethodPlugin info = createPaymentMethodPlugin();
+        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, false, info, PLUGIN_PROPERTIES, callContext);
+
+        testPaymentControlWithControl.setAdjustedPaymentMethodId(paymentMethodId);
+
+        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
+        final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, null, null, BigDecimal.ONE, account.getCurrency(), null, null,
+                                                                                 properties, paymentOptions, callContext);
+        assertListenerStatus();
+
+        final Payment paymentWithAttempts = paymentApi.getPayment(payment.getId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertEquals(paymentWithAttempts.getPaymentMethodId(), paymentMethodId);
+        Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().size(), 1);
+        Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().get(0).getPaymentMethodId(), paymentMethodId);
+        Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().get(0).getStateName(), "SUCCESS");
+    }
+
+    @Test(groups = "slow")
+    public void testAuthFailureWithPaymentControlNullPaymentMethodId() throws Exception {
+        final AccountData accountData = getAccountData(1);
+        final Account account = accountUserApi.createAccount(accountData, callContext);
+
+        // Add non-default payment method
+        final PaymentMethodPlugin info = createPaymentMethodPlugin();
+        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, false, info, PLUGIN_PROPERTIES, callContext);
+
+        testPaymentControlWithControl.setAdjustedPaymentMethodId(paymentMethodId);
+
+        paymentPlugin.makeNextPaymentFailWithError();
+
+        busHandler.pushExpectedEvents(NextEvent.PAYMENT_ERROR);
+        final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, null, null, BigDecimal.ONE, account.getCurrency(), null, null,
+                                                                                 properties, paymentOptions, callContext);
+        assertListenerStatus();
+
+        final Payment paymentWithAttempts = paymentApi.getPayment(payment.getId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertEquals(paymentWithAttempts.getPaymentMethodId(), paymentMethodId);
+        Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().size(), 1);
+        Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().get(0).getPaymentMethodId(), paymentMethodId);
+        Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().get(0).getStateName(), "ABORTED");
+    }
+
+    @Test(groups = "slow")
     public void testAuthCaptureWithPaymentControl() throws Exception {
 
         final AccountData accountData = getAccountData(1);
@@ -184,6 +233,8 @@ public class TestPaymentWithControl extends TestIntegrationBase {
 
         private final Map<String, Integer> calls;
 
+        private UUID adjustedPaymentMethodId = null;
+
         public TestPaymentControlPluginApi() {
             calls = new HashMap<String, Integer>();
         }
@@ -194,6 +245,11 @@ public class TestPaymentWithControl extends TestIntegrationBase {
 
         public void reset() {
             calls.clear();
+            adjustedPaymentMethodId = null;
+        }
+
+        public void setAdjustedPaymentMethodId(final UUID adjustedPaymentMethodId) {
+            this.adjustedPaymentMethodId = adjustedPaymentMethodId;
         }
 
         @Override
@@ -213,7 +269,7 @@ public class TestPaymentWithControl extends TestIntegrationBase {
                 }
                 @Override
                 public UUID getAdjustedPaymentMethodId() {
-                    return null;
+                    return adjustedPaymentMethodId;
                 }
                 @Override
                 public Iterable<PluginProperty> getAdjustedPluginProperties() {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java
index f8150da..17763cf 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java
@@ -1,5 +1,6 @@
 /*
- * Copyright 2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -85,13 +86,14 @@ public class PaymentAttemptJson extends JsonBase {
 
     public PaymentAttemptJson(final PaymentAttempt paymentAttempt, final String paymentExternalKey, @Nullable final List<AuditLog> attemptsLogs) {
         this(paymentAttempt.getAccountId().toString(),
-             paymentAttempt.getPaymentMethodId().toString(),
+             // Could be null if aborted in the priorCall
+             paymentAttempt.getPaymentMethodId() != null ? paymentAttempt.getPaymentMethodId().toString() : null,
              paymentExternalKey,
              paymentAttempt.getTransactionId() != null ? paymentAttempt.getTransactionId().toString() : null,
              paymentAttempt.getTransactionExternalKey(),
              paymentAttempt.getTransactionType().toString(),
              paymentAttempt.getEffectiveDate(),
-             paymentAttempt.getStateName() != null ? paymentAttempt.getStateName() : null,
+             paymentAttempt.getStateName(),
              paymentAttempt.getAmount(),
              paymentAttempt.getCurrency() != null ? paymentAttempt.getCurrency().toString() : null,
              paymentAttempt.getPluginName(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java
index 016be05..f64e772 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java
@@ -20,6 +20,7 @@ package org.killbill.billing.payment.core;
 import java.util.Set;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.killbill.billing.ErrorCode;
@@ -45,9 +46,11 @@ public class PaymentPluginServiceRegistration {
         return pluginRegistry.getAllServices();
     }
 
-    public PaymentMethodModelDao getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+    public PaymentMethodModelDao getPaymentMethodById(@Nullable final UUID paymentMethodId, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
         final PaymentMethodModelDao paymentMethodModel;
-        if (includedDeleted) {
+        if (paymentMethodId == null) {
+            paymentMethodModel = null;
+        } else if (includedDeleted) {
             paymentMethodModel = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, context);
         } else {
             paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
index 0d3f97f..4f67980 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -62,7 +62,12 @@ public class DefaultControlCompleted implements EnteringStateCallback {
                                    null;
 
         // At this stage we can update the paymentAttempt state AND serialize the plugin properties. Control plugins will have had the opportunity to erase sensitive data if required.
-        retryablePaymentAutomatonRunner.getPaymentDao().updatePaymentAttemptWithProperties(attempt.getId(), transactionId, state.getName(), getSerializedProperties(), paymentStateContext.getInternalCallContext());
+        retryablePaymentAutomatonRunner.getPaymentDao().updatePaymentAttemptWithProperties(attempt.getId(),
+                                                                                           paymentStateContext.getPaymentMethodId(),
+                                                                                           transactionId,
+                                                                                           state.getName(),
+                                                                                           getSerializedProperties(),
+                                                                                           paymentStateContext.getInternalCallContext());
 
         if (retriedState.getName().equals(state.getName()) && !isUnknownTransaction()) {
             retryServiceScheduler.scheduleRetry(ObjectType.PAYMENT_ATTEMPT, attempt.getId(), attempt.getId(), attempt.getTenantRecordId(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index 22baff9..f06040a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -126,14 +126,14 @@ public class DefaultPaymentDao extends EntityDaoBase<PaymentModelDao, Payment, P
     }
 
     @Override
-    public void updatePaymentAttemptWithProperties(final UUID paymentAttemptId, final UUID transactionId, final String state, final byte[] pluginProperties, final InternalCallContext context) {
+    public void updatePaymentAttemptWithProperties(final UUID paymentAttemptId, final UUID paymentMethodId, final UUID transactionId, final String state, final byte[] pluginProperties, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
 
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final String transactionIdStr = transactionId != null ? transactionId.toString() : null;
                 final PaymentAttemptSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class);
-                transactional.updateAttemptWithProperties(paymentAttemptId.toString(), transactionIdStr, state, pluginProperties, contextWithUpdatedDate(context));
+                transactional.updateAttemptWithProperties(paymentAttemptId.toString(), paymentMethodId == null ? null : paymentMethodId.toString(), transactionIdStr, state, pluginProperties, contextWithUpdatedDate(context));
                 return null;
             }
         });
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
index d9aef5d..cbbbc66 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -46,6 +48,7 @@ public interface PaymentAttemptSqlDao extends EntitySqlDao<PaymentAttemptModelDa
     @SqlUpdate
     @Audited(ChangeType.UPDATE)
     void updateAttemptWithProperties(@Bind("id") final String attemptId,
+                                     @Bind("paymentMethodId") final String paymentMethodId,
                                      @Bind("transactionId") final String transactionId,
                                      @Bind("stateName") final String stateName,
                                      @Bind("pluginProperties") final byte[] pluginProperties,
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
index b7180e8..a2be2ae 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -41,7 +41,7 @@ public interface PaymentDao extends EntityDao<PaymentModelDao, Payment, PaymentA
 
     public void updatePaymentAttempt(UUID paymentAttemptId, UUID transactionId, String state, InternalCallContext context);
 
-    public void updatePaymentAttemptWithProperties(UUID paymentAttemptId, UUID transactionId, String state, final byte[] pluginProperties, InternalCallContext context);
+    public void updatePaymentAttemptWithProperties(UUID paymentAttemptId, UUID paymentMethodId, UUID transactionId, String state, final byte[] pluginProperties, InternalCallContext context);
 
     public Pagination<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(String stateName, DateTime createdBeforeDate, final Long offset, final Long limit);
 
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index d8699fe..ebde91c 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -102,6 +102,7 @@ where id = :id
 updateAttemptWithProperties() ::= <<
 update <tableName()>
 set state_name = :stateName
+, payment_method_id = :paymentMethodId
 , transaction_id = :transactionId
 , plugin_properties = :pluginProperties
 , updated_by = :updatedBy
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
index f167975..b932f15 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
@@ -100,15 +101,18 @@ public class MockPaymentDao extends MockEntityDaoBase<PaymentModelDao, Payment, 
 
     @Override
     public void updatePaymentAttempt(final UUID paymentAttemptId, final UUID transactionId, final String state, final InternalCallContext context) {
-        updatePaymentAttemptWithProperties(paymentAttemptId, transactionId, state, null, context);
+        updatePaymentAttemptWithProperties(paymentAttemptId, null, transactionId, state, null, context);
     }
 
     @Override
-    public void updatePaymentAttemptWithProperties(final UUID paymentAttemptId, final UUID transactionId, final String state, final byte[] pluginProperties, final InternalCallContext context) {
+    public void updatePaymentAttemptWithProperties(final UUID paymentAttemptId, @Nullable final UUID paymentMethodId, final UUID transactionId, final String state, final byte[] pluginProperties, final InternalCallContext context) {
         boolean success = false;
         synchronized (this) {
             for (PaymentAttemptModelDao cur : attempts.values()) {
                 if (cur.getId().equals(paymentAttemptId)) {
+                    if (paymentMethodId != null) {
+                        cur.setPaymentMethodId(paymentMethodId);
+                    }
                     cur.setStateName(state);
                     cur.setTransactionId(transactionId);
                     if (pluginProperties != null) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index 113c147..8e0f0fe 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -564,7 +564,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         properties.add(new PluginProperty("prop2", "value2", false));
 
         final byte [] serializedProperties = PluginPropertySerializer.serialize(properties);
-        paymentDao.updatePaymentAttemptWithProperties(rehydratedAttempt.getId(), transactionId, newStateName, serializedProperties, internalCallContext);
+        paymentDao.updatePaymentAttemptWithProperties(rehydratedAttempt.getId(), rehydratedAttempt.getPaymentMethodId(), transactionId, newStateName, serializedProperties, internalCallContext);
         final PaymentAttemptModelDao attempt2 = paymentDao.getPaymentAttempt(rehydratedAttempt.getId(), internalCallContext);
         assertEquals(attempt2.getStateName(), newStateName);
         assertEquals(attempt2.getTransactionId(), transactionId);
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
index 3719daf..f451943 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -17,6 +17,8 @@
 
 package org.killbill.billing.payment.provider;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.retry.DefaultFailureCallResult;
@@ -33,6 +35,7 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
 
     public static final String PLUGIN_NAME = "MOCK_RETRY_PLUGIN";
 
+    private UUID adjustedPaymentMethodId;
     private boolean isAborted;
     private DateTime nextRetryDate;
     private Exception exception;
@@ -41,6 +44,11 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
     private boolean onSuccessCallExecuted;
     private boolean onFailureCallExecuted;
 
+    public MockPaymentControlProviderPlugin setAdjustedPaymentMethodId(final UUID adjustedPaymentMethodId) {
+        this.adjustedPaymentMethodId = adjustedPaymentMethodId;
+        return this;
+    }
+
     public MockPaymentControlProviderPlugin setAborted(final boolean isAborted) {
         this.isAborted = isAborted;
         return this;
@@ -69,7 +77,7 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
         } else if (exception instanceof RuntimeException) {
             throw (RuntimeException) exception;
         }
-        return new DefaultPriorPaymentControlResult(isAborted);
+        return new DefaultPriorPaymentControlResult(isAborted, adjustedPaymentMethodId, null, null, null);
     }
 
     @Override
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 25517ab..319573b 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
@@ -29,6 +29,7 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.KillBillClient;
+import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.KillBillHttpClient;
 import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Account;
@@ -119,11 +120,16 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
     protected Account createAccountWithExternalPaymentMethod() throws Exception {
         final Account input = createAccount();
 
+        createPaymentMethod(input, true);
+
+        return killBillClient.getAccount(input.getExternalKey(), requestOptions);
+    }
+
+    protected PaymentMethod createPaymentMethod(final Account input, final boolean isDefault) throws KillBillClientException {
         final PaymentMethodPluginDetail info = new PaymentMethodPluginDetail();
         final PaymentMethod paymentMethodJson = new PaymentMethod(null, UUIDs.randomUUID().toString(), input.getAccountId(),
-                                                                  true, ExternalPaymentProviderPlugin.PLUGIN_NAME, info);
-        killBillClient.createPaymentMethod(paymentMethodJson, requestOptions);
-        return killBillClient.getAccount(input.getExternalKey(), requestOptions);
+                                                                  isDefault, ExternalPaymentProviderPlugin.PLUGIN_NAME, info);
+        return killBillClient.createPaymentMethod(paymentMethodJson, requestOptions);
     }
 
     protected Account createAccount() throws Exception {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index 13b0fa4..69c17a0 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -19,6 +19,7 @@ package org.killbill.billing.jaxrs;
 
 import java.math.BigDecimal;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
@@ -617,6 +618,29 @@ public class TestPayment extends TestJaxrsBase {
     }
 
     @Test(groups = "slow")
+    public void testComboAuthorizationControlWithNullPaymentMethodId() throws Exception {
+        final Account accountJson = createAccount();
+
+        // Non-default payment method
+        final PaymentMethod paymentMethod = createPaymentMethod(accountJson, false);
+        mockPaymentControlProviderPlugin.setAdjustedPaymentMethodId(paymentMethod.getPaymentMethodId());
+
+        accountJson.setAccountId(null);
+        final String paymentExternalKey = UUID.randomUUID().toString();
+
+        final PaymentTransaction authTransactionJson = new PaymentTransaction();
+        authTransactionJson.setPaymentExternalKey(paymentExternalKey);
+        authTransactionJson.setAmount(BigDecimal.TEN);
+        authTransactionJson.setTransactionType("AUTHORIZE");
+        final ComboPaymentTransaction comboPaymentTransaction = new ComboPaymentTransaction(accountJson, null, authTransactionJson, ImmutableList.<PluginProperty>of(), ImmutableList.<PluginProperty>of());
+
+        final Payment payment = killBillClient.createPayment(comboPaymentTransaction, ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME), ImmutableMap.<String, String>of(), requestOptions);
+        verifyComboPayment(payment, paymentExternalKey, BigDecimal.TEN, BigDecimal.ZERO, BigDecimal.ZERO, 1, 1);
+
+        assertEquals(killBillClient.getPayment(payment.getPaymentId(), false, true, ImmutableMap.<String, String>of(), AuditLevel.NONE, requestOptions).getPaymentAttempts().size(), 1);
+    }
+
+    @Test(groups = "slow")
     public void testComboAuthorizationControlPluginException() throws Exception {
         final Account accountJson = getAccount();
         accountJson.setAccountId(null);
@@ -799,7 +823,7 @@ public class TestPayment extends TestJaxrsBase {
         assertEquals(payment.getRefundedAmount().compareTo(refundedAmount), 0);
         assertEquals(payment.getTransactions().size(), nbTransactions);
 
-        final Payments Payments = killBillClient.getPayments();
+        final Payments Payments = killBillClient.getPayments(requestOptions);
         assertEquals(Payments.size(), paymentNb);
         assertEquals(Payments.get(paymentNb - 1), payment);
     }