Authentication
Path-based referral link and the HMAC-SHA256 signing contract (optional SSO).
Every Shop session begins with a path-based referral link. Bluecom issues you a provider slug; you open that link inside an iframe, WebView, or Telegram Mini App. User identity (SSO) is optional and is added via a short-lived HMAC-SHA256 signature on the URL.
Link format
/<slug>[/<sale_channel>][?uid=<userId>&ts=<unix>&sign=<hmac>][&lang=vi|en]| Component | Location | Required | Description |
|---|---|---|---|
slug | Path segment #1 | Yes | Your provider slug, issued by Bluecom. Replaces the old ?partnerCode=. |
sale_channel | Path segment #2 | No | Pins a sale channel. Omit → default channel. The channel must be linked + active and carry a payment method. |
uid | Query | No¹ | Stable identifier of the user in your system — the link key to their Shop account. |
ts | Query | No¹ | Unix epoch in seconds (not milliseconds). A positive integer. |
sign | Query | No¹ | hex(HMAC-SHA256(partnerSecret, "${uid}:${ts}")). Lowercase hex. |
lang | Query | No | vi (default) or en. Overrides locale. |
¹ The uid + ts + sign trio travels together — all of it or none. No trio → a guest session (unauthenticated, still bound to the right channel). A sign present but missing uid/ts is treated as a tampered link and returns INVALID_LINK.
Signing contract
When you want to pre-link a user account (SSO), your backend signs the identity trio:
- The signed string is exactly
uid:ts— colon-separated, no spaces, no JSON. slugis not part of the signed string — it only selects whichpartnerSecretto verify against.signis the lowercase hex ofHMAC-SHA256.
Validation rules
tsmust be within ±5 minutes of server time, else the link is rejected.signis compared in constant time.slugmust resolve to an active provider, elseNO_PARTNER.sale_channel(if present) must be linked + active for the provider; otherwise the mint is rejected and the Shop does not fall back to a default channel.
Signed vs guest
Whether a signature is required depends on the surface (see Embedding):
| Surface | Signature | Identity |
|---|---|---|
iframe | Unsigned | Enters as a guest; the user signs in inside the Shop if needed. |
Native WebView | Signed | HMAC trio required — missing/invalid is a terminal error. |
| Telegram Mini App | Provider credential | Uses Telegram initData, not HMAC. |
| KBZPay In-App H5 | Provider credential | Uses kbzpay_token, not HMAC. |
Security rules
partnerSecretMUST live only on your backend. Never embed it in app binaries, web bundles, or repo code.- Mint
signimmediately before opening the link. The 5-minute window is short by design. uidmust be stable per end user. Changing it creates a new Shop account.- HTTPS end-to-end. Plain HTTP links are rejected in production.
Not signing yourself? Use the Hub
If you don't run a signing backend, the Bluecom team can generate a referral link in the Hub (including a signed link for WebView). For iframe you only need an unsigned link — no partnerSecret required.
Code samples
The samples below build a signed link (for WebView / SSO). For iframe, drop the entire uid/ts/sign query.
Node.js
import crypto from "node:crypto";
const SLUG = "acme-bank"; // issued by Bluecom
const SALE_CHANNEL = "acme-default"; // optional: pin a sale channel
const PARTNER_SECRET = process.env.BLUECOM_PARTNER_SECRET!; // server-only
export function buildShopUrl(userId: string): string {
const ts = Math.floor(Date.now() / 1000);
const sign = crypto
.createHmac("sha256", PARTNER_SECRET)
.update(`${userId}:${ts}`)
.digest("hex");
const params = new URLSearchParams({ uid: userId, ts: String(ts), sign });
return `https://<shop-host>/${SLUG}/${SALE_CHANNEL}?${params.toString()}`;
}C# (.NET 8+)
using System.Security.Cryptography;
using System.Text;
using System.Web;
public static class BluecomShopUrlBuilder
{
private const string Slug = "acme-bank";
private const string SaleChannel = "acme-default";
private static readonly string PartnerSecret =
Environment.GetEnvironmentVariable("BLUECOM_PARTNER_SECRET")!;
public static string Build(string userId)
{
var ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var message = $"{userId}:{ts}";
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(PartnerSecret));
var sign = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(message)))
.ToLowerInvariant();
var qs = HttpUtility.ParseQueryString(string.Empty);
qs["uid"] = userId;
qs["ts"] = ts.ToString();
qs["sign"] = sign;
return $"https://<shop-host>/{Slug}/{SaleChannel}?{qs}";
}
}Error responses
NO_PARTNER (slug not found), INVALID_LINK (bad/expired signature or invalid channel), VERIFICATION_FAILED (identity sign-in failed). See Reference › Error codes.
