> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loopreturns.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Partner (Third Party App) Deep Link

Deep linking customers into the returns portal enables a frictionless returns experience. You only need two pieces of information to deep link: the order name (as used in Loop, often the customer-facing order number) and the store's `shop_domain` (the hostname registered for that merchant, e.g. a `*.myshopify.com` domain for Shopify). Here's how to get started.

<Info>
  If you are creating deep links for your own store and have access to your Loop
  admin, use the [generate order
  link](/api-reference/latest/return-actions/create-return-deep-link) endpoint
  instead.
</Info>

## Become a Registered Third-Party App

Please submit the [Loop Partner Registration form](https://share.hsforms.com/1MpoNYotqQmOxipxw7ZdKTw2upqu) to become a registered third-party app in the Loop App Marketplace. Becoming a third-party app enables you to deep link customers into the returns portal. Once approved, Loop will provide a shared secret. Securely store this secret because you will use it to sign requests. Never allow this secret to be visible in the browser.

## Redirect to Deep Link Endpoint

To deep link the customer into the returns portal you need to redirect the browser to our deep link API. When successful, the deep link API redirects to the returns portal.

### Deep Link API

The deep link API can be reached at GET `https://api.loopreturns.com/api/v1/app/deep-link`

Required query parameters:

| Key           | Description                                                                             |
| ------------- | --------------------------------------------------------------------------------------- |
| `order`       | Order name (as used in Loop)                                                            |
| `partner`     | Your registered partner name                                                            |
| `shop_domain` | Store domain for the merchant, e.g. `example.myshopify.com` when the shop is on Shopify |
| `hmac`        | HMAC value used to verify authenticity of request                                       |

Use the shared secret to generate the HMAC digest, as described below. The HMAC should be added to the request's query parameters. If the HMAC is verified and the other query parameters are valid then we will respond with a `307 Temporary Redirect` that loads the returns portal for the order.

## Generate the HMAC Digest for the Request

The HMAC used in the above API call should be generated using the following steps:

1. Construct the query string with the keys sorted in alphabetical order.

   Example: `order=1000&partner=your-company&shop_domain=example.myshopify.com`

2. Using the query string, generate a (lowercase) hex encoded HMAC using SHA-256.

### HMAC Calculation Examples

<CodeGroup>
  ```javascript Javascript theme={null}
  const crypto = require('crypto');
  const qs = require('qs');

  const parameters = {
  partner: "your-company", // Registered partner name
  order: "1000", // Order name
  shop_domain: "example.myshopify.com" // shop_domain
  }

  const alphabetizedQueryString = qs.stringify(parameters, { sort: ((a,b) => {
  return a.localeCompare(b);
  })});

  const hmac = crypto.createHmac('sha256', 'your-partner-secret')
  .update(alphabetizedQueryString.trim())
  .digest('hex');

  ```

  ```php PHP theme={null}
  <?php
  $parameters = [
      "partner" => "your-company", // Registered partner name
      "order" => "1000", // Order name
      "shop_domain" => "example.myshopify.com" // shop_domain
  ];

  ksort($parameters);
  $queryString = http_build_query($parameters);

  $hmac = hash_hmac('sha256', trim($queryString), 'your-partner-secret');
  echo $hmac;
  ?>
  ```

  ```ruby Ruby theme={null}
  require 'openssl'
  require 'uri'

  parameters = {
    "partner" => "your-company", # Registered partner name
    "order" => "1000", # Order name
    "shop_domain" => "example.myshopify.com" # shop_domain
  }

  sorted_params = parameters.sort.to_h
  query_string = URI.encode_www_form(sorted_params)

  hmac = OpenSSL::HMAC.hexdigest('sha256', 'your-partner-secret', query_string.strip)
  puts hmac
  ```

  ```python Python theme={null}
  import hashlib
  import hmac
  import urllib.parse

  parameters = {
      "partner": "your-company", # Registered partner name
      "order": "1000", # Order name
      "shop_domain": "example.myshopify.com" # shop_domain
  }

  # Sort parameters alphabetically and create query string
  sorted_params = sorted(parameters.items())
  query_string = urllib.parse.urlencode(sorted_params)

  # Generate HMAC
  secret = b"your-partner-secret"
  hmac_hex = hmac.new(secret, query_string.strip().encode('utf-8'), hashlib.sha256).hexdigest()

  print(hmac_hex)
  ```

  ```java Java theme={null}
  import java.util.*;
  import java.util.stream.Collectors;
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import java.nio.charset.StandardCharsets;

  public class GenerateHMAC {
      public static void main(String[] args) throws Exception {
          Map<String, String> parameters = new TreeMap<>();
          parameters.put("partner", "your-company"); // Registered partner name
          parameters.put("order", "1000"); // Order name
          parameters.put("shop_domain", "example.myshopify.com"); // shop_domain

          String queryString = parameters.entrySet()
                                          .stream()
                                          .map(entry -> entry.getKey() + "=" + entry.getValue())
                                          .collect(Collectors.joining("&"));

          Mac mac = Mac.getInstance("HmacSHA256");
          SecretKeySpec secretKeySpec = new SecretKeySpec("your-partner-secret".getBytes(StandardCharsets.UTF_8), "HmacSHA256");
          mac.init(secretKeySpec);

          byte[] hash = mac.doFinal(queryString.trim().getBytes(StandardCharsets.UTF_8));
          StringBuilder hmacHex = new StringBuilder();
          for (byte b : hash) {
              hmacHex.append(String.format("%02x", b));
          }

          System.out.println(hmacHex.toString());
      }
  }
  ```

  ```csharp C# theme={null}
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Security.Cryptography;
  using System.Text;

  class Program
  {
      static void Main()
      {
          var parameters = new SortedDictionary<string, string>
          {
              { "partner", "your-company" },
              { "order", "1000" },
              { "shop_domain", "example.myshopify.com" }
          };

          var queryString = string.Join("&", parameters.Select(kv => $"{kv.Key}={kv.Value}"));

          using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes("your-partner-secret")))
          {
              var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(queryString.Trim()));
              string hmacHex = BitConverter.ToString(hash).Replace("-", "").ToLower();
              Console.WriteLine(hmacHex);
          }
      }
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/hex"
  	"fmt"
  	"net/url"
  	"sort"
  )

  func main() {
  	parameters := map[string]string{
  		"partner":     "your-company", // Registered partner name
  		"order":       "1000",         // Order name
  		"shop_domain": "example.myshopify.com", // shop_domain
  	}

  	// Sort the parameters by key
  	keys := make([]string, 0, len(parameters))
  	for k := range parameters {
  		keys = append(keys, k)
  	}
  	sort.Strings(keys)

  	queryString := ""
  	for i, key := range keys {
  		queryString += key + "=" + url.QueryEscape(parameters[key])
  		if i < len(keys)-1 {
  			queryString += "&"
  		}
  	}

  	// Generate HMAC
  	secret := []byte("your-partner-secret")
  	mac := hmac.New(sha256.New, secret)
  	mac.Write([]byte(queryString))
  	hmacHex := hex.EncodeToString(mac.Sum(nil))

  	fmt.Println(hmacHex)
  }
  ```
</CodeGroup>
