V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
chouqiu
V2EX  ›  iDev

iOS 内购 的若干问题

  •  
  •   chouqiu · 1 天前 · 1849 次点击

    现在是在 App Store Connect 后台开启了 App Store 服务器通知

    填写了 生产环境服务器 URL沙盒环境服务器 URL 两个回调地址

    生产环境服务器 URL: https://prod.xxx.com/iapNotify

    沙盒环境服务器 URL: https://test.xxx.com/iapNotify

    在开发过程中,测试同事使用 TestFlight 安装了最新版本的 app

    当客户端完成支付时,沙盒环境服务器 URL 会收到苹果服务器通知,一个是 JWT 字符串

    signedPayload解出来是下面这样的:

    {
      "notificationType": "ONE_TIME_CHARGE",
      "notificationUUID": "d03b3bae-a63c-4fd3-ad4f-69e52fb966fd",
      "data": {
        "appAppleId": 6751180999,
        "bundleId": "xx.yy.zz",
        "bundleVersion": "62",
        "environment": "Sandbox",
        "signedTransactionInfo": "eyJhbG..."
      },
      "version": "2.0",
      "signedDate": 1759197475796
    }
    

    再把signedTransactionInfo解出来是这样的:

    {
      "transactionId": "2000001023950888",
      "originalTransactionId": "2000001023950888",
      "bundleId": "xx.yy.zz",
      "productId": "xx.yy.zz.product_01",
      "purchaseDate": 1759197470000,
      "originalPurchaseDate": 1759197470000,
      "quantity": 1,
      "type": "Consumable",
      "appAccountToken": "856bb959-ae22-4711-2005-af69286aceed",
      "inAppOwnershipType": "PURCHASED",
      "signedDate": 1759197475796,
      "environment": "Sandbox",
      "transactionReason": "PURCHASE",
      "storefront": "CHN",
      "storefrontId": "143465",
      "price": 6000,
      "currency": "CNY",
      "appTransactionId": "704888381761368888"
    }
    
    

    appAccountToken 是我们的订单唯一标识

    问题:

    1.怎么确认这个订单已完成支付

    是只要有这个回调就认为是支付成功了吗?

    2.如果订单退款是怎么处理

    沙盒环境好像不能测试内购的退款流程,如果上线了退款时,也会有这种服务器通知吗

    3.现在要提审 app ,更换了生产环境接口 https://prod.xxx.com ,在 TestFlight 里更新到最新版

    支付完成后,App Store 的服务器通知还是会请求到 沙盒环境服务器 https://test.xxx.com ,这样订单标识就对不上了。那 App Store 的审核人员审核内购时会审核不通过吧。

    首次对接 iOS 内购,问题有点多,希望各位大佬赐教

    25 条回复    2025-09-30 18:51:39 +08:00
    ccyq1994
        1
    ccyq1994  
       1 天前
    只有在 App 过审之后的订单才会是真是的环境,其他的都是走的沙盒。
    ccyq1994
        2
    ccyq1994  
       1 天前
    有一个通知回调的地址,需要你们填写在 App Store Connect 后台。当发生退款后,苹果会调用你们填写的地址进行通知
    ccyq1994
        3
    ccyq1994  
       1 天前
    沙盒想测试退款有点困难,也有点复杂,但是还是可以做到的,需要在沙盒创建一个订阅类型的商品,然后让你们的 iOS 工程师参考苹果的文档去写一个支付跟取消的功能,然后你再在通知中去看,你最好是在回调的地方打个 log
    chouqiu
        4
    chouqiu  
    OP
       1 天前   ❤️ 1
    @ccyq1994 #2

    审核时,支付成功后回调也请求到沙盒环境服务器 URL ,这样怎么做支付确认呢

    审核时把 App Store Connect 后台的沙盒环境服务器 URL 改成生产环境的服务器 URL 吗
    ccyq1994
        5
    ccyq1994  
       1 天前
    @chouqiu 你验证的时候不是有一个状态的吗?那个状态可以可以判断是不是成功。你可以先用生产环境去验证,如果失败的话,会给你报一个是沙盒环境的状态码,再根据那个状态码去沙盒环境验证。
    amarantin1
        6
    amarantin1  
       1 天前
    https://juejin.cn/post/7551258620349431854

    这个文章看起来描述的挺清晰,我正在看

    错误处理:如果使用生产环境 URL 调用此 API 返回错误码 4040010 ( TransactionIdNotFoundError ),表明该交易 ID 在生产环境中不存在,应尝试使用沙盒环境 URL 再次调用 。
    whoisjohnlee
        7
    whoisjohnlee  
       1 天前
    Store Kit2 支持退款通知测试,让客户端的人仔细看下文档
    chouqiu
        8
    chouqiu  
    OP
       1 天前   ❤️ 1
    @ccyq1994 #5
    审核时,审核人员购买成功后,服务器通知请求到了沙盒环境服务器 URL ( https://test.xxx.com/iapNotify


    @amarantin1 #6
    谢谢,正在看,确实很详细


    不过,好像提审时,还是得把 沙盒环境服务器 URL 改成 生产环境服务器 URL
    jonssonyan
        9
    jonssonyan  
       1 天前
    https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
    appstore v2 的官方文档写的很清楚
    1. 收到 ONE_TIME_CHARGE 就是购买成功
    2. 退款的 notificationType 是 REFUND
    3. 回调都配置同一个地址,数据表多加一个字段区分环境,signedTransactionInfo 的 environment:Sandbox 沙箱 / Production 生产
    iSteven
        10
    iSteven  
       1 天前
    放宽心,审核人员是不会真的内购的,能弹出支付面板估计就当该功能通过审核了,不会真的走后面的支付流程。
    weofuh
        11
    weofuh  
       1 天前
    多看两遍官方文档就没问题了: https://developer.apple.com/documentation/appstoreservernotifications/app-store-server-notifications-v2

    里面有很多种通知类型:普通内购成功、首次订阅成功、续费订阅成功、退款、测试...

    (退款的我也没亲自测试过,之前的测试工具方法都不可以用了,但好像能让你们开发从 Xcode 上发起)

    通过 TestFlight 安装的,发生的交易都默认就是 Sandbox 环境,不会真正的扣款,交易通知自然也就发送到你们的测试环境了。
    苹果审核不会给你们测试整个内购流程的,他们只管你们的内购符不符合他们的规范。至于交易消息通知处理,你爱处理不处理,哈哈哈。
    chouqiu
        12
    chouqiu  
    OP
       1 天前   ❤️ 1
    @jonssonyan #9
    @weofuh #11
    了解了,谢谢


    @iSteven #10
    是这样吗,现在已经做了兼容了


    目前把沙盒环境服务器 URL 和生产环境服务器 URL 改成一样的了
    ryh
        13
    ryh  
       1 天前
    13 天 前有 v2 网友 @alioth0909 发了个 https://v2ex.com/t/1159685 “[开源] iOS 开发者的防退款神器” 估计你们能从中学到关于推送的
    wogogoing
        14
    wogogoing  
    PRO
       1 天前
    很多年前跟朋友一起接过内购,感觉流程很神奇:

    https://blog.keepchen.com/a/apple-internal-purchase-data-validation.html
    chouqiu
        15
    chouqiu  
    OP
       1 天前   ❤️ 1
    @wogogoing #14
    现在有服务器通知了

    客户端是使用 flutter 开发的,拿到的 Receipt 也是一个 JWS 字符串,解析出来的数据,和这篇文章里的不一样。


    @ryh #13
    谢谢,得空学习学习
    wogogoing
        16
    wogogoing  
    PRO
       1 天前
    @chouqiu 了解了,感谢。
    mbtfdwlx
        17
    mbtfdwlx  
       1 天前
    请教一下 appAccountToken 是怎么赋值的,看了下没找到相关的 api 接口
    chouqiu
        18
    chouqiu  
    OP
       1 天前   ❤️ 1
    @mbtfdwlx #17

    客户端拉支付时传递的 applicationUserName
    mbtfdwlx
        19
    mbtfdwlx  
       1 天前
    @mbtfdwlx 找到问题了,我是 oc 的代码,storekit2 不支持 oc
    mbtfdwlx
        20
    mbtfdwlx  
       1 天前
    @chouqiu #18 我刚试了下,服务拿到的数据解出来不包括 appAccountToken 字段,可能是我 oc 代码的原因么,我再看看 多谢
    chouqiu
        21
    chouqiu  
    OP
       1 天前   ❤️ 1
    @mbtfdwlx #20

    得传 36 位的 UUID 格式字符串

    这种:
    856bb959-ae22-4711-2005-af69286aceed
    mbtfdwlx
        22
    mbtfdwlx  
       1 天前
    @chouqiu #21 多谢老哥,解决了困扰我几天的一个问题,多谢
    alioth0909
        23
    alioth0909  
       1 天前
    其它问题网友们都回答了。提醒一点,上线后,记得处理下 CONSUMPTION_REQUEST 的通知( production 环境),这是用户发起退款后,Apple 跟开发者“征求意见”的关键一步,不处理的话,Apple 会倾向于允许退款。可以自己处理,也可以用这个开源项目 https://github.com/argus-sight/refund-swatter-lite
    xz410236056
        24
    xz410236056  
       1 天前
    @whoisjohnlee storeKit2 最低 iOS15+ swift ,就老中这现在还在 iOS10 、iOS12 开发的,甚至一帮遗老连 swift 都不想学,你还想用 storekit2 ? 想太多了吧
    whoisjohnlee
        25
    whoisjohnlee  
       1 天前
    @xz410236056
    1 、demo 里加个退款的按钮给服务器做退款通知测试,总比线上出事故强吧?

    2 、我们公司现在是两个版本 StoreKit 同时使用,支持 StoreKit2 的优先使用,这也不是很复杂的事情,毕竟接过 iOS IAP 的都知道,StoreKit2 能通过订单号关联票据信息解决掉单问题是这么重要。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2103 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 15:08 · PVG 23:08 · LAX 08:08 · JFK 11:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.