package xxxxx;
public enum IosStatusCodeEnum {
CODE_SUCCESS("0", "收据是有效, 验证成功"),
CODE_NULL("-1", "苹果服务器没有返回验证结果"),
CODE_21000("21000", "没有使用HTTP POST请求方法向App Store发出请求"),
CODE_21001("21001", "这个状态码不再由App Store发送"),
CODE_21002("21002", "receipt-data属性中的数据格式错误或服务遇到临时问题"),
CODE_21003("21003", "这张收据无法证实真伪"),
CODE_21004("21004", "您提供的共享秘密与您的帐户文件中的共享秘密不匹配"),
CODE_21005("21005", "收据服务器暂时无法提供收据"),
CODE_21006("21006", "订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中"),
CODE_21007("21007", "这个收据来自测试环境,但是它被发送到生产环境进行验证"),
CODE_21008("21008", "这个收据来自生产环境,但是它被发送到测试环境进行验证"),
CODE_21009("21009", "内部数据访问错误"),
CODE_21010("21010", "用户帐户找不到或已被删除");
public String code;
public String desc;
IosStatusCodeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static IosStatusCodeEnum getByCode(String code) {
for (IosStatusCodeEnum value : values()) {
if(value.code.equals(code)) return value;
}
return null;
}
}
package xxxxx;
import lombok.Data;
@Data
public class IosVerifyDTO {
private String transaction_id;
private String original_purchase_date;
private String quantity;
private String original_transaction_id;
private String purchase_date_pst;
private String original_purchase_date_ms;
private String purchase_date_ms;
private String product_id;
private String original_purchase_date_pst;
private String is_trial_period;
private String purchase_date;
}
package xxxxx;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import xxxxx.IosStatusCodeEnum;
import xxxxx.ServiceException;
import xxxxx.EnvironmentUtil;
import xxxxx.JsonUtils;
import xxxxx.IosVerifyDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.net.ssl.*;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.Locale;
import java.util.Optional;
@Service
public class IosVerifyService {
private static final String URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";
private static final String URL_VERIFY = "https://buy.itunes.apple.com/verifyReceipt";
private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
public IosVerifyDTO applePayVerify(String receipt, String transactionId) {
IosVerifyDTO iosVerifyDTO = null;
String url;
if(EnvironmentUtil.isProd()){
url = URL_VERIFY;
}else {
url = URL_SANDBOX;
}
String verifyResult = buyAppVerify(receipt, url);
JSONObject resultJob = JSONObject.parseObject(verifyResult);
String status = resultJob.getString("status");
if (IosStatusCodeEnum.CODE_21007.code.equals(status)) {
verifyResult = buyAppVerify(receipt, URL_SANDBOX);
resultJob = JSONObject.parseObject(verifyResult);
status = resultJob.getString("status");
}
if (!IosStatusCodeEnum.CODE_SUCCESS.code.equals(status)) {
log.error("【苹果服务器验证失败】返回验证结果。 transactionId: {}, status: {}, msg: {}", transactionId, status, IosStatusCodeEnum.getByCode(status).desc);
return iosVerifyDTO;
}
String resultReceipt = resultJob.getString("receipt");
JSONObject receiptJson = JSONObject.parseObject(resultReceipt);
String in_app = receiptJson.getString("in_app");
if(StringUtils.isEmpty(in_app)) {
log.error("【苹果服务器验证失败】未查询到收据信息。 transactionId: {}, status: {}, msg: {}", transactionId, status, IosStatusCodeEnum.getByCode(status).desc);
return iosVerifyDTO;
}
JSONArray in_app_array = JSONArray.parseArray(in_app);
Optional<Object> optional = in_app_array.stream().filter(r ->
(JsonUtils.fromJson(r.toString(), IosVerifyDTO.class).getTransaction_id().equals(transactionId))).findFirst();
if(optional.isPresent()) iosVerifyDTO = JsonUtils.fromJson(optional.get().toString(), IosVerifyDTO.class);
return iosVerifyDTO;
}
private String buyAppVerify(String receipt, String url) {
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
URL console = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type","application/json");
conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
conn.setDoInput(true);
conn.setDoOutput(true);
BufferedOutputStream hurlBufOus = new BufferedOutputStream(conn.getOutputStream());
String str = String.format(Locale.CHINA, "{\"receipt-data\":\"" + receipt + "\"}");
hurlBufOus.write(str.getBytes());
hurlBufOus.flush();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer sb = new StringBuffer();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (Exception e) {
log.error("苹果服务器异常 errMsg: {}", e);
throw new ServiceException("苹果服务器异常");
}
}
}
String verifyResult = buyAppVerify(receipt, URL_VERIFY);
JSONObject resultJob = JSONObject.parseObject(verifyResult);
String status = resultJob.getString("status");
if (IosStatusCodeEnum.CODE_21007.code.equals(status)) {
verifyResult = buyAppVerify(receipt, URL_SANDBOX);
resultJob = JSONObject.parseObject(verifyResult);
status = resultJob.getString("status");
}