# Integration

Connect Suger to Alibaba Cloud Marketplace to list your SaaS products, manage offers, sync entitlements, and report usage — across both China (domestic) and international markets.

---

## Overview

Alibaba Cloud Marketplace connects Alibaba Cloud customers with trusted SaaS providers and ISVs worldwide. Regardless of the region you sell in, the operational logic stays consistent with the other major clouds:

| Concept | [Alibaba Cloud](https://marketplace.alibabacloud.com/) equivalent | Suger function |
|---------|------------------------------------------------------------------|----------------|
| Listing | Marketplace Product | Centralized product catalog management |
| Offer | Private Quote / Pricing | Automated creation of custom contract terms |
| Entitlement | Instance / Subscription | Real-time provisioning and access control |

:::info
**Org-Level only.** Alibaba Marketplace connects with organization-wide API credentials and does not support user-level integrations.
:::

## Prerequisites

To establish a secure handshake between Suger and Alibaba Cloud, gather four pieces of information from your Alibaba Cloud account:

- **Alibaba Cloud Account ID** — your unique root account **Uid** (aliyunUid).
- **Access Key ID** — the public identifier for your API credentials.
- **Access Key Secret** — the private key for API authentication.
- **SPI Key** — the [Service Provider Interface](https://www.alibabacloud.com/help/en/marketplace/latest/spi-security-scheme) key, used to verify security for notification events (such as a customer purchase).

You will also need the **Region ID** you sell in (e.g. `cn-beijing` for China, or an international region).

## Connect

Link your Alibaba Cloud credentials to Suger to enable automated order management and revenue recognition.

1. Navigate to **Settings > Integrations** and locate the **Alibaba Marketplace** card. Click **Connect**.
2. Enter your **Region ID**, **Cloud Account ID** (root account Uid), **Access Key ID**, **Access Key Secret**, and **SPI Key**.
3. (Optional) Enter the **Product Code(s)** you want to list — these can also be configured later.
4. Click **Create** (or **Verify**) to validate the credentials and confirm the connection.

:::note
The Region ID determines whether Suger uses the Alibaba Cloud **domestic (China)** or **international** SDK. Access keys and the SPI key are stored in Suger's secret store and are never persisted in plain text.
:::

## Manage settings

Once connected, you can fine-tune how Suger interacts with your Alibaba listings from the integration's edit menu:

- **Product Fetching** — by default, Suger fetches all products under your Account ID. To limit this, manually enter specific **Product Code(s)**.
- **Metering Control** — toggle **Usage Metering Disabled** if you need to pause usage reporting to Alibaba.
- **Renewal Notifications** — enable **Entitlement End Soon** alerts to notify your team by email (10–60 days in advance) when a buyer's contract is approaching its end date.

## Auth-Free URL redirection

Let your buyers reach your backend directly from the Alibaba Console without re-authenticating. When a buyer opens your service, Suger signs the request with your SPI Key and redirects the buyer to your **Fulfillment URL** with a token you verify on your side.

1. **Configure fulfillment** — set your fulfillment URL in the Alibaba Marketplace to point to the Suger service.
2. **Redirection flow** — when a buyer clicks to access your service, Suger resolves the entitlement, signs the request with your SPI Key, and redirects the buyer to your Fulfillment URL:

   ```
   https://<your-fulfillment-url>/?sugerEntitlementId=<id>&partner=alibaba&timestamp=<timestamp>&token=<token>
   ```

   | Parameter | Value |
   |---|---|
   | `sugerEntitlementId` | The Suger entitlement (contract) ID. |
   | `partner` | The literal string `alibaba` (lowercase). |
   | `timestamp` | The event time in RFC 3339 UTC, e.g. `2026-01-15T09:30:00Z`. |
   | `token` | A 32-character lowercase-hex signature (see below). |

3. **Verify on your backend** — recompute the signature from the query parameters and your SPI Key and compare it to `token` before you trust the redirect.

```mermaid
sequenceDiagram
    participant Buyer
    participant Alibaba as Alibaba Console
    participant Suger
    participant Backend as Your backend
    Buyer->>Alibaba: Click "Access service"
    Alibaba->>Suger: Open the auth-free URL (SPI webhook)
    Suger->>Suger: Resolve entitlement, sign params with SPI Key (MD5)
    Suger-->>Buyer: 303 redirect to your Fulfillment URL with signed params
    Buyer->>Backend: GET Fulfillment URL (sugerEntitlementId, partner, timestamp, token)
    Backend->>Backend: Recompute MD5(params + "&key=" + SPI Key), compare to token
    alt token matches
        Backend-->>Buyer: Grant access (log the buyer in)
    else mismatch or stale timestamp
        Backend-->>Buyer: Reject the request
    end
```

### Validation contract

The `token` is a **lowercase-hexadecimal MD5** digest of the UTF-8 bytes of the signed string. Build the signed string, hash it, and compare:

1. Take the redirect's query string and **remove the `token` parameter**. Keep every other parameter **in the exact order received** — `sugerEntitlementId`, then `partner`, then `timestamp`. **Do not sort or re-encode the parameters.**
2. Append `&key=<your SPI Key>` to that string.
3. Compute `md5(...)` over the UTF-8 bytes and hex-encode it in lowercase.
4. Compare the result to the received `token` using a **constant-time** comparison.

So the string you hash is:

```
sugerEntitlementId=<id>&partner=alibaba&timestamp=<timestamp>&key=<SPI Key>
```

**Worked example.** With SPI Key `my-spi-key`, entitlement `ent-abc123`, and timestamp `2026-01-15T09:30:00Z`, the signed string is:

```
sugerEntitlementId=ent-abc123&partner=alibaba&timestamp=2026-01-15T09:30:00Z&key=my-spi-key
```

and `token` = `bd6b70b1b180ec9d0f8ed112d8b18129`.

:::warning
**Suger does not enforce expiry or replay protection on this redirect.** The `timestamp` is signed (so it cannot be tampered with) but Suger never rejects an old one, and the same `token` validates indefinitely. If you need freshness or one-time use, **enforce your own maximum age on `timestamp` and your own replay/dedup** on your backend.
:::

The SPI Key used to sign is the same **SPI Key** you configured on the integration. Keep it secret; anyone with it can forge a valid redirect.

### Reference validators

#### Go

```go
	"crypto/md5"
	"crypto/subtle"
	"encoding/hex"
	"strings"
)

// validateRedirect verifies the Alibaba auth-free redirect token.
// rawQuery is the URL query string exactly as received, e.g.
// "sugerEntitlementId=ent-abc123&partner=alibaba&timestamp=2026-01-15T09:30:00Z&token=bd6b...".
func validateRedirect(rawQuery, spiKey string) bool {
	var received string
	kept := make([]string, 0)
	for _, p := range strings.Split(strings.TrimPrefix(rawQuery, "?"), "&") {
		if strings.HasPrefix(p, "token=") {
			received = strings.TrimPrefix(p, "token=")
			continue
		}
		kept = append(kept, p) // keep received order; do NOT sort
	}
	sum := md5.Sum([]byte(strings.Join(kept, "&") + "&key=" + spiKey))
	expected := hex.EncodeToString(sum[:])
	return subtle.ConstantTimeCompare([]byte(received), []byte(expected)) == 1
}
```

#### Python

```python

def validate_redirect(raw_query: str, spi_key: str) -> bool:
    received = ""
    kept = []
    for p in raw_query.lstrip("?").split("&"):
        if p.startswith("token="):
            received = p[len("token="):]
        else:
            kept.append(p)  # keep received order; do NOT sort
    string_to_sign = "&".join(kept) + "&key=" + spi_key
    expected = hashlib.md5(string_to_sign.encode("utf-8")).hexdigest()
    return hmac.compare_digest(received, expected)
```

#### TypeScript

```ts

function validateRedirect(rawQuery: string, spiKey: string): boolean {
  let received = "";
  const kept: string[] = [];
  for (const p of rawQuery.replace(/^\?/, "").split("&")) {
    if (p.startsWith("token=")) received = p.slice("token=".length);
    else kept.push(p); // keep received order; do NOT sort
  }
  const expected = createHash("md5")
    .update(kept.join("&") + "&key=" + spiKey, "utf8")
    .digest("hex");
  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}
```

## Edit / Delete

You can update your **Account ID** or **Product Codes** at any time by editing the integration. To rotate security-sensitive keys (such as the **Access Key Secret** or **SPI Key**), delete the integration and reconnect with the new credentials.

Deleting the integration immediately erases all stored API and SPI keys from Suger and stops product sync, entitlement processing, and metering. This won't delete your products on Alibaba, but automated syncing and entitlement tracking stay paused until you manually reconnect.

:::warning
Deleting the integration may affect active marketplace subscriptions and billing relationships.
:::
