killbill-memoizeit

subscription,beatrix: Add bundle externalKey in subscription

12/28/2016 10:38:10 PM

Changes

pom.xml 2(+1 -1)

Details

diff --git a/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java b/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java
index 1073d87..7937ed8 100644
--- a/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java
+++ b/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java
@@ -30,6 +30,8 @@ public interface SubscriptionInternalEvent extends BusInternalEvent {
 
     UUID getBundleId();
 
+    String getBundleExternalKey();
+
     UUID getSubscriptionId();
 
     DateTime getSubscriptionStartDate();
diff --git a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
index 8d4eff7..913eb94 100644
--- a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
@@ -162,7 +162,7 @@ public class BeatrixListener {
                 }
 
                 SubscriptionMetadata.ActionType actionType = (event instanceof EffectiveSubscriptionInternalEvent) ? ActionType.EFFECTIVE : ActionType.REQUESTED;
-                final SubscriptionMetadata subscriptionMetadataObj = new SubscriptionMetadata(actionType);
+                final SubscriptionMetadata subscriptionMetadataObj = new SubscriptionMetadata(actionType, realEventST.getBundleExternalKey());
                 metaData = objectMapper.writeValueAsString(subscriptionMetadataObj);
                 break;
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
index ae8b7a3..2d5f61e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
@@ -18,6 +18,7 @@
 
 package org.killbill.billing.beatrix.integration;
 
+import java.io.IOException;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -26,12 +27,15 @@ import org.joda.time.DateTime;
 import org.killbill.billing.DBTestingHelper;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.extbus.DefaultBusExternalEvent;
 import org.killbill.billing.callcontext.DefaultCallContext;
 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.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.notification.plugin.api.ExtBusEvent;
+import org.killbill.billing.notification.plugin.api.ExtBusEventType;
+import org.killbill.billing.notification.plugin.api.SubscriptionMetadata;
 import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.DefaultTenant;
@@ -41,15 +45,19 @@ import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.UserType;
+import org.killbill.billing.util.jackson.ObjectMapper;
 import org.killbill.billing.util.nodes.NodeCommand;
 import org.killbill.billing.util.nodes.NodeCommandMetadata;
 import org.killbill.billing.util.nodes.NodeCommandProperty;
 import org.killbill.billing.util.nodes.PluginNodeCommandMetadata;
 import org.killbill.billing.util.nodes.SystemNodeCommandType;
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.eventbus.Subscribe;
@@ -64,6 +72,8 @@ public class TestPublicBus extends TestIntegrationBase {
 
     private AtomicInteger externalBusCount;
 
+    private final ObjectMapper mapper = new ObjectMapper();
+
     @Override
     protected KillbillConfigSource getConfigSource() {
         ImmutableMap additionalProperties = new ImmutableMap.Builder()
@@ -78,6 +88,26 @@ public class TestPublicBus extends TestIntegrationBase {
         @Subscribe
         public void handleExternalEvents(final ExtBusEvent event) {
             log.info("GOT EXT EVENT " + event);
+
+            if (event.getEventType() == ExtBusEventType.SUBSCRIPTION_CREATION ||
+                event.getEventType() == ExtBusEventType.SUBSCRIPTION_CANCEL ||
+                event.getEventType() == ExtBusEventType.SUBSCRIPTION_PHASE ||
+                event.getEventType() == ExtBusEventType.SUBSCRIPTION_CHANGE ||
+                event.getEventType() == ExtBusEventType.SUBSCRIPTION_UNCANCEL ||
+                event.getEventType() == ExtBusEventType.SUBSCRIPTION_BCD_CHANGE) {
+                try {
+                    final SubscriptionMetadata obj = (SubscriptionMetadata) mapper.readValue(event.getMetaData(), SubscriptionMetadata.class);
+                    Assert.assertNotNull(obj.getBundleExternalKey());
+                    Assert.assertNotNull(obj.getActionType());
+                } catch (JsonParseException e) {
+                    Assert.fail("Could not deserialize metada section", e);
+                } catch (JsonMappingException e) {
+                    Assert.fail("Could not deserialize metada section", e);
+                } catch (IOException e) {
+                    Assert.fail("Could not deserialize metada section", e);
+                }
+            }
+
             externalBusCount.incrementAndGet();
 
         }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index dc0198b..dcff960 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -53,11 +53,13 @@ import static org.testng.Assert.assertNull;
 public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteNoDB {
 
     private UUID bundleId;
+    private String bundleExternalKey;
 
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
         super.beforeClass();
         bundleId = UUID.randomUUID();
+        bundleExternalKey = bundleId.toString();
     }
 
     public class TestSubscriptionBundleTimeline extends DefaultSubscriptionBundleTimeline {
@@ -1559,6 +1561,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         return new SubscriptionBaseTransitionData(UUID.randomUUID(),
                                                   entitlementId,
                                                   bundleId,
+                                                  bundleExternalKey,
                                                   eventType,
                                                   apiEventType,
                                                   effectiveDate,
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
index eddb672..b6bc999 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
@@ -78,6 +78,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     private static final UUID eventId = new UUID(0L, 0L);
     private static final UUID subId = new UUID(1L, 0L);
     private static final UUID bunId = new UUID(2L, 0L);
+    private static final String bunKey = bunId.toString();
 
     private List<EffectiveSubscriptionInternalEvent> effectiveSubscriptionTransitions;
     private SubscriptionBase subscription;
@@ -282,7 +283,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         final PriceList nextPriceList = catalog.findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
 
         final EffectiveSubscriptionInternalEvent t = new MockEffectiveSubscriptionEvent(
-                eventId, subId, bunId, then, now, null, null, null, null, null, EntitlementState.ACTIVE,
+                eventId, subId, bunId, bunKey, then, now, null, null, null, null, null, EntitlementState.ACTIVE,
                 nextPlan.getName(), nextPhase.getName(),
                 nextPriceList.getName(), null, 1L,
                 SubscriptionBaseTransitionType.CREATE, 1, null, 1L, 2L, null);

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index c2cdcb7..e12f6bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.140.7</version>
+        <version>0.140.8</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.18.2-SNAPSHOT</version>
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 798a653..877aeb1 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -192,6 +192,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             return apiService.createPlan(new SubscriptionBuilder()
                                                  .setId(UUIDs.randomUUID())
                                                  .setBundleId(bundleId)
+                                                 .setBundleExternalKey(bundle.getExternalKey())
                                                  .setCategory(plan.getProduct().getCategory())
                                                  .setBundleStartDate(bundleStartDate)
                                                  .setAlignStartDate(effectiveDate)
@@ -202,7 +203,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         }
     }
 
-    private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final UUID bundleId, final Iterable<EntitlementSpecifier> entitlements, final boolean isMigrated, final InternalCallContext context, final DateTime now, final DateTime effectiveDate, final Catalog catalog, final CallContext callContext) throws SubscriptionBaseApiException, CatalogApiException {
+    private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final UUID bundleId, final String externalKey, final Iterable<EntitlementSpecifier> entitlements, final boolean isMigrated, final InternalCallContext context, final DateTime now, final DateTime effectiveDate, final Catalog catalog, final CallContext callContext) throws SubscriptionBaseApiException, CatalogApiException {
         final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
         boolean first = true;
         final List<SubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundleId, null, context);
@@ -248,6 +249,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             subscription.setBuilder(new SubscriptionBuilder()
                                             .setId(UUIDs.randomUUID())
                                             .setBundleId(bundleId)
+                                            .setBundleExternalKey(externalKey)
                                             .setCategory(plan.getProduct().getCategory())
                                             .setBundleStartDate(effectiveDate)
                                             .setAlignStartDate(effectiveDate)
@@ -276,6 +278,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                         bundle.getId(),
                         effectiveDate,
                         verifyAndBuildSubscriptionSpecifiers(bundle.getId(),
+                                                             bundle.getExternalKey(),
                                                              entitlementWithAddOnsSpecifier.getEntitlementSpecifier(),
                                                              entitlementWithAddOnsSpecifier.isMigrated(),
                                                              context,
@@ -641,6 +644,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                     final SubscriptionBuilder builder = new SubscriptionBuilder()
                             .setId(subscriptionId)
                             .setBundleId(bundleId)
+                            .setBundleExternalKey(null)
                             .setCategory(plan.getProduct().getCategory())
                             .setBundleStartDate(bundleStartDate)
                             .setAlignStartDate(startEffectiveDate);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index bd62893..0c45606 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -248,6 +248,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
                 final DefaultSubscriptionBase defaultSubscriptionBase = createSubscriptionForApiUse(new SubscriptionBuilder()
                                                                                                             .setId(UUIDs.randomUUID())
                                                                                                             .setBundleId(subscriptionBundleData.getId())
+                                                                                                            .setBundleExternalKey(subscriptionBundleData.getExternalKey())
                                                                                                             .setCategory(productCategory)
                                                                                                             .setBundleStartDate(effectiveTransferDate)
                                                                                                             .setAlignStartDate(subscriptionAlignStartDate),
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java
index 4e4d7b7..cc03427 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java
@@ -37,6 +37,7 @@ public class DefaultEffectiveSubscriptionEvent extends DefaultSubscriptionEvent 
     public DefaultEffectiveSubscriptionEvent(@JsonProperty("eventId") final UUID eventId,
                                              @JsonProperty("subscriptionId") final UUID subscriptionId,
                                              @JsonProperty("bundleId") final UUID bundleId,
+                                             @JsonProperty("bundleExternalKey") final String bundleExternalKey,
                                              @JsonProperty("effectiveTransitionTime") final DateTime effectiveTransitionTime,
                                              @JsonProperty("previousState") final EntitlementState previousState,
                                              @JsonProperty("previousPlan") final String previousPlan,
@@ -55,7 +56,7 @@ public class DefaultEffectiveSubscriptionEvent extends DefaultSubscriptionEvent 
                                              @JsonProperty("searchKey1") final Long searchKey1,
                                              @JsonProperty("searchKey2") final Long searchKey2,
                                              @JsonProperty("userToken") final UUID userToken) {
-        super(eventId, subscriptionId, bundleId, effectiveTransitionTime, effectiveTransitionTime, previousState, previousPlan,
+        super(eventId, subscriptionId, bundleId, bundleExternalKey, effectiveTransitionTime, effectiveTransitionTime, previousState, previousPlan,
               previousPhase, previousPriceList, previousBillCycleDayLocal, nextState, nextPlan, nextPhase, nextPriceList, nextBillCycleDayLocal, totalOrdering,
               transitionType, remainingEventsForUserOperation, startDate, searchKey1, searchKey2, userToken);
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java
index 1e918ef..f9ecaa3 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java
@@ -34,6 +34,7 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
     public DefaultRequestedSubscriptionEvent(@JsonProperty("eventId") final UUID eventId,
                                              @JsonProperty("subscriptionId") final UUID subscriptionId,
                                              @JsonProperty("bundleId") final UUID bundleId,
+                                             @JsonProperty("bundleExternalKey") final String bundleExternalKey,
                                              @JsonProperty("requestedTransitionTime") final DateTime requestedTransitionTime,
                                              @JsonProperty("effectiveTransitionTime") final DateTime effectiveTransitionTime,
                                              @JsonProperty("previousState") final EntitlementState previousState,
@@ -53,7 +54,7 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
                                              @JsonProperty("searchKey1") final Long searchKey1,
                                              @JsonProperty("searchKey2") final Long searchKey2,
                                              @JsonProperty("userToken") final UUID userToken) {
-        super(eventId, subscriptionId, bundleId, requestedTransitionTime, effectiveTransitionTime, previousState, previousPlan,
+        super(eventId, subscriptionId, bundleId, bundleExternalKey, requestedTransitionTime, effectiveTransitionTime, previousState, previousPlan,
               previousPhase, previousPriceList, previousBillCycleDayLocal, nextState, nextPlan, nextPhase, nextPriceList, nextBillCycleDayLocal, totalOrdering,
               transitionType, remainingEventsForUserOperation, startDate, searchKey1, searchKey2, userToken);
     }
@@ -64,7 +65,7 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
                                              final Long searchKey1,
                                              final Long searchKey2,
                                              final UUID userToken) {
-        this(nextEvent.getId(), nextEvent.getSubscriptionId(), subscription.getBundleId(), nextEvent.getEffectiveDate(), nextEvent.getEffectiveDate(),
+        this(nextEvent.getId(), nextEvent.getSubscriptionId(), subscription.getBundleId(), subscription.getBundleExternalKey(), nextEvent.getEffectiveDate(), nextEvent.getEffectiveDate(),
              null, null, null, null, null, null, null, null, null, null, nextEvent.getTotalOrdering(), transitionType, 0, null, searchKey1, searchKey2, userToken);
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index 91acbb2..e255419 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -83,6 +83,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     // Final subscription fields
     //
     private final UUID bundleId;
+    private final String bundleExternalKey;
     private final DateTime alignStartDate;
     private final DateTime bundleStartDate;
     private final ProductCategory category;
@@ -118,6 +119,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         this.apiService = apiService;
         this.clock = clock;
         this.bundleId = builder.getBundleId();
+        this.bundleExternalKey = builder.getBundleExternalKey();
         this.alignStartDate = builder.getAlignStartDate();
         this.bundleStartDate = builder.getBundleStartDate();
         this.category = builder.getCategory();
@@ -131,6 +133,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         this.apiService = apiService;
         this.clock = clock;
         this.bundleId = internalSubscription.getBundleId();
+        this.bundleExternalKey = internalSubscription.getBundleExternalKey();
         this.alignStartDate = internalSubscription.getAlignStartDate();
         this.bundleStartDate = internalSubscription.getBundleStartDate();
         this.category = internalSubscription.getCategory();
@@ -145,6 +148,10 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         return bundleId;
     }
 
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
     @Override
     public DateTime getStartDate() {
         return transitions.get(0).getEffectiveTransitionTime();
@@ -713,7 +720,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             nextPriceList = (nextPlan != null) ? catalog.findPriceListForPlan(nextPlanName, cur.getEffectiveDate(), getAlignStartDate()) : null;
 
             final SubscriptionBaseTransitionData transition = new SubscriptionBaseTransitionData(
-                    cur.getId(), id, bundleId, cur.getType(), apiEventType,
+                    cur.getId(), id, bundleId, bundleExternalKey, cur.getType(), apiEventType,
                     cur.getEffectiveDate(),
                     prevEventId, prevCreatedDate,
                     previousState, previousPlan, previousPhase,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java
index 89f8aa7..71ab5da 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java
@@ -34,6 +34,7 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
     private final Long totalOrdering;
     private final UUID subscriptionId;
     private final UUID bundleId;
+    private final String bundleExternalKey;
     private final UUID eventId;
     private final DateTime requestedTransitionTime;
     private final DateTime effectiveTransitionTime;
@@ -59,6 +60,7 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         this(in.getId(),
              in.getSubscriptionId(),
              in.getBundleId(),
+             in.getBundleExternalKey(),
              in.getEffectiveTransitionTime(),
              in.getEffectiveTransitionTime(),
              in.getPreviousState(),
@@ -84,6 +86,7 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
     public DefaultSubscriptionEvent(@JsonProperty("eventId") final UUID eventId,
                                     @JsonProperty("subscriptionId") final UUID subscriptionId,
                                     @JsonProperty("bundleId") final UUID bundleId,
+                                    @JsonProperty("bundleExternalKey") final String bundleExternalKey,
                                     @JsonProperty("requestedTransitionTime") final DateTime requestedTransitionTime,
                                     @JsonProperty("effectiveTransitionTime") final DateTime effectiveTransitionTime,
                                     @JsonProperty("previousState") final EntitlementState previousState,
@@ -107,6 +110,7 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         this.eventId = eventId;
         this.subscriptionId = subscriptionId;
         this.bundleId = bundleId;
+        this.bundleExternalKey = bundleExternalKey;
         this.requestedTransitionTime = requestedTransitionTime;
         this.effectiveTransitionTime = effectiveTransitionTime;
         this.previousState = previousState;
@@ -148,6 +152,11 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
     }
 
     @Override
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
+    @Override
     public EntitlementState getPreviousState() {
         return previousState;
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java
index 511b142..b8beb50 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java
@@ -33,6 +33,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
     private final Long totalOrdering;
     private final UUID subscriptionId;
     private final UUID bundleId;
+    private final String bundleExternalKey;
     private final UUID eventId;
     private final EventType eventType;
     private final ApiEventType apiEventType;
@@ -59,6 +60,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
     public SubscriptionBaseTransitionData(final UUID eventId,
                                           final UUID subscriptionId,
                                           final UUID bundleId,
+                                          final String bundleExternalKey,
                                           final EventType eventType,
                                           final ApiEventType apiEventType,
                                           final DateTime effectiveTransitionTime,
@@ -83,6 +85,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         this.eventId = eventId;
         this.subscriptionId = subscriptionId;
         this.bundleId = bundleId;
+        this.bundleExternalKey = bundleExternalKey;
         this.eventType = eventType;
         this.apiEventType = apiEventType;
         this.effectiveTransitionTime = effectiveTransitionTime;
@@ -117,6 +120,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         this.eventId = input.getId();
         this.subscriptionId = input.getSubscriptionId();
         this.bundleId = input.getBundleId();
+        this.bundleExternalKey = input.getBundleExternalKey();
         this.eventType = eventType;
         this.apiEventType = apiEventType;
         this.effectiveTransitionTime = input.getEffectiveTransitionTime();
@@ -156,6 +160,10 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         return bundleId;
     }
 
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
     @Override
     public EntitlementState getPreviousState() {
         return previousState;
@@ -285,6 +293,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         sb.append(", totalOrdering=").append(totalOrdering);
         sb.append(", subscriptionId=").append(subscriptionId);
         sb.append(", bundleId=").append(bundleId);
+        sb.append(", bundleExternalKey=").append(bundleExternalKey);
         sb.append(", eventId=").append(eventId);
         sb.append(", eventType=").append(eventType);
         sb.append(", effectiveTransitionTime=").append(effectiveTransitionTime);
@@ -322,6 +331,9 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
             return false;
         }
+        if (bundleExternalKey != null ? !bundleExternalKey.equals(that.bundleExternalKey) : that.bundleExternalKey != null) {
+            return false;
+        }
         if (effectiveTransitionTime != null ? effectiveTransitionTime.compareTo(that.effectiveTransitionTime) != 0 : that.effectiveTransitionTime != null) {
             return false;
         }
@@ -384,6 +396,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         int result = totalOrdering != null ? totalOrdering.hashCode() : 0;
         result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (bundleExternalKey != null ? bundleExternalKey.hashCode() : 0);
         result = 31 * result + (eventId != null ? eventId.hashCode() : 0);
         result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
         result = 31 * result + (apiEventType != null ? apiEventType.hashCode() : 0);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
index 7beabf6..97df7fd 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
@@ -28,6 +28,7 @@ public class SubscriptionBuilder {
 
     private UUID id;
     private UUID bundleId;
+    private String bundleExternalKey;
     private DateTime createdDate;
     private DateTime updatedDate;
     private DateTime alignStartDate;
@@ -42,6 +43,7 @@ public class SubscriptionBuilder {
     public SubscriptionBuilder(final DefaultSubscriptionBase original) {
         this.id = original.getId();
         this.bundleId = original.getBundleId();
+        this.bundleExternalKey = original.getBundleExternalKey();
         this.alignStartDate = original.getAlignStartDate();
         this.bundleStartDate = original.getBundleStartDate();
         this.category = original.getCategory();
@@ -69,6 +71,11 @@ public class SubscriptionBuilder {
         return this;
     }
 
+    public SubscriptionBuilder setBundleExternalKey(final String bundleExternalKey) {
+        this.bundleExternalKey = bundleExternalKey;
+        return this;
+    }
+
     public SubscriptionBuilder setAlignStartDate(final DateTime alignStartDate) {
         this.alignStartDate = alignStartDate;
         return this;
@@ -109,6 +116,10 @@ public class SubscriptionBuilder {
         return bundleId;
     }
 
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
     public DateTime getAlignStartDate() {
         return alignStartDate;
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 9c842a1..cff861a 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -55,7 +55,6 @@ import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEv
 import org.killbill.billing.subscription.api.user.DefaultRequestedSubscriptionEvent;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
-import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionData;
 import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
@@ -305,8 +304,9 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         final SubscriptionBase shellSubscription = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<SubscriptionBase>() {
             @Override
             public SubscriptionBase inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final SubscriptionModelDao model = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getById(subscriptionId.toString(), context);
-                return SubscriptionModelDao.toSubscription(model);
+                final SubscriptionModelDao subscriptionModel = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getById(subscriptionId.toString(), context);
+                final SubscriptionBundleModelDao bundleModel = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getById(subscriptionModel.getBundleId().toString(), context);
+                return SubscriptionModelDao.toSubscription(subscriptionModel, bundleModel.getExternalKey());
             }
         });
         return buildSubscription(shellSubscription, context);
@@ -321,11 +321,14 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBase>>() {
             @Override
             public List<SubscriptionBase> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+
+                final SubscriptionBundleModelDao bundleModel = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getById(bundleId.toString(), context);
+
                 final List<SubscriptionModelDao> models = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getSubscriptionsFromBundleId(bundleId.toString(), context);
                 return new ArrayList<SubscriptionBase>(Collections2.transform(models, new Function<SubscriptionModelDao, SubscriptionBase>() {
                     @Override
                     public SubscriptionBase apply(@Nullable final SubscriptionModelDao input) {
-                        return SubscriptionModelDao.toSubscription(input);
+                        return SubscriptionModelDao.toSubscription(input, bundleModel.getExternalKey());
                     }
                 }));
             }
@@ -364,11 +367,20 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         final List<SubscriptionBase> allSubscriptions = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBase>>() {
             @Override
             public List<SubscriptionBase> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final List<SubscriptionModelDao> models = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getByAccountRecordId(context);
-                return new ArrayList<SubscriptionBase>(Collections2.transform(models, new Function<SubscriptionModelDao, SubscriptionBase>() {
+
+                final List<SubscriptionBundleModelDao> bundleModels = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getByAccountRecordId(context);
+
+                final List<SubscriptionModelDao> subscriptionModels = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getByAccountRecordId(context);
+                return new ArrayList<SubscriptionBase>(Collections2.transform(subscriptionModels, new Function<SubscriptionModelDao, SubscriptionBase>() {
                     @Override
                     public SubscriptionBase apply(final SubscriptionModelDao input) {
-                        return SubscriptionModelDao.toSubscription(input);
+                        final SubscriptionBundleModelDao bundleModel = Iterables.find(bundleModels, new Predicate<SubscriptionBundleModelDao>() {
+                            @Override
+                            public boolean apply(final SubscriptionBundleModelDao bundleInput) {
+                                return bundleInput.getId().equals(input.getBundleId());
+                            }
+                        });
+                        return SubscriptionModelDao.toSubscription(input, bundleModel.getExternalKey());
                     }
                 }));
             }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
index b53a9cd..e0761b9 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
@@ -25,7 +25,6 @@ import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.util.dao.TableName;
-import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
@@ -106,13 +105,14 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
         this.migrated = migrated;
     }
 
-    public static SubscriptionBase toSubscription(final SubscriptionModelDao src) {
+    public static SubscriptionBase toSubscription(final SubscriptionModelDao src, final String externalKey) {
         if (src == null) {
             return null;
         }
         return new DefaultSubscriptionBase(new SubscriptionBuilder()
                                             .setId(src.getId())
                                             .setBundleId(src.getBundleId())
+                                            .setBundleExternalKey(externalKey)
                                             .setCategory(src.getCategory())
                                             .setCreatedDate(src.getCreatedDate())
                                             .setUpdatedDate(src.getUpdatedDate())
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java b/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
index 772055c..941b6cb 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
@@ -36,7 +36,7 @@ public class TestEventJson extends SubscriptionTestSuiteNoDB {
     @Test(groups = "fast")
     public void testSubscriptionEvent() throws Exception {
 
-        final EffectiveSubscriptionInternalEvent e = new DefaultEffectiveSubscriptionEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), new DateTime(),
+        final EffectiveSubscriptionInternalEvent e = new DefaultEffectiveSubscriptionEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), null, new DateTime(),
                                                                                            EntitlementState.ACTIVE, "pro", "TRIAL", "DEFAULT", null, EntitlementState.CANCELLED, null, null, null, null, 3L,
                                                                                            SubscriptionBaseTransitionType.CANCEL, 0, new DateTime(), 1L, 2L, null);
 
diff --git a/util/src/test/java/org/killbill/billing/api/TestApiListener.java b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
index be41d8d..95ac19d 100644
--- a/util/src/test/java/org/killbill/billing/api/TestApiListener.java
+++ b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
@@ -144,6 +144,9 @@ public class TestApiListener {
     @Subscribe
     public void handleSubscriptionEvents(final EffectiveSubscriptionInternalEvent eventEffective) {
         log.info(String.format("Got subscription event %s", eventEffective.toString()));
+
+        Assert.assertNotNull(eventEffective.getBundleExternalKey());
+
         switch (eventEffective.getTransitionType()) {
             case TRANSFER:
                 assertEqualsNicely(NextEvent.TRANSFER);
diff --git a/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java b/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java
index 60f76fa..24e3b48 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java
@@ -34,6 +34,7 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
     private final Long totalOrdering;
     private final UUID subscriptionId;
     private final UUID bundleId;
+    private final String bundleExternalKey;
     private final UUID eventId;
     private final DateTime requestedTransitionTime;
     private final DateTime effectiveTransitionTime;
@@ -57,6 +58,7 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
     public MockEffectiveSubscriptionEvent(@JsonProperty("eventId") final UUID eventId,
                                           @JsonProperty("subscriptionId") final UUID subscriptionId,
                                           @JsonProperty("bundleId") final UUID bundleId,
+                                          @JsonProperty("bundleExternalKey") final String bundleExternalKey,
                                           @JsonProperty("requestedTransitionTime") final DateTime requestedTransitionTime,
                                           @JsonProperty("effectiveTransitionTime") final DateTime effectiveTransitionTime,
                                           @JsonProperty("previousState") final EntitlementState previousState,
@@ -80,6 +82,7 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
         this.eventId = eventId;
         this.subscriptionId = subscriptionId;
         this.bundleId = bundleId;
+        this.bundleExternalKey = bundleExternalKey;
         this.requestedTransitionTime = requestedTransitionTime;
         this.effectiveTransitionTime = effectiveTransitionTime;
         this.previousState = previousState;
@@ -121,6 +124,10 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
         return bundleId;
     }
 
+    @Override
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
 
     @Override
     public EntitlementState getPreviousState() {