Then, in the previous single purchase and subscription payment, when accepting the callback of the app store, it is found that the callback speed of the sandbox environment is sometimes slow, and then some optimization needs to be made. If the logic in Chapter 2 was followed, the backend interface should have monitored the subscription receipt status, but in doing so, the user experience may not be particularly good, We can't view the products we purchased in time after purchase. Our optimization is that when customers manually refresh the status, they can send corresponding requests to obtain the latest subscription status of the app store. The reference documents are as follows
Apple Developer Documentationhttps://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses Here we need to add a little. First, the requested parameter is originalTransactionId. This parameter is called the original transaction id, which will be generated independently after each transaction. Therefore, it should be remembered that during purchase, we need to associate the originalTransactionId with our corresponding order id and the buyer, and this relationship will be used later.
According to the document, if you directly access the request, there will probably be a 401 unverified prompt. At this time, we need to carry a token on the request header. This token is generated by the jwt specified in the app store. If you don't know what jwt is, you can baidu it yourself.
Here's how to get the corresponding token
Reference documents are as follows
Apple Developer Documentationhttps://developer.apple.com/documentation/appstoreserverapi/generating_tokens_for_api_requests Note that if the header and payload parameters are wrong, the generated token will be accessed
Here is the code for generating 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 Generate corresponding 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 ""; } /** * Get PrivateKey object * * @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; } }
Here are a few points to note
one Base64.decodeBase64 parses the contents of the p8 file. Do not bring begin and end, and do not use openssl to parse
2.exp's time can't move randomly, there will be problems
3. The instance obtained by keyfactory is called EC, which corresponds to ES256.
The generated token can be attached to the request header
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();
This is the sandbox environment. Change the request header in the formal environment.
Then you can access the corresponding parameters, where 1 represents that the subscription is active, which means that the user has completed the subscription action, and the business party can distribute the products