JAVA服务端对接AppStoreAPI接口全流程分析

背景是需要调用appStore的历史订单查询接口进行数据统计对比,之前没有做过相关的对接,这次写出来记录一下。

有一说一,苹果的官方文档确实是非常详尽,特别是教你如果操作生成秘钥那边,可以说是保姆级教学了,不过很多的文档都是英文的,相对来说看起来比较容易遗漏重点,今天就用查询历史订单这边的逻辑给给大家说下对接的流程以及各种注意事项。

  1. 首先我们先找到我们需要调用的接口的苹果官方文档,例如我这次需要调用的就是查询历史订单接口,参考文档为: 苹果官方文档
  2. 在这个文档中记录着这个接口的url、请求参数、返回码,注意下响应体是不在这边记录的,需要在最下面返回体的部分点击跳转到另一个页面查看。例如Get Transaction History这个接口的url是https://api.storekit.itunes.apple.com/inApps/v2/history/{transactionId},其中的transactionId是用户的任何订单号(注意下这边如果传入了无效的transactionId,会直接报404),其他的参数是通过Query Parameters传的,这里有个注意点,有一些传参,苹果文档中会加一个中括号,这表示可以多传,多传的方式时在Query Parameters里传多次。例如https://api.storekit.itunes.apple.com/inApps/v2/history/{transactionId}?productType=AUTO_RENEWABLE&productType=NON_RENEWABLE。传参中的sort适用于时间排序,不传表示正序,即从远到近
  3. 调用时不仅需要传过滤参数和transactionId,header里还需要放AuthorizationAuthorization的规则是Bearer [signed token],传参方式可以参考curl -v -H 'Authorization: Bearer [signed token]' "https://api.storekit.itunes.apple.com/inApps/v2/history/{transactionId}",这里最终要的就是signed token
  4. 首先我们需要按照指引生成一个私钥,这里需要登录苹果开发者账户的管理账号,具体的生成规则是傻瓜式的,可以参考文档 生成私钥在这里插入图片描述
  5. 生成完毕以后需要保存三个值,第一个是在页面上面的ISSUER_ID(有些文档上也会携程TEAM_ID),第二个是生成秘钥的ID(一般在秘钥文件上也会有),第三个是秘钥文件(一个.p8的文件)
  6. 下面是用java实现的生成jwt的流程`
    private static final String ISSUER_ID = "57246542-96fe-1a63e053-0824d011072a"; // 替换为你的ISSUER_ID
    private static final String KEY_ID = "D123456ASDF"; // 替换为你的Key ID
    private static final String PRIVATE_KEY_PATH = "/SubscriptionKey_D123456ASDF.p8"; // 替换为你的私钥文件路径

private static String generateJWT(String issuerId, String keyId, String privateKeyFilePath) throws Exception {
        PrivateKey privateKey = parsePEMPrivateKey(privateKeyFilePath);
        // 设置JWT的负载
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        // 设置JWT的过期时间(这里设置为10分钟)
        Date expiration = new Date(nowMillis + 1000 * 60 * 50);
        // 创建一个JWT Builder
        Map<String, Object> payLoad = new HashMap<>();
        payLoad.put("iss", issuerId);
        payLoad.put("iat", now.getTime()/1000);//生成时间
        payLoad.put("exp", expiration.getTime()/1000);//失效时间不能超过开始时间60分钟
        payLoad.put("aud", "appstoreconnect-v1");//固定传参
        payLoad.put("bid", "cloud.example");//这里设置你的应用ID(bundleId)
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "ES256");//签名方式,固定传参
        header.put("kid", keyId);//秘钥ID
        header.put("type", "JWT");//固定传参
        JwtBuilder builder = Jwts.builder()
                .setClaims(payLoad)
                .setHeader(header)
                .signWith(privateKey, SignatureAlgorithm.ES256);
        // 生成并返回JWT
        return builder.compact();
    }`

public static PrivateKey parsePEMPrivateKey(String privateKeyFilePath) throws Exception {
        FileReader fileReader = new FileReader(privateKeyFilePath);
        PEMParser pemParser = new PEMParser(fileReader);
        Object pemObject = pemParser.readObject();

        if (pemObject instanceof PEMKeyPair) {
            PEMKeyPair pemKeyPair = (PEMKeyPair) pemObject;
            byte[] privateKeyInfoBytes = pemKeyPair.getPrivateKeyInfo().getEncoded();
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyInfoBytes);
            return keyFactory.generatePrivate(keySpec);
        } else if (pemObject instanceof PrivateKeyInfo) {
            PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemObject;
            byte[] privateKeyInfoBytes = privateKeyInfo.getEncoded();
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyInfoBytes);
            return keyFactory.generatePrivate(keySpec);
        }

        throw new IllegalArgumentException("Unsupported PEM object: " + pemObject.getClass().getName());
    }
  1. 需要引入以下pom依赖
        <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> <!-- or jjwt-gson if Gson is preferred -->
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
  1. 到这里就结束了?nonono,还有最重要的解析苹果的响应,这里不同接口会有不同的返回参数,类似我这边调用的接口里,返回值signedTransactions是最重要的,这里面存放了订单列表,但并非明文传输,而是以JSON Web Signature (JWS)的格式传递,这种格式的参数是有规律的,是一个以.链接的三段字符串,第一段为header,第二段为payload,第三段为签名,与传参的JWT一致,我们所需要的一般是payload部分,因此只需要把payload解析出来然后用base64解码即可。
            String payloadCode = StrUtil.split(jws, '.').get(1);
            String decodedString = new String(Base64.getDecoder().decode(payloadCode));

写到这里就算结束啦,苹果还提供了SDK的接入方式,不过最低版本为java11,大家看各自的需要吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值