You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PHP调用Azure Marketplace SaaS履约API解析订阅遇403错误

Azure SaaS履约API PHP调用403授权错误排查

我正在尝试用Azure履约API解析Azure Marketplace订阅,按照Microsoft SaaS履约API文档操作后,PHP端始终无法完成解析。目前能正常获取access_token,且在Postman中结合订阅者跳转到SaaS着陆页时从Azure门户拿到的购买标识token可以成功返回JSON响应,但PHP代码调用时出现403错误,提示“Authorization is missing, incorrect or invalid”。已经排除权限问题(Postman可正常运行),怀疑是授权参数格式有误,相关PHP代码如下:

<?php
 use Microsoft\Graph\Graph;
 use Microsoft\Graph\Http;
 use Microsoft\Graph\Model;
 use GuzzleHttp\Client;

 class GraphHelper {
private static Client $tokenClient;
private static Client $tokenWebClient;
private static string $clientId = '';
private static string $tenantId = '';
private static string $clientSec = '';
private static string $graphUserScopes = '';
private static Graph $userClient;
private static string $userToken;
private static string $resolveToken;
private static string $subToken= '';

public static function initializeGraphForUserAuth(): void {
    GraphHelper::$tokenClient = new Client();
    GraphHelper::$clientId = $_ENV['CLIENT_ID'];
    GraphHelper::$clientSec = $_ENV['CLIENT_SECRET'];
    GraphHelper::$tenantId = $_ENV['TENANT_ID'];
    GraphHelper::$graphUserScopes = $_ENV['GRAPH_USER_SCOPES'];
    GraphHelper::$userClient = new Graph();
    
}
public static function getUserToken(): void {
//getting the access token 
    $accessCodeRequestUrl = 'https://login.microsoftonline.com/'.GraphHelper::$tenantId.'/oauth2/token';
    $tokenRequestUrl = 'https://marketplaceapi.microsoft.com/api/saas/subscriptions/resolve?api-version=2018-08-31';
    $subToken = $_SESSION['subToken'];
  $tokenResponse = GraphHelper::$tokenClient->post($accessCodeRequestUrl, [
        'form_params' => [
            'client_id' =>  GraphHelper::$clientId,
            'grant_type' => 'client_credentials',
            'client_secret' => GraphHelper::$clientSec,
            'resource' => '20e940b3-4c77-4b0b-9a53-9e16a1b010a7'
        ],
        // These options are needed to enable getting
            // the response body from a 4xx response
            'http_errors' => false,
            'curl' => [
                CURLOPT_FAILONERROR => false
            ]
    ]);
        if ($tokenResponse->getStatusCode() == 200) {
            // Return the access_token
            $responseBody = json_decode($tokenResponse->getBody()->getContents());
            GraphHelper::$resolveToken = $responseBody->access_token;
            $resolveAccessToken= $responseBody->access_token;
        } else if ($tokenResponse->getStatusCode() == 400) {
            // Check the error in the response body
            $responseBody = json_decode($tokenResponse->getBody()->getContents());
            if (isset($responseBody->error)) {
                $error = $responseBody->error;
                // authorization_pending means we should keep polling
                if (strcmp($error, 'authorization_pending') != 0) {
                    throw new Exception('Token endpoint returned '.$error, 100);
                }
            }
        }

//resolving the subscription
    

 $resolveResponse = GraphHelper::$tokenClient->post($tokenRequestUrl, [
            'form_params' => [
                'content-type' => 'application/json', 
                
                'authorization' =>   'Bearer '.$resolveAccessToken,
            'x-ms-marketplace-token'=>$subToken
            ],
            // These options are needed to enable getting
            // the response body from a 4xx response
            'http_errors' => false,
            'curl' => [
                CURLOPT_FAILONERROR => false
            ]
        ]);

//test whether there is a reponse 
return  $resolveResponse->getStatusCode(); // this returns a 403 - Authorization is missing, incorrect or invalid.
    }
}


?>

问题原因与修复方案

核心问题是请求头参数放置位置错误:你把Authorization、Content-Type、x-ms-marketplace-token这些请求头参数放到了form_params数组中,而Guzzle的form_params是用来传递表单格式的请求体数据,并非设置请求头。

修改后的resolve接口调用代码:

$resolveResponse = GraphHelper::$tokenClient->post($tokenRequestUrl, [
    'headers' => [
        'Content-Type' => 'application/json',
        'Authorization' => 'Bearer ' . $resolveAccessToken,
        'x-ms-marketplace-token' => $subToken
    ],
    // POST请求需传递符合要求的JSON体,此处传空对象即可
    'body' => json_encode([]),
    'http_errors' => false,
    'curl' => [
        CURLOPT_FAILONERROR => false
    ]
]);

额外验证点:

  • 确认$subToken是完整有效的购买标识token,未被转义或截断
  • 检查获取access_token时的resource参数20e940b3-4c77-4b0b-9a53-9e16a1b010a7是否正确(这是Marketplace API的官方资源ID)
  • 确保Guzzle版本无兼容性问题,避免请求头处理异常

内容的提问来源于stack exchange,提问作者Tovs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:05:26