Build a multi-product bundle
A bundle can hold many designs at once. In this tutorial you will create a bundle from a product collection, add a workflow experience for several of its products, change a quantity, remove a design, display the bundle total, and finish with the cart add mode you want.
Before you begin
You need:
- An application key, and a
SpiffCommerceClientconstructed with it. See Getting Started with the Core SDK. - The ID of an activation. Inside a theme this is
context.activation.id. - The ID of a product collection linked to that activation.
1. Create a bundle for the collection
Pass the product collection ID and the activation ID when you create the bundle. Without a collection ID, the bundle cannot retrieve the collection in the next step.
const bundle = await client.getNewBundle(
productCollectionId, // The product collection the customer chooses products from.
undefined, // Optional: Initial metadata for the bundle.
{}, // Optional: GetBundleOptions
activationId, // The activation the bundle belongs to.
);
2. List the products in the collection
Get the collection from the bundle, then fetch its products. getProducts() throws if the products have not been fetched yet, so call fetchProducts() first.
const collection = bundle.getProductCollection();
if (!collection) {
throw new Error("The bundle has no product collection.");
}
const products = await collection.fetchProducts();
products.forEach((product) => {
console.log(product.getId(), product.getName());
});
Each entry is a CollectionProduct. Adding a design for a product that is not in the bundle's collection results in an error, so build your product picker from this list.
3. Add several designs at once
Create one workflow experience for each product the customer has chosen, then add them all to the bundle in a single call. Always use the list-based getWorkflowExperiences and addWorkflowExperiences, even when you only have one product. Fewer requests are made when you pass several at once.
A product's current integration identifies it, and its default workflow tells Spiff Commerce how it is designed.
const chosenProducts = products.slice(0, 3);
const experiences = await client.getWorkflowExperiences(
chosenProducts.map((product) => ({
type: "integration" as const,
integrationProductId: product.getCurrentIntegration().id,
workflowId: product.getDefaultWorkflow().getId(),
})),
);
await bundle.addWorkflowExperiences(experiences);
console.log(bundle.getWorkflowExperienceCount()); // 3
Be sure to await both calls. See the GetWorkflowOptions reference for the other ways to create an experience, such as from an existing transaction.
4. Change a quantity and show the total
Each design has its own quantity and price. The bundle adds them together for you. All prices are in the smallest unit of your active currency, such as cents for USD.
await experiences[0].setQuantity(5);
const designTotals = bundle.getWorkflowExperiences().map((experience) => ({
quantity: experience.getQuantity(),
totalSubunits: experience.getTotalPriceSubunits(),
}));
const bundleTotalSubunits = bundle.getTotalSubunits();
Call getTotalSubunits() again after any change to the bundle, such as a new quantity, a different selection, or an added or removed design, to refresh your display. See Bundle Pricing for formatting subunits as a currency.
5. Remove a design
Remove designs with the list-based functions as well. You can pass the workflow experiences themselves, or their transactions if that is what you have.
await bundle.removeWorkflowExperiences([experiences[2]]);
// Or, using transactions:
// await bundle.removeWorkflowExperiencesByTransactions([experiences[2].getTransaction()]);
6. Finish the bundle and choose a cart add mode
When the customer is done, call finish(). It finalizes every design and returns a BundleDesignCreationMessage with one entry in items for each design.
const message = await bundle.finish((progressText) => console.log(progressText));
console.log(message.items.map((item) => item.transactionId));
The message's optional cartAddMode controls how those designs reach your cart. Your theme decides it, and sets it on the message before handing it over. If you leave it unset, Default is used.
| Mode | Behavior |
|---|---|
Default | Consolidates the designs into as few line items as possible. |
FirstTransaction | Adds only the first design to the cart. |
OneToOne | Adds each design as its own line item, with no consolidation. |
For example, to add every design as its own line:
import { BundleDesignCreationCartAddMode } from "@spiffcommerce/core";
const message = await bundle.finish();
message.cartAddMode = BundleDesignCreationCartAddMode.OneToOne;
getSpiffThemeLoader().addToEcommerceCart(message);
Next steps
- From a finished bundle to a fulfilled order shows what happens after
finish(). - Product Collections covers global properties and other collection features.
- Bundles lists everything else you can do with a bundle, including inserting designs at a specific position.