Shipping plugins are used to calculate the shipping cost in real time during the checkout in the online storefront. A plugin accepts basic data of the products being shipped (product list, total weight, total cost, etc.) and returns the shipping cost, calculated using the plugin's internal algorithm; e.g., as a percentage of the total order cost or by sending a request to the shipping service server via its API (if available). The specific implementation is individual for each shipping plugin and is defined by its developer.
In the Webasyst framework the basis for development of shipping plugins is provided by the system kernel. The framework offers a set of methods and a certain data exchange format for retrieving order details and returning the shipping cost calculation results. These basic methods also include methods for printing related documents, such as waybills, for example.
Take a look at the source code of the “Flat rate” shipping plugin as an example (GitHub repository).
Shipping plugins are supported at the framework's system level rather than at the application level. This allows various applications (not only Shop-Script 5) to rely on the same shipping plugins existing in the system.
When developing your own shipping plugin, we recommend using plugin FlatRate as a simple example. This plugin allows specifying a fixed shipping rate for all orders and does not contain complex algorithms; however, it is a convenient template, which you can use for development of a new shipping plugin.
The source code of shipping plugins resides inside the wa-plugins/shipping/
directory. Each plugin is stored in a separate subdirectory containing implementation of a class extending base class waShipping
. The name of the subdirectory with plugin files is the plugin's identifier, which is used to form the correct name for the class and the file it is contained in. Below is provided an example of the correct class and file names for some plugin with identifier yourshipping
:
wa-plugins/shipping/yourshipping/lib/yourshippingShipping.class.php
<?php class yourshippingShipping extends waShipping { ... }
In addition to the main plugin class extending base class waShipping
, each plugin also requires configuration files for correct functioning, which, like in applications and plugins, must be placed inside the lib/config/
subdirectory:
plugin.php
: contains general plugin description (name, description, path to icon file, path to logo file, version number, and developer name);
settings.php
: contains an associative array of parameters required for the plugin functioning (the exact list of settings is defined by the specific algorithm of each plugin). The contents of this array is implicitly included in the plugin class code and is accessible as class fields (e.g., $this->some_setting_id
).
The development of a shipping plugin is very similar to that of an application plugin with the exception that, unlike the plugin development, creation of a shipping plugin often requires only implementation of one or several main methods of the base class, listed below.
Methods
In methods of the main plugin class you can access values of plugin settings specified in its configuration filelib/config/settings.php
by using private class fields of the form$this->field_name
. Instead of field_name specify the id of the settings field whose value you need to obtain.
Methods which must be implemented in every shipping plugin
calculate()
The main method of each shipping method which returns the shipping cost and the approximate shipping time or a message for a customer. This method accepts no arguments.
Types of return values
One shipping option
Array containing one sub-array of elements.
array( 'name' => '', //shipping variant name; e.g., “Ground”, “Air”, “Express Mail”, etc. 'comment' => '', //optional description of the shipping variant 'est_delivery' => '', //optional string containing information about the approximate delivery date and time 'currency' => $this->currency, //ISO3 code of the currency of the calculated shipping cost value 'rate_min' => $this->cost, //the lower limit if the shipping cost has been calculated approximately 'rate_max' => $this->cost, //the higher limit if the shipping cost has been calculated approximately 'rate' => $this->cost, //exact shipping cost value 'type' => ..., //of one shipping types waShipping::TYPE_TODOOR, waShipping::TYPE_PICKUP, or waShipping::TYPE_POST 'delivery_date' => ..., //shipping date or interval of shipping dates in SQL DATETIME format 'service' => ..., //name of shipping service to be the name of the transporting company that will do the actual delivery ),
Depending on the shipping type specified in 'type'
element, the array must also contain an element with 'custom_data'
key with additional data about returned shipping option:
waShipping::TYPE_PICKUP
:id
string: shipping option IDlat
float: coordinates latitudelng
float: coordinates longitudename
string: shipping option name—extracted fromname
field of a shipping option or fromname
fields of a shipping method and a shipping optiondescription
string: HTML descriptionway
string: walking path descriptionadditional
string: extra HTML descriptiontimezone
string: time zoneschedule
string|array: order pickup point working schedule in the form of either an HTML string or an array of data for each of the days of the week, each array items containing the following entries:type
string: type of a day of the week, eitherworkday
orweekend
meaning a day offstart_work
string: start of work time in SQL DATETIME formatend_work
string: end of work time in SQL DATETIME formatadditional
string: additional description for a specific day, maximum 64 characters
payment
array: supported payment methods as an array, with keys aswaShipping::PAYMENT_TYPE_*
constantsphotos
array: simple array of pickup point image URLs, or array of sub-arrays containing keysuri
(image URL),title
(image title),description
(image description)storage
int: order storage time in daysintervals
array: available order pickup time intervals as an array; array keys must be time intervals expressed in hours and minutes, array values must be sub-arrays of the numbers of the days of the week; e.g.:array( `10:00-18:00` => array(0,1,2,3,4), //from Monday through Friday shipping from 10:00 till 18:00 `10:00-15:00` => array(5), // on Saturday shipping from 10:00 till 15:00 )
waShipping::TYPE_TODOOR
:id
string: shipping option IDintervals
array: available order delivery intervals
Several shipping options
Array containing several sub-arrays.
array( 'variant_1' => array( 'name' => ..., 'comment' => ..., 'est_delivery' => ..., 'currency' => ..., 'rate_min' => ..., 'rate_max' => ..., 'rate' => ..., 'type' => ..., //one of shipping types waShipping::TYPE_TODOOR, waShipping::TYPE_PICKUP, or waShipping::TYPE_POST ), 'variant_2' => array( 'name' => ..., 'comment' => ..., 'est_delivery' => ..., 'currency' => ..., 'rate_min' => ..., 'rate_max' => ..., 'rate' => ..., 'type' => ..., ), ... ),
Message of unsupported shipping address
Array containing a sub-array with empty shipping rate and a message for a customer. A plugin should offer a customer to edit address fields.
array( 'rate' => null, 'comment' => '...', ),
Message of inability to calculate shipping cost
String with an error message.
'...'
Shipping options’ data attributes
Method calculate()
can return additional data
attributes for shipping options. Those attributes can be used by plugin’s JavaScript code in app backend in order editing mode.
To add such data
attributes, you need to define a sub-array with key 'custom_data'
for each item of the array returned by calculate()
method. The keys of that sub-array will be used as data
attributes’ names for option
HTML elements, and their values will be used as data
attributes’ values.
Example
array( 'variant_id_1' => array( 'name' => '...', 'est_delivery' => '...', 'currency' => '...', 'rate' => '...', 'custom_data' => array( 'some-attribute' => 'some data attribute value', 'another-attribute' => 'another data attribute value', '...' => '...', ), ), ... ),
allowedCurrency()
Returns a string or an array with the list of ISO3 codes of the currencies for which the plugin can calculate shipping rates.
allowedWeightUnit()
Returns a string ID, or an array of IDs, of weight units supported by the shipping plugin.
Optional methods which may be implemented if required by plugin's functionality
tracking($tracking_id = null)
This method returns a string (HTML tags are allowed), which may contain information about the current state of the order delivery; e.g., a link to the delivery tracking page of the corresponding online service or the delivery status if this information is available via the shipping service API.
$tracking_id
is an optional tracking id received from user. The result returned by this method may be displayed to a user in the order-viewing page.
getPrintForms(waOrder $order = null)
Returns the list of supported printable forms in the form of an associative array as shown below:
array( key1 => array( 'name' => '...', 'description' => '...', ), key2 => array( 'name' => '...', 'description' => '...', ), ... )
key is an arbitrary unique (within this shipping plugin) identifier of a printable form;
name — form name;
description — form description.
displayPrintForm($id, waOrder $order, $params = array())
Prints a form by the specified identifier (one of array keys defined in method getPrintForms()
). The contents and look of each printable document are defined by the individual template residing in plugin subdirectory templates/
. The method must return HTML code of the generated printable form.
allowedAddress()
This method returns an array with the list of countries, regions or also other address parameters, for which the current plugin can calculate shipping rates. The contents of the returned array can be used to verify whether the current customer address is supported by this shipping plugin. The returned array must have the following structure:
array( array( 'country' => ..., 'region' => ..., ) )
Each array item may contain either a string (single value), or an array of strings (list of allowed country or regions codes). If you do not need to limit shipping to individual regions of the specified country, then omit the 'region'
item.
requestedAddressFields()
Returns array of address form fields which must be requested from customer on the shipping option selection step. The method must return either false
(no address fields must be requested), or array()
(all fields must be requested), or an array with the structure similar to that shown in the example below:
return array( 'field_id_1' => ..., 'field_id_2' => ..., ... );
Instead of array keys field_id_*
specify codes of address fields: 'country
', 'region
', 'city
', 'zip
', 'street
', or any custom address field, if available. Array items may have the following values:
array()
— field is not requestedarray('cost' => true)
— field is requested, and its value is used for calculation of the shipping costarray('hidden' => true, 'value' => ...)
— field is hidden and contains the value specified in parameter'value'
array('cost' => true, 'hidden' => true, 'value' => ...)
— combination of the two previous optionsfalse
— field is not requested
requestedAddressFieldsForService($service)
By overriding this method in your plugin class, you can return additional address fields for a selected shipping option specified in $service
parameter. If the extra address field’s value is required to calculate the shipping cost, add $field['cost']=true
property to it. If a field is simply required for completion by a user, add $field['required']=true
.
Configuration files
plugin.php
File plugin.php
is plugin's main configuration file used to store basic information: plugin name, description, version, etc. as shown below:
<?php return array( 'name' => ..., 'description' => ..., 'icon' => ..., 'logo' => ..., 'vendor' => ..., 'version' => ..., 'type' => ..., 'external' => ..., //optional parameter 'external_tracking' => ..., //optional parameter 'services_by_type' => true, //optional parameter 'type' => ..., //optional parameter );
Field descriptions
- name: plugin name; if plugin's localization file contains a string with key matching the specified name, then the name will be automatically translated;
- description: plugin description; if plugin's localization file contains a string with key matching the specified description, then the description will be automatically translated;
- icon: path to plugin icon file, relative to main plugin directory; e.g.,
img/flatrate16.png
(path to standard 16x16 px large plugin icon file); - logo: path to plugin logo file, relative to main plugin directory; e.g.,
img/flatrate.png
(path to 60x32 px large logo file, standard for shipping plugins); - vendor: numerical developer id available in your developer account in Webasyst Customer Center;
- version: string version value; e.g., '1.0.0';
- external: flag requiring requests to remote resources in plugin's PHP code be sent via an additional AJAX request to ensure better performance of the shipping option selection page,
- external_tracking: flag requiring shipping status requests be sent via an additional AJAX request to ensure better performance of the order-viewing page in backend,
- services_by_type: flag denoting that a plugin is capable of returning shipping options by shipping type selected by a user.
- type: one of constants
waShipping::TYPE_TODOOR
,waShipping::TYPE_PICKUP
, orwaShipping::TYPE_POST
if a plugin is capable of returning shipping options of one type only.
settings.php
File settings.php
is used for automatic generation of settings interface for a shipping plugin displayed in the backend of the app utilizing the plugin. Read a detailed description of the plugin settings file.
requirements.php
File requirements.php
is used to specify additional system requirements applicable to the functioning of your shipping plugin (e.g., availability of extra PHP extensions or configuration parameters, or installed Webasyst apps). See a detailed description of the system requirements configuration file.
Using custom fields for checkout and order editing modes
A shipping plugin can offer a user its own custom fields, in addition to standard ones, which are defined by app settings. During checkout, a user can specify extra parameters in those custom fields to more precisely calculate the shipping cost. Those custom fields’ values can also be editing in app backend, if the app supports this functionality.
To offer a user custom fields, you need to add public method customFields()
to plugin class. The method must return an array containing custom fields configuration data. See an example:
public function customFields(waOrder $order) { if (!$this->getAdapter()->getAppProperties('custom_fields')) { //if an app does not support custom fields, do not show them either in backend or in frontend return array(); } //checking current environment if (wa()->getEnv() === 'backend') { //checking if an app supports editing of custom fields’ values in backend if (!$this->getAdapter()->getAppProperties('backend_custom_fields')) { //do not show custom fields, if an app does not support them return array(); } } //getting the values of previously completed custom fields $shipping_params = $order->shipping_params; return array( 'field1' => array( 'value' => $default_field_value, //default value, if necessary 'title' => _wp('First field name'), 'control_type' => waHtmlControl::INPUT, ), 'field2' => array( 'value' => $default_field_value, //default value, if necessary 'title' => _wp('Second field name'), 'control_type' => 'MyShippingPluginControl', //custom control ID, if applicable ), //... ); }
Validation of values entered in custom fields
If a customer has entered an incorrect value in a custom field then you can block further checkout process and show an error message. To do so, add an element with the 'errors'
key to the field parameters array. It must contain a sub-array with a list of the custom fields that must be marked as incorrectly filled in. The sub-array’s keys must be the names of those fields and their values must be localized error message to be displayed next to them.
if (!strlen(trim($order->shipping_params['myfield']))) { $myfield_errors = [ 'myfield' => $this->_w('This field must be filled in.'), ]; } $fields['myfield'] = [ 'value' => '', 'title' => $this->_w('My field'), 'control_type' => waHtmlControl::INPUT, 'errors' => $myfield_errors, ];
Custom form fields
For 'control_type'
parameters you may specify a custom control key, which can be any string of HTML or JavaScript code.
To implement a custom control, the following is required:
- Add a public method to your plugin class to return the desired control HTML code.
-
At the beginning of
customFields()
method, add the following call:$this->registerControl('MyShippingPluginControl', array($this, 'myShippingPluginControl'));
In this example,
'MyShippingPluginControl'
as the first parameter of theregisterControl()
method is your custom control key, and'myShippingPluginControl'
is its implementation method name.
Custom control keys and their implementation methods can be named freely.
If a change of a custom field’s value must immediately update the calculated shipping cost, then there must be sub-array 'data'
with 'affects-rate' => true
element in that field’s configuration.
Example
array( 'value' => ifset($shipping_params['some_custom_field']), 'title' => _wp('Custom field name'), 'control_type' => waHtmlControl::SELECT, 'description' => '...', 'options' => array(...), 'data' => array( 'affects-rate' => true, ), )
On-the-fly updating of the shipping cost on changing the value of a custom field must be implemented by means of JavaScript logic, which must allow custom fields to interact with shipping plugin’s standard fields. That JavaScript logic must be included in a custom control using method registerControl()
.
To obtain the value of a custom field, call method getPackageProperty($field)
of the system shipping class. The method accepts a string ID of a custom field as a parameter, which is defined in method customFields()
.
Example
$shipping_params = $this->getPackageProperty('param_name');
Values of completed custom fields are displayed next to their names in app backend on an order-viewing page. Displayed custom field names are retrieved from 'title'
element of the custom field configuration returned by method customFields()
.
For each shipping option extra shipping fields can be offered to a user by means of customFieldsForService(waOrder $order, $service)
method. By default, this method returns the same result as customFields()
. Override it in your plugin class to return extra checkout form elements using waHtmlControl
class functionality.
For the waShipping::TYPE_TODOOR
shipping type it is recommended to use an additional field with desired_delivery
key and waHtmlControl::DATETIME
type. The value entered by a user will be available for viewing and editing. If the change of an additional field’s value must trigger automatic re-calculation of the shipping cost, add $field['data']['affects-rate'] = true
to the field properties.
Requesting preferred delivery date & time
To allow a user to enable delivery date or time selection options and to set up available time intervals for clients, add a control with key desired_delivery
and control type DeliveryIntervalControl
to configuration file settings.php
as shown below:
'desired_delivery' => array( 'title' => 'Preferred delivery time', 'control_type' => 'DeliveryIntervalControl', 'minutes' => true, ),
Parameter 'minutes'
denotes the requirement to specify minutes in delivery time intervals in addition to hours.
To display fields for specifying delivery date (with a pop-up calendar) and time intervals (as a dropdown list of options set up in shipping method settings), you have to implement public method customFields()
in the main plugin class using the following example, which you can customize to match your individual logic:
public function customFields(waOrder $order) { $fields = parent::customFields($order); $this->registerControl('CustomDeliveryIntervalControl'); $setting = $this->getSettings('customer_interval'); if (!empty($setting['interval']) || !empty($setting['date'])) { if (!strlen($this->delivery_time)) { $from = time(); } else { $from = strtotime(preg_replace('@,.+$@', '', $this->delivery_time)); } $offset = max(0, ceil(($from - time()) / (24 * 3600))); $fields['desired_delivery'] = array( 'value' => null, 'title' => $this->_w('Preferred delivery time'), 'control_type' => 'CustomDeliveryIntervalControl', 'params' => array( 'date' => empty($setting['date']) ? null : ifempty($offset, 0), 'interval' => ifset($setting['interval']), 'intervals' => ifset($setting['intervals']), ), ); } return $fields; }
The capability to request preferred delivery time from a client must be supported by the app your shipping plugin is used with. In case of Shop-Script its version must be 7.2.4.114 or higher.
Interaction of shipping method selection form in frontend with plugin
During input of user data in frontend you may need to call some PHP code of your shipping plugin to update the shipping cost displayed to a user or also other interface elements. This can be achieved by the use of AJAX requests sent to a URL generated using this example:
$url_params = array( 'action_id' => 'foo', //in this example will be called plugin’s public method named 'fooAction', which must return a complete and valid response to an AJAX request 'plugin_id' => $this->key, ); wa()->getRouteUrl(sprintf('%s/frontend/shippingPlugin', $this->app_id), $url_params, true);
Shipment state
A shipping plugin deals with shipments. A shipment is an instance of waOrder
class. It may be in one of the following states designated by constants of waShipping
class:
waShipping::STATE_DRAFT
— shipment is being created or editedwaShipping::STATE_READY
— shipment has been created and is ready for shippingwaShipping::STATE_SHIPPING
— shipment is being passed to a shipping servicewaShipping::STATE_CANCELED
— shipment has been canceled
Use the following methods of class waShipping
to read or change the status of a shipment.
setPackageState (waOrder $order, $state, $params = array())
Sets specified state for a shipment and returns result in one of the following formats:
null
— plugin does not support this state.string
— single string with an error message to be written by an app to order processing history; HTML tags are allowed and are not escaped.array[string]
— associative array of data to be saved toshipping_data
item of order details. The exception of this rule is'view_data'
, which contains a string to be displayed in order processing history. HTML tags are allowed and are not escaped.
getStateFields ($state, waOrder $order = null, $params = array())
Returns an array of data submitted via the web form that a user is offered to complete when transferring a shipment to the specified $state.
The values of completed form fields are copied to argument $params['shipping_data']
when method setPackageState()
is called.
getAdapter()->getAppProperties ($property_name = null)
Helps detect the shipping plugins support level of an app.
The method returns values from parameter 'shipping_plugins'
specified in app’s configuration file app.php
. It can be either a Boolean value or an associative array of the following items corresponding to various shipment handling functions:
'desired_date'
— desired shipping date'draft'
— transferring of shipment draft data'ready'
— confirmation of shipment data'shipping'
— request for shipping'cancel'
— shipping cancellation
Order ready time for passing on to delivery service
During the shipping cost calculation a plugin has access to the order time for passing on to the delivery service by means of getPackageProperty()
method as shown in the example:
/** @var string $departure_datetime SQL DATETIME */ $departure_datetime = $this->getPackageProperty('departure_datetime');
Reading order dimensions
Each order item may have its dimensions specified in height
, width
, length
properties if an app supports passing of these data on to shipping plugins. They can be obtained using method $this->getItems()
. Parameters total_height
, total_width
, and total_length
are available only in the case if an app has calculated an order’s total dimensions. They can be obtained using method $this->getPackageProperty()
.
Order dimensions unit
To set the measure unit for available order dimensions, override allowedLinearUnit()
method—it must return one of the following string values:
m
: metercm
: centimetermm
: millimeterin
: inchft
: footkm
: kilometermi
: mile
Verifying app’s support for order dimensions
In your plugin class, you can verify if an app is capable of passing order dimensions to a shipping plugin using this example:
$this->getAdapter()->getAppProperties('dimensions')
Values which can be returned by this method are:
(string)
: ID of a plugin used to calculate total order dimensions, if such a plugin is set up, and the functionality is supported by the app and is set up.true
: the functionality is supported by the app and is set up, but a plugin is not set up.false
: the functionality is supported by the app but is not set up.null
: the functionality is not supported.
Tips
Base system class of shipping plugins waShipping
provides several useful methods, which you can you in the main plugin class.
Useful methods
getAddress($field = null)
Returns either full array of shipping address data or only the value of specified field code; e.g., 'country
', 'region
', 'city
', 'zip
', 'street
', or any custom field code, if available.
getItems()
Returns array of ordered items (products).
getTotalPrice()
Returns the total shipping cost.
getTotalWeight()
Returns the total shipping weight.
Controllers & actions
A shipping plugin can have its own controllers to process user requests. Plugin’s controllers must extends one of the following system classes:
waJsonActions
waJsonController
waLongActionController
waSystemPluginActions
waSystemPluginAction
Requests to own controllers can be sent, for instance, on the plugin settings page.
Before a controller begins to process a user request in the backend, user access rights are automatically verified.
URLs to send requests to plugin controllers can be obtained by means of plugin’s main class method getInteractionUrl($action = 'default', $module = 'backend')
.
HTML template files for action classes must be stored at paths formed according to the templates/actions/[module]/[Module][Action].html
pattern.
Additional PHP classes
To ensure that plugin’s additional classes are auto-loaded, their files must be stored in lib/classes/
directory and named by the shipping[Plugin_id][Class_name].class.php
pattern. Class names must be formed by the shipping[Plugin_id][Class_name]
pattern. Here [Plugin_id]
is a plugin ID and [Class_name]
is an arbitrary class name part added by the developer.
Database
General plugin settings
Common settings, valid for all apps, can be stored in wa_app_settings
table. You can save and read such values using these built-in methods:
setGeneralSettings($name, $value)
getGeneralSettings($name = null, $default = '')
Such settings are saved to the database table with app_id
field values formed by the webasyst.shipping.[id]
pattern. Here [id]
is the plugin ID.
The following settings names, being reserved identifiers, cannot be used by a plugin developer:
update_time
: used by meta updates,sync_time
: used by scheduled tasks,sync_success_time
: used by scheduled tasks,sync_failure_time
: used by scheduled tasks.
Own database tables
Shipping plugin’s tables must be named using the wa_shipping_[plugin_id]
prefix.
The structure of tables must be described in file lib/config/db.php
in the same format as for apps and their plugins.
To access values in its table, a plugin can use waSystemPluginModel
, a child class of waModel
. In this case you do not need to create model classes for of plugin’s tables. You can use method setPlugin(waSystemPlugin $plugin, $table = null)
of that class to get an instance of waModel
for a specified table name and start working with the data in that table.
If, however, you need custom methods which are not available in waModel
, this is not a good option for you. In this case you need to create model classes for your plugin’s tables. They can extend waSystemPluginModel
class, too.
In the main plugin class method getModel($table = null)
is available. You can pass a table name to it and get an instance of that table’s model class, if there is such a class in the plugin, or an instance of waSystemPluginModel
for the specified table name. To specify a table name, use only its variable name part after the mandatory prefix.
If no table name is specified, then the method returns a model class instance for the plugin table whose name consists of the plugin’s mandatory prefix only. This can be useful in the cases when a shipping plugin uses only one database table.
Data files
Shipping plugins can store their data in files located in wa-data/
directory. To access such files, you can use these built-in methods of the main plugin class:
getDataPath($path = null, $public = false, $create = true)
: get a path to a file,getDataUrl($path = null, $absolute = false)
: get a public file URL.
Scheduled jobs
The programming code to support scheduled jobs consists of three main parts:
- App controller which calls
runSync()
method for each shipping plugin setup. - Method
runSync()
of the plugin’s parent classwaShipping
, which calls methodssyncRequired()
andhandleSync($result)
to determine whether a particular scheduled job needs to be executed and to process the results of an executed job. When necessary, you may override those methods in your plugin’s main class. - Main plugin class
sync()
containing the actual scheduled jobs execution algorithm.
Information on the jobs execution status is displayed on the plugin settings screen. For instance, if an app does not support execution of scheduled jobs, a warning is displayed for a user. If method getSettingsHtml()
used for obtaining plugin settings is overridden, then it might be convenient to use the logic of displaying information on the status of the scheduled jobs execution contained in method getNoticeHtml()
.
An app shows server scheduler setup instructions to users with a recommended execution interval and a command example like shown below:
php cli.php shop shipping
Plugin configuration parameters
- Description of available functionality in file
plugin.php
:sync
: support for updatable and cacheable data, can contain either a boolean value or a recommended data update interval.
- Description of functionality to be supported by an app in file
app.php
:sync
: scheduled update of plugin-related data and shipments statuses.rights
: required access rights level for users who are allowed to interact with the plugin’s controllers. If empty or not specified, then administrative access rights for an app are required. Forshop
app, access rights levelsettings
must be enabled to allow users access to app settings and shipping plugin settings.
- Descriptions of callback URLs must be contained in file
guide.php
.
Meta updates
Meta update files must be stored in the lib/updates/
directory.