Skip to content

Basket services

Most of the time you want to offer some shipping and/or payment options to your customers. Even if you don't show the shipping or payment options, you need to add it to the basket so the process after creating the order continues as expected.

List options#

At first you need to get the available delivery and payment options from the service's resource URL listed in the OPTIONS request. With that URL you can query for the available services, either all together in one request or separated by their type ("delivery" or "payment"). The example code below will return all payment options:

curl -b cookies.txt -c cookies.txt \
-X GET 'http://localhost:8000/jsonapi/service?filter[cs_type]=payment&include=text,price,media'
const args = {
    'filter': {
        'cs_type': 'payment' // or "delivery" (optional)
    },
    'include': 'text,price,media'
}
let params = {};

if(options.meta.prefix) { // returned from OPTIONS call
    params[options.meta.prefix] = args
} else {
    params = args
}

// returned from OPTIONS call
const url = options.meta.resources['service']
    + (options.meta.resources['service'].includes('?') ? '&' : '?')
    + window.param(params) // from https://github.com/knowledgecode/jquery-param

fetch(url).then(result => {
    if(!result.ok) {
        throw new Error(`Response error: ${response.status}`)
    }
    return result.json()
}).then(result => {
    console.log(result.data)
})
var args = {
    'filter': {
        'cs_type': 'payment' // or "delivery" (optional)
    },
    'include': 'text,price,media'
};
var params = {};

if(options.meta.prefix) { // returned from OPTIONS call
    params[options.meta.prefix] = args;
} else {
    params = args;
}

$.ajax({
    method: "GET",
    dataType: "json",
    url: options.meta.resources['service'], // returned from OPTIONS call
    data: params
}).done( function( result ) {
    console.log( result );
});

This returns the list of payment or delivery options (or both if the "filter" parameter is omitted) including the associated texts, prices and images due to the "included" parameter:

{
    "meta": {
        "total": 5,
        "prefix": null,
        "content-baseurl": "http://localhost:8000/",
        "content-baseurls": {
            "fs-media": "http://localhost:8000/aimeos",
            "fs-mimeicon": "http://localhost:8000/vendor/shop/mimeicons",
            "fs-theme": "http://localhost:8000/vendor/shop/themes"
        },
        "csrf": {
            "name": "_token",
            "value": "..."
        }
    },
    "links": {
        "self": "http://localhost:8000/jsonapi/service?filter%5Bcs_type%5D=delivery&include=text%2Cprice%2Cmedia"
    },
    "data": [{
        "id": "1",
        "type": "service",
        "links": {
            "self": {
                "href": "http://localhost:8000/jsonapi/service?id=1",
                "allow": ["GET"]
            },
            "basket.service": {
                "href": "http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery",
                "allow": ["POST"],
                "meta": {
                    "time.hourminute": {
                        "code": "time.hourminute",
                        "type": "time",
                        "label": "Delivery time",
                        "public": true,
                        "default": "07:15",
                        "required": true
                    },
                    "supplier.code": {
                        "code": "supplier.code",
                        "type": "list",
                        "label": "Pick-up address",
                        "public": true,
                        "default": {
                            "demo-test1": "Test supplier 1\nTest company\nTest street 1\n10000 Test city\nNY US\ndemo1@example.com\n",
                            "demo-test2": "Test supplier 2\nTest company\nTest road 10\n20000 Test town\nNY US\ndemo2@example.com\n"
                        },
                        "required": true
                    }
                }
            }
        },
        "attributes": {
            "service.id": "1",
            "service.type": "delivery",
            "service.code": "demo-pickup",
            "service.label": "Click & Collect",
            "service.provider": "Standard,Time,Supplier",
            "service.position": 0,
            "service.datestart": null,
            "service.dateend": null,
            "service.status": 1,
            "price": {
                "price.id": "27",
                "price.type": "default",
                "price.currencyid": "EUR",
                "price.domain": "service",
                "price.quantity": 1,
                "price.value": "0.00",
                "price.costs": "0.00",
                "price.rebate": "0.00",
                "price.taxvalue": "0.0000",
                "price.taxrates": {
                    "": "0.00"
                },
                "price.taxrate": "0.00",
                "price.taxflag": true,
                "price.status": 1,
                "price.label": "Demo: DHL"
            }
        },
        "relationships": {
            "media": {
                "data": [{
                    "id": "18",
                    "type": "media",
                    "attributes": {
                        "service.lists.id": "1",
                        "service.lists.domain": "media",
                        "service.lists.refid": "18",
                        "service.lists.datestart": null,
                        "service.lists.dateend": null,
                        "service.lists.config": [],
                        "service.lists.position": 0,
                        "service.lists.status": 1,
                        "service.lists.type": "default"
                    }
                }]
            },
            "price": {
                "data": [{
                    "id": "27",
                    "type": "price",
                    "attributes": {
                        "service.lists.id": "2",
                        "service.lists.domain": "price",
                        "service.lists.refid": "27",
                        "service.lists.datestart": null,
                        "service.lists.dateend": null,
                        "service.lists.config": [],
                        "service.lists.position": 0,
                        "service.lists.status": 1,
                        "service.lists.type": "default"
                    }
                }]
            },
            "text": {
                "data": [{
                    "id": "86",
                    "type": "text",
                    "attributes": {
                        "service.lists.id": "6",
                        "service.lists.domain": "text",
                        "service.lists.refid": "86",
                        "service.lists.datestart": null,
                        "service.lists.dateend": null,
                        "service.lists.config": [],
                        "service.lists.position": 3,
                        "service.lists.status": 1,
                        "service.lists.type": "default"
                    }
                }]
            }
        }
    }],
    "included": [{
        "id": "18",
        "type": "media",
        "attributes": {
        "media.id": "18",
        "media.domain": "service",
        "media.label": "Demo: dhl.png",
        "media.languageid": null,
        "media.mimetype": "image/png",
        "media.type": "icon",
        "media.preview": "http://demo.aimeos.org/media/service/pickup.png",
        "media.previews": {
        "1": "http://demo.aimeos.org/media/service/pickup.png"
        },
        "media.url": "http://demo.aimeos.org/media/service/pickup.png",
        "media.status": 1
        }
    },{
        "id": "27",
        "type": "price",
        "attributes": {
            "price.id": "27",
            "price.type": "default",
            "price.currencyid": "EUR",
            "price.domain": "service",
            "price.quantity": 1,
            "price.value": "0.00",
            "price.costs": "0.00",
            "price.rebate": "0.00",
            "price.taxvalue": "0.0000",
            "price.taxrates": {
                "": "0.00"
            },
            "price.taxrate": "0.00",
            "price.taxflag": true,
            "price.status": 1,
            "price.label": "Demo: Click&Collect"
        }
    },{
        "id": "86",
        "type": "text",
        "attributes": {
            "text.id": "86",
            "text.languageid": "en",
            "text.type": "short",
            "text.label": "Demo short/en: Local pick-up",
            "text.domain": "service",
            "text.content": "Local pick-up",
            "text.status": 1
        }
    }]
}

Add delivery/payment#

To add a service to the basket you need the "basket.service" URL from the service resource response. In our previous example, the URL of the first delivery option that would add this service to the basket is:

http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery

To add this service as delivery option to the current basket you should use:

curl -b cookies.txt -c cookies.txt \
-X POST 'http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery&_token=...' \
-H 'Content-Type: application/json' \
-d '{"data": [{
    "id": "delivery",
    "attributes": {
        "service.id": "1",
        "time.hourminute": "15:00",
        "supplier.code": "demo-test2"
    }
}]}'
const params = {'data': [{
    'id': 'payment',
    'attributes': {
        'service.id': '1', // from service response
        'time.hourminute': '15:00', // key/value pairs of data entered by the customer
        'supplier.code': 'demo-test2'
    }
}]};

let url = response['data'][0]['links']['basket.service']['href']; // from service response

if(response['meta']['csrf']) { // add CSRF token if available and therefore required
    const csrf = {}
    csrf[response['meta']['csrf']['name']] = response['meta']['csrf']['value']
    url += (url.indexOf('?') === -1 ? '?' : '&') + window.param(csrf) // from https://github.com/knowledgecode/jquery-param
}

fetch(url, {
    method: "POST",
    body: JSON.stringify(params)
}).then(result => {
    if(!result.ok) {
        throw new Error(`Response error: ${response.status}`)
    }
    return result.json()
}).then(result => {
    console.log(result.data)
})
var params = {'data': [{
    'id': 'payment',
    'attributes': {
        'service.id': '1', // from service response
        'time.hourminute': '15:00', // key/value pairs of data entered by the customer
        'supplier.code': 'demo-test2'
    }
}]};

var url = response['data'][0]['links']['basket.service']['href']; // from service response

if(response['meta']['csrf']) { // add CSRF token if available and therefore required
    var csrf = {};
    csrf[response['meta']['csrf']['name']] = response['meta']['csrf']['value'];
    url += (url.indexOf('?') === -1 ? '?' : '&') + $.param(csrf);
}

$.ajax({
    url: url,
    method: "POST",
    dataType: "json",
    data: JSON.stringify(params)
}).done( function( result ) {
    console.log( result );
});

It's important to add the id: "delivery" (or "payment") and the service ID in the attributes section of the parameters sent to the server. The value for "id" is the service type while the value for "service.id" must be the ID of the service option that should be added as delivery or payment entry to the basket.

The response to this request would be similar to this:

{
    "meta": {
        "total": 1,
        "prefix": null,
        "content-baseurl": "http://localhost:8000/",
        "content-baseurls": {
            "fs-media": "http://localhost:8000/aimeos",
            "fs-mimeicon": "http://localhost:8000/vendor/shop/mimeicons",
            "fs-theme": "http://localhost:8000/vendor/shop/themes"
        },
        "csrf": {
            "name": "_token",
            "value": "..."
        }
    },
    "links": {
        "self": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery",
            "allow": ["DELETE","GET","PATCH","POST"]
        },
        "basket.product": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=product",
            "allow": ["POST"]
        },
        "basket.service": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=service",
            "allow": ["POST"]
        },
        "basket.address": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=address",
            "allow": ["POST"]
        },
        "basket.coupon": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=coupon",
            "allow": ["POST"]
        }
    },
    "data": {
        "id": "default",
        "type": "basket",
        "links": {
            "self": {
                "href": "http://localhost:8000/jsonapi/basket?id=default",
                "allow": ["DELETE","GET","PATCH","POST"]
            }
        },
        "attributes": {
            "order.id": null,
            "order.sitecode": "",
            "order.customerid": "",
            "order.languageid": "en",
            "order.currencyid": "EUR",
            "order.price": "0.00",
            "order.costs": "0.00",
            "order.rebate": "0.00",
            "order.taxvalue": "0.0000",
            "order.taxflag": true,
            "order.customerref": "",
            "order.comment": ""
        },
        "relationships": {
            "basket.service": {
                "data": [{
                    "type": "basket.service",
                    "id": "delivery"
                }]
            }
        }
    },
    "included": [{
        "id": "delivery",
        "type": "basket.service",
        "attributes": {
            "order.service.id": null,
            "order.service.price": "0.00",
            "order.service.costs": "0.00",
            "order.service.rebate": "0.00",
            "order.service.taxrate": "0.00",
            "order.service.taxrates": {
                "": "0.00"
            },
            "order.service.type": "delivery",
            "order.service.code": "demo-pickup",
            "order.service.name": "Click & Collect",
            "order.service.position": null,
            "order.service.mediaurl": "",
            "attribute": [{
                "order.service.attribute.id": null,
                "order.service.attribute.type": "delivery",
                "order.service.attribute.name": "",
                "order.service.attribute.code": "time.hourminute",
                "order.service.attribute.value": "15:00",
                "order.service.attribute.quantity": 1
            },{
                "order.service.attribute.id": null,
                "order.service.attribute.type": "delivery",
                "order.service.attribute.name": "",
                "order.service.attribute.code": "supplier.address",
                "order.service.attribute.value": "Test supplier 1\nTest company\nTest street 1\n10000 Test city\nNY US\ndemo1@example.com\n",
                "order.service.attribute.quantity": 1
            }]
        },
        "links": {
            "self": {
                "href": "http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery",
                "allow": ["DELETE", "PATCH"]
            }
        }
    }]
}

It contains an additional "relationships" entry for the basket which points to the basket service entry in the "included" section.

Note

If you perform the same request again, existing options in the basket are NOT overwritten but an additional option added! Thus you can add several delivery or payment options in one order.

Passing additional data#

The "attributes" section in "data" can contain additional key/value pairs whose values are entered by the customer. Here the "pickup time" and "location" are passed. The available keys are listed in the "meta" section of the "basket.service" link of each entry returned by the service response:

"meta": {
    "time.hourminute": {
        "code": "time.hourminute",
        "type": "time",
        "label": "Pick-up time",
        "public": true,
        "default": "07:15",
        "required": true
    },
    "supplier.code": {
        "code": "supplier.code",
        "type": "list",
        "label": "Pick-up address",
        "public": true,
        "default": {
            "demo-test1": "Test supplier 1\nTest company\nTest street 1\n10000 Test city\nNY US\ndemo1@example.com\n",
            "demo-test2": "Test supplier 2\nTest company\nTest road 10\n20000 Test town\nNY US\ndemo2@example.com\n"
        },
        "required": true
    }
}

Only values for these keys will be accepted and if the "required" property is true, they must be passed in the request!

Each of these additional service attributes contains a type property which can be one of these:

boolean
True/false value (input of type "checkbox")
string
Short text field (input)
text
Long text field (textarea)
integer
Integer value (input of type "number" and no decimal places)
number
Decimal value (input of type "number" with two decimal places)
date
Date selector (input of type "date")
time
Time selector (input of type "time")
datetime
Date and time selector (input of type datetime-local)
select
Select drop-down (with options from "default" property)
list
List of values (input elements of type "radio" for value in "default" property)
map
Key/value pairs (select drop-down with keys as option value and values as option labels)

The additional service attributes are then stored in the "attribute" section of the service option in the basket ("included" section):

"attribute": [{
    "order.service.attribute.id": null,
    "order.service.attribute.type": "delivery",
    "order.service.attribute.name": "",
    "order.service.attribute.code": "time.hourminute",
    "order.service.attribute.value": "15:00",
    "order.service.attribute.quantity": 1
},{
    "order.service.attribute.id": null,
    "order.service.attribute.type": "delivery",
    "order.service.attribute.name": "",
    "order.service.attribute.code": "supplier.address",
    "order.service.attribute.value": "Test supplier 2\nTest company\nTest road 10\n20000 Test town\nNY US\ndemo2@example.com\n",
    "order.service.attribute.quantity": 1
}]

Update services#

Replacing existing delivery/payment services in the basket with only one request is possible since 2022.07 by using the PATCH request of the basket service resource. It will remove the already available delivery or payment option (depending on the relatedid parameter) and add the new options afterwards from the body sent with the request.

From the previous GET request or the response returned for your last request you know that the URL for modifying the basket delivery service options is e.g.:

http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery

To update the delivery service in the current basket you should use:

curl -b cookies.txt -c cookies.txt \
-X PATCH 'http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery&_token=...' \
-H 'Content-Type: application/json' \
-d '{"data": [{
    "id": "delivery",
    "attributes": {
        "service.id": "1",
        "time.hourminute": "17:00",
        "supplier.code": "demo-test1"
    }
}]}'
const params = {'data': [{
    'id': 'payment',
    'attributes': {
        'service.id': '1', // from service response
        'time.hourminute': '17:00', // key/value pairs of data entered by the customer
        'supplier.code': 'demo-test1'
    }
}]};

let url = response['data'][0]['links']['basket.service']['href']; // from service response

if(response['meta']['csrf']) { // add CSRF token if available and therefore required
    const csrf = {}
    csrf[response['meta']['csrf']['name']] = response['meta']['csrf']['value']
    url += (url.indexOf('?') === -1 ? '?' : '&') + window.param(csrf) // from https://github.com/knowledgecode/jquery-param
}

fetch(url, {
    method: "PATCH",
    body: JSON.stringify(params)
}).then(result => {
    if(!result.ok) {
        throw new Error(`Response error: ${response.status}`)
    }
    return result.json()
}).then(result => {
    console.log(result.data)
})
var params = {'data': [{
    'id': 'payment',
    'attributes': {
        'service.id': '1', // from service response
        'time.hourminute': '17:00', // key/value pairs of data entered by the customer
        'supplier.code': 'demo-test1'
    }
}]};

var url = response['data'][0]['links']['basket.service']['href']; // from service response

if(response['meta']['csrf']) { // add CSRF token if available and therefore required
    var csrf = {};
    csrf[response['meta']['csrf']['name']] = response['meta']['csrf']['value'];
    url += (url.indexOf('?') === -1 ? '?' : '&') + $.param(csrf);
}

$.ajax({
    url: url,
    method: "PATCH",
    dataType: "json",
    data: JSON.stringify(params)
}).done( function( result ) {
    console.log( result );
});

It's important to add the id: "delivery" (or "payment") and the service ID in the attributes section of the parameters sent to the server. The value for "id" is the service type while the value for "service.id" must be the ID of the service option that should be added as delivery or payment entry to the basket.

The response to this request would be similar to this:

{
    "meta": {
        "total": 1,
        "prefix": null,
        "content-baseurl": "http://localhost:8000/",
        "content-baseurls": {
            "fs-media": "http://localhost:8000/aimeos",
            "fs-mimeicon": "http://localhost:8000/vendor/shop/mimeicons",
            "fs-theme": "http://localhost:8000/vendor/shop/themes"
        },
        "csrf": {
            "name": "_token",
            "value": "..."
        }
    },
    "links": {
        "self": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery",
            "allow": ["DELETE","GET","PATCH","POST"]
        },
        "basket.product": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=product",
            "allow": ["POST"]
        },
        "basket.service": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=service",
            "allow": ["POST"]
        },
        "basket.address": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=address",
            "allow": ["POST"]
        },
        "basket.coupon": {
            "href": "http://localhost:8000/jsonapi/basket?id=default&related=coupon",
            "allow": ["POST"]
        }
    },
    "data": {
        "id": "default",
        "type": "basket",
        "links": {
            "self": {
                "href": "http://localhost:8000/jsonapi/basket?id=default",
                "allow": ["DELETE","GET","PATCH","POST"]
            }
        },
        "attributes": {
            "order.id": null,
            "order.sitecode": "",
            "order.customerid": "",
            "order.languageid": "en",
            "order.currencyid": "EUR",
            "order.price": "0.00",
            "order.costs": "0.00",
            "order.rebate": "0.00",
            "order.taxvalue": "0.0000",
            "order.taxflag": true,
            "order.customerref": "",
            "order.comment": ""
        },
        "relationships": {
            "basket.service": {
                "data": [{
                    "type": "basket.service",
                    "id": "delivery"
                }]
            }
        }
    },
    "included": [{
        "id": "delivery",
        "type": "basket.service",
        "attributes": {
            "order.service.id": null,
            "order.service.price": "0.00",
            "order.service.costs": "0.00",
            "order.service.rebate": "0.00",
            "order.service.taxrate": "0.00",
            "order.service.taxrates": {
                "": "0.00"
            },
            "order.service.type": "delivery",
            "order.service.code": "demo-pickup",
            "order.service.name": "Click & Collect",
            "order.service.position": null,
            "order.service.mediaurl": "",
            "attribute": [{
                "order.service.attribute.id": null,
                "order.service.attribute.type": "delivery",
                "order.service.attribute.name": "",
                "order.service.attribute.code": "time.hourminute",
                "order.service.attribute.value": "17:00",
                "order.service.attribute.quantity": 1
            },{
                "order.service.attribute.id": null,
                "order.service.attribute.type": "delivery",
                "order.service.attribute.name": "",
                "order.service.attribute.code": "supplier.address",
                "order.service.attribute.value": "Test supplier 2\nTest company\nTest road 10\n20000 Test town\nNY US\ndemo2@example.com\n",
                "order.service.attribute.quantity": 1
            }]
        },
        "links": {
            "self": {
                "href": "http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery",
                "allow": ["DELETE", "PATCH"]
            }
        }
    }]
}

Aimeos also allows several delivery or payment option within the basket and there can be more than one delivery or payment option sent in the PATCH request. You can also mix delivery and payment option within one PATCH request but only the type passed in relatedid ("delivery" or "payment") is removed before the new ones are added.

Delete services#

Delivery or payment options added to the basket can also be removed again. For this you need the URL from the basket service entry, which is returned in the basket response. For the example above it is:

http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery

Executing a DELETE request using this URL will remove the service option from the basket again:

curl -b cookies.txt -c cookies.txt \
-X DELETE 'http://localhost:8000/jsonapi/basket?id=default&related=service&relatedid=delivery&_token=...'
// basket service URL returned from basket response
let url = response['included'][0]['links']['self']['href'];

if(response['meta']['csrf']) { // add CSRF token if available and therefore required
    const csrf = {}
    csrf[response['meta']['csrf']['name']] = response['meta']['csrf']['value']
    url += (url.indexOf('?') === -1 ? '?' : '&') + window.param(csrf) // from https://github.com/knowledgecode/jquery-param
}

fetch(url, {
    method: "DELETE"
}).then(result => {
    if(!result.ok) {
        throw new Error(`Response error: ${response.status}`)
    }
    return result.json()
}).then(result => {
    console.log(result.data)
})
// basket service URL returned from basket response
var url = response['included'][0]['links']['self']['href'];

if(response['meta']['csrf']) { // add CSRF token if available and therefore required
    var csrf = {};
    csrf[response['meta']['csrf']['name']] = response['meta']['csrf']['value'];
    url += (url.indexOf('?') === -1 ? '?' : '&') + $.param(csrf);
}

$.ajax({
    url: url
    method: "DELETE",
    dataType: "json"
}).done( function( result ) {
    console.log( result );
});

Note

In the default configuration, the "Autofill" plugin will automatically add a delivery option after removing the delivery option from the basket.

Comments

Become an Aimeos Partner

Aimeos partners are first-class specialists in creating or hosting your Aimeos e-commerce project. They have proven their expertise by building top level e-commerce applications using Aimeos.