PAYATHON 2026

如何从 Azure subscriptionID 获取 OfferDurableId 等参数?

支付小周

结论

不能只凭 subscriptionID,通过某个通用的 Azure API 稳定获取 OfferDurableId、Currency、Locale 和 RegionInfo。

这些参数并不都属于订阅资源:

  • OfferDurableId 取决于订阅采用的商业报价或计费协议。
  • Currency 通常由计费账户、计费配置文件或协议决定。
  • RegionInfo 表示计费市场,不是资源部署区域。
  • Locale 用于控制 RateCard 返回内容的本地化格式,通常由调用方指定。

订阅查询 API 可以返回 quotaId、locationPlacementId 等策略信息,但这些字段不一定对应 RateCard 所需的 OfferDurableId 和 RegionInfo,不能直接转换后用于 RateCard 查询。

可以先查询订阅元数据

使用 Azure Resource Manager 的 Subscriptions - Get API:

GET https://management.azure.com/subscriptions/{subscriptionId}?api-version=2022-12-01
Authorization: Bearer {accessToken}

典型响应可能包含:

{
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000",
  "subscriptionId": "00000000-0000-0000-0000-000000000000",
  "displayName": "Production",
  "state": "Enabled",
  "subscriptionPolicies": {
    "locationPlacementId": "Public_2014-09-01",
    "quotaId": "PayAsYouGo_2014-09-01",
    "spendingLimit": "Off"
  }
}

这些字段有助于识别订阅类型,但有两个限制:

  • quotaId 不一定是 MS-AZR-xxxxP 格式的 OfferDurableId。
  • locationPlacementId 不是 RateCard 要求的计费国家或地区代码。

因此,这个 API 只能辅助判断订阅性质,无法补齐全部 RateCard 参数。

各参数应该从哪里获得

OfferDurableId

OfferDurableId 是报价的持久标识,例如:

MS-AZR-0003P

它由订阅的购买渠道和计费协议决定。对于部分传统 Pay-As-You-Go、EA 或开发测试订阅,可以根据已知报价建立映射。但 Azure 没有提供适用于所有订阅类型的公共 ARM API,无法将任意 subscriptionID 直接转换为 OfferDurableId。

不要假设:

subscriptionPolicies.quotaId == OfferDurableId

两者即使在业务上有关联,格式和含义也可能不同。

Currency

Currency 应从订阅对应的计费账户、计费配置文件或协议中获取,而不是从订阅资源中读取。

如果订阅属于 Microsoft Customer Agreement,可以通过 Microsoft Billing API 查询:

  1. 订阅对应的计费账户。
  2. 对应的计费配置文件。
  3. 计费配置文件中的币种和账单地址。

调用这些 API 通常需要计费范围权限,普通的订阅 Reader 或 Contributor 权限不一定够用。

EA、MCA、Microsoft Partner Agreement 和旧式在线订阅使用的计费 API、资源路径及权限模型各不相同,因此无法用同一套查询逻辑覆盖所有订阅。

Locale

Locale 通常由应用自行指定,例如:

en-US
zh-CN

它主要影响服务名称、说明等内容的本地化格式,不是必须从订阅中读取的固有属性。服务端支持哪些区域设置,应以 RateCard API 实际支持的值为准。

如果系统不需要本地化,可以将它设为应用配置,例如统一使用:

en-US

RegionInfo

RegionInfo 表示订阅报价所属的计费市场,通常采用国家或地区代码,例如:

US
CN
GB

它不是以下任何一种信息:

  • Azure Resource Manager 的资源位置;
  • 资源组的 location;
  • subscriptionPolicies.locationPlacementId;
  • 任意资源所在的数据中心区域。

对于 MCA 订阅,可以从计费配置文件的账单地址中获取国家或地区信息,再按照 RateCard 的要求进行映射。其他协议则需要从相应的计费账户或合同资料中获取。

C# 查询订阅元数据示例

下面的代码只能读取订阅策略信息,不能保证获得完整的 RateCard 参数:

using System.Net.Http.Headers;
using System.Text.Json;

public sealed record SubscriptionHints(
    string? QuotaId,
    string? LocationPlacementId,
    string? SpendingLimit);

public static async Task<SubscriptionHints> GetSubscriptionHintsAsync(
    HttpClient httpClient,
    string subscriptionId,
    string accessToken)
{
    var url =
        $"https://management.azure.com/subscriptions/{subscriptionId}" +
        "?api-version=2022-12-01";

    using var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.Headers.Authorization =
        new AuthenticationHeaderValue("Bearer", accessToken);

    using var response = await httpClient.SendAsync(request);
    response.EnsureSuccessStatusCode();

    await using var stream = await response.Content.ReadAsStreamAsync();
    using var document = await JsonDocument.ParseAsync(stream);

    JsonElement policies;
    if (!document.RootElement.TryGetProperty(
            "subscriptionPolicies",
            out policies))
    {
        return new SubscriptionHints(null, null, null);
    }

    return new SubscriptionHints(
        GetString(policies, "quotaId"),
        GetString(policies, "locationPlacementId"),
        GetString(policies, "spendingLimit"));
}

private static string? GetString(JsonElement element, string propertyName)
{
    return element.TryGetProperty(propertyName, out var property)
        ? property.GetString()
        : null;
}

即使查询结果中有 quotaId,也只能把它当作识别订阅的线索,不能直接用作 OfferDurableId。

组装 RateCard 请求

应用从可信的计费配置中取得这些值后,可以按下面的方式构造请求:

using System.Globalization;

static string BuildRateCardUrl(
    string subscriptionId,
    string offerDurableId,
    string currency,
    string locale,
    string regionInfo)
{
    static string EscapeOData(string value) =>
        value.Replace("'", "''", StringComparison.Ordinal);

    var filter = string.Format(
        CultureInfo.InvariantCulture,
        "OfferDurableId eq '{0}' and Currency eq '{1}' " +
        "and Locale eq '{2}' and RegionInfo eq '{3}'",
        EscapeOData(offerDurableId),
        EscapeOData(currency),
        EscapeOData(locale),
        EscapeOData(regionInfo));

    return
        $"https://management.azure.com/subscriptions/{subscriptionId}" +
        "/providers/Microsoft.Commerce/RateCard" +
        "?api-version=2015-06-01-preview" +
        $"&$filter={Uri.EscapeDataString(filter)}";
}

Currency 也是字符串,应写成:

Currency eq 'USD'

不能写成:

Currency eq USD

还需要对整个 OData $filter 进行 URL 编码,以免空格、引号或特殊字符导致请求解析失败。

更稳妥的实现方案

如果必须继续使用旧版 RateCard API,可以按以下流程处理:

  1. 调用 Subscriptions - Get,获取订阅基本信息和策略字段。
  2. 根据租户中的计费关系,识别订阅属于 EA、MCA、MPA 还是旧式在线订阅。
  3. 使用对应的 Microsoft Billing API 查询计费账户或计费配置文件。
  4. 从计费资料中获取币种以及计费国家或地区。
  5. 将 Locale 设为应用级配置。
  6. 如果无法通过公共 API 确定 OfferDurableId,则在首次接入时由计费管理员确认,并按订阅或计费范围缓存。
  7. 不要只根据 quotaId 或订阅显示名称猜测报价。

如果需要查询实际协商价格,而不是继续维护旧 RateCard 集成,通常更适合使用对应协议下的 Price Sheet API。Price Sheet 直接面向计费账户或计费范围,能够反映合同价格,也不依赖 OfferDurableId、Locale 和 RegionInfo 这组旧式筛选参数。

如果只需查询公开零售价,可以使用 Azure Retail Prices API。它不要求 subscriptionID,但返回的是公开零售价,不包含该订阅的折扣、企业协议价格或合作伙伴价格。

注意事项

  • 订阅管理权限和计费数据权限属于两套权限体系。
  • 拥有订阅 Owner 权限,不等于可以读取计费账户或价格表。
  • EA、MCA、MPA、CSP 和旧式订阅的 API 路径与数据模型可能不同。
  • 不能用订阅的资源区域推断计费市场。
  • 同一个计费账户下可能有多个计费配置文件,各自使用不同的币种或账单地址。
  • RateCard 属于较早的 Commerce API。新系统应优先评估 Price Sheet 或 Cost Management API。
  • API 版本可能因计费协议和 Azure 云环境而异。实际落地时,应使用目标环境当前支持的稳定版本。

备注:内容仅供参考。