简介:
苹果支付是直接由ios客户端调起苹果支付并支付完成后,java后台提供一个支付回调接口供ios客户端进行同步回调,只需要在该接口进行进行验证苹果支付是否支付成功,跟微信支付和支付宝支付不一样,不需要统一下单接口,不需要配置异步回调接口。
base64传输遇到的坑:从ios传来的base64字符串+会变成[+]可能还会+变空格,所以接收到的字符串需要替换成+
package cn.network.pay.apple;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.log4j.Log4j2;
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.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Locale;
/**
* 苹果IAP内购验证工具类
* Created by wangqichang on 2019/2/26.
*/
@Log4j2
public class IosVerifyUtil {
private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
private static final String url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
private static final String url_verify = "https://buy.itunes.apple.com/verifyReceipt";
/**
* 苹果服务器验证
*
* @param receipt
* 账单
* @url 要验证的地址
* @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
*
*/
public static String buyAppVerify(String receipt,String type) {
//环境判断 线上/开发环境用不同的请求链接
String url = "";
if("test".equals(type)){
url = url_sandbox; //沙盒测试
}else{
url = url_verify; //线上测试
}
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", "text/json");
conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
conn.setDoInput(true);
conn.setDoOutput(true);
BufferedOutputStream hurlBufOus = new BufferedOutputStream(conn.getOutputStream());
receipt = receipt.replace(" ", "+");
receipt = receipt.replace("[+]", "+");
log.info("receipt:"+receipt);
JSONObject obj = new JSONObject();
obj.put("receipt-data", receipt);
log.info("拼接格式传给苹果平台:"+obj);
// String str = String.format(Locale.CHINA, "{\"receipt-data\":\"" + receipt + "\"}");//拼成固定的格式传给平台
// hurlBufOus.write(str.getBytes());
hurlBufOus.write(obj.toString().getBytes());
hurlBufOus.flush();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (Exception ex) {
System.out.println("苹果服务器异常");
ex.printStackTrace();
}
return null;
}
/**
* 用BASE64加密
*
* @param str
* @return
*/
public static String getBASE64(String str) {
byte[] b = str.getBytes();
String s = null;
if (b != null) {
s = new sun.misc.BASE64Encoder().encode(b);
}
return s;
}
}
package cn.network.pay.apple;
import cn.network.exception.ValidataException;
import cn.network.pay.exception.PayException;
import cn.network.pay.model.CallBack;
import cn.network.util.ParamValidation;
import cn.network.util.StringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@Log4j2
public class ApplePayAPi {
@Value("${spring.profiles.active}")
private String applePayType;
/**
* 苹果内购校验
* @return
*/
public void applePayCallBack(HttpServletRequest request, HttpServletResponse response, CallBack callBack) throws Exception {
AppleModel appleModel=new AppleModel();
appleModel.setPayload(request.getParameter("payload"));
appleModel.setTransactionId(request.getParameter("transactionId"));
log.info("苹果内购校验开始》》》");
log.info("交易ID》》》" + appleModel.getTransactionId());
log.info(" base64校验体》》》" + appleModel.getPayload());
//验证
String verifyResult = IosVerifyUtil.buyAppVerify(appleModel.getPayload(), applePayType);
log.info("苹果平台返回JSON》》》" + verifyResult);
ParamValidation.isNotNull(verifyResult,"苹果内购无数据返回");
JSONObject appleReturn= JSONObject.parseObject(verifyResult);;
String states = appleReturn.getString("status");
log.info("states》》》" + states);
log.info("appleReturn》》》" + appleReturn);
//验证状态码
viCodeState(states);
String receipt = appleReturn.getString("receipt");
JSONObject returnJson = JSONObject.parseObject(receipt);
String inApp = returnJson.getString("in_app");
List<HashMap> inApps = JSONObject.parseArray(inApp, HashMap.class);
if (!CollectionUtils.isEmpty(inApps)) {
ArrayList<String> transactionIds = new ArrayList<String>();
for (HashMap app : inApps) {
transactionIds.add((String) app.get("transaction_id"));
}
//交易列表包含当前交易,则认为交易成功
if (transactionIds.contains(appleModel.getTransactionId())) {
//处理业务逻辑
Map<String,String> map=new HashMap<>();
map.put("transactionId:",appleModel.getTransactionId());
callBack.callBack(map);
return ;
}
throw new ValidataException("当前交易不在交易列表中");
}
}
private void viCodeState(String states){
if (StringUtils.isNotBlank(AppleCodeConstant.getValueBykey(states))) {
throw new ValidataException(AppleCodeConstant.getValueBykey(states));
}
// 前端所提供的收据是有效的
if (!StringUtil.isEquals(states,"0")) {
throw new ValidataException("支付失败,错误码"+states);
}
}
}