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

Apple Wallet更新APNs推送403错误排查求助

Apple Wallet APNs推送403错误排查求助

发布Apple Wallet凭证时,设备注册请求返回信息:

{"device_id":"9b39b68fa1e4b01485857160ecdde0e0","pass_serial":"4LTCAg","push_token":"7c8a5c327bce2a6d18710aebabdc5225f3b3a252e7ff8027906b1243ad498687"}

使用该信息发送APNs推送时,得到响应:

{403 Forbidden 403 HTTP/2.0 2 0 map[Apns-Id:[7731E8A4-D049-A982-5B31-11894BA891D4]] {0xc000178900} -1 [] false false map[] 0xc000602100 0xc00049ce70}

403错误指向认证问题,但未给出具体原因。我的Go语言APNs推送代码如下:

func getAppleBearerAuth() (*string, error) {
    kid := os.Getenv("APPLE_PUSH_KEY")
    iss := os.Getenv("APPLE_TEAM_ID")

    // 创建JWT令牌
    token := jwt.New(jwt.SigningMethodRS256)

    // 添加声明
    claims := token.Claims.(jwt.MapClaims)
    claims["iss"] = iss
    claims["iat"] = time.Now().Unix()

    // 向头部添加kid
    token.Header["kid"] = kid
    token.Header["alg"] = "ES256"

    // 加载ES256密钥
    key, err := loadPrivateKey()
    if err != nil {
        fmt.Println("failure to load private key")
        return nil, err
    }
    rsaKey, ok := key.(*rsa.PrivateKey)
    if !ok {
        return nil, fmt.Errorf("private key is not of type *rsa.PrivateKey")
    }

    // 签名并获取完整编码的令牌字符串
    tokenString, err := token.SignedString(rsaKey)
    if err != nil {
        fmt.Println("Failure to sign key")
        return nil, err
    }

    // 添加"Bearer "前缀生成Bearer令牌
    bearer := "Bearer " + tokenString
    fmt.Println(bearer)
    return &bearer, nil
}

func loadPrivateKey() (interface{}, error) {
    certPath := os.Getenv("APPLE_CERT")
    certPassword := os.Getenv("PASSKEYPASS")

    p12Bytes, err := ioutil.ReadFile(certPath)
    if err != nil {
        fmt.Println("failure to read cert file")
        return nil, err
    }

    blocks, err := pkcs12.ToPEM(p12Bytes, certPassword)
    if err != nil {
        fmt.Println("failure to convert to PEM file")
        return nil, err
    }

    var pemData []byte
    for _, block := range blocks {
        if block.Type == "PRIVATE KEY" || block.Type == "RSA PRIVATE KEY" {
            pemData = pem.EncodeToMemory(&pem.Block{
                Type:  block.Type,
                Bytes: block.Bytes,
            })
            break
        }
    }

    if pemData == nil {
        fmt.Println("no private key found")
        return nil, err
    }

    block, _ := pem.Decode(pemData)
    if block == nil {
        fmt.Println("failed to parse PEM block containing the key")
        return nil, err
    }

    key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
        fmt.Println("failure parse private key")
        return nil, err
    }
    return key, nil
}

func SendPushNotification(device_id string, push_token string) error {
    authorization, err := getAppleBearerAuth()
    if err != nil {
        fmt.Println("Failure to get apple auth: " + err.Error())
        return err
    }
    endpoint := fmt.Sprintf("https://api.sandbox.push.apple.com/3/device/%s", device_id) //APNs开发环境端点

    // 按文档要求构造负载
    payload := map[string]interface{}{"pushToken": push_token}
    payloadJSON, err := json.Marshal(payload)
    if err != nil {
        fmt.Println("Failed to marshal payload:", err)
        return err
    }
    fmt.Println("payload: " + string(payloadJSON))
    expiration := time.Now().Add(24 * time.Hour).Unix()
    // 设置请求头
    headers := map[string]string{
        "Authorization":   *authorization,
        "Content-Type":    "application/json",
        "apns-push-type":  "background",
        "apns-topic":      os.Getenv("APPLE_PASS_TYPE_IDENTIFIER"),
        "apns-expiration": fmt.Sprintf("%d", expiration),
    }

    httpClient := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                InsecureSkipVerify: true, // 生产环境需设为false
            },
            ForceAttemptHTTP2: true,
        },
    }

    request, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(payloadJSON))

    if err != nil {
        fmt.Println("Failed to create request:", err)
        return err
    }

    for key, value := range headers {
        request.Header.Set(key, value)
    }

    response, err := httpClient.Do(request)
    if err != nil {
        fmt.Println("Failed to send request:", err)
        return err
    }
    fmt.Println("Sent push notification!")
    defer response.Body.Close()

    if response.StatusCode == http.StatusOK {
        fmt.Println("Push notification sent successfully.")
    } else {
        fmt.Println("Failed to send the push notification. Status code:", response.StatusCode)
        fmt.Println("response content ", response)
    }
    return nil
}

用于签名的证书与创建Apple Wallet凭证时使用的证书一致,调用SendPushNotification("9b39b68fa1e4b01485857160ecdde0e0", "7c8a5c327bce2a6d18710aebabdc5225f3b3a252e7ff8027906b1243ad498687")测试仍返回403错误。我已尝试给URL添加端口、调整请求内容和请求头、更换设备信息等方法,均无效。Bearer令牌格式正常,凭证发布的凭据可正常使用,唯独推送请求返回403。

求解决该问题的方法,或提供可用的Apple Wallet更新APNs推送示例代码。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:54:57