V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
seth19960929
V2EX  ›  问与答

像微信支付一样处理苹果支付服务端回调

  •  
  •   seth19960929 · 59 天前 · 349 次点击
    这是一个创建于 59 天前的主题,其中的信息可能已经有所发展或是发生改变。

    客户端请使用StoreKit2, 服务端通知

    数据库设计(省略不重要字段)

    # 订单表
    mysql> desc orders;
    +------------------+---------------------+------+-----+---------+----------------+
    | Field            | Type                | Null | Key | Default | Extra          |
    +------------------+---------------------+------+-----+---------+----------------+
    | id               | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
    | no               | varchar(255)        | NO   | UNI | NULL    |                |
    | trade_no         | varchar(255)        | YES  |     | NULL    |                |
    | status           | tinyint(4)          | NO   |     | NULL    |                |
    | origin_no        | varchar(255)        | YES  |     | NULL    |                |
    +------------------+---------------------+------+-----+---------+----------------+
    
    # 用户签约表
    mysql> desc member_contracts;
    +-----------------+---------------------+------+-----+---------+-------+
    | Field           | Type                | Null | Key | Default | Extra |
    +-----------------+---------------------+------+-----+---------+-------+
    | user_id         | bigint(20) unsigned | NO   | PRI | NULL    |       |
    | status          | tinyint(4)          | NO   |     | NULL    |       |
    | contract_id     | varchar(255)        | NO   | MUL | NULL    |       |
    +-----------------+---------------------+------+-----+---------+-------+
    

    服务端处理逻辑

    • 订阅到期重新订阅appAccountToken,originalTransactionId会变(订阅退订, 然后超过有效期后)
    • 订阅期内取消订阅, 然后又重新订阅appAccountToken,originalTransactionId不变

    客户端下单

    ## 客户端请求创建订单接口生成订单号`$uuid`, 返回给客户端放入`appAccountToken`
    INSERT INTO orders(no) VALUES('$uuid');
    

    服务端事件回调-用户订阅

    • 苹果服务端notificationV2回调处理 && 客户端回调处理 (一个JWS字符串)
    ## 解析服务端回调, 确认`notificationType=SUBSCRIBED`且二级事件`subtype in INITIAL_BUY RESUBSCRIBE AUTO_RENEW_ENABLED`(根据自己要求处理事件, 验证商品`ID`和包名等是否符合条件)
    ## 重要的字段: appAccountToken,transactionId,originalTransactionId,expiresDate
    ## 1. 查询 appAccountToken 订单是否存在
    select * from orders where no='{$appAccountToken}' limit 1 => $id;
    ## 2. 修改订单状态信息, 包括已付款
    update orders set trace_no='$transactionId', origin_no='$originalTransactionId' where id=$id;
    ## 3. 修改用户订阅状态
    update member_contracts set contract_id='$originalTransactionId';
    ## ... 赠送会员等等等
    

    服务端事件回调-用户订阅

    ## notificationType=DID_RENEW
    ## 1. 通过`transactionId`查询订单是否存在, 如果存在直接返回已经通知过
    select * from orders where trade_no='{$transactionId}' limit 1 => $id
    ## 2. 如果不存在, 那么通过 originalTransactionId 查询到订阅表, 得到订阅的创建订单信息;
    select * from member_contracts where contract_id='$originalTransactionId' limit 1;
    ## 3. 通过订阅表信息直接插入一条已付款的订单信息
    INSERT INTO orders(no, trade_no, origin_no) VALUES('$uuid', '$transactionId', '$originalTransactionId');
    ## ... 赠送会员等等等
    
    

    退订事件

    update member_contracts set status=xxx where contract_id='$originalTransactionId';
    

    客户端回调

    • 客户端回调得到的JWS字符串解析出来的字段 === 苹果服务端回调的字段.Data.SignedTransactionInfo
    • 所以客户端把凭证串发给服务端, 服务端抽象出订阅事件的代码, 在苹果服务端回调和客户端验单共用就行
    // 获取商品信息
    products = try await Product.products(for: Set(productIds))
    
    // 支付,先请求服务端创建订单接口获得 uuid
    let uuid = Product.PurchaseOption.appAccountToken(UUID.init(uuidString: "$uuid")!)
    let result = try await product.purchase(options: [uuid])
    
    //处理支付结果,此时苹果内部已经进行了 JWS 校验
    switch result {
        case .success(let verificationResult):
            // 把 verificationResult 发给服务端去校验, 防止苹果服务端回调慢
            // 把 verificationResult 发给服务端去校验, 防止苹果服务端回调慢
            // 把 verificationResult 发给服务端去校验, 防止苹果服务端回调慢
        case .userCancelled:
            //处理取消
        case .pending:
            //交易可能会在未来成功,通过 Transaction.updates 进行通知。
    }
    

    更多事件请按需处理

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1262 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 23:13 · PVG 07:13 · LAX 16:13 · JFK 19:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.