iOS 内购 的若干问题 - V2EX
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 9 天前 2599 次点击

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

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

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

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

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


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


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

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

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

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


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


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

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

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


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

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

    得传 36 位的 UUID 格式字符串

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

    2 、我们公司现在是两个版本 StoreKit 同时使用,支持 StoreKit2 的优先使用,这也不是很复杂的事情,毕竟接过 iOS IAP 的都知道,StoreKit2 能通过订单号关联票据信息解决掉单问题是这么重要。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     907 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 20:18 PVG 04:18 LAX 13:18 JFK 16:18
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86