接着前面的单次购买和订阅支付,接受app store回调的时候,发现沙盒环境有些时候回调速度比较慢,然后需要做出一些优化,如果按照第二章的逻辑的话,本应该是后端接口监听订阅到账的状态,但是这样做,可能用户体验感不是特别好,购买以后无法及时查看自己购买的产品,我们这边做出的优化是客户手动刷新状态时候,可以发送对应的请求去获取app store 最新的订阅状态,参考文档如下
Apple Developer Documentationhttps://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses这里就要补充一点了,首先请求的参数是originalTransactionId,这个参数叫做原始事务id,是每次交易后都会独自生成一份的,所以需要切记一点,在购买的时候,需要把originalTransactionId和我们对应的订单id和购买人关联起来,后面会用到这个关系。
按照文档的说法,直接去访问这个请求,大概率会出现401未验证的提示,这个时候我们需要在请求头上面携带一个token,这个token 由app store 所规定的jwt去生成,如果不知道jwt是什么的话,可以自行百度一下。
下面主要讲一下怎么获取对应的token
参考文档如下
Apple Developer Documentationhttps://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代表订阅活跃中,意味着用户已经完成订阅动作,业务方可以发放产品了