# Send Abandoned Cart Reminders with SendGrid

Use the [Send Abandoned Cart Reminders with SendGrid Firebase extension](https://firebase.google.com/products/extensions/twilio-abandoned-cart-emails/) to automate sending an email reminder to users about items they left in their shopping cart.

This extension will watch the documents added to a specified Cloud Firestore collection. For each document the extension records the last updated time of the document. Then, when the last updated time goes beyond a configurable threshold, the information in the document is copied to a new collection which triggers an email using the SendGrid API. The information in the document is sent as the template data for a dynamic transactional email.

## How it works

This extension uses the following flow to send Abandoned Cart Reminders:

1. You create a new document in the `cartFirebase` collection. The document should either have the same ID as your customer in Firebase Auth or it should have a property `userId` which is the same as your customer in Firebase Auth. This document represents the Customer's cart, and typically contains an array property with `items`, for example.
2. When you create the cart document or update properties on the cart, the extension will update a property called `lastUpdated`, giving the extension a sense of whether the cart is abandoned.
3. A function will regularly check the collection of cart documents to determine if the cart has been abandoned
4. If the `lastUpdated` metadata property indicates that the cart was abandoned (as determined by an interval you can define during setup), it will add a document to an `email` collection. This tells the extension that it needs to email the customer, prompting them to return to their cart

   1. The metadata will be updated to indicate that an email has been sent and/or an error has occurred while trying to send an email
5. Otherwise, the extension determines that the cart isn't abandoned and will ignore it until the document is removed (indicating that the customer has checked out)

## How to set up Abandoned Cart Reminders

### Requirements

Before installing this extension, make sure:

* You have [set up a Cloud Firestore database](https://firebase.google.com/docs/firestore/quickstart) in your Firebase project
* You have [set up Firebase Authentication](https://firebase.google.com/docs/auth/where-to-start) in your Firebase project
* You have [signed up for a Twilio SendGrid Marketing Campaigns account](https://signup.sendgrid.com/)
* You have created a SendGrid API Key with access to send emails
* You have verified a single sender email address or set up domain authentication with SendGrid
* You have a dynamic transactional template setup with which to send emails

### Set up a composite Firestore index

This extension requires a composite Firestore index. You can add the index in the Firebase console or by the command line.

#### Method 1: Indexes in the Firebase Console

1. Go to the **Cloud Firestore** section of the [Firebase console](https://console.firebase.google.com/project/_/firestore/data)
2. Go to the **Indexes** tab and click **Add Index**
3. Enter the collection name for your cart collection
4. Add the following fields to the index:

   * `metadata.emailSent` - Ascending
   * `metadata.error` - Ascending
   * `metadata.lastUpdated` - Ascending
5. Set the **Query scopes** to **Collection**
6. Click **Create**

#### Method 2: Indexes with the Firebase CLI

1. In your Firebase project, open your index configuration file, with default filename `firestore.indexes.json`
2. Add the following object to the `indexes` array:

   ```json
   {
     "collectionGroup": "cart",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "metadata.emailSent", "order": "ASCENDING" },
       { "fieldPath": "metadata.error", "order": "ASCENDING" },
       { "fieldPath": "metadata.lastUpdated", "order": "ASCENDING" }
     ]
   }
   ```

   The `collectionGroup` name should be the collection name for your cart collection.
3. Deploy your index configuration with the `firebase deploy` command. If you only want to deploy indexes, add the `--only firestore:indexes` flag.

### Check your Installation

Due to the nature of the extension, it's difficult to see the full effects of this extension immediately. Here's what you can do:

1. Go to your [Cloud Firestore dashboard](https://console.firebase.google.com/project/$%7Bparam:PROJECT_ID%7D/firestore/data) in the Firebase console.
2. If it doesn't already exist, create the cart collection you specified during installation: `${param:CART_COLLECTION}`
3. Add a document with an `items` field containing some products:
   ```json
   {
       "items": [{ "name": "Test product 1", "price": "$9.99" }]
   }
   ```
4. In a few seconds, you will see a `metadata` field appear in the document. The field will contain a `lastUpdated` property that will be set to the current time
5. If you update any properties outside the `metadata` property, you will see the `metadata.lastUpdated` property update shortly after
6. Once `${param:ABANDONED_TIMEOUT}` minutes have passed, the cart will be updated again and a new document will be placed in the `${param:EMAIL_COLLECTION}` collection
7. In a few seconds, you will see a `delivery` field appear on the new document in the `${param:EMAIL_COLLECTION}` collection. The field will update as the extension processes the message and sends it via the SendGrid API

## Use the Abandoned Cart Reminders Extension

Using the extension begins with tracking your customers' carts. A shopping cart should be represented as a single document in the collection per cart. How you store items in the document is up to you. Typically, a cart will include an array property called `items`, which contains information about each of the items in the cart.

The document should also have a reference to a [Firebase Authentication](https://firebase.google.com/docs/auth) User. Either the cart document ID should match the user ID or there should be a `userId` property on the document. When you create the cart document or update properties on the cart document, the extension will update a `metadata.lastUpdated` timestamp.

### Checking the cart

You can configure the period with which the extension checks the cart by updating the `CART_CHECK_INTERVAL`, which should be declared in `extension.yaml` using [cron.yaml syntax](https://cloud.google.com/appengine/docs/standard/python/config/cronref).

A cart document must fulfill the following conditions before it triggers an abandoned cart reminder:

* the `metadata.lastUpdated` timestamp should be older than the configurable `ABANDONED_TIMEOUT` time in minutes
* the `metadata.emailSent` Boolean property should be `false`
* there should be no errors present in the `metadata.error` property

If all of these conditions are met, then the extension will attempt to load the user data using the `userId` property or the document's ID.

If the user doesn't have an email address, an error will be recorded.

If the user has an email address then a document will be created in the `EMAIL_COLLECTION`. The document will include the user email and a property for `dynamicTemplateData` consisting of the contents of the user's cart and a `user` property including the user's `email` and `displayName` if present. This `dynamicTemplateData` is used to fill in the fields in a SendGrid dynamic email template.

### Sending the email

When a document is added to the `EMAIL_COLLECTION` the contents are queued up to be emailed using the Twilio SendGrid API. All the information from the cart document is added as dynamic template data for the email. You can configure a `DEFAULT_TEMPLATE_ID` which is the ID of a SendGrid dynamic template.

You can create dynamic transactional templates in the SendGrid dashboard. SendGrid Templates use Handlebars to render dynamic data into the email.

You also need to configure your `DEFAULT_FROM` to be an email address that you have verified with SendGrid as either a single sender or via [domain authentication](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication). You can also set a `DEFAULT_REPLY_TO` email.

Furthermore, you can send an email at any time by adding a document to the `EMAIL_COLLECTION` with a `to` email address and a `dynamicTemplateData` property.

```javascript
admin.firestore().collection('cart_emails').add({
  to: 'example@example.com',
  dynamicTemplateData: {
    name: "Example"
  }
});
```

## Need further assistance?

[Check out the GitHub repo](https://github.com/twilio-labs/twilio-firebase-extensions)

On GitHub, you can take a look at the source code, or read through the existing issues to see if somebody else had a similar problem. If you're still stuck, feel free to create a pull request or open an issue yourself!
