Velo Tutorial: Bookings Pricing Custom Extension

Wix custom extensions allow you to expand what your site can do by integrating with 3rd-party services not currently supported by Wix. They also allow you to implement custom logic to change how your site displays and behaves. For example, when you set up Wix Bookings, there is only a single price per service. What if you want to offer different service prices to your customers, such as varied pricing based on different choices? Or different rates for weekends and holidays? Custom extensions are the answer. 

Custom extensions are implemented with Velo using Wix SPIs,  and you can manage multiple extended services from the Public & Backend Code section in the Velo Sidebar. Learn more about custom extensions

With a Bookings Custom Pricing extension, you can implement custom pricing options using code. You can also connect your site to external pricing rate providers whose functionality is not currently supported by Wix. These prices can be displayed on your site's checkout pages. 

This tutorial explains how to set up a custom pricing extension on your site using Velo.

The process has 3 steps:

  1. Create a Bookings Pricing extension on your site.
  2. Implement your extension with custom code.
  3. Deploy the extension.

Step 1: Create a Bookings Pricing extension

The first step in setting up your new extension is to add it to your site. This process creates a new folder in the Custom Extensions section of the Velo Sidebar that contains the files for your backend code.

  1. Add Wix Bookings to your site.

  2. With Velo Dev Mode enabled, click the Public & Backend  tab on the Velo Sidebar.

  3. Scroll down to the Custom Extensions panel at the bottom of the sidebar.

  4. Hover over Custom Extensions and click Add an extension .

  5. Select New Bookings Pricing Extension.

  6. Click Start Now.

  7. If displayed, select I understand the Terms and Conditions and click Accept.

  8. Enter a name for your extension and click Add & Edit Code.

    The name can't contain spaces or special characters. Hyphens are allowed.

Step 2: Implement the extension

The procedure in the previous section creates a folder called bookings-custom-pricing under Custom Extensions in the Public & Backend Code section of the Velo Sidebar. Inside this folder is another folder with the name of the extension you set up. This folder contains 2 default extension files:

  • <my-extension-name>.js: The code in this file generally defines a function named after the purpose of the custom extension, such a calculatePrice(). The function is called by Wix to retrieve the data provided by your extension.
  • <my-extension-name>-config.js: The code in this file generally defines the name of the main SPI function.

Implement the custom code for your extension in these files.

Here are some guidelines for writing your code:

.js

The code in this file defines a function named calculatePrice(). Wix Bookings calls this function at checkout to retrieve the custom pricing options provided by your extension. The function accepts the following parameters:

  • options: An object holding information about the booking, including the service, location, time, and participants. For more details, see the SPI Reference.

    Sample options object:

Copy
1
{
2
"options": {
3
"booking": {
4
"numberOfParticipants": 2,
5
"endDate": "2023-04-11T19:00:00.000Z",
6
"bookingSource": {
7
"platform": "WEB",
8
"actor": "CUSTOMER"
9
},
10
"sendSmsReminder": false,
11
"flowControlSettings": {
12
"withRefund": false
13
},
14
"revision": "1",
15
"_id": "35f6fd1f-df83-4195-bd67-7f7237b428b5",
16
"createdBy": {
17
"anonymousVisitorId": "869f0e9d-40be-4f2b-a415-14f1316f295e"
18
},
19
"selectedPaymentOption": "ONLINE",
20
"contactDetails": {
21
"firstName": "Joy Smith",
22
"email": "joysmith@company.com",
23
"phone": "0001231234"
24
},
25
"additionalFields": [
26
{
27
"_id": "709498bf-e275-4c82-aa9c-e76d3d4e9af6",
28
"value": "1",
29
"label": "Addon 1"
30
},
31
{
32
"_id": "35a296f6-d71e-4d61-b8ef-cd2973cd9e99",
33
"value": "1",
34
"label": "Addon 2"
35
},
36
{
37
"_id": "e3fe7974-3142-4fd5-993c-41472f36f697",
38
"value": "0",
39
"label": "Addon 3"
40
},
41
{
42
"_id": "9f1252be-0625-457b-b5cf-96a1fa42281a",
43
"value": "false",
44
"label": "weekendRate",
45
"valueType": "CHECK_BOX"
46
}
47
],
48
"bookedEntity": {
49
"slot": {
50
"location": {
51
"_id": "f8290483-b327-418e-8aab-0a661c80a46f",
52
"name": "5th Avenue",
53
"formattedAddress": "1002 5th Avenue, New York, NY, USA",
54
"locationType": "OWNER_BUSINESS"
55
},
56
"endDate": "2023-04-11T15:00:00.000-04:00",
57
"timezone": "America/New_York",
58
"resource": {
59
"_id": "76570209-101f-409b-af97-b445bdb63125",
60
"name": "Bathhouse Staff",
61
"scheduleId": "7046edd2-0ee0-43d1-a60f-2ed9cfd36b64"
62
},
63
"scheduleId": "c4f2bde3-c327-4424-a99f-7fe0294d419c",
64
"sessionId": "4jOkD28c0FrsNUSgzQzuLRkA2t1rv1FLTwquKmJyctoZm00vdeKFMyH4n9cCLtmQe8wwAEJlPoJAU9LSPJu26G42joedYythPDtgGeAtC7N5ThJYEmZmm8qrccxj7YxJ36UA6sb9deXOfkwnK2r9A5KWYeM77OPTjaIupKlbIU186GE5wQEBmM2uHCyMUqOvOtoDPyGMJXTlr3DAVEYYiIL0N3VXBmHbBVD25LmgexOYDN2MqqZ3eLMX5wVArHPeJPAWeI5bfVKgSi0ozO16W66hj6HudqeIem8fuHQdEqDXgfkVsUE8QPagmlsf8abmjGqFtSPEI7jsoOb",
65
"startDate": "2023-04-11T12:00:00.000-04:00",
66
"serviceId": "851bca8b-dd5b-4575-8e66-f0bcaca2bfdf"
67
},
68
"title": "Bathhouse session",
69
"tags": [
70
"GROUP"
71
]
72
},
73
"startDate": "2023-04-11T16:00:00.000Z",
74
"participantNotification": {
75
"notifyParticipants": true
76
},
77
"_updatedDate": "2023-04-09T10:48:19.751Z",
78
"totalParticipants": 2,
79
"_createdDate": "2023-04-09T10:48:19.751Z"
80
}
81
}
82
}
  • context: An object that holds the values or variables that are available and relevant within the scope of the function during its execution.

The calculatePrice() function must return an object with a calculatedPrice integer. This objects defines the booking's price that site visitors see on the Checkout page. For more details, see the SPI Reference.

Example return value:

Copy
1
{
2
"calculatedPrice": 16.0
3
}

-config.js

The code in this file defines a function named getConfig() that returns the name of the main SPI function that is defined in the <my-extension-name>.js file as an object. This is important because you can have multiple .js files and multiple functions in these .js files.

In our case, the main SPI function name is calculatePrice().

Example getConfig() function:

Copy
1
export function getConfig() {
2
return {pricingProviderName: "calculatePrice"}
3
}

Example return object: 

Copy
1
{
2
"pricingProviderName": "calculatePrice"
3
}

Add files to an extension

If you don't want to keep all of your code in the main extension files, you can add files to the extension's folder and import functions and objects into the main files.

  1. Hover over the extension folder's name and click Show More .

  2. Select New .js file.

  3. To import from these files to the main extension files, use the following syntax:

Copy
1
import { functionName } from './myFileName.js';

Test an extension

You can test your extension before publishing your site using functional testing like you would with any backend Velo code. Make sure your calculatePrice() function's return values are properly formatted. To test your extension after deploying, add console logs to your code. The results appear in the Site Events log.

Step 3: Deploy the extension

Once your code files are ready, you need to publish the site.

  1. Publish your site.

Note There may be a delay between publishing the site and the new bookings pricing options appearing on the live site.

Update an extension

You can make updates to your extension as necessary. Make sure to publish your site for your updates to take effect.

Remove an extension

You can remove an extension from your site from the Velo Sidebar.

  1. Hover over the extension's folder and click Show More .

  2. Select **Remove.

  3. Click Remove.

Example

We created an example to demonstrate how you can use custom extensions to extend the pricing for bookings on your site.

Offer custom pricing for add-ons and weekend rates

In this example, we calculate and display custom pricing that offers lower rates for weekdays and varied pricing for different addon options. To test this code, paste it into your .js file.

Custom extensions allow you to implement pricing with your own customized business logic and display the new pricing on your site during checkout. In this example, we calculate and display custom pricing that offers lower rates for weekdays and varied pricing for different addon options.

We use the addtionalFields property of the booking to get the weekend rate and the price of each addon.

To test this code, paste it into your <my-extension-name>.js file.

Copy
1
import { getPrices } from 'backend/queries';
2
3
export const calculatePrice = async (options, context) => {
4
const prices = getPrices();
5
const additionalFields = options.booking.additionalFields;
6
const numberOfParticipants = options.booking.numberOfParticipants;
7
const weekendRate = getFieldValue(additionalFields, "weekendRate");
8
const addon1Value = getFieldValue(additionalFields, "Addon 1");
9
const addon2Value = getFieldValue(additionalFields, "Addon 2");
10
const addon3Value = getFieldValue(additionalFields, "Addon 3");
11
const rate = weekendRate === "true" ? prices.weekend : prices.weekday;
12
const finalPrice === numberOfParticipants * rate +
13
addon1Value * prices.addon1 +
14
addon2Value * prices.addon2 +
15
addon3Value * prices.addon3;
16
return {calculatedPrice: finalPrice};
17
};
18
19
export function getFieldValue(additionalFields, text) {
20
const foundFieldArray = additionalFields.filter(additionalField => additionalField.label === text);
21
return foundFieldArray.length === 0 ? undefined : foundFieldArray[0].value;
22
}

In this example, the booking's price is modified based on add-ons the customer can add to their booking, and a more expensive rate if the booking is for the weekend.

We built a sample site where you can see this code in action.

Note Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account.

Was this helpful?
Yes
No