接着前面的单次购买和订阅支付,接受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代表订阅活跃中,意味着用户已经完成订阅动作,业务方可以发放产品了