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

Apple Wallet Pass更新文档存疑及后端实现问题咨询

Apple Wallet Pass 更新实现疑问与代码求助

我正在遵循Apple官方PassKit文档实现Apple Wallet Pass的更新功能,目前已获取注册端点并记录deviceID与push_token,但仍有以下疑问:

  • 问题1:Pass Type Identifier和push token在APNs推送中如何使用?文档提到Pass Type Identifier在证书中,是否需要加入JWT?若需要,是放在claims还是headers中?push token文档提及用于与APNs通信,是否确实放在payload中?
  • 问题2:APNs推送的apns-push-type、apns-topic、apns-priority字段需配置什么值?我猜测类型为background,主题为Pass Type Identifier,无需设置优先级,但代码未生效。
  • 问题3:是否可在推送通知中添加Pass序列号,跳过「设备请求变更序列号」步骤?我的API丢失上下文后难以确定需更新的Pass,且用户不应持有同标识的多个Pass。
  • 问题4:最后一步「设备请求最新Pass版本」中,设备期望的Pass数据格式是什么?我猜测是包含pass-data字节数组(代表同序列号的签名pkpass文件)的JSON,但未找到确认依据。

当前我已完成Pass创建与注册,记录push token,并用Go语言实现APNs推送代码如下:

authorization, err := getAppleBearerAuth()
endpoint := fmt.Sprintf("https://api.sandbox.push.apple.com/3/device/%s", device_id) //APNs dev endpoint

//token in payload
payload := map[string]interface{}{"push_token": push_token}
payloadJSON, err := json.Marshal(payload)
if err != nil {
    fmt.Println("Failed to marshal payload:", err)
    return err
}

expiration := time.Now().Add(24 * time.Hour).Unix()
// Set the headers for the request
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, // Set to false in production
        },
    },
}

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
}

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)
}
return nil
}

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

    // Create a new JWT token
    token := jwt.New(jwt.SigningMethodES256)

    // Add claims
    claims := token.Claims.(jwt.MapClaims)
    claims["iss"] = iss
    claims["iat"] = time.Now().Unix()

    // Add kid to the header
    token.Header["kid"] = kid

    // Load your ES256 key
    key, err := loadPrivateKey()
    if err != nil {
        return nil, err
    }

    // Sign and get the complete encoded token as a string
    tokenString, err := token.SignedString(key)
    if err != nil {
        return nil, err
    }

    // Add "Bearer " to the token string to make it a bearer token
    bearer := "Bearer " + tokenString
    return &bearer, nil
}

func loadPrivateKey() (interface{}, error) {
    // Here you should implement loading your private key
    // Usually it involves reading from a PEM file and parsing the key

    // Path to the .p12 file
    certPath := os.Getenv("APPLE_CERT")
    // Password for the .p12 file
    certPassword := os.Getenv("PASSKEYPASS")

    p12Bytes, err := ioutil.ReadFile(certPath)
    if err != nil {
        return nil, err
    }

    blocks, err := pkcs12.ToPEM(p12Bytes, certPassword)
    if err != nil {
        return nil, err
    }

    pemData := pem.EncodeToMemory(&pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: blocks[0].Bytes,
    })

    key, err := jwt.ParseRSAPrivateKeyFromPEM(pemData)
    if err != nil {
        return nil, err
    }

    return key, nil
}

推送使用创建Pass时的同一证书签名,我怀疑Pass Type Identifier的配置位置有误导致功能失效,但不确定正确位置。求有Apple Pass后端更新实现经验的开发者解答。

内容的提问来源于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.19 21:48:20