SMRT API Checkout How to guide

Checkout Flow API Documentation

Introduction

The SMRT API is a Graphql API which uses HTTP JSON POST.
You can explore the documentation, and play with our API easily using below chrome extension, or just use the examples further below, tailored for the checkout flow. To explore our API with that extension, set the URL and the Authorization header given below.
https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja?hl=en

The example API calls below, goes to the testing instance https://apitesting.smrtapp.com . Once development is done, you will switch this to the customers live subdomain (somecustomer.smrtapp.com). Please reach out to SMRT support (support@smrtsystems.com) to request both sign in credentials and an API key to our apitesting.smrtapp.com testing instance, specifying your use case and which SMRT customer you are developing the integration for.

 

Lastly, please see attached .php file at the bottom of this article, for an example implementing all the below steps.

 

GraphQL Overview

Below is an example CURL request to our API, with a working API key (that you should use). As you can see in the example CURL request, you need to set a authorization header and a content type JSON header. Other than that, you’re just POSTing and getting JSON back from our API. In examples further down in this documentation, we’re only modifying the JSON POST DATA in the request vs below example. The URL (/graphql), http method (POST) and the remain the same for all requests to our API.
Please run these requests on your backend servers and not using AJAX on a frontend, as the API key is a “sudo key” to the business instance which could cause a lot of harm in the wrong hands. You will get a new API key for the live URL.

Curl Request (you can copy and paste this into your terminal to try it if you’re on mac/linux): You need to request an API key from SMRT and replace PLEASE_REQUEST_FROM_SMRT with it for below example to work.

curl 'https://apitesting.smrtapp.com/graphql' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer PLEASE_REQUEST_FROM_SMRT' \
--data-binary '{"query":"query GetCustomer($by: GetCustomerFieldEnum!, $term: String!, $includeInactive: Boolean) { business { getCustomer(by: $by, term: $term, includeInactive: $includeInactive) { id name creditCardIFrameURL } }}","variables":{"by": "email", "term": "test@example.com"}}'

HTTP RESPONSE should be:

{
  "data": {
    "business": {
      "getCustomer": {
        "id": "Customer_1001_1339",
        "name": "API Test Customer",
        "creditCardIFrameURL": "https://qa-pay2.smrtapp.com/iframe/1001_1339/load?access_token=2e2f6f8cae602f9dadb7d9ef7bb36099&accepted_cards=visa,master,amex,discover&style=profile"
      }
    }
  }
}

You should have received credentials to the test instance separately. Sign into this instance, and once signed in, you should be able to “search” the customer records by name in the top left search field.
Once you open a specific customer, you should be able to verify their name, address, email, phone number and credit card details in the top section.

Given the two relevant steps for the POS software doing a mail order integration, we’ve come up with below five interactions needed with the SMRT API. Step 1-4 are for the submission of the “shipping screen” and step 5 is for the load and submission of the “payment” screen.

 

 

Step 1

When you press submit on the shipping step in the form, you must check with SMRT if this is a new or existing customer. Two active customers in SMRT cannot have the same email nor primary phone number. As such, this step will let you know if you should create or update a customer.

You can search for a customer by either email or phone, with this JSON POST. Send two requests, one to check if the customer exists by email (like below), one if they exist by phone. To search by phone, change the “by” param to “phone” and the “term” param to the actual phone number (numerics only) such as “4156025810”. Depending on the result, go to step 2 or 3…

{
    "query": "query GetCustomer($by: GetCustomerFieldEnum!, $term: String!, $includeInactive: Boolean) { business { getCustomer(by: $by, term: $term, includeInactive: $includeInactive) { id name creditCardIFrameURL } }}",
    "variables": {
        "by": "email",
        "term": "test@example.com",
        "includeInactive": false
    }
}

Step 2

If an existing customer was not found, create a new one:

{
    "query": "mutation CreateCustomer($input: CustomerInput!, $agentId: String) { createCustomer(input: $input, agentId: $agentId) { id name creditCardIFrameURL } }",
    "variables": {
        "input": {
            "email": "test@example.com",
            "phone": "4156025810",
            "firstName": "Test1",
            "lastName": "Testersson"
        },
        "agentId": null
    }
}

Step 3

If a customer was found, you can update the record like so:

{
    "query": "mutation PutCustomer($customerId: ID!, $input: CustomerInput!) { putCustomer(customerId: $customerId, input: $input) { id name creditCardIFrameURL } }",
    "variables": {
        "customerId": "Customer_1001_1302",
        "input": {
            "email": "test@example.com",
            "phone": "4156025810",
            "firstName": "Test1Updated",
            "lastName": "Testersson"
        }
    }
}

Step 4

Both the create (#2) and update (#3) requests will return the customer identifier (“id” field in the response).
Now, you can use this endpoint to add the primary address to the account (this will serve as both shipping and billing). If this address already exists (in the case of an existing customer), this will be a “no op”. It will also update an existing address. Please reach out to us if you’d like to send both the billing and shipping address to SMRT, it didn’t look like that was needed but if so let us know. You should also know that SMRT will actually validate these addresses with the lob api as valid postal addresses. Make sure you handle an address rejected by our endpoint as a validation error to your form. You can try it by just entering a non existing street name and city.

{
    "query": "mutation PutCustomerAddresses($customerId: ID!, $defaultName: String!, $addresses: [CustomerAddressInput]!){ putCustomerAddresses(customerId: $customerId, defaultName: $defaultName, addresses: $addresses) { info } }",
    "variables": {
        "customerId": "Customer_1001_1302",
        "defaultName": "Billing",
        "addresses": [{
            "name": "Billing",
            "streetAddress": "1844 Belles St",
            "streetAddress2": "",
            "city": "San Francisco",
            "state": "CA",
            "zip": "94245"
        }]
    }
}

Step 5

The “payment” step in the mail order form is slightly more complicated. We’d like you to use a PCI compliant iframe solution where the card details never hit your servers. The URL for the customer specific iframe, is included in the JSON reply for when you created or updated the customer record in step 2 or 3, as the field creditCardIFrameURL.
You should replace the credit card fields on the payment step, with our iframe. The iframe will contain a credit card number and expiration date field. If you’d like to modify the styling of this iframe, please send us a css file.

Here’s an example for how to load in the iframe and how to interract with it. Notice how the submit button is outside the iframe, and you send a message to the iframe when it’s supposed to submit to our servers. Once that’s done on our end, we send the iframe details back to you in the onCardIframeMessage function. The parameters in e.data.card, you should send to your backend as part of the form post, which will in turn run a request to our servers to link the card to the customer record.

Frontend code:

<!-- TODO: insert the customer unique credit card url ($creditCardIFrameURL) from previous step -->
<iframe id="creditCardIframe" src="<%= $creditCardIFrameURL %>"></iframe> 

<button onclick="submitPaymentIframe()">Submit Form</button>

<script>
    function onCardIframeMessage(e) {
        if (e.data.smrt_action === 'save_card_response') {
            if (e.data.success) {
                console.log(e.data.card); //TODO: the card info to your servers, will contain all fields needed for putCreditCard.
            }
            else {
                onError(e.data.error);
            }
        }
    }

    window.addEventListener('message', onCardIframeMessage);


    function submitPaymentIframe() {
        var iframe = document.getElementById("creditCardIframe")
        console.log('posting save_card instruction to iframe', iframe);
        iframe.contentWindow.postMessage({ smrt_action: 'save_card' }, '*');
    }

</script>

Backend request to link the card from the iframe, to the the customer in the SMRT POS. The variables needed, you should you get from the frontend in e.data.card above):

{
    "query": "mutation PutCreditCard($customerId: ID!, $id: String!, $type: CreditCardTypeEnum!, $last4: Int!, $expiryMonth: Int!, $expiryYear: Int!) { putCreditCard(customerId: $customerId, id: $id, type: $type, last4: $last4, expiryMonth: $expiryMonth, expiryYear: $expiryYear) }",
    "variables": {
        "customerId": "Customer_1001_1302",
        "id": "c7947d2c-480e-43f3-91e4-a8ac554b9869",
        "type": "mastercard",
        "last4": "1234",
        "expiryMonth": 10,
        "expiryYear": 2020
    }
}





Creating a Pickup through API

If you, in addition to creating signing up a customer in smrt using the above steps, also wish to schedule a pickup in SMRT through your checkout form, please follow the below steps. These steps, will help you to create an experience for the customer where they’re able to select from a list of available dates and times to schedule a pickup.
Once the pickup is created, SMRT will add the scheduled stop to the driver manifest, and also optionally send out a text notification the day before the pickup to remind the customer of the pickup.

Step 1

Step 1 is to present the user with a screen with a dropdown for selecting date and time.
To get the available dates and times, you first need to have a customer with a delivery address on file, and routes setup.
Here’s an example graphql query for how to get the list of customers:

query {
  node(id: "Customer_37_1301") {
    ...cust
    
  }
}

fragment cust on Customer{
    datesForSchedulingAppointment {
        date
            timeSlots {
                id
                start
                end
            routeId
            }
    }
}

This query will give you objects like the below back:

{
  "data": {
    "business": {
      "getCustomer": {
        "datesForSchedulingAppointment": [
          {
            "id": "1602129600",
            "date": "2020-10-08T00:00:00-04:00",
            "timeSlots": [
              {
                "id": "-MJ6REZjE6NQhTeuxKDE",
                "start": "0800",
                "end": "1000",
                "routeId": "3"
              },
              {
                "id": "-MJ6REasgj5dfoPilLCK",
                "start": "1600",
                "end": "1800",
                "routeId": "3"
              },
              {
                "id": "anytime_3_1602129600",
                "start": "0800",
                "end": "1800",
                "routeId": "3"
              }
            ]
          },
          {
            "id": "1602475200",
            "date": "2020-10-12T00:00:00-04:00",
            "timeSlots": [
              {
                "id": "-MGwvsezZ-JZif0dDoD0",
                "start": "0800",
                "end": "1000",
                "routeId": "2"
              },
              {
                "id": "-MGwvsf7ShC7VfLYxBGe",
                "start": "1500",
                "end": "1700",
                "routeId": "2"
              },
              {
                "id": "anytime_2_1602475200",
                "start": "0800",
                "end": "1800",
                "routeId": "2"
              }
            ]
          }
        ]
      }
    }
  }
}

The objective now is for the customer to select a timeslot id (for example, -MGwvsezZ-JZif0dDoD0 or anytime_2_1602475200).
I would recommend displaying one dropdown to select the day (in this example 2020-10-08 or 2020-10-12).
If the user selects 2020-10-08, you would then present a dropdown with its 3 time frames:

  • 8am-10am
  • 4pm-6pm
  • 8am-6pm (Anytime during the day)

Once the user has selected their timeslot id, it is time to create the pickup request:

Step 2

Creating the pickup request requires the timeslot id and customer id from the previous step.

    mutation PutAppointment($input: AppointmentInput!, $isPickup: Boolean){
        putAppointment(input: $input, isPickup: $isPickup){
            id
            status
        }
    }

Below we show how to define the $input variables for the above PutAppointment mutation. The below code is written in javascript and would vary depending on language…

var customerId = "Customer_37_1301"l //the customer id
var addressId = "25"; //take the address id of the customer.

var cleaningInstructions = "a note for the store";
var driverInstructions = "a note for the driver";

var routeId = "2"; //the route id of the selected time slot object in the previous step
var timeSLotId = "-MGwvsezZ-JZif0dDoD0"; //the selected timeslot

//you need to parse out these date parameters from the selected date in the previous step.
var selectedDate = 8;
var selectedMonth = 10;
var selectedYear = 2020;


variables: {
    input: {
      customerId: "Customer_37_1301"
      addressId: addressId
      cleaningInstructions: cleaningInstructions
      driverInstructions: driverInstructions
      regular: false
      routeId: routeId
      selectedDate: selectedDate
      selectedMonth: selectedMonth
      selectedYear: selectedYear
      timeSlotId: timeSLotId

    },
    isPickup: true
},
Have more questions? Submit a request