const isEditAllowed = !!subscriptionWithItems.availableActions
.find(action => action.actionType === DefaultSubscriptionActionType.EDIT)
Editing a subscription allows a customer or an administrator to modify aspects of an existing subscription. Common edits include changing the quantity of the subscription, adding new add-ons, removing existing add-ons, or updating the quantity of those add-ons.
The InitiateEditSubscriptionHandler is responsible for handling the EDIT action type.
When a user initiates an edit:
Validation:
Verifies that the requested EDIT action is present in the subscription’s availableActions list.
Evaluates business rules based on the product’s settings (e.g. allowQtyEditAfterInitialPurchase and allowChangingBillingFrequency) and any associated item choice post-purchase edit rules to ensure edits are currently permitted.
Ensures that the subscription doesn’t have any delayed downgrades due at the end of the current period. Because a delayed downgrade resets the landscape of the subscription at the end of the term, editing the current subscription configuration beforehand would create an incompatible state. If a customer wishes to edit their current subscription, they must cancel the pending delayed downgrade first.
Cart Generation: Once validation succeeds, the handler creates a new Cart pre-populated with the subscription and its items.
Item Configuration: Each cart item is generated to mirror its corresponding SubscriptionItem. Attributes such as quantity, termDurationLength, termDurationType, recurringPeriodFrequency, recurringPeriodType, and paymentStrategy are copied over.
Existing Item References: The generated cart items retain a link to their original subscription item via the EXISTING_SUBSCRIPTION_ITEM_ID internal attribute.
It also injects attributes into the cart to coordinate the edit, such as:
SUBSCRIPTION_ACTION_FLOW set to EDIT.
SUBSCRIPTION_PRICE_EXPIRATION_DATE_UTC.
EXISTING_SUBSCRIPTION_DETAILS populated by SubscriptionModificationUtils to help map line items back to their original subscription items.
This generated cart is then returned in the ModifySubscriptionResponse.
In the My Subscriptions UI, a list of available actions is provided on the SubscriptionWithItems object. The UI can check if the EDIT action is present in the availableActions list:
const isEditAllowed = !!subscriptionWithItems.availableActions
.find(action => action.actionType === DefaultSubscriptionActionType.EDIT)
If the action is permitted, an edit action can be initiated via the modifySubscription hook using the CustomerClient or AccountClient.
const response = await modifySubscription({
subscriptionId: subscription.id,
action: {
actionType: DefaultSubscriptionActionType.EDIT
}
});
After receiving the response, the UI should extract the pre-populated cart ID (response.cart.id) and the root item reference (subscription.rootItemRef).
The user should then be redirected to a specialized subscription edit page. In the Broadleaf demo frontend, we leverage the Product Details Page (PDP) for these purposes:
/my-account/subscriptions/${subscription.id}/edit?cart=${response.cart.id}&product=${subscription.rootItemRef}
On this page, the user can adjust quantities, select new add-ons, or remove add-ons using standard cart operations before proceeding to checkout.
During the add-to-cart process for an edit, the SubscriptionPricingService calculates prorated differences based on the changes in quantity and add-ons.
Prepaid Edit Example: If a customer is editing a Prepaid subscription mid-cycle to increase the quantity of an item from 1 to 2:
The system calculates the proratedAmount for the 2 items from the edit date to the next bill date.
It calculates a creditedAmount representing the value of the original 1 item that the customer had already paid for from the edit date to the next bill date.
The amountDueNow is the difference: proratedAmount - creditedAmount.
When decreasing the quantity or removing an add-on on a Prepaid subscription, the amountDueNow is typically $0, and the actual removal is usually scheduled as a delayed action applied at the end of the period.
Postpaid Edit Example: If a customer edits a Postpaid subscription to increase the quantity from 1 to 2 mid-cycle:
The amountDueNow remains $0, because charges are deferred to the invoice date.
However, the estimatedFuturePayments are recalculated. The next bill will include the proratedAmount for the updated configuration (e.g., quantity 2) from the edit date to the bill date.
The priorUnbilledAmount calculates the value of the original configuration (e.g., quantity 1) from the start of the period to the edit date. Both amounts are combined onto the next invoice.
When the user completes checkout for the edited subscription cart, the order is submitted. The subscriptionActionFlow on the order is identified as EDIT.
The system leverages the subscription root item’s fulfillmentWorkflow along with the EDIT action flow to identify the correct workflow to execute. See the following configuration to understand how the exact workflow is identified:
broadleaf:
orderfulfillment:
workflow:
mapping:
flows:
match:
fulfillmentTypeAEditWorkflow:
- fulfillmentTypeA-EDIT
fulfillmentTypeBEditWorkflow:
- fulfillmentTypeB-EDIT
fulfillmentTypeCEditWorkflow:
- fulfillmentTypeC-EDIT
Sample Configuration for an Edit Workflow:
broadleaf:
workflow:
client:
flows:
fulfillmentTypeAEditWorkflow:
description: Subscription Edit Workflow for Type-A Subscriptions
historical-reset-enabled: true
retry-enabled: true
steps:
fulfillmentTypeAEditWorkflow:
prepareSubscriptionFulfillmentActivity:
admin-selectable: true
decisions:
ok: subscriptionModificationActivity
subscriptionModificationActivity:
admin-selectable: true
decisions:
ok: subscriptionBillingEventGenerationActivity
subscriptionBillingEventGenerationActivity:
admin-selectable: true
decisions:
ok: myCustomProvisioningActivityForFulfillmentTypeA
myCustomProvisioningActivityForFulfillmentTypeA:
admin-selectable: true
decisions:
ok: releaseSubscriptionLockActivity
releaseSubscriptionLockActivity:
admin-selectable: true
decisions:
ok: fulfillmentStatusFinalizationActivity
fulfillmentStatusFinalizationActivity:
admin-selectable: true
Initial Workflow Context Parameters
Order.id
Order.submitDate
OrderFulfillment.id
Subscription.id
LockStatus.lockId (the subscription lock)
Application.id
Tenant.id
subscriptionActionFlow (EDIT)
OrderFulfillment.fulfillmentWorkflow
customerTimezone (if applicable)
Workflow Activity Descriptions
prepareSubscriptionFulfillmentActivity (broadleaf-subscription-operation-services-workflow) - Makes a production update to the subscription, setting Subscription#subscriptionStatus to FULFILLMENT_IN_PROCESS.
subscriptionModificationActivity (broadleaf-subscription-operation-services-workflow) - Delegates to the EditUpgradeDowngradeSubscriptionModificationHandler since the action flow is EDIT. Makes a sandbox update to the subscription, replacing the previous configuration and subscription items with the newly configured subscription items that were just checked out. It also applies any updated pricing to the subscription and its items, and sets the subscription status back to ACTIVE.
subscriptionBillingEventGenerationActivity (broadleaf-subscription-operation-services-workflow) - Delegates to the SubscriptionModificationBillingEventGenerationHandler. Generates new BillingEvents in the sandbox for the subscription based on the new items and prices.
myCustomProvisioningActivityForFulfillmentTypeA - An example of a custom workflow activity that might be executed for a specific fulfillment type to provision a service.
releaseSubscriptionLockActivity (broadleaf-subscription-operation-services-workflow) - The subscription lock is released and all subscription sandbox updates are applied to the production record.
fulfillmentStatusFinalizationActivity (broadleaf-order-operation-services-workflow) - Updates the OrderFulfillment status to indicate that fulfillment has completed (e.g., setting it to FULFILLED).