Reference Experience

Add a conversational interface to the Elastic Path platform by implementing this reference experience of an Amazon Lex chatbot. Learn how to integrate the Reference Chatbot with the React PWA Reference Storefront.

Customer Segments

Customer Segments are the functionality which enables customer to be grouped together. Once available, customer segments can then be used to outline conditions for delivering price lists, dynamic content and cart promotions. In Elastic Path Commerce, Customer Segment tags are used to determine whether or not a customer belongs to a segment and resulting in content, prices, or promotions to be delivered to the current customer in the storefront.

More information about this feature can be found in the 6.10.0 Documentation.

Digital Returns

This adds the ability for a CSR to initiate a return of digital goods via the CM Client. The refactoring also adds server-side validation of returns to support future development of API-based CSR initiated returns and/or customer self-service returns.

Note that returns of subscription products are not included. Typically these would be done via the downstream subscription/billing system.

In order to support caches for both storefront and Cortex, a new pluggable Caching API was created in the ep-cache bundle. It supports the following capabilities:

Pluggable implementations, including JSR-107 caches and Ehcache with future support for distributed caches (e.g. memcached) envisioned.

Threadsafe operation with OpenJPA entities.

Pub/Sub cache invalidation (Future)

In an additional note, ep-cache is now the first module in Commerce Engine which uses mockito for unit tests instead of JMock.

Thread safe, in-memory Category Caching

The overall goal of this change is to add thread safe, in-memory caching for Categories to both Cortex and Storefront. Accomplishing this goal required some substantive changes to the domain model, which can also be considered a non-trivial sub-goal. The domain model changes were: adding a true guid field to the Category object and severing the Category.getParent() and Category.getChildren() relationships.

Tag Definition Caching

Caching has been introduced for for TagDefinitions using ehcache and roughly aligns with the new caching strategy for other objects such as Product and Category. TagDefinitionService has been split into TagDefinitionService for writes and TagDefinitionReader for reads.

In addition, a handful of homegrown simple timeout caches in the tagging subsystem have been replaced with the ehcache version.

Enhancements

Better Event Messaging System

Event Message Consumption Configurability

Changes were made to the bean definitions and configuration of Event Message consumption to make it significantly more configurable, which facilitates making the settings needed to consume Event Messages reliably in production.

Default JMS type changed to Pooled

The default JMS configuration previously had a default type of ActiveMQConnectionFactory. This creates a new tcp connection for every call, which can cause some machines to crash. Consequently, this default was changed to be AmqJNDIPolledConnectionFactory so that a connection pool is used instead. This requires your application server to have the libraries activemq-pool and commons-pool.

Messaging as a fundamental part of Commerce Engine

The new ep-core-customer-messaging and the existing ep-core-order-messaging modules are now compile-scoped, not optional, dependencies of ep-core. Previously the ep-core-order-messaging module was an optional dependency, as this was only required when performing a checkout, and therefore omitted from applications such as Import/Export, DST, ep-core-tool, etc. Now that much more prevalent, the model has been reversed from an opt-in approach, in which adding the relevant dependency would overwrite the no-op messaging implementation, to an opt-out model, in which messaging is expected to be present and must explicitly be disabled if not desired. This more clearly documents of the intent and behaviour of each component.

Reliable Email Delivery

Email delivery in Elastic Path Commerce Engine is triggered by messages sent within the Event Messaging framework. Certain Event Messages will trigger the publishing of a corresponding Email message which contains the contents and metadata regarding the email message. For example, when a new Order is created, an ORDER_CREATED event message is published. One of the consumers of this message is the component that generates an Order Confirmation Email.

Payment Gateway API ExternalAuth capability

The Payment Gateway API was enhanced to support ExternalAuth capabilities for payment-gateways.

Programatic Access to Import/Export Configuration

The method getImportConfiguration() has been added to Import/Export's ImportController, allowing programatic access to the Import/Export configuration.

Configurable Import/Export Configuration Location

When using the Import/Export tool, the operator is now able to supply an external importexporttool.config file, so he does not have to invade the deployment to configure database and search server configuration. For example,

If the argument is not specified, then the default config file is used so it is backwards compatible.

Tax Details

The storefront Order History page, Order Confirmation (Receipt), and the Order Confirmation Email now display all tax details instead of a summary.

Checkout failure rollback

Previously, when an order was created, the new order is filled with details right away before it is saved to the context. So if anything goes wrong while filling in the order details, the created order can't be rolled back. The CreateNewOrderCheckoutAction was changed to split this into 2 steps, saving the order in the context for rollback action after the order is created but before details are filled.

Publish event for changesets

This included adding capabilities to trigger event to the CM Client

Upgraded maven tomcat plugin to tomcat7

For developers running tomcat from the command line via maven: where before you would have used mvn tomcat:run or mvn tomcat:run-war, you should now do mvn tomcat7:run or mvn tomcat7:run-war. The plugin configuration was also set to disable http-only cookies due to compatibility issues with DWR.

Import Export System Tests

Automated end-to-end testing has been added to Import/Export. In particular, a Java process builder is used to run the Import/Export CLI and ensure it can be used successfully.

New ep-core-tool command

The EP Core Tool has had a new command added to update the Store URL:

set-store-url <storecode>=<url>

SKU Level Tax Code

ProductSku now has its own optional tax code field. If not set, the
tax code is obtained from the parent product. The tax code inheritance now lives in a
new TaxCodeRetriever service. The
old get/setTaxCode methods on Product have been
renamed to get/setTaxCodeOverride to more clearly convey the field's
intended use and have also been added to ProductSku.

Product Association Search Criteria

Additional fields have been added
to ProductAssociationSearchCriteria to allow inclusion of Start
Date and End Date in the query. The query building logic
in ProductAssocationServiceImpl was extracted out into a separate
class, ProductAssociationQueryBuilder.

Default Sku Behaviour in multi-sku bundles

When you view a product bundle with a multi-sku product constituent and the multi-sku
constituents' default sku is not available for purchase, the entire bundle becomes
unavailable for purchase even when the multi-sku constituent has other skus still
available for purchase.

This premature marking of bundles as out of stock in this scenario was addressed by
adding a step to the process of wrapping a product into a storeProduct. This extra
step traverses a bundle's constituents and checks whether a product constituent's
default sku is available. If it is not, then the product constituent's default sku is
set to an available sku if one can be found.

Tax Journal Entries for Zero Shipping Cost

Previously when a shipment is processed and has a zero-amount shipping cost, no tax
journal entries are recorded for the shipping cost. These tax journal entries will
now be craeted. Note that this applies to physical shipments but not electronic
shipments, because electronic shipments are always free. This means there is
different treatment for "zero-cost" shipping and an absence of shipping.

Expose the applied rule discount records on the cart with a
DiscountRecordContainer.

Previously, discounts applied to the shopping cart were stored in a private Map field
in the cart. Applications such as Cortex may want to use this information, so the
private Map of Maps was changed to be a
new DiscountRecordContainer class, and this is exposed on the
shopping cart via getDiscountRecordContainer().

New pluggable Product Caching API

Methods which read Product and ProductSku entities by immutable unique keys were
moved from the ProductService and ProductSkuService to the new ProductLookup and
ProductSkuLookup interfaces. These new lookups have the following
characteristics:

To reduce confusion, both the ProductService and ProductLookup interfaces and
ProductSkuService and ProductSkuLookup interfaces are completely disjoint. The
services do not share any methods with the lookups.

Lookup interfaces do not support any FetchGroup or LoadTuner arguments. The
expected contract is that an entity returned from a lookup will always be "fully
loaded"

The following methods were moved from ProductService to ProductLookup:

ProductService

ProductLookup

get(long)

findByUid(long)

getObject(long)

findByUid(long)

getWithCategories(long)

findByUid(long)

load(long)

findByUid(long)

findByGuid(String)

findByGuid(String)

findByGuid(String, ProductLoadTuner)

findByGuid(String)

getProductWithSkus(Product)

findByUid(long) or findByGuid(String)

findByGuids(Collection<String>)

findByUids(Collection<Long>)

The following methods were moved from ProductSkuService to ProductSkuLookup:

ProductSkuService

ProductSkuLookup

get(long)

findByUid(long)

getObject(long)

findByUid(long)

getTuned(long)

findByUid(long)

getWithProduct(long)

findByUid(long)

load(long)

findByUid(long)

findByUids(Collection<Long>, FetchGroupLoadTuner)

findByUids(Collection<Long>)

findByUids(Collection<Long>, ProductSkuLoadTuner)

findByUids(Collection<Long>)

findByGuid(String)

findByGuid(String)

findBySkuCode(String)

findBySkuCode(String)

findBySkuCodeWithAll(String)

findBySkuCode(String)

findBySkuCodes(Collection<String>)

findBySkuCodes(Collection<String>)

Breaking the relationships from ShoppingItem and OrderSku to
ProductSku

One of the preconditions for caching ProductSkus was to remove the
CascadeType.MERGE annotations from ShoppingItem and OrderSku to ProductSku. While
removing only the annotation was necessary for thread-safety, removing the
relationship entirely also gives us much better integration possibilities with
non-OpenJPA catalog data sources. As a result, the direct
ShoppingItem->ProductSku and OrderSku->ProductSku relationships were
replaced with indirect relationships using the sku guid.

In addition, instead of linking TSHOPPINGITEM and TORDERSKU to TPRODUCTSKU via the
TPRODUCTSKU.uidPk column, TSHOPPINGITEM and TORDERSKU are now linked to
TPRODUCTSKU via the TPRODUCTSKU.guid column.

Note:

The associated database conversion requires mandatory down time, as well as a mandatory database backup before the conversion!

Caching ProductLookup and ProductSkuLookup

Caching implementations of the ProductLookup and ProductSkuLookup were added to a
new ep-core-caching bundle. ep-core-caching is imported as a jar dependency by
storefront and as a standalone bundle dependency in cortex.

When wired in, the caching ProductLookup and ProductSkuLookups are automatically
made available to the spring/blueprint contexts.

In other changes, the ProductLookup and ProductSkuLookup caches are configured
using EP System Settings instead of maven properties.

Thread safe, in-memory Category Caching

Category Guid

Previously, category.getGuid() and category.setGuid() would
delegate
to category.getCode() and setCode() respectively,
where the category code was a business key which is guaranteed to be unique only
within a single Catalog). This is no longer true!. The Category code and
Category guid are now two distinct properties with different values. Using the
"compound" category guid which was constructed by combining a category's code with
its catalog's code, delimited with a | (e.g. 'category-code|catalog-code') should
now be considered deprecated (although it is still in use in Promotion Rules).

Category now has a true guid, which is stored in a separate database column and
domain property and is guaranteed to be unique across the entire system. New
Categories will be created using UUID style guids. Existing categories have their
guid's initialized to their pre-existing compound guid during the database
conversion.

Data Sync Tool

Initializing the guid of existing categories to their old compound guid
preserves compatibility with pre-existing changesets and also ensures that
pre-existing Categories receive the same guid in staging and production
post-conversion. As a result, no changes should be required for users of the
DST, and changesets created before the database upgrade should still be
syncable post upgrade.

XML Import/Export

The XML DTO for categories now has additional columns for the new guid field in
both the Category DTO and the linked category sub-object. Therefore XML
category exports created before this version cannot be imported post
upgrade.

The new guid field has also required another change to how XML import/export
works with linked categories. Assume a category tree, with a (master) root
category A, and two children B and C. Previously, importing an XML category
document with a new linked category for category A would automatically create
the linked categories for categories B and C, regardless of whether or not B
and C were included in the document. Post upgrade, importing an XML category
document with a new linked category for category A
will NOT automatically create the linked categories for categories B and
C. Linked categories for B and C will only be created if they are explicitly
included in the document. This change is necessary because otherwise there is
no way for import/export to determine the guid's for the linked B and C
categories.

CSV Import

For users of CSV import there is no change to the CSV import format. However,
there is also no way to specify a categories GUID using CSV import/export -
only the code can be specified with CSV import. Users who require control of
the category guids on import should use XML import/export instead.

Category.getParent() and Category.getChild()

Although Categories still exist in a tree structure,
both Category.getParent() and Category.getChild() have
been removed. Callers of the following domain object methods should now use the
following alternatives:

CategoryService

Many methods in CategoryService have either been moved,
renamed, or removed. In general, finders that retrieved categories by a unique
key or keys have been moved to CategoryLookup. In addition,
several older methods that were no longer being called have been removed
from CategoryService.

CategoryService Method

Replacement

findByCompoundGuid( * )

CategoryLookup.findByCompoundCategoryAndCatalogCodes()

findByGuid(String)

CategoryService.findByCode()

findByGuid(String, Catalog)

CategoryLookup.findByCategoryCodeAndCatalog()

findByGuid(String, Catalog, FetchGroupLoadTuner)

CategoryLookup.findByCategoryCodeAndCatalog()

findByGuid(String, String)

CategoryLookup.findByCategoryAndCatalogCode()

findByGuid(String, String, CategoryLoadTuner)

CategoryLookup.findByCategoryAndCatalogCode()

findByUids( * )

CategoryLookup.findByUids()

findByUidsWithFetchGroupLoadTuner()

CategoryLookup.findByUids()

findDirectDescendantCategories(long)

CategoryService.findDirectDescendantCategories(String
guid)

get()

CategoryLookup.findByUid()

getCategoryWithAttribute()

CategoryLookup.findByUid()

getCategoryWithSubCategories()

CategoryLookup.findByUid()

getObject()

CategoryLookup.findByUid()

getSubcategories()

CategoryLookup.findChildren()

guidExists(String)

CategoryService.isCodeInUse() /
CategoryService.isGuidInUse()

load( * )

CategoryLookup.findByUid()

loadWithAllChildren()

CategoryLookup.findByUid()

listRootCategories(Catalog, availableOnly,
FetchGroupLoadTuner)

CategoryService.listRootCategories(Catalog,
availableOnly)

listRootCategoriesWithTree( * )

CategoryService.listRootCategories(Catalog,
availableOnly)

populateChildrenCategory()

Removed. Call is no longer necessary.

updatePosition()

Removed

CategoryLookup

A new CategoryLookup interface containing finders by unique
key has been added. The contract for the new finders
in CategoryLookup is that they should always return a
"fully loaded" version of the Category. As a result, most clients should no
longer have a need to use FetchGroupLoadTuners and/or CategoryLoadTuners when
loading categories.

Spring

Consumers of ep-core will be able to obtain
a CategoryLookup in the spring context via two bean ids -
"nonCachingCategoryLookup" and
"categoryLookup".
"nonCachingCategoryLookup" guarantees a non-caching
implementation. By default, the "categoryLookup" is proxied to
the non-caching implementation unless ep-core-caching is also in the classpath,
in which case "categoryLookup" is proxied to the
"cachingCategoryLookup" implementation.

OSGI

Consumers of the ep-core bundle will be able to obtain a non-caching version of
the CategoryLookup from the blueprint by importing a
"com.elasticpath.service.catalog.CategoryLookup" with
filter "(caching=false)". If the ep-core-caching bundle is
available, then a caching version of the CategoryLookup can be
obtained via the filter "(caching=true)". Note that while
cached implementations have a higher rank than non-cached implementations,
bundle starting order can mean that non-cached services are selected at start
up time even if cached services become available later on. For that reason,
using the filter is preferred if your bundle requires a caching version.

Event Messaging System

New Event Messages are published by Commerce Engine that will trigger email
delivery:

Commerce Manager User Events

CM User created

CM User password changed

CM User password reset

Customer Events

Anonymous Customer registered

Customer registered

Customer password changed

Customer password forgotten

Wish List shared

Data Import Events

Import job completed

Gift Certificate Events

Gift Certificate purchased

Order Events

Order exchanged

Order shipment created

Order shipment release failure

Order shipment shipped

Order returned

Solr Upgrade

Solr was upgraded from version 1.4.1 to 4.5.1. This resolves problems that some
customer have encountered with replication of large indexes, fuzzy matching, as well
as various other Solr bugs, including:

SOLR-309: A solr.StrField that has analyzers configured should emit warning to
log

SOLR-1846: Remove support for (broken) abortOnConfigurationError

SOLR-2591: Remove commitLockTimeout option from solrconfig.xml

SOLR-5227: attempting to configured a dynamicField as required, or using a
default value, should fail.

If you have made customizations to any of the index schema files, solr indexing code
or querying code you should review the out-of-the-box changes made in these areas to
see if similar changes will be required for your customizations.

OpenJPA Upgrade

OpenJPA was upgraded from version 1.2.1 to 2.3.0. This means we now support the JPA
2.0 standard for persistence. Some changes made to support this:

No private setters

A handful of classes had property setter methods marked
as private. JPA 2.0 doesn't support this for persistent
fields so the setters were changed to protected

Non-standard Join syntax

We previously used a non-standard join syntax for joins
to TLOCALIZEDPROPERTIES. This is no-longer supported,
and has been replaced by use of
the @ElementClassCriteria annotation

Persistence Entity Listeners

The old PersistenceEngineEntityListener which was attached
to the PersistenceEngine has been replaced by two separate listener
interfaces. OpenJPA LifecycleListeners can now be added to
the EntityManagerFactory via the spring context. The
EntityManagerFactory takes responsibility for attaching these listeners to
each EntityManager created by the factory at creation time. This means that
OpenJPA Lifecycle listeners now receive all lifecycle events exposed by
OpenJPA.

The PersistenceEngine specific events
in PersistenceEngineEntityListener have been moved
to PersistenceEngineOperationListener. These events were
only used by
the AuditEntityListener. PersistenceEngineOperationListeners are
attached directly to the PersistenceEngine singleton. Of
course, individual listeners like AuditEntityListener are
free to listen to both sets of events, but are not required to do so.

ORM file changes

The *-orm.xml files have been changed to update the persistence schema
version as well as removing the <package> and <entity> elements
- basically they now only hold the named queries. Customers no longer need
to duplicate the file and suppress the OOTB version in order to override
entity persistence annotations - they can just introduce a new file with
their entity definitions. The OOTB orm files only need to be duplicated and
changed if OOTB named queries are being replaced.

jpa-persistence.xml changes

Customers overriding the jpa-persistence.xml need to ensure that the
following properties are present:

In a new or overriding jpa-persistence.xml file you may also need to
include the following element to ensure JSR-303 validation is not performed
during
persistence: <validation-mode>NONE</validation-mode>

RFC-4180 Compliant CSV Encoding/Decoding (Multi-Valued Attributes)

Previously, multi-valued attribute data became corrupted if the values contained a
comma because delimitting of these attributes were handled by manually calling
StringUtils.split() or split() on a comma..

There are now 2 formats of encoding and decoding of multi-value attribute:

Note: SuperCSV is our candidate encoder because it is licensed under Apache v2 and it
has a release. We did not use Opencsv because it is not fully RFC compliant and it
returns the entire row as the first element when parsing.

The reason for having legacy encoding is that the legacy data are not properly
formatted and may result in data corruption.

For example, consider the encoded string,

3" DVD-R, 3" DVD-L

RFC-complaint encoding will treat the double-quotes as escape characters. The result
after decoding is,

{ "3 DVD-R, 3 DVD-L" }a list of 1 element

Thus, the "multi-valueness" of an attribute is no longer a boolean state, but of
three modes: 1) a single-value; 2) legacy encoded; 3) rfc-4180 encoded.

All existing data is kept in legacy encoding. All newly created attributes will be in
RFC 4180 encoding.

New CustomerSessionService Method

The
method changeFromAnonymousToRegisteredCustomer(CustomerSession customerSession, Customer customer, String storeCode) has
been added to CustomerSessionService. This will change the Customer
associated with the session to a new registered Customer, create a new Shopper and
handle updates (such as merging ShoppingCarts and WishLists and updating the
CustomerSession. This functionality was previously spread across private methods.

New ProductAssociationService Methods

Two new methods have been added to ProductAssociationService:

findByCriteria(ProductAssociationSearchCriteria criteria, int startIndex, int maxResults) - Searches the product associations by given search criteria, at the starting index with a result limit

findCountForCriteria(ProductAssociationSearchCriteria criteria) - Finds the count of product associations by the given search criteria.

New Promotions/Coupons Service Methods

Validation logic that was previously within private methods in the shopping cart has
been moved to new methods on the appropriate services:

RuleService.isRuleValid(Rule rule, String storeCode) will check if the given
rule is valid in the given store

CouponUsageService.isValidCouponUsage(String customerEmailAddress, String couponCode) will
check that the coupon usage is valid for the given
coupon code and email address

CartDirectorService.reApplyCatalogPromotions

A new method reApplyCatalogPromotions has been added
to CartDirectorService that re-applies the catalog promotions to
the ShoppingCart so that the applied rules are tracked on the cart.

Money

The Money interface and MoneyFactory class have
been replaced with a single Money implementation. This
implementation is final, and contains factory methods that can be used to create new
Monies, making it analogous to core JRE datatypes such as BigDecimal or Long. As with
the core JRE datatypes, a correct Money implementation is critical
to the correct functioning of Commerce Engine and therefore it should not be possible
nor necessary to replace this object with a different implementation.

As a result of this change it is no longer possible to create or encounter Monies in
invalid states (ie. missing a currency or amount), and therefore it should be more
difficult to introduce bugs. Any defects or shortcomings with the Money
implementation should be reported and remedied in the platform.

Implementation Details

Money now resides in the ep-base module, and
it's package has changed
from com.elasticpath.domain.misc to com.elasticpath.money. MoneyFactory is
no longer used to create new Money objects, so code of the
form:

MoneyFactory.createMoney(BigDecimal.ZERO, CURRENCY_USD)

must to be changed to the following:

Money.valueOf(BigDecimal.ZERO, CURRENCY_USD)

CustomerRegistrationService

A CustomerRegistrationService was added which houses customer registration related logic. This currently has two methods:

registerAnonymousCustomer(Customer) - Performs field validation on the customer, and updates it to registered status and sends a confirmation email. Systems using Commerce Engine will no longer have to do these operations separately by calling into different services, making integration less brittle. Returns a CustomerRegistrationResult object which holds any field constraint violations that were found during validation, as well as the updated Customer object.

registerCustomerAndSendPassword(Customer) - was moved from CustomerService to this new service. This method is currently used by Commerce Manager Client only.

The domain class CustomerImpl was invoking service methods on AttributeService and CustomerService to load metadata and settings values respectively. These values were lazy loaded at run-time when setters were invoked.

This has several problems:

It makes CustomerImpl make unexpected calls to the service/persistence layer at run time. This is made worse by the fact that CustomerImpl can be serialized to the Commerce Manager Client.

Invoking reads in the middle of a transaction can cause unexpected flushes of partially changed objects

CustomerImpl requires static access to the ElasticPath singleton and the bean factory.

Calling services from domain objects is not best practice.

This change does three main things:

It adds a generic OpenJPA post-load listener to the EntityManagerFactory. This post-load listener handles distribution of PostLoad events to PostLoadStrategies, configured with spring. The new listener provides a place to hang spring-configured post load handlers.

It removes the calls in CustomerImpl which lazy-loaded data from the AttributeService and CustomerService. Instead, CustomerImpl objects are eagerly loaded with all the configuration they require to execute properly. Existing CustomerImpl instances retrieved from the database have their metadata populated with a PostLoadStrategy. New CustomerImpl instances created by the spring bean factory have their metadata populated from spring.

It removes CustomerProfileImpl and CustomerProfileValueImpl from the bean factory. Extension projects can still override these classes if they need to by overriding protected factory methods in CustomerImpl and CustomerProfileImpl respectively.

Promotion Logging

Logging of applied rule codes & checksums during order creation has been changed from being at level INFO to level TRACE.

Recording Applied Rules on Shipping Service Level

A transient field has been added to ShippingServiceLevel to record any applied promotion Rule ids. The ShoppingCart.fireRules() lifecycle governs the lifecycle of the applied promotions on a ShippingServiceLevel. The following two methods were added to ShippingServiceLevel:

Save rule description, and display name on TAPPLIEDRULE

In order to be able display promotion information that was captured at the time of purchase (e.g. for Cortex) we need to save the display name (in the shopper's locale at the time of purchase), and the description into TAPPLIEDRULE. In addition, to retrieve a promotion applied to a purchase an Applied Rule must be distinguishable from other Applied Rules so a Guid has been added to AppliedRule (and consequently to the TAPPLIEDRULE table).h3.

Setting Definitions and Values with a path starting with "COMMERCE/SYSTEM/ASYNC" were removed as they are not used.

Code Changes

Code Moves

ImageService was moved from ep-core to ep-common-web

Shopping Cart Refresh functionality was moved from storefront to core.

getWebInfPath() was removed from ElasticPath/ElasticPathImpl. You should now use getConfigurationRootPath() on EnvironmentInfoService.

All *-cucumber-tests modules have been renamed *-acceptance-tests to better reflect what they are

Refactoring

Refactored Customer Tagging logic into reusable modules

Extracted PriceListStack setup logic from WebCustomerSessionService

The Product Association type constants on ProductAssociation have been removed, and a ProductAssociationType extensible enum is now used instead.

Payment Handlers now throw a consistent error message if an authorization is attempted for a zero amount

Coupon validation logic has been moved into a ValidCouponUseSpecification class.

Coupon auto-apply logic has been moved into a CouponAutoApplierService class.

PaymentType was changed to be an extensible enum.

Incorrectly spelled method setResoureDirectory in EsapiSecurityConfig was renamed to setResourceDirectory

Previously in checkout.xml , abstractCheckoutServiceDelegate had three properties for defining the checkout actions to run. Only the reversibleActionList property was a reference value. The other two checkout actions list are now also reference values. This enables extension projects to override the list beans to define their own checkout actions for setupCheckoutActions and finalizeCheckoutActions.

Code Removal

Removed fncLoader from ElasticPathImpl

Removed ctxEp from velocity

Removed unused getAllRegions method from ShippingRegionService

Removed DST dependency on ElasticPathImpl

AsyncService was unused and has been removed.

MessageSource was removed from ElasticPath & ElasticPathImpl. Classes requiring this can just have coreMessageSource injected or get it from the bean factory.

The EpService interface and AbstractEpServiceImpl class were removed. Classes that used this to get access to ElasticPath for bean creation now use an injected BeanFactory instead.