Documentation You are here: start » v » 2.0 » customer_portal

Building a Customer Portal (New Functionality)

OUR BETA IS OPEN! If you're landing on this page, please contact us to discuss your needs, as you may want to just use our “ready to go” portal front-end instead of building your own.

Working with the New ''/s/customer'' endpoint

The new /s/customer endpoint is a bit different than the other public-facing endpoints in Foxy. Some notes:

  • CORS: You must specify the domains from which your customer portal will function. This prevents certain types of attacks or unauthorized access against your users. Note that this does prevent use of certain super old browsers, but that's probably for the best.
  • REST: In general, GET requests are for retrieving (and not modifying) data. POST is to create something new. PATCH is for modifying existing things.
    • Do not include a body in GET requests.
  • Request Bodies: For POST and PATCH, you should submit a JSON object in the body of the request.
  • Successful Requests: A 200 status code indicates a successful request/response, and will have a JSON payload.
  • Errors: Error responses will use the appropriate HTTP response code (ie. non-2XX or 3XX), and will generally be JSON that looks something like this:
    {
        "type": "authentication",
        "code": "422",
        "message": "`password` not defined or invalid."
    }

Authenticating

POST /s/customer/authenticate

  • Required fields:
    • email
    • password
  • Response:
    {
        "cookieMaxAge": 172800,
        "cookieName": "fx.customer",
        "cookieValue": "s:_OChIdiRabcdefghijkfvwGMnpPAMDGl.ivurNCzVRxIJpxmf89gkDnb8nwUqVamvpAF2D99LE4M",
        "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdG9yZV9pZCI6MTIzNDUsImN1c3RvbWVyX2lkIjo2Nzg5MCwiY3VzdG9tZXJfZW1haWwiOiJmYW5jeXBhbnRzeUBnbWFpbC50bGQiLCJpYXQiOjE1Nzg0NzA3MDYsImV4cCI6MTU3ODQ3NjEwNn0.r7gimkYED0GlcT0CkrxPIYmMoux5pE9VFdxJ9D4Le-XClPB1UM0-0-A1gWeQqkpUIfADsm_jPAdAmIWaThvCi_ItUSHTtQD9RWZQMzyHvLbQf5KwyjDMlim8G7CtCl0LObpbBWuXDomqytlyxlhA1ynafTYJ5DuB4chBAX5U4suvTIAIrwi2lGQsXMSPmjzUXhceuX9UacWPpP6j_dGeaECEW8vlS4ImefGRcOeG8sjJiRkYsQOeW-u7zCUsKAAYoE7iuJ0FGlYvWxDnpUkU7zMAGAVfkK_SpNOl4p1nG9SEMLZ6iSM_-SquCKvUB0Rpf6iC4ioG_KZQjEEvD0g0Q_bqpeCdY9rkgfCud2O-oihLs_l0YjGqCcqsOFgBJLxZUuWjxIXQ5NUqBrCflmCg102N1PfMPo_qWHBJ_-RC0bbX6dOql3MLWTn4XxWArCNdHvBGwEfjv0i7Nps-gootMftYdo6TqNreqGY0cHpR-FdNYQ8W0dfR7hfFiorhzyDgGnduWZ7KlwTruf3ogo3O0XaHc_d5SdFqnirus3lSrpz8U9LgbRe3a52D_OJImkj1fH-sz3UgKZBx5rV8xDgW4dPJKskNIWFBPh17LR37U0NiIlxVTQV87gUTEKp8maOukEg2hAJklqpVzK2eZaEnLivBF20CMemwG4358UpFYMk"
    }
    • The cookieMaxAge is an integer value, and matches the session lifetime as configured in Foxy (in seconds).
    • The jwt uses HS256 (shared secret) signing. This is optional functionality. More details at JWT.io. Note that we do have keypair-based JWT capabilities, but because the returned token is so huge, and the use case is often cookies, we feel that's less helpful. If you'd like to use a public/private keypair instead of shared secret, however, please let us know.
    • This cookie must be set manually, for a variety of reasons (including the continuing obsolescence of 3rd party cookies). It is required for subsequent requests (below). If it cannot be sent in the Cookie header, it can be sent in a fx.customer header instead (since the Cookie header cannot be set via javascript for js-based requests).

Retrieving a Customer

GET /s/customer

  • Optional URI parameters:
    • zoom: Zoomable resources are:
      • transactions
      • subscriptions: Returns active subscriptions.
      • subscriptions:transactions: Returns subscriptions' transactions, embedded in the subscriptions.
      • default_billing_address
      • default_shipping_address
      • default_payment_method
    • sso: An SSO URI will be returned as _links[“fx:checkout”]. NOTE: This is optional. See notes about SSO below.
  • Response:
    {
        "_embedded": {
            "fx:attributes": [
                {
                    "name": "Loyalty Points Balance",
                    "value": "100",
                    "visibility": "public",
                    "date_created": "2019-07-09T00:41:44-07:00",
                    "date_modified": "2019-07-09T00:41:44-07:00"
                }
            ],
            "fx:default_billing_address": {
                "address_name": "Default Billing Address",
                "first_name": "foo",
                "last_name": "bar",
                "company": "aloha",
                "address1": "4612 avenue b",
                "address2": "unit a",
                "city": "Mullberry",
                "region": "TX",
                "postal_code": "12345",
                "country": "US",
                "phone": "87654321",
                "is_default_billing": true,
                "is_default_shipping": false,
                "date_created": "2018-10-18T17:29:20-07:00",
                "date_modified": "2020-01-27T00:26:58-08:00"
            },
            "fx:default_shipping_address": {
                "address_name": "Me",
                "first_name": "foo",
                "last_name": "baz3",
                "company": "aloha 10",
                "address1": "4612 avenue b",
                "address2": "unit a",
                "city": "Mullberry",
                "region": "TX",
                "postal_code": "12345",
                "country": "US",
                "phone": "87654321",
                "is_default_billing": false,
                "is_default_shipping": true,
                "date_created": "2018-10-18T17:29:20-07:00",
                "date_modified": "2020-01-27T00:26:58-08:00"
            },
            "fx:default_payment_method": {
                "save_cc": false,
                "cc_type": "Visa",
                "cc_number_masked": "",
                "cc_exp_month": "",
                "cc_exp_year": "",
                "date_created": "2018-10-18T17:29:20-07:00",
                "date_modified": "2020-01-27T00:26:58-08:00"
            },
            "fx:transactions": [
                {
                    "_links": {
                        "fx:receipt": {
                            "href": "https://demo.foxy.tld/receipt?id=abc123",
                            "title": "This Receipt",
                            "type": "text/html"
                        }
                    },
                    "_embedded": {
                        "fx:attributes": [
                            {
                                "_links": {},
                                "name": "cart attribute 1",
                                "value": "value 1",
                                "visibility": "public",
                                "date_created": "2020-01-07T16:54:34-08:00",
                                "date_modified": "2020-01-07T16:54:34-08:00"
                            },
                            {
                                "_links": {},
                                "name": "cart attribute 2",
                                "value": "value 2",
                                "visibility": "public",
                                "date_created": "2020-01-07T16:54:34-08:00",
                                "date_modified": "2020-01-07T16:54:34-08:00"
                            }
                        ]
                    },
                    "id": 3881585,
                    "display_id": 3881585,
                    "is_test": true,
                    "hide_transaction": false,
                    "data_is_fed": false,
                    "transaction_date": "2020-01-08T10:55:29+10:00",
                    "locale_code": "en_US",
                    "customer_first_name": "Grace",
                    "customer_last_name": "Hopper",
                    "customer_tax_id": "1234567890",
                    "customer_email": "foobar@foxy.tld",
                    "customer_ip": "",
                    "ip_country": null,
                    "user_agent": "",
                    "total_item_price": 209.99,
                    "total_tax": 16.5,
                    "total_shipping": 0,
                    "total_future_shipping": 0,
                    "total_order": 216.5,
                    "status": "captured",
                    "date_created": "2020-01-07T16:45:14-08:00",
                    "date_modified": "2020-01-07T16:58:16-08:00"
                }
            ],
            "fx:subscriptions": [
                {
                    "_links": {
                        "self": {
                            "href": "https://demo.foxy.tld/s/customer/subscriptions/2258",
                            "title": "This Subscription"
                        },
                        "fx:sub_token_url": {
                            "href": "https://demo.foxy.tld/cart?sub_token=xyz789",
                            "title": "This Sub Token",
                            "type": "text/html"
                        }
                    },
                    "start_date": "2019-03-18T00:00:00-07:00",
                    "next_transaction_date": "2019-04-18T00:00:00-07:00",
                    "end_date": null,
                    "frequency": "1m",
                    "error_message": "",
                    "past_due_amount": 0,
                    "first_failed_transaction_date": null,
                    "is_active": true,
                    "date_created": "2019-03-18T00:02:46-07:00",
                    "date_modified": "2019-03-18T00:02:46-07:00",
                    "_embedded": {
                        "template_config": {
                            "allow_next_date_modification": true
                        }
                    }
                }
            ]
        },
        "_links": {
            "fx:checkout": {
                "href": "https://demo.foxy.tld/checkout?fc_customer_id=12345&timestamp=1580209218&fc_auth_token=poiuytrewq",
                "title": "Checkout"
            }
        },
        "date_created": "2018-10-18T17:29:20-07:00",
        "date_modified": "2020-01-27T00:26:58-08:00",
        "email": "foobar@foxy.tld",
        "first_name": "foo",
        "id": 52008,
        "is_anonymous": false,
        "last_login_date": "2020-01-09T23:15:32-08:00",
        "last_name": "bar",
        "tax_id": "1234567890"
    }

Modifying a Customer

PATCH /s/customer

  • Accepted values in the JSON body:
    • first_name
    • last_name
    • email
    • tax_id
    • password: A new password. Allows for password resets. Must be 8 characters. Note that if you want to enforce password complexity requirements, you must do that in your UI.
    • password_old: Required if password is passed in. Must match the old password, or the temporary pasword generated from a password reset request (sent via email).
    • _embedded: Can contain the following objects. Note that you must still zoom any embedded resources to get them back in the response.
  • Response: The response will match a GET response to /s/customer.

Retrieving Transactions

GET /s/customer/transactions

  • Optional parameters:
    • limit: See the API docs for details. Defaults to 10.
    • offset: See the API docs for details. Defaults to 0.
    • order: See the API docs for details. Defaults to transaction_date desc.
  • Response:
    {
        "_links": {
            "self": {
                "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=0&order=transaction_date+desc",
                "title": "This Collection"
            },
            "first": {
                "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=0&order=transaction_date+desc",
                "title": "First Page of this Collection"
            },
            "prev": {
                "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=0&order=transaction_date+desc",
                "title": "Previous Page of this Collection"
            },
            "next": {
                "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=2&order=transaction_date+desc",
                "title": "Next Page of this Collection"
            },
            "last": {
                "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=52&order=transaction_date+desc",
                "title": "Last Page of this Collection"
            }
        },
        "_embedded": {
            "fx:transactions": [
                {
                    "_links": {
                        "self": {
                            "href": "https://demo.foxy.tld/s/customer/transactions/3881585",
                            "title": "This Transaction"
                        },
                        "fx:receipt": {
                            "href": "https://demo.foxy.tld/receipt?id=abc123",
                            "title": "This Receipt",
                            "type": "text/html"
                        }
                    },
                    "_embedded": {
                        "fx:attributes": [
                            {
                                "_links": {
                                    "self": {
                                        "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1599",
                                        "title": "This transaction attribute"
                                    }
                                },
                                "name": "cart attribute 1",
                                "value": "value 1",
                                "visibility": "public",
                                "date_created": "2020-01-07T16:54:34-08:00",
                                "date_modified": "2020-01-07T16:54:34-08:00"
                            },
                            {
                                "_links": {
                                    "self": {
                                        "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1600",
                                        "title": "This transaction attribute"
                                    }
                                },
                                "name": "cart attribute 2",
                                "value": "value 2",
                                "visibility": "public",
                                "date_created": "2020-01-07T16:54:34-08:00",
                                "date_modified": "2020-01-07T16:54:34-08:00"
                            }
                        ]
                    },
                    "id": 3881585,
                    "display_id": 3881585,
                    "is_test": true,
                    "hide_transaction": false,
                    "data_is_fed": false,
                    "transaction_date": "2020-01-08T10:55:29+10:00",
                    "locale_code": "en_US",
                    "customer_first_name": "Grace",
                    "customer_last_name": "Hopper",
                    "customer_tax_id": "1234567890",
                    "customer_email": "foobar@foxy.tld",
                    "customer_ip": "",
                    "ip_country": null,
                    "user_agent": "",
                    "total_item_price": 209.99,
                    "total_tax": 16.5,
                    "total_shipping": 0,
                    "total_future_shipping": 0,
                    "total_order": 216.5,
                    "status": "captured",
                    "date_created": "2020-01-07T16:45:14-08:00",
                    "date_modified": "2020-01-07T16:58:16-08:00"
                },
                {
                    "_links": {
                        "self": {
                            "href": "https://demo.foxy.tld/s/customer/transactions/3876502",
                            "title": "This Transaction"
                        },
                        "fx:receipt": {
                            "href": "https://demo.foxy.tld/receipt?id=abc123",
                            "title": "This Receipt",
                            "type": "text/html"
                        }
                    },
                    "_embedded": {
                        "fx:attributes": [
                            {
                                "_links": {
                                    "self": {
                                        "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1592",
                                        "title": "This transaction attribute"
                                    }
                                },
                                "name": "cart attribute 1",
                                "value": "value 1",
                                "visibility": "public",
                                "date_created": "2019-12-07T00:08:08-08:00",
                                "date_modified": "2019-12-07T00:08:08-08:00"
                            },
                            {
                                "_links": {
                                    "self": {
                                        "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1593",
                                        "title": "This transaction attribute"
                                    }
                                },
                                "name": "cart attribute 2",
                                "value": "value 2",
                                "visibility": "public",
                                "date_created": "2019-12-07T00:08:08-08:00",
                                "date_modified": "2019-12-07T00:08:08-08:00"
                            }
                        ]
                    },
                    "id": 3876502,
                    "display_id": 3876502,
                    "is_test": true,
                    "hide_transaction": false,
                    "data_is_fed": false,
                    "transaction_date": "2019-12-07T18:08:15+10:00",
                    "locale_code": "en_US",
                    "customer_first_name": "Grace",
                    "customer_last_name": "Hopper",
                    "customer_tax_id": "1234567890",
                    "customer_email": "foobar@foxy.tld",
                    "customer_ip": "",
                    "ip_country": null,
                    "user_agent": "",
                    "total_item_price": 419.98,
                    "total_tax": 33.82,
                    "total_shipping": 0,
                    "total_future_shipping": 0,
                    "total_order": 443.81,
                    "status": "authorized",
                    "date_created": "2019-12-05T21:58:02-08:00",
                    "date_modified": "2019-12-07T00:10:35-08:00"
                }
            ]
        },
        "total_items": 53,
        "returned_items": 2,
        "limit": 2,
        "offset": 0
    }

Retrieving & Modifying Subscriptions

GET /s/customer/subscriptions

  • Optional parameters:
    • zoom: Zoomable resources are:
      • transactions
    • limit: See the API docs for details. Defaults to 10.
    • offset: See the API docs for details. Defaults to 0.
    • order: See the API docs for details. Defaults to next_transaction_date asc.
    • is_active: See the API docs for details. Defaults to true.
  • Notes:
    • The _embedded.template_config.allow_next_date_modification value is a boolean that indicates whether or not a subscription's next date can be modified by the customer. Note that this is included per transaction, though the setting at this point is store-wide. In the future, there may be additional template_config options to help inform the UI.
  • Response:
    {
        "_links": {
            "self": {
                "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=0&order=next_transaction_date+asc",
                "title": "This Collection"
            },
            "first": {
                "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=0&order=next_transaction_date+asc",
                "title": "First Page of this Collection"
            },
            "prev": {
                "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=0&order=next_transaction_date+asc",
                "title": "Previous Page of this Collection"
            },
            "next": {
                "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=1&order=next_transaction_date+asc",
                "title": "Next Page of this Collection"
            },
            "last": {
                "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=21&order=next_transaction_date+asc",
                "title": "Last Page of this Collection"
            }
        },
        "_embedded": {
            "fx:subscriptions": [
                {
                    "_links": {
                        "self": {
                            "href": "https://demo.foxycart.tld/s/customer/subscriptions/2258",
                            "title": "This Subscription"
                        },
                        "fx:sub_token_url": {
                            "href": "https://demo.foxycart.tld/cart?sub_token=abc123",
                            "title": "This Sub Token",
                            "type": "text/html"
                        }
                    },
                    "start_date": "2019-03-18T00:00:00-07:00",
                    "next_transaction_date": "2019-04-18T00:00:00-07:00",
                    "end_date": null,
                    "frequency": "1m",
                    "error_message": "",
                    "past_due_amount": 0,
                    "first_failed_transaction_date": null,
                    "is_active": true,
                    "date_created": "2019-03-18T00:02:46-07:00",
                    "date_modified": "2019-03-18T00:02:46-07:00",
                    "_embedded": {
                        "template_config": {
                            "allow_next_date_modification": true,
                            "allow_frequency_modification": [
                                ["1m","2m","3m","6m","1y"]
                            ]
                        }
                    }
                }
            ]
        },
        "total_items": 21,
        "returned_items": 1,
        "limit": 1,
        "offset": 0
    }

PATCH /s/customer/subscriptions/SUBSCRIPTION_ID

  • Pre-requisites & Notes:
    • The “Allow Next Date Modification” setting must be true in the customer portal store config.
    • The “Allow Frequency Modification” settings must exist in the form of a valid JSONata query with an array of frequency values that are allowed if the JSONata query returns “truthy”. For instance: $contains(frequency, “w”) allows [“1w”,“2w”,“3w”,“4w”,“5w”,“6w”,“7w”,“8w”,“9w”,“10w”,“11w”,“12w”], and $contains(frequency, “m”) allows [“1m”,“2m”,“3m”,“6m”,“1y”]. (The transaction_template with items and item_options is zoomed on the transaction, and can be used in your JSONata queries. Only values returned in the allow_frequency_modification node are allowed to be patched, or the request will error.
    • The URI is returned in the _links.self value (seen above).
  • Response: The response will be an object of the subscription.

Single Sign-On with the Customer Portal Session

Note: This functionality may become native in the future. For the time being, SSO between the portal and the checkout requires just a bit of extra setup.

If you're using SSO elsewhere in your site, be aware that enabling it in the portal bypasses your own endpoint. If you're only doing authentication, this is generally not a problem, but if you're doing other things with your SSO endpoint (like checking inventory, verifying cart contents, etc.), this will bypass that.

Standalone SSO with the Portal

This section is for if you're not using SSO elsewhere.

To ensure your customers don't need to login to the checkout after they've already logged into the portal, you can set up Foxy's SSO functionality following the steps below. If you're already using SSO, you'll want to approach this in a different way, depending on your other systems.

When you do a GET /s/customer?sso=true (which happens by default if you're using the normal portal functionality), it includes the ._links[“fx:checkout”].ref value, which is a URI that will load the checkout with your customer already logged in, and sets a cookie named fx.customer.sso. From there, you'll need to do the following:

  1. Create a SSO URL for a guest (ie. customer_id=0). (You can use an online tool like CyberChef to generate a SHA1 hash of something like 0|1893456000|YOUR_SECRET_HERE.)
  2. Create a new page on your website (at the same domain as the page that contains the <foxy-customer-portal /> element) with the following code. Replace your domain, and the fc_auth_token=REPLACE_THIS bit with the SSO URL token from the above step.) (NOTE: The below code includes the doctype, html, head, and body tags. If you're inserting this into an existing template, you just want to grab the <script /> block, and make sure the page is set to not be indexed by search engines.)
    <!doctype html>
    <html class="no-js" lang="">
     
    <head>
    	<meta charset="utf-8">
    	<title>SSO Redirect</title>
    	<meta name="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
     
    	<script>
     
    		function getCookie(name) {
    			var value = "; " + document.cookie;
    			var parts = value.split("; " + name + "=");
    			if (parts.length == 2) return parts.pop().split(";").shift();
    		}
     
    		function getUrlParameter(name) {
    			name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    			var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
    			var results = regex.exec(location.search);
    			return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
    		}
     
    		if (getCookie("fx.customer.sso")) {
    			window.location.replace(getCookie("fx.customer.sso") + "&fcsid=" + getUrlParameter("fcsid"));
    		} else {
    			window.location.replace("https://YOUR_FOXY_DOMAIN/checkout?fc_customer_id=0&timestamp=1893456000&fc_auth_token=REPLACE_THIS&fcsid="+getUrlParameter("fcsid"));
    		}
     
    	</script>
    </head>
    <body></body>
    </html>
  3. Go to the “advanced settings” page in your Foxy admin, check the “enable signle sign on”, then enter the URL of the page you created in the step above. (NOTE: This page must be publicly accessible.) Save the settings.
  4. Test this by logging into the portal, then proceeding to checkout. You should be logged into the checkout.

Incorporating the Customer Portal SSO with an Existing SSO Endpoint

If you've already got SSO enabled and configured on your Foxy store, you'll need to handle things separately. We can't give step-by-step instructions, as there are countless ways to handle SSO, but here are the general ideas:

  • Create the customer portal session + cookie when a customer logs in. So when a customer enters their username and password, you can make a quick POST /s/customer/authenticate request with their entered username + password. Set the fx.customer cookie with the value you receive. Note that this approach requires the Foxy password to be synced with your own auth system.
  • If you don't have access to the customer's password… You can use your store's configured UOE password password. (This should only ever be serverside, as you should never share that password in the browser.)
  • Unlike the checkout, you likely would not want to allow login to the portal if the customer's not already logged into your system. Instead, redirect the customer to your login page so they'll be fully logged in (instead of logged into the Foxy customer portal, but not to your system or the checkout).

BETA Setup Instructions

Insert the following code in your page (head or footer, depending on your requirements; you may remove the comments, of course):

<!-- The following 2 lines are required. -->
<script type="module" src="https://static.www.foxycart.com/beta/s/customer-portal/v0.9/dist/lumo/foxy/foxy.esm.js"></script>
<script nomodule src="https://static.www.foxycart.com/beta/s/customer-portal/v0.9/dist/lumo/foxy.js"></script>
<!-- The following style block is optional. You can customize your styles at https://demo.vaadin.com/lumo-editor/.
    Simply change all the `--lumo-` prefixes to `--foxy-`, and insert them below. -->
<style>
html {
    --foxy-border-radius: 0.5em;
    --foxy-shade-5pct: rgba(41, 41, 41, 0.05);
    --foxy-shade-10pct: rgba(41, 41, 41, 0.1);
    --foxy-shade-20pct: rgba(41, 41, 41, 0.2);
    --foxy-shade-30pct: rgba(41, 41, 41, 0.3);
    --foxy-shade-40pct: rgba(41, 41, 41, 0.4);
    --foxy-shade-50pct: rgba(41, 41, 41, 0.5);
    --foxy-shade-60pct: rgba(41, 41, 41, 0.6);
    --foxy-shade-70pct: rgba(41, 41, 41, 0.7);
    --foxy-shade-80pct: rgba(41, 41, 41, 0.8);
    --foxy-shade-90pct: rgba(41, 41, 41, 0.9);
    --foxy-shade: hsl(214, 0%, 16%);
    --foxy-primary-text-color: rgb(83, 39, 94);
    --foxy-primary-color-50pct: rgba(83, 39, 94, 0.5);
    --foxy-primary-color-10pct: rgba(83, 39, 94, 0.1);
    --foxy-primary-color: #53275E;
    --foxy-body-text-color: hsl(214, 0%, 16%);
    --foxy-secondary-text-color: hsl(214, 0%, 42%);
    --foxy-tertiary-text-color: rgba(87, 87, 87, 0.5);
    --foxy-disabled-text-color: rgba(173, 173, 173, 0.3);
    --foxy-header-text-color: hsl(214, 0%, 16%);
 
    background: var(--foxy-shade-5pct);
}
</style>
 
<!-- Change the `endpoint` value to match your Foxy store domain. -->
<foxy-customer-portal endpoint="https://your-api-endpoint.tld">
  <!-- If you'd like to add a note above the login section, uncomment the below -->
  <foxy-sign-in slot="sign-in">
    <h1>Hello! Please login…</h1>
    <p>If you have any trouble logging in, please try resetting your password below. Contact us if you can't get it working.</p>
  </foxy-sign-in>
  <!-- If you're not using subscriptions, uncomment the following line to override that default block. -->
  <!-- <div slot="subscriptions-container"></div> -->
</foxy-customer-portal>

Some Ideas and Best Practices

Loyalty Points

An often-requested feature (that we will likely add as a native feature, but it's not available yet) is the idea of “loyalty points” that can be redeemed or exchanged for discounts. Using the API and the customer portal functionality described on this page, you can whip something up. The idea is, roughly:

  1. Create custom code on your end that processes the Foxy webhook. According to your own logic, calculate the number of points that a transaction should generate. For instance, the product total less coupons. (So points would only be generated for the amount paid, excluding shipping and tax.)
  2. Check the attributes for the transaction and look for an attribute named Loyalty_Points_Applied_On. If it exists, you've already applied the points for this transaction, so exit.
  3. Retrieve the attributes for the customer record via the API. If a Loyalty_Points attribute exists, update the value accordingly. On success, add the Loyalty_Points_Applied_On attribute to the transaction, so you don't re-apply points if the webhook fires again. (If the Loyalty_Points attribute doesn't exist, create it and make sure the visibility is public, so it will be returned in the customer portal front-end.)

That gets you keeping track of points, and the public visibility ensures that your customers can see their points value when they login.

To allow customers to redeem their points, you'd need to add some logic to your portal and your backend custom code. It'd look like this, roughly:

  1. Customer clicks the button to redeem their points. Fires an AJAX request to your endpoint. (Use the JWT returned in the authenticate action to validate the request.)
  2. Your backend retrieves the customer's attributes via the Foxy API.
  3. If everything looks good, your backend creates a coupon code for a pre-existing coupon, and returns the code to the client. (You may also want to add this code as an attribute to the customer, for later retrieval.)
  4. Your portal javascript receives the coupon code, and applies it to the customer's session.

This is obviously a bit of work, and requires some more advanced programming knowledge. But hopefully it helps with some ideas of how you can use the Foxy backend API, paired with the front-end javascript portal API, to create more advanced functionality.

Site Tools