官方地址:https://developer.apple.com/apple-pay/sandbox-testing/
简介
初次接触苹果支付,首先要搞懂流程。苹果支付不同于支付宝,微信支付。
比较微信支付与苹果支付的支付流程:
- 微信支付:客户端支付——>微信——>微信提现——>银行账户。
- 如果你不提现,所有的收益都在微信平台;如果提现,微信平台就要收取相关的手续费,具体手续费跟行业不同有差异。这也是微信平台的直接收益。
- 微信支付通过服务端api调用实现。
- 苹果支付:客户端支付——>商户银行账户。
- 它是通过客户端(苹果设备)将用户银行卡上的钱直接转到商户的银行账户上。因为钱没有第三方(苹果)经手,所以,苹果支付没有像支付宝、微信一样有直接收益。为了维护这套服务功能,苹果需要在有支付功能的应用上收取相应的提成。具体收费情况也要根据具体的产品内容有关。如有实物,可能就不需要提成。如软件需要开通会员等虚拟服务,就需要收取近3成的费用。详情去官方文档查询。
- 苹果支付通过苹果设备(iphone等)支付,前端支付,不需要走服务端,但需要在服务端验证支付结果。
支付接口
接口与验证
/**
* 苹果端APP订单支付成功回调
* @param userId 用户ID
* @param receipt 前台传过来的苹果支付收据简码,是base64加密参数。
*/
@RequestMapping(value = "/*****")
public @ResponseBody Map<String,Object> iosPaymentSuccess(HttpServletRequest request,
@RequestParam(name="userId", required=true)int userId,@RequestParam(required=true) String receipt){
Map<String, Object> resultMap =new HashMap<String, Object>();
try {
logger.info("苹果支付:receipt=="+receipt);
//校验用户是否存在
User user = userService.queryUserById(userId);
if (user == null){
resultMap=createResults(ErrorCode.ACCOUNT_USERID_INVALIDURL);
resultMap.put("success", false);
return resultMap;
}
//验证苹果收据是否有效
Map<String,Object> verifyMap = iPayNotify(receipt);
if(!(boolean)verifyMap.get("success")){//验证失败,直接返回失败信息
resultMap = createResults(ErrorCode.IOS_PAY_VERIFY_FAILED);
resultMap.put("success", false);
return resultMap;
}
String verifyResult = (String) verifyMap.get("verifyResult");//苹果返回的收据信息
/*********************此处写验证成功后的逻辑*******************/
//返回处理结果
resultMap = createResults(ErrorCode.API_SUCCESS);
resultMap.put("success",true);
} catch (Exception e) {
resultMap=createResults(ErrorCode.FAILED);
resultMap.put("success",false);
}
return resultMap;
}
/**
* 验证苹果收据是否有效
* @param receipt
* @return
*/
public Map<String,Object> iPayNotify(String receipt) {
Map<String,Object> map = new HashMap<String,Object>();
// 1.先线上测试 发送平台验证
String verifyResult = IosVerifyUtil.buyAppVerify(receipt, 1);
if (verifyResult == null) {// 苹果服务器没有返回验证结果
map.put("success", false);
return map;
} else {// 苹果验证有返回结果
JSONObject job = JSONObject.parseObject(verifyResult);
String states = job.getString("status");
//2、是沙盒环境,验证沙盒测试
if ("21007".equals(states)) {
// 3.再沙盒测试 发送平台验证
verifyResult = IosVerifyUtil.buyAppVerify(receipt, 0);
job = JSONObject.parseObject(verifyResult);
states = job.getString("status");
}
//收据线上验证有效,本地验证
if ("0".equals(states)) {
// 4、查询数据库,看是否是己经验证过的该支付收据
JSONObject verifyResultJson = JSONObject.parseObject(verifyResult);
JSONObject receiptJson = verifyResultJson.getJSONObject("receipt");
JSONArray inApp2 = receiptJson.getJSONArray("in_app");
JSONObject inApp = JSONObject.parseObject(inApp2.getString(0));
String outTradeNo = inApp.getString("transaction_id");//收据唯一标识
int receiptIsExis = orderService.checkTradeNoIsExist(outTradeNo);
if(receiptIsExis != 0){//已经存在的订单收据无效
map.put("success", false);
return map;
}
map.put("verifyResult", verifyResult);
map.put("success", true);
return map;
}else{
map.put("success", false);
return map;
}
}
}
验证工具类:
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;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* @desc: 苹果IAP内购验证工具类
* @author: QDC
* @date: 2021/03/09
*/
public class IosVerifyUtil {
private static class TrustAnyTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
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 账单
* @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
* @url 要验证的地址
*/
public static String buyAppVerify(String receipt, int type) {
//环境判断 线上/开发环境用不同的请求链接
String url = "";
if (type == 0) {
url = url_sandbox; //沙盒测试
} else {
url = url_verify; //线上测试
}
//String url = EnvUtils.isOnline() ?url_verify : url_sandbox;
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("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 = 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;
}
}
ps:附上返回收据的示例。
大超 11:30:52
{
"status": 0,
"environment": "Production",
"receipt": {
"receipt_type": "Production",
"adam_id": 2341443613,
"app_item_id": 2234443613,
"bundle_id": "com.xxxxx.xxxxx",
"application_version": "1",
"download_id": 23456572706673,
"version_external_ident ifier": 821223402,
"receipt_creation_date": "2017-01-25 00:52:37 Etc/GMT",
"receipt_creation_date_ms": "3333897657000",
"receipt_creation_date_pst": "2017-01-25 17:57:37 America/Los_Angeles",
"request_date": "2017-01-26 00:57:38 Etc/GMT",
"request_date_ms": "1445897657000",
"request_date_pst": "2017-05-29 17:57:38 America/Los_Angeles",
"original_purchase_date": "2016-01-25 15:37:18 Etc/GMT",
"original_purchase_ date_ms": "145234568000",
"original_purchase_date_pst": "2016-01-25 07:37:18 America/Los_Angeles",
"original_application_version": "12",
"in_app": [
{
"quantity": "1",
"product_id": "xxxxxxxxx",//留存在苹果应用商店产品id
"transaction_id": "110000290198443",//收据唯一标识
"original_transaction_id": "110000290198443",
"purchase_date": "2017-01-26 00:23:36 Etc/GMT",
"purchase_date_ms": "1496105856000",
"purchase_date_pst": "2017-01-26 00:35:30 America/Los_Angeles",
"original_purchase_date": "2017-01-26 00:57:36 Etc/GMT",
"original_purchase_date_ms": "14347896000",
"original_purchase_date_pst": "2017-01-25 17:57:36 America/Los_Angeles",
"is_trial_period": "false"
}
]
}
}