App Store内部购买产品总结--Java版(三)

        接着前面的单次购买和订阅支付,接受app store回调的时候,发现沙盒环境有些时候回调速度比较慢,然后需要做出一些优化,如果按照第二章的逻辑的话,本应该是后端接口监听订阅到账的状态,但是这样做,可能用户体验感不是特别好,购买以后无法及时查看自己购买的产品,我们这边做出的优化是客户手动刷新状态时候,可以发送对应的请求去获取app store 最新的订阅状态,参考文档如下

Apple Developer DocumentationApp Store内部购买产品总结--Java版(三)https://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses这里就要补充一点了,首先请求的参数是originalTransactionId,这个参数叫做原始事务id,是每次交易后都会独自生成一份的,所以需要切记一点,在购买的时候,需要把originalTransactionId和我们对应的订单id和购买人关联起来,后面会用到这个关系。

按照文档的说法,直接去访问这个请求,大概率会出现401未验证的提示,这个时候我们需要在请求头上面携带一个token,这个token 由app store 所规定的jwt去生成,如果不知道jwt是什么的话,可以自行百度一下。

   App Store内部购买产品总结--Java版(三)

 下面主要讲一下怎么获取对应的token

参考文档如下
Apple Developer DocumentationApp Store内部购买产品总结--Java版(三)https://developer.apple.com/documentation/appstoreserverapi/generating_tokens_for_api_requests  注意header和payload 这两个参数错一个,生成的token去访问都会出现401

 这里贴一下生成app store token的代码

package com.yxzq.payment.utils;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;

/**
 * @description: IOS 生成对应的token
 * @author lijian
 * @date 2021/11/29 14:47
 * @version 1.0
 */
public class IosTokenUtils {

  public static String getToken() {

    Map<String, Object> header = new HashMap<>();

    header.put("alg", "ES256");
    header.put("kid", "xxx");
    header.put("typ", "JWT");

    Map<String, Object> claim = new HashMap<>();
    claim.put("iss", "xxx");
    claim.put("iat", Math.floor(System.currentTimeMillis() / 1000));
    //claim.put("exp", DateUtil.addTime(currentDate,DateUtil.MINUTE,60).getTime());
    claim.put("exp", Math.floor(System.currentTimeMillis() / 1000) + 1800);
    claim.put("aud", "appstoreconnect-v1");
    claim.put("nonce", UUID.randomUUID());
    claim.put("bid", "xxxx");
    PrivateKey privateKey = getECPrivateKey();
    try {
      JwtBuilder jwtBuilder = Jwts.builder().setHeader(header).setClaims(claim)
          .signWith(SignatureAlgorithm.ES256, privateKey);

      String token = jwtBuilder.compact();
      return token;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return  "";
  }

  /**
   * 获取PrivateKey对象
   *
   * @return
   */
  private static PrivateKey getECPrivateKey() {
    try {
      PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
          Base64.decodeBase64("xxxxx"));
      KeyFactory keyFactory = KeyFactory.getInstance("EC");
      return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
}

   这里需要注意几个点

1. Base64.decodeBase64 里面所解析的就是p8文件里面的内容,千万不要把begin和end带上去,也不需要用openssl去解析

2.exp的时间不能乱动,会有问题

3.KeyFactory获取的实例叫做EC,对应的就是ES256。

生成好的token附带在请求头上即可

 ResponseEntity<SubscriptionStatusResp> responseEntity = restOperations.exchange(
          "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/subscriptions/"
              + originalTransactionId,
          HttpMethod.GET, entity, SubscriptionStatusResp.class);
      List<SubscriptionGroupIdentifierItem> data = responseEntity.getBody().getData();

这是沙盒环境,正式环境换一下请求头。

然后就能访问到相应参数了,其中1代表订阅活跃中,意味着用户已经完成订阅动作,业务方可以发放产品了

上一篇:工作中使用的git提交规范


下一篇:使用Jsoup爬取网络请求的方法(java,post,get,代理IP)