苹果 服务通知 V2

    在iOS开发中如果涉及到虚拟物品购买,就需要使用iap服务。iap支付确实很蛋疼,商品分类多:消耗品、非消耗品、自动订阅商品,非续订商品。像非消耗品需要支持访客模式购买,还需要支持恢复的功能,就是用户换了手机在没有登录(app自己的账号)的情况下也可以恢复之前购买的商品(相同icloud ID),确实给开发带来不少的额外工作,你还必须得支持,不然审核不给过。这还是正向流程,像逆向流程对接更是一言难尽。这里分享下在对接苹果通知(退款、订阅状态变更等消息)过程中一些经验,苹果后台可以配置接收通知的url 分沙盒和线上两个url。
    苹果支付通知v2跟v1区别还是很大的。首先是流程上 v1在收到通知后需要再次调用苹果的http接口校验票据的合法性,而v2版本是通过jws(JSON Web Signature)验证其合法性,不需要再次调用苹果服务,直接公钥验签就行。再次是数据结构上的不同,v1通知 http body里直接给了明文,v2由于是jws ,所以内容本身是不可读的。
通知内容:
通知的大致样子
    signedPayload 就是jws,通过 . 号拼接的,分为三部分,分别是 header,payload,signature。分别 通过new String(Base64.getUrlDecoder().decode("***"))就可以解码看到可读的文本。
header解码:
header
payload解码:
payload解码内容

里面的signedTransactionInfo也是个jws,做同样的操作就可以了。
    获取通知内容不难,关键是怎么验签。因为苹果后台提供下载私钥,开始以为就用这个私钥导出的公钥验签就可以了。为啥会有这个想法,主要还是思维定势。像支付宝、微信的通知都是这么弄的,会给我们提供公钥来验签。但隐约的又觉得不太靠谱,签名的私钥不应该给到外人。后来在 https://jwt.io/ 网站 测试验签的时候,发现把整个jws贴进来就能验签通过,但是我并没有告诉它公钥啊。经过仔细发现,原来它直接使用header里的证书验签的。至此问题得到解决,苹果后台提供下载的私钥其实是在我们请求苹果接口时签名用的。这也有点不合理的地方,私钥是除了自己随都不可以知道的,但是苹果霸道的给我们直接提供私钥下载,而不是给我们提供一个配置公钥的入口。
    结论:苹果支付通知的验签 是通过jwt里面的证书验签的,x5c就是 header解码后的内容,这里使用的是第一个证书。

    public Jws<Claims> verifyJWT(List<String> x5c, String jws){
        try {
            X509Certificate cert = getCert(x5c.get(0));
            if (!cert.getSubjectDN().getName().contains("Apple Inc")){
                logger.info("not apple cert . name = {}", cert.getIssuerX500Principal().getName());
                return null;
            }
            return Jwts.parserBuilder().setSigningKey(cert.getPublicKey()).build().parseClaimsJws(jws);
        }catch (JwtException exc){
            logger.info("jws verify failure.", exc);
            return null;
        } catch (Exception exc){
            logger.info("jws verify error.", exc);
            return null;
        }
    }

    public static X509Certificate getCert(String x5c) throws CertificateException {
        String stripped = x5c.replaceAll("-----BEGIN (.*)-----", "");
        stripped = stripped.replaceAll("-----END (.*)----", "");
        stripped = stripped.replaceAll("\r\n", "");
        stripped = stripped.replaceAll("\n", "");
        stripped.trim();
        byte[] keyBytes = Base64.getDecoder().decode(stripped);
        CertificateFactory fact = CertificateFactory.getInstance("X.509");
        return (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(keyBytes));
    }

jw验签的工具类 maven依赖:

       <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId> 
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值