Documentation
Feedback
Guides
Storefront Development

Storefront Development
Store Framework
Dealing with events
Cleaning cart data on log out

The VTEX shopping cart default behavior is to keep the cart alive until an order is placed. When this happens, a new cart is created. If a business rule requires a new cart every time a user logs out, we can use the Session Watcher to clear cart information.

It is not possible to get a brand new cart, but we can repurpose the current cart by removing any existing information on it.

This document will guide you on how to use the Session Watcher app to clean up an existing cart during logout.

Clients

VTEX IO Service Context gives you access to external clients. Clients are additional services with methods that give you access to different core applications. Here is a list of out-of-the-box client services available.

  • apps
  • assets
  • billing
  • billingMetrics
  • builder
  • events
  • id
  • licenseManager
  • masterdata
  • messagesGraphQL
  • metadata
  • registry
  • router
  • segment
  • settings
  • session
  • tenant
  • vbase
  • workspaces
  • catalogGraphQL
  • paymentProvider

You can always extend your App Clients if you need to give it access to external or internal providers.

Checkout Client

The VTEX IO Service Context does not have an out-of-the-box checkout client. The following steps will guide you on this setup of a client from our internal APIs.

  1. Create a new ./node/clients/checkout.ts file where you can keep all the methods for the Checkout class. This class will extend JanusClient (VTEX’s internal router), you can import it from @vtex/api. It should look like the example below. Types are also imported from @vtex/api.

_10
import type { InstanceOptions, IOContext } from ‘@’vtex/api’
_10
import { JanusClient } from ‘@vtex/api’
_10
_10
export class Checkout extends JanusClient {
_10
constructor(context: IOContext, options?: InstanceOptions) {
_10
super(context, { …options })
_10
}
_10
}

  1. These internal APIs are going through portal.vtexcommercestable.com.br/api/*, which means we need to declare it as an outbound-access in our manifest.json file under "policies".

_10
"policies": [
_10
{
_10
"name": "outbound-access",
_10
"attrs": {
_10
"host": "portal.vtexcommercestable.com.br",
_10
"path": "/api/*"
_10
}
_10
},

The same thing is required if you're accessing an external endpoint. Always add the host and path as an outbound-access.

Do not use protocol in the host (http, https). The host does not support wildcard within the address. It can be either fixed host or only * , never both. The path supports wildcard.

  1. Now that you have an initial class setup, you can let your application know that it exists. Do this by importing it to the Clients constructor under ./node/clients/index.ts

_10
import { IOClients } from ‘@vtex/api’
_10
_10
import { Checkout } from ‘@vtex/api’
_10
_10
// Extend the default IOClients implementation with our own custom clients.
_10
export class Clients extends IOClients {
_10
public get checkout() {
_10
return this.getOrSet(‘checkout’, Checkout)
_10
}
_10
}

The checkout client will now be available to our handler, just like the others.


_10
{
_10
"name": "outbound-access",
_10
"attrs": {
_10
"host": "portal.vtexcommercestable.com.br",
_10
"path": "/checkout/changeToAnonymousUser/*"
_10
}

  1. Now you need to create your methods. The first thing you need is to change our orderForm (shopping cart) to anonymous. This will erase the clientProfileData, but will not remove the items from the cart, which will be done in another moment.

This method uses a path starting with /checkout, which means we need to include it to our policy by creating a new outbound-access entry. Back tocheckout.ts, add a new public method inside the Checkout class.


_10
public changeToAnonymous = (orderFormId: string) => {
_10
return this.http.getRaw(`/checkout/changeToAnonymousUser/$\{orderFormId\}`, {
_10
metric ‘change-to-anonymous’,
_10
})
_10
}

JanusClient gives you access to http and its methods. This example uses getRaw, which will return the entire response, including httpCode, headers, etc. If you do not need it, you can use only get instead. This metric property is used for logging, it is not not strictly necessary.

  1. Create the other methods, orderForm, and updateItems, as shown below.

_19
public orderForm = (orderFormId: string) => {
_19
return this.http.post(
_19
`/api/checkout/pub/orderForm/$\{orderFormId\}`,
_19
{ expectedOrderFormSections: [‘items’] },
_19
{
_19
metric: ‘get-orderForm’,
_19
}
_19
)
_19
}
_19
_19
public updateItems = (orderFormId: string, orderItems: any) => {
_19
return this.http.post(
_19
`/api/checkout/pub/orderForm/$\{orderFormId\}/items/update`,
_19
{ orderItems } ,
_19
{
_19
Metric: ‘update-orderForm-items’,
_19
}
_19
)
_19
}

Clear Cart handler

Back to ./resolvers/index.ts load the checkout client from the context of our method clearCart and expose both orderFormId and email from the request body.

The logical flow is:

{"base64":"  ","img":{"width":1672,"height":1428,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":218160,"url":"https://cdn.jsdelivr.net/gh/vtexdocs/dev-portal-content@main/images/vtex-io-documentation-cleaning-cart-data-on-log-out-0.png"}}

You should have code similar to the example below.


_70
/* eslint-disable no-console */
_70
import { json } from ‘co-body’
_70
_70
export const resolvers = {
_70
Routes: {
_70
clearCart: async (ctx: Context) => {
_70
Const {
_70
req,
_70
clients: { checkout },
_70
} = ctx
_70
_70
const body: any = await json(req)
_70
const email = body?.authentication?.storeUserEmail?.value ?? null
_70
const orderFormId = body?.checkout?.orderFormId?.value ?? null
_70
_70
console.log(‘clearCart =>’, body)
_70
_70
ctx.set(‘Content-Type, ‘application/json’)
_70
ctx.set(‘Cache-Control’, ‘no-cache, no-store’)
_70
_70
const res = {
_70
public: {
_70
demo: {
_70
value: email ? ‘User Authenticated’ : ‘User not authenticated’,
_70
},
_70
},
_70
}
_70
_70
// If user is not authenticated, and we have an orderFormId
_70
if (!email && orderFormId) {
_70
// Get the current orderForm data
_70
const orderform: any await checkout.orderForm(orderFormId)
_70
_70
// Only if we have a user assigned to this orderForm
_70
if (orderForm?.clientProfileData?.email) {
_70
// Change to anonymous
_70
await checkout.changeToAnonymous(orderFormId).catch((err) => {
_70
if (err.response.status >= 400) {
_70
console.log(‘Cart response error =>’, err.response.statusText)
_70
} else {
_70
console.log(‘Cart response all good =>’, err.response.status)
_70
}
_70
})
_70
_70
/*
_70
* To remove items from the cart
_70
* If we have items in the cart
_70
*/
_70
if (orderForm?.items?.length) {
_70
// Create a orderItems array with the item position and new quantity set to 0
_70
const orderItems = orderForm.items.map((_: any, index: number) => {
_70
return {
_70
index,
_70
quantity: 0,
_70
}
_70
})
_70
_70
// Update the cart items with the new quantity
_70
await checkout.updateItems(orderFormId, orderItems).catch((err) => {
_70
console.log(‘Error removing all items =>’, err)
_70
})
_70
}
_70
}
_70
}
_70
_70
ctx.response.body = res
_70
ctx.response.status = 200
_70
},
_70
},
_70
}

Don't forget to change the vendor in your ./manifest.json file.

To link the app, run this command:


_10
vtex link

If you wish, you can download this complete example

Contributors
2
Photo of the contributor
Photo of the contributor
+ 2 contributors
Was this helpful?
Yes
No
Suggest edits (Github)
Contributors
2
Photo of the contributor
Photo of the contributor
+ 2 contributors
On this page