普通网友 2025-08-14 05:25 采纳率: 98.7%
浏览 1
已采纳

Python对接苹果支付时如何验证收据?

在使用Python对接苹果支付(Apple In-App Purchase)时,验证收据(Receipt)是确保交易真实性的关键步骤。常见的一个技术问题是:**如何通过Python正确调用苹果的验证接口并处理收据验证结果?** 开发人员通常会遇到如下问题:如何构造请求体、如何处理苹果返回的JSON数据、如何判断收据是否有效、如何应对沙盒与生产环境的差异、以及如何安全地处理共享密钥(shared secret)等。此外,苹果官方文档对部分字段的解释较为模糊,例如`latest_receipt_info`和`pending_renewal_info`的解析方式,也常导致困惑。 本文将围绕Python实现苹果支付收据验证的完整流程,解答上述技术问题,并提供可复用的代码示例和最佳实践建议。
  • 写回答

1条回答 默认 最新

  • 白萝卜道士 2025-08-14 05:25
    关注

    使用Python验证苹果支付收据的完整指南

    1. 苹果支付收据验证的基本流程

    苹果支付(In-App Purchase)的收据验证是确保用户真实购买行为的关键步骤。苹果提供了两个验证接口:

    • 生产环境:https://buy.itunes.apple.com/verifyReceipt
    • 沙盒环境:https://sandbox.itunes.apple.com/verifyReceipt

    验证流程如下:

    
    import requests
    import json
    
    def verify_apple_receipt(receipt_data, shared_secret, is_sandbox=False):
        url = "https://sandbox.itunes.apple.com/verifyReceipt" if is_sandbox else "https://buy.itunes.apple.com/verifyReceipt"
        payload = {
            "receipt-data": receipt_data,
            "password": shared_secret
        }
        response = requests.post(url, data=json.dumps(payload))
        return response.json()
        

    2. 构造请求体的关键字段

    请求体必须包含两个核心字段:

    字段名说明
    receipt-data客户端传来的Base64编码的收据数据
    password应用的共享密钥(Shared Secret),用于订阅产品验证

    注意:如果应用没有订阅产品,可以省略 password 字段。

    3. 处理苹果返回的JSON数据

    苹果返回的JSON结构包含多个关键字段:

    
    {
        "status": 0,
        "receipt": {
            "in_app": [
                {
                    "product_id": "com.example.product",
                    "transaction_id": "1000000356891234",
                    "purchase_date_ms": "1612139011000"
                }
            ]
        },
        "latest_receipt_info": [...],
        "pending_renewal_info": [...]
    }
        

    status 为 0 表示验证成功,其他值表示错误。例如:

    • 21002:receipt-data 格式错误
    • 21007:收据来自沙盒环境,但请求发给了生产环境

    4. 判断收据是否有效

    除了检查 status 字段,还需要验证以下内容:

    • 收据中的 product_id 是否与预期一致
    • transaction_id 是否已处理过(防止重复消费)
    • purchase_date_ms 是否在有效期内(如订阅产品)

    示例代码片段:

    
    def is_valid_receipt(data, expected_product_id):
        if data.get("status") != 0:
            return False
        in_app_receipts = data.get("receipt", {}).get("in_app", [])
        for receipt in in_app_receipts:
            if receipt.get("product_id") == expected_product_id:
                return True
        return False
        

    5. 沙盒与生产环境的差异处理

    开发和测试阶段应使用沙盒环境进行验证。当遇到 status 为 21007 时,说明收据来自沙盒,但请求发给了生产接口。此时应切换 URL 重新验证。

    自动切换示例逻辑:

    
    def auto_retry_verify(receipt_data, shared_secret):
        result = verify_apple_receipt(receipt_data, shared_secret, is_sandbox=False)
        if result.get("status") == 21007:
            result = verify_apple_receipt(receipt_data, shared_secret, is_sandbox=True)
        return result
        

    6. 安全处理共享密钥(Shared Secret)

    Shared Secret 是用于订阅产品验证的关键字段,必须安全存储。建议:

    • 不要硬编码在代码中,应使用配置文件或环境变量
    • 在生产环境中使用密钥管理系统(如AWS Secrets Manager)

    获取 Shared Secret 的步骤:

    1. 登录 App Store Connect
    2. 进入【我的App】→【App特定密码】→【Apple ID和密码】→【共享密钥】
    3. 生成或查看现有的 Shared Secret

    7. 解析 latest_receipt_info 和 pending_renewal_info

    这两个字段用于处理订阅产品状态:

    • latest_receipt_info:最新的收据信息,适用于自动续订订阅
    • pending_renewal_info:订阅的续订状态信息,如是否取消、是否自动续订等

    示例解析逻辑:

    
    def parse_latest_receipt_info(data):
        latest_info = data.get("latest_receipt_info", [])
        for receipt in latest_info:
            print(f"Product ID: {receipt.get('product_id')}")
            print(f"Expires Date: {receipt.get('expires_date_ms')}")
        

    8. 完整流程图

    graph TD
    A[客户端提交收据] --> B{是否沙盒环境?}
    B -->|是| C[调用沙盒验证接口]
    B -->|否| D[调用生产验证接口]
    C --> E[处理返回JSON]
    D --> E
    E --> F{status是否为0?}
    F -->|否| G[处理错误码]
    F -->|是| H[解析in_app数据]
    H --> I[验证product_id和transaction_id]
    G --> J[尝试切换环境重新验证]
    J --> K[返回最终结果]
            
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 10月23日
  • 创建了问题 8月14日