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

Building a Customer Portal (New Functionality)

Changes Are Coming! If you're landing on this page, please contact us to discuss your needs. We are releasing a native customer portal. What follows is documentation in progress.

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"
    }
    • The cookieMaxAge is an integer value, and matches the session lifetime as configured in Foxy (in seconds).
    • 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.
      • default_billing_address
      • default_shipping_address
      • default_payment_method
    • sso: An SSO URI will be returned as _links[“fx:checkout”].
  • Response:
    {
      "_embedded": {
        "fx:attributes": [
          {
            "name": "Loyalty Points Balance",
            "value": "100",
            "visibility": "public",
            "date_created": "2019-07-09T00:41:44-0700",
            "date_modified": "2019-07-09T00:41:44-0700"
          }
        ],
        "fx:default_payment_method": {
          "save_cc": true,
          "cc_type": "Visa",
          "cc_number_masked": "",
          "cc_exp_month": "12",
          "cc_exp_year": "2025",
          "date_created": "2018-10-18T17:29:20-0700",
          "date_modified": "2019-10-28T12:55:46-0700"
        }
        "fx:default_billing_address": {
          "address_name": "Default Billing Address",
          "first_name": "John",
          "last_name": "bar",
          "company": "aloha",
          "address1": "12345 avenue b",
          "address2": "unit a",
          "city": "AUSTIN",
          "region": "TX",
          "postal_code": "78767",
          "country": "US",
          "phone": "87654321",
          "is_default_billing": true,
          "is_default_shipping": false,
          "date_created": "2018-10-18T17:29:20-0700",
          "date_modified": "2019-10-28T01:35:49-0700"
        },
        "fx:default_shipping_address": {
          "address_name": "Me",
          "first_name": "foo",
          "last_name": "baz",
          "company": "aloha 5",
          "address1": "12345 avenue b",
          "address2": "unit a",
          "city": "AUSTIN",
          "region": "TX",
          "postal_code": "78767",
          "country": "US",
          "phone": "87654321",
          "is_default_billing": false,
          "is_default_shipping": true,
          "date_created": "2018-10-18T17:29:20-0700",
          "date_modified": "2019-10-28T01:35:49-0700"
        },
        "fx:transactions": [
          {
            "_links": {
              "fx:receipt": {
                "href": "https://demo.foxycart.tld/receipt?id=83654f5af55cd695ff7eda400ad5ff64",
                "title": "This Receipt",
                "type": "text/html"
              }
            },
            "_embedded": {},
            "id": 3866535,
            "display_id": 3866535,
            "is_test": true,
            "hide_transaction": false,
            "data_is_fed": false,
            "transaction_date": "2019-09-26T08:06:57+1000",
            "locale_code": "en_US",
            "customer_first_name": "foo",
            "customer_last_name": "bar",
            "customer_tax_id": "1234567890",
            "customer_email": "brettflorio@gmail.com",
            "customer_ip": "34.209.10.212",
            "ip_country": "United States",
            "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36",
            "total_item_price": 8,
            "total_tax": 0.66,
            "total_shipping": 0,
            "total_future_shipping": 0,
            "total_order": 8.66,
            "status": "verified",
            "date_created": "2019-09-25T15:00:49-0700",
            "date_modified": "2019-09-25T15:09:12-0700"
          }
        ],
        "fx:subscriptions": [
          {
            "_links": {
              "fx:sub_token_url": {
                "href": "https://demo.foxycart.tld/cart?sub_token=6969108bde0cb7c83676543dc046e39783d5b68db1983246bc33dd207e42f7c0",
                "title": "This Sub Token",
                "type": "text/html"
              }
            },
            "start_date": "2019-03-18T00:00:00-0700",
            "next_transaction_date": "2019-04-18T00:00:00-0700",
            "end_date": null,
            "frequency": "1m",
            "error_message": "",
            "past_due_amount": 0,
            "first_failed_transaction_date": null,
            "is_active": true,
            "third_party_id": "",
            "date_created": "2019-03-18T00:02:46-0700",
            "date_modified": "2019-03-18T00:02:46-0700"
          }
        ]
      },
      "_links": {
        "fx:checkout": {
          "href": "https://demo.foxycart.tld/checkout?fc_customer_id=789012345&timestamp=1572258301&fc_auth_token=3fe36042a181df47065215bad145dce6e27aa9e1",
          "title": "Checkout"
        }
      },
      "date_created": "2018-10-18T17:29:20-0700",
      "date_modified": "2019-10-28T01:35:49-0700",
      "email": "foo@example.tld",
      "first_name": "John",
      "id": 52008,
      "is_anonymous": false,
      "last_login_date": "2019-10-21T00:55:11-0700",
      "last_name": "Foobar",
      "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.

Retrieving & Modifying Subscriptions

GET /s/customer/subscriptions

  • 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 next_transaction_date asc.
  • Response:
    [
        {
            "_links": {
                "self": {
                    "href": "https://demo.foxycart.tld/s/customer/subscriptions/123456",
                    "title": "This Subscription"
                },
                "fx:sub_token_url": {
                    "href": "https://demo.foxycart.tld/cart?sub_token=52ceabc123",
                    "title": "This Sub Token",
                    "type": "text/html"
                }
            },
            "start_date": "2019-11-03T00:00:00-0700",
            "next_transaction_date": "2019-12-01T00:00:00-0800",
            "end_date": null,
            "frequency": "2w",
            "error_message": "",
            "past_due_amount": 0,
            "first_failed_transaction_date": null,
            "is_active": true,
            "third_party_id": "",
            "date_created": "2019-11-03T15:49:20-0800",
            "date_modified": "2019-11-21T01:27:36-0800"
        }
    ]

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 URI is returned in the _links.self value (seen above).
  • Response: The response will be an object of the subscription.

Building a Customer Portal (Old Functionality)

FoxyCart doesn't currently (as of v2.0) support a customer portal, but if you'd like to build one yourself, this page has a brief overview of one possible approach. Please vote for customer portal functionality if you'd like to see native support.

This page will describe the basic idea to allow customers to login using their FoxyCart username (email) and password, and to see their orders. The important pieces are:

  1. Authentication
  2. Retrieving orders via the API

High-Level Overview for Programmers

Authenticating your Customers

If you already have a login section on your own website, you should skip this section and instead use the Single Sign-On functionality.

If you don't have a login section, you'll want to create functionality (serverside) on your system that does the following:

  1. Accepts an email and password.
  2. Sends the email and password (again, server side and not clientside) to a URL like this (or to the URL without the querystring, and POST the values instead):
    https://EXAMPLE.foxycart.com/v/2.0.0/api_json.php?store_id=ID&customer_email=EMAIL_ADDRESS&customer_password=PASSWORD&ThisAction=CheckLogin&fcsid=SESSION_ID

    Replace EXAMPLE with your store's domain, ID with your store's ID (please email our support for your ID), and the EMAIL_ADDRESS and PASSWORD with the values provided by the customer, and SESSION_ID with the current Foxy session ID. You can get the customers current session by ensuring that you've included the loader.js on your login page, and populate it in the login form using javascript like this (updating the form element you're targeting appropriately):

    <script>
    var FC = FC || {};
    FC.onLoad = function() {
      FC.client.on("ready.done", function() {
        $("#LoginForm").append('<input type="hidden" name="' + FC.json.session_name + '" value="' + FC.json.session_id + '">');
      });
    };
    </script>
  3. You'll get JSON formatted data in return. Experiment with a valid customer email and password, valid email but invalid password, and invalid email to see the responses. If you have an active cart session, you'll notice that a valid email and password will return the customer's full information. If there isn't an active cart session though - you'll get a response like {“ok”:false,“details”:“Transaction not found”}. You can consider this a successful authentication, but as our existing login functionality expects the customer to be on the checkout, that also means it assumes that the customer has an active cart session too. As such, for a successful authentication you can check for either ok being true, or ok being false, and a details of Transaction not found.
  4. If a valid email + password has been entered, set a session cookie (or handle session management however you'd like for your language of choice).

Displaying Orders

Once the customer has successfully authenticated, you can use the FoxyCart API to retrieve orders for that customer's email address (as entered above), probably using the transaction_list method and filtering by customer_email_filter. The API returns XML, and usually you'll take that XML and output HTML as needed from it.

The easiest approach is simply to retrieve transactions and then link to them using the receipt_url node in the returned XML, but since you have all the data available, you can do whatever you'd like.

More Advanced Options

Instead of just displaying transactions, you could use the API to allow the customer to cancel or modify a subscription, or update their own address information.

Security Considerations

  • As with all things, if you're collecting a password, you should have SSL on your own site to ensure things are secure.
  • Never rely on javascript for authentication. When submitting the password to the FoxyCart endpoint, that must be done serverside.

Site Tools