Modules
Modules are plug in pieces of code that can be added and used in Shopizer with minimum configuration and integration efforts. Modules can also represent classes that can be interchanged for other implementations for providing extra or different functionalities. Shopizer modules are implemented using Spring framework IOC bean container (http://static.springsource.org/spring/docs/2.5.x/reference/beans.html) in order to easily add or change module bean definitions. There are two main types of modules which are application and integration modules.
Application modules are functionalities that have been identified with the possibilities of being changed or enhanced according to specific merchant needs. For instance new price types can be added to the system or it is possible to change password generation algorithm or change order fulfillment item packing logic.
Integration modules are sub system integration piece of codes that can be added to Shopizer in order to integrate with an external system such as Paypal, AuthorizeNet payment systems or Canada Post shipping system. Module implementations reside in sm-core project under com.salesmanager.core.module package (integration & application) and sm-shop project com.salesmanager.integration (portlets – as of Shopizer version 1.1.3).
Each module must implement a specific interface according to the module type. Modules will be available at runtime by adding or modifying an entry to sm-core/conf/spring/sm-modules.xml or by adding a component annotation (@Component) to the implementation class.
Properties defined modules
Modules defined in sm-core/conf/spring/sm-modules.xml follow Spring IOC container bean definition. The following syntax represent a module configured using Spring bean definition configuration file.
<bean id="mypricemodule" class="com.salesmanager.core.module.impl.application.prices.MonthlyPriceModule"/>
Annotation defined module
The following syntax represent a module configured using annotation stereotype syntax. In order to support @Component syntax, make sure your package is defined in annotation scanning directive in sm-core/conf/spring/sm-core-config.xml
@Component("mypricemodule")
public class MyModule implements PriceModule {
Annotation scanning directive is currently defined for those packages
<!-- Bean annotation scanning -->
<context:annotation-config />
<context:component-scan base-package="com.salesmanager.core.service" />
<context:component-scan base-package="com.salesmanager.core.module" />
Integration modules and some of the application modules requiring configuration and visibility for selection by a merchant need to be defined in CORE_MODULES_SERVICES table
Column name | Description |
---|---|
CORE_MD_SERVICES_ID | Auto increment identifier |
CORE_MD_SERVICES_CODE | Internal service (see note 1) |
CORE_MD_NAME | Module code (unique text identifier no white space or special characters) ie paypal |
COUNTRY_ISO_CODE_2 | US or CA or GB or any country ISO CODE. If the module applies to all countries put XX |
CORE_MD_SERVICES_SUBTYPE | Internal service sub type code (see note 2) |
CORE_MD_SERVICES_DESCRIPTION | Textual description |
CORE_MD_SERVICES_LOGO_PATH | A logo is used in admin panel (central) in shipping and payment modules sections (sm-central\WebContent\common\img\en\payment) and fr |
CORE_MD_SERVICES_POSITION | Position displayed in admin panel (central) |
CORE_MD_SERVICES_VISIBLE | Boolean 0 or 1 |
CORE_MD_SERVICES_NEW | Boolean 0 or 1 |
CORE_MD_SERVICES_URL | External system URL for reference |
CORE_MD_SERVICES_DEV_PROTOCOL | External system test environment (http / https) |
CORE_MD_SERVICES_DEV_DOMAIN | External system test environment (www.systemdomain.com) |
CORE_MD_SERVICES_DEV_PORT | External system test environment |
CORE_MD_SERVICES_DEV_ENV | External system test environment |
CORE_MD_SERVICES_PROD_PROTOCOL | External system production environment (http / https) |
CORE_MD_SERVICES_PROD_DOMAIN | External system production environment |
CORE_MD_SERVICES_PROD_PORT | External system production environment |
CORE_MD_SERVICES_PROD_ENV | External system production environment |
CORE_MD_SERVICES_CONFIGURABLE | Requires the module to be configured - Boolean 0 or 1 |
Any new entry to the system can be added to <SHOPIZER>\schema\sql\data\shopizer_data.sql
note 1
Internal services
RESERVED CODES
1= Shipping
2= Payment
4= Price
20= Portlet
note 2
Internal service sub type code
Sub types are defined to provide more details on service codes.
Here is the list of RESERVED sub types per service code
**Service Code 1 (Shipping)**
- sub type 0 -> Shipping quotes module
- sub type 1 -> Address validation module
**Service Code 2 (Payment)**
- sub type 0 -> non gateway payment module
- sub type 1 -> gateway payment module
**Service Code 4 (Price)**
- sub type 1 -> One time price module
- sub type 2 -> Recursive price module
**Service Code 20 (Portlet)**
- sub type 1 -> Store front portlet
- sub type 2 -> Store front portlet
Integration modules
Example of a payment integration module
Payment modules can be added to Shopizer by following a few design and configuration directives. The following section describes in details the steps and the code required to integrate a new credit card payment gateway.
Suppose for example that you want to create a new payment module for credit card gateway system called 'netcharge' [this is a ficticious name]. To fully allow the configuration and the credit card processing through this system (netcharge) you will need to create:
sm-central\WebContent\payment\netcharge.jsp
This jsp file above will be displayed in Shopizer administration (central) allowing the configurations and settings to use netcharge gateway. A naming convention requires that jsp file to match the payment module name that will be used all across the system for identifying this external gateway system. Do not use any special charactes nor white spaces in your module name.
You will also need to create an action class to implement business logic in the administration section
sm-central\src\com\salesmanager\central\payment\PaymentnetchargeAction.java
This java class file above is the Struts action class in the administration section that will handle the creation, edition and delition of netcharge payment gateway configurations and settings. The structure of the new class is represented below.
By extending com.salesmanager.central.payment.PaymentModuleAction The new payment module will force the implementation of delete, display, prepare and save methods for this module. A naming directive requires the name of the class to be Payment<module name>Action.class. A coding directive also uses IntegrationProperties and IntegrationKeys objects to store credentials and settings submited by the merchant. From that point you can look at the other payment gateway implementations and do precisely the same to implement the Action methods.
public class PaymentnetchargeAction extends PaymentModuleAction {
private IntegrationProperties properties = new IntegrationProperties();
private IntegrationKeys keys = new IntegrationKeys();
@Override
public void deleteModule() throws Exception {
// TODO Auto-generated method stub
}
@Override
public void displayModule() throws Exception {
// TODO Auto-generated method stub
}
@Override
public void prepareModule() throws Exception {
// TODO Auto-generated method stub
}
@Override
public void saveModule() throws Exception {
// TODO Auto-generated method stub
}
public IntegrationProperties getProperties() {
return properties;
}
public void setProperties(IntegrationProperties properties) {
this.properties = properties;
}
public IntegrationKeys getKeys() {
return keys;
}
public void setKeys(IntegrationKeys keys) {
this.keys = keys;
}
}
You will need to specify your module name in
sm-core\conf\resources\modules.properties (same for modules_<lang>.properties)
module.netcharge=Netcharge
Add the labels to be used in central configuration page
sm-core\conf\resources\central-payments.properties (same for central-payments_<lang>.properties)
label.payment.methods.netcharge.userid=User name
label.payment.methods.netcharge.password=Password
label.payment.methods.netcharge.storeid=Merchant id
label.payment.methods.title.netcharge =Net Charge
label.payment.methods.text.netcharge=<a href=\"http://www.netcharge.com/">Sign up for an account</a>.<br>Net Charge merchantid currency should match your store.
label.payment.methods.footer.netcharge=
You will need your payment module class. This class will be responsible to handle processing, authorization and credit transactions. This class is will build the transaction to Net Charge and handle the payment response. All payment implementation module must reside in com.salesmanager.core.module.impl.integration.payment package.
sm-core\src\com\salesmanager\core\module\impl\integration\payment\NetChargeTransactionImpl.java
Not that the module uses @Component annotation to declare the module name. Again you can look at the other modules to get an idea on how methods need to be implemented.
package com.salesmanager.core.module.impl.integration.payment;
/**
* Manages credit card transactions with Net Charge API
* @author Carl Samson
*
*/
@Component("netcharge")
public class NetChargeTransactionImpl extends CreditCardGatewayTransactionImpl {
@Override
public GatewayTransactionVO authorizeAndCapture(IntegrationKeys keys,
IntegrationProperties properties, MerchantStore store, Order order)
throws TransactionException {
//Will invoke make transaction
return null;
}
@Override
public GatewayTransactionVO authorizeTransaction(IntegrationKeys keys,
IntegrationProperties properties, MerchantStore store, Order order)
throws TransactionException {
//Will invoke make transaction
return null;
}
/**
* Invoked from admin panel to capture after an authorization
*/
public GatewayTransactionVO captureTransaction(MerchantStore store,
Order order) throws TransactionException {
//Will invoke make transaction
return null;
}
/**
* no need to initialize a transaction
*/
public Map<String, String> initTransaction(
CoreModuleService serviceDefinition, Order order)
throws TransactionException {
// TODO Auto-generated method stub
return null;
}
/**
* no need to invoke any 'post transaction' url once completed
*/
public Order postTransaction(Order order) throws TransactionException {
// TODO Auto-generated method stub
return null;
}
/**
* Invoked from admin panel to refund after a capture
*/
public GatewayTransactionVO refundTransaction(MerchantStore store,
Order order, BigDecimal amount) throws TransactionException {
// TODO Auto-generated method stub
return null;
}
/**
* Retrieve transaction history
*/
public List<SalesManagerTransactionVO> retreiveTransactions(int merchantid,
Order order) throws Exception {
// TODO Auto-generated method stub
return null;
}
private GatewayTransactionVO makeTransaction(String type,
IntegrationKeys ik, IntegrationProperties props,
MerchantStore store, Order order) throws TransactionException {
//Handles processing for all transaction types
return null;
}
public ConfigurationResponse getConfiguration(
MerchantConfiguration configurations, ConfigurationResponse vo)
throws Exception {
//get payment gatemay configuration from MERCHANT_CONFIGURATION table
return null;
}
public void storeConfiguration(int merchantid, HttpServletRequest request)
throws Exception {
// can be used to store merchant gateway configuration
}
}
The implementation class extends CreditCardGatewayTransactionImpl. Administration panel and order fulfillment functions uses methods of the super classCreditCardGatewayTransactionImpl which delegates to the sub class the processing for capture (After pre-authorization), refund (after capture) and process transactions (pre-authorization or capture) Pre-authorization or capture are configured in the module option according to merchant's selection.
Finally here are the values for CORE_MODULES_SERVICES table for service discovery and meta information
Column name | Description |
---|---|
CORE_MD_SERVICES_ID | Auto increment identifier |
CORE_MD_SERVICES_CODE | 2 |
CORE_MD_NAME | netcharge |
COUNTRY_ISO_CODE_2 | CA (if this service is available in US too, add a second row with US) |
CORE_MD_SERVICES_SUBTYPE | 1 |
CORE_MD_SERVICES_DESCRIPTION | Netcharge payment gateway integration |
CORE_MD_SERVICES_LOGO_PATH | /payment/beanstream.gif |
CORE_MD_SERVICES_POSITION | 4 |
CORE_MD_SERVICES_VISIBLE | 1 |
CORE_MD_SERVICES_NEW | 1 |
CORE_MD_SERVICES_URL | www.netcharge.com |
CORE_MD_SERVICES_DEV_PROTOCOL | https |
CORE_MD_SERVICES_DEV_DOMAIN | test.netcharge.com |
CORE_MD_SERVICES_DEV_PORT | 443 |
CORE_MD_SERVICES_DEV_ENV | /processTransaction |
CORE_MD_SERVICES_PROD_PROTOCOL | https |
CORE_MD_SERVICES_PROD_DOMAIN | www.netcharge.com |
CORE_MD_SERVICES_PROD_PORT | 443 |
CORE_MD_SERVICES_PROD_ENV | /processTransaction |
CORE_MD_SERVICES_CONFIGURABLE | 1 |
More details to come on Shipping Modules
Application modules
Files modules
Files modules are bean classes used for uploading product images and digital downloads to the system. Currently defined in sm-core/conf/spring/sm-modules.xml
<bean id="localfile" class="com.salesmanager.core.module.impl.application.files.LocalFileImpl"/>
<bean id="productfile" singleton="false" class="com.salesmanager.core.module.impl.application.files.LocalFileImpl"/>
Product image file
localfile bean uploads products images and product options images to the server. Productfile bean uploads digital downloads to the server. Both modules used the same implementation which points to com.salesmanager.core.module.impl.application.files.LocalFileImpl. It is possible to change the implementation of digital products uploads (productfile) if those files have to be uploaded to Amazon S3 for instance.
Here is a code sample to upload a product image
ProductImageUtil image = new ProductImageUtil();
image.uploadCropedProductImages(imageFile, imageFileName, imageFileContentType,product, moduleConfigMap);
uploadCropedProductImages will upload the product image file on the server and will create a large, medium and small version of it for the catalogue listing and details and shopping cart pages. This method accepts a java.io.File (uploaded file on the server) uploaded using Struts 2 file upload principles (http://struts.apache.org/2.0.14/docs/file-upload.html). It also takes the uploaded file and its content type. The method also accepts the com.salesmanager.core.entity.catalog.Product entity and the catalog configuration map for resizing the image according to the template specification.
The moduleConfigMap HashMap object can be retrieved using the following code
ReferenceService rservice = (ReferenceService) ServiceFactory.getService(ServiceFactory.ReferenceService);
MerchantService service = (MerchantService) ServiceFactory.getService(ServiceFactory.MerchantService);
MerchantStore mStore = service.getMerchantStore(ctx.getMerchantid());
Map<String, String> moduleConfigMap = rservice.getModuleConfigurationsKeyValue(mStore.getTemplateModule(),mStore.getCountry());
To retrieve the current merchant store from sm-central (admin panel) in an Action class, first retrieve the Context object which contains Merchant and User information by doing
Context ctx = super.getContext();
Int merchantId = ctx.getMerchantId();
An example of this can be found in com.salesmanager.central.catalog.EditProductAction
Digital download file
To upload a digital product file, it is quite similar to the method above, but the invocation needs to be done on CatalogService in a transactional context. So the code will be
CatalogService cservice = (CatalogService) ServiceFactory.getService(ServiceFactory.CatalogService);
cservice.persistUploadProduct(product,productFile, productFileName,productFileContentType);
Files module use properties defined in sm-core/conf/properties/sm-core-config.properties
Product image upload
core.product.image.maxfilesize=3145728
core.product.image.contenttypes=image/pjpeg;image/gif;image/jpeg;image/jpg;image/png;image/tiff;image/x-png
core.product.image.cleanup=false
core.product.image.maxwidth=700
core.product.image.maxheight=700
Product file upload
core.product.file.maxfilesize=8000000
core.product.file.downloadmaxdays=2
core.product.file.downloadmaxcount=5
Branding files
core.branding.cart.contenttypes=image/pjpeg;image/gif;image/jpeg;image/jpg;image/png;image/tiff;image/x-png
core.branding.cart.maxfilesize=100000
core.branding.cart.cleanup=false
core.branding.cart.dirname=header
core.branding.banner.contenttypes=image/pjpeg;image/gif;image/jpeg;image/jpg;image/png;image/tiff;image/x-png
core.branding.banner.maxfilesize=100000
core.branding.banner.cleanup=true
core.branding.banner.dirname=banner
Other file uploads (logo, banner…)
Other file uploads use File modules directly. File module API can be invoked this way:
FileModule futil = (FileModule) SpringUtil.getBean("localfile");
String finalfilename = futil.uploadFile(ctx.getMerchantid(),"core.branding.cart",uploadFileName,uploadFileContentType);
core.branding.cart pecifies the group of properties defined in sm-core/conf/properties/sm-core-config.properties which identifies specifications for image size and types allowed in the system.
Prices Modules
Shopizer supports one to multiple prices per item. The system is configured out of the box to display and calculate one to multiple one time prices and one to multiple monthly prices. All those configured prices will be displayed in the product details page, in the shopping cart and during the checkout process.
It is possible to add new prices, for instances weekly recursive or annual recursive etc which should be displayed in a distinct row when applicable. In order to implement a new ProductPrice module, create a new class in com.salesmanager.core.module.impl.application.prices and make that class extend PriceModule interface
package com.salesmanager.core.module.impl.application.prices;
@Component("mypricemodule")
public class MyPriceModule implements PriceModule {
public OrderTotalSummary calculateOrderPrice(Order order,
OrderTotalSummary orderSummary, OrderProduct orderProduct,
OrderProductPrice productPrice, String currency) {
//calculate this price type and add it to OrderTotalSummary
}
public OrderTotalSummary calculateOrderPrice(Order order,
OrderTotalSummary orderSummary, OrderProduct orderProduct,
OrderProductPrice productPrice, String currency, Locale locale) {
//calculate this price type and add it to OrderTotalSummary
}
public boolean isTaxApplicable() {
}
public BigDecimal getPrice(ProductPrice productPrice, String currency) {
}
public String getHtmlPriceFormated(String prefix,
ProductPrice productPrice, Locale locale, String currency) {
//return a string displaying how the price will be displayed
}
public String getPricePrefixText(String currency, Locale locale) {
}
public String getPriceSuffixText(String currency, Locale locale) {
}
public String getPriceText(String currency, Locale locale) {
//price formatted as a text
}
}
Your new price will have to be inserted into CORE_MODULES_SERVICE in order to be available in sm-central (admin panel) drop down list
Column name | Description |
---|---|
CORE_MD_SERVICES_ID | Auto increment identifier |
CORE_MD_SERVICES_CODE | 4 |
CORE_MD_NAME | mypricemodule |
COUNTRY_ISO_CODE_2 | XX |
CORE_MD_SERVICES_SUBTYPE | 1 (one time) or 2 (recursive) |
CORE_MD_SERVICES_DESCRIPTION | My new payment module |
CORE_MD_SERVICES_LOGO_PATH | null |
CORE_MD_SERVICES_POSITION | 3 |
CORE_MD_SERVICES_VISIBLE | 1 |
CORE_MD_SERVICES_NEW | 0 |
CORE_MD_SERVICES_URL | null |
CORE_MD_SERVICES_DEV_PROTOCOL | null |
CORE_MD_SERVICES_DEV_DOMAIN | null |
CORE_MD_SERVICES_DEV_PORT | null |
CORE_MD_SERVICES_DEV_ENV | null |
CORE_MD_SERVICES_PROD_PROTOCOL | null |
CORE_MD_SERVICES_PROD_DOMAIN | null |
CORE_MD_SERVICES_PROD_PORT | null |
CORE_MD_SERVICES_PROD_ENV | null |
CORE_MD_SERVICES_CONFIGURABLE | 0 |
Packing modules
Packing modules are classes responsible for preparing shipping quote calculation. There are many ways a merchant can ship items to customers and those modules should provide accuracy for specific shipping needs a merchant can have. The system has 2 packing algorithm included which are Item packing basis and Box packing basis. It is possible to implement and add other packing module. CalculateBoxPackingModule already does a lot when having to pack items together. From admin panel (sm-central) a configuration page is available for configuring your options.
To implement your own packing algorithm your class needs to implement CalculatePackingModule
@Component("bagpacking")
public class MyPackingModule implements CalculatePackingModule {
public void storeConfiguration(int merchantid, ConfigurationResponse vo,
HttpServletRequest request) throws Exception {
// TODO Auto-generated method stub
}
public ConfigurationResponse getConfiguration(
MerchantConfiguration configurations, ConfigurationResponse vo)
throws Exception {
// TODO Auto-generated method stub
return null;
}
public Collection<PackageDetail> calculatePacking(
Collection<OrderProduct> products, MerchantConfiguration config,
int merchantId) throws Exception {
// TODO Auto-generated method stub
return null;
}
public String getConfigurationOptionsFileName(Locale locale)
throws Exception {
// TODO Auto-generated method stub
return null;
}
public PackageDetail getConfigurationOptions(MerchantConfiguration config,
String currency) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
getConfigurationOptionsFileName method can return a configuration jsp page specific to your module if your packing option needs to be configured. The jsp file needs to be named as your packing module, so in this case I will have a jsp bagpacking.jsp that I will drop in sm-central/WebContent/shipping directory. If no configuration is required, that method should return null.
storeConfiguration method will be invoked when the merchant submit your packing configuration option. ConfigurationResponse being the original values stored in the database. HttpServletRequest contain new configuration options submitted.
getConfiguration is a callback invoked when options are retrieved from the database in case extra manipulations are required. Simply return ConfigurationResponse when no manipulations are required in the packing options.
getConfigurationOptions return selected options to be displayed in optional module configuration jsp page. calculatePacking is the method invoked for shipping quotes during checkout or when creating an invoice. This method returns a Collection of PackageDetail which contains appropriate information for shipping quote calculation. Although the method is called calculatePacking, there is no cost calculation, but an organisation of items inside a Collection of PackageDetail.
Once your module class is implemented, a new entry in CORE_MODULES_SERVICES table is required for discovering this new module and for the option to be displayed in admin panel (sm-central)
Column name | Description |
---|---|
CORE_MD_SERVICES_ID | Auto increment identifier |
CORE_MD_SERVICES_CODE | 1 |
CORE_MD_NAME | bagpacking |
COUNTRY_ISO_CODE_2 | XX |
CORE_MD_SERVICES_SUBTYPE | 1 |
CORE_MD_SERVICES_DESCRIPTION | My new packing module |
CORE_MD_SERVICES_LOGO_PATH | null |
CORE_MD_SERVICES_POSITION | 3 |
CORE_MD_SERVICES_VISIBLE | 1 |
CORE_MD_SERVICES_NEW | 0 |
CORE_MD_SERVICES_URL | null |
CORE_MD_SERVICES_DEV_PROTOCOL | null |
CORE_MD_SERVICES_DEV_DOMAIN | null |
CORE_MD_SERVICES_DEV_PORT | null |
CORE_MD_SERVICES_DEV_ENV | null |
CORE_MD_SERVICES_PROD_PROTOCOL | null |
CORE_MD_SERVICES_PROD_DOMAIN | null |
CORE_MD_SERVICES_PROD_PORT | null |
CORE_MD_SERVICES_PROD_ENV | null |
CORE_MD_SERVICES_CONFIGURABLE | 0 or 1 |