這個估計是我踩過的最大的坑,當時做微信支付的時候也沒這么坑爹,當然他倆也半斤八兩。。。
蘋果官方明確表示:驗證支付時,可能會有一定的延遲。第一次處理的時間就專注的解決這個問題了,忽略了掉單的問題(稍后再說),讓我多次更新支付代碼才降低了掉單率。
常識:
1,返回狀態碼含義:
2、正常返回結果格式:

{ "environment": "Sandbox", "receipt": { "in_app": [ { "transaction_id": "10000004111119001", "original_purchase_date": "2018-07-06 03:16:41 Etc/GMT", "quantity": "1", "original_transaction_id": "1000000414619001", "purchase_date_pst": "2018-07-05 20:16:41 America/Los_Angeles", "original_purchase_date_ms": "1530847001000", "purchase_date_ms": "1530847001000", "product_id": "com.Beixxxxxxxxxon.fourc", "original_purchase_date_pst": "2018-07-05 20:16:41 America/Los_Angeles", "is_trial_period": "false", "purchase_date": "2018-07-06 03:16:41 Etc/GMT" } ], "adam_id": 0, "receipt_creation_date": "2018-07-06 03:16:41 Etc/GMT", "original_application_version": "1.0", "app_item_id": 0, "original_purchase_date_ms": "1375340400000", "request_date_ms": "1530847558058", "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles", "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT", "receipt_creation_date_pst": "2018-07-05 20:16:41 America/Los_Angeles", "receipt_type": "ProductionSandbox", "bundle_id": "com.jiaxxxxxmei.www.Gxxxxxxxrooms", "receipt_creation_date_ms": "1530847001000", "request_date": "2018-07-06 03:25:58 Etc/GMT", "version_external_identifier": 0, "request_date_pst": "2018-07-05 20:25:58 America/Los_Angeles", "download_id": 0, "application_version": "3" }, "status": 0 }
3、憑證(此處提供的是測試環境的成功支付憑證)

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
4、測試|正式驗證地址
//購買憑證驗證地址
private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt";
//測試的購買憑證驗證地址
private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt";
開始JAVA開發接口驗證:
初步代碼,第一版,實現正常的支付功能(其中充值金額最好從返回結果中獲取,不過我是讓前端傳過來的):

/** * 接收iOS端發過來的購買憑證 * @param */ @RequestMapping("/setIapCertificate") @ResponseBody public R setIapCertificate(HttpServletRequest req){ String certificateCode = req.getParameter("certificateCode"); String money = req.getParameter("money");//充值金額 String type = req.getParameter("type");//1、正式 2、測試 String url = null; url = type.equals("1")?certificateUrl : certificateUrlTest; // if(StringUtils.isNotEmpty(certificateCode)){ String r = sendHttpsCoon(url, certificateCode); //向蘋果服務器發送憑證並接受結果 JSONObject params = JSONObject.parseObject(r); System.out.println("*****************"+params); String code = ""; String environment = ""; String transaction_id = ""; final String orderNum = DateUtil.getRechargeOrderNum();//根據當前時間生成訂單號,yyMMddHHmmssSSS + 6位隨機數 if(params != null) { code = String.valueOf(params.get("status")); if("0".equals(code)) {//成功 environment = String.valueOf(params.get("environment")); String r_receipt = params.getString("receipt"); JSONObject returnJson = JSONObject.parseObject(r_receipt); System.out.println("*****************"+returnJson); String in_app = returnJson.getString("in_app"); if (in_app.endsWith("]")) { JSONObject in_appJson = JSONObject.parseObject(in_app.substring(1, in_app.length()-1)); transaction_id = in_appJson.getString("transaction_id"); // 訂單號 } //保存充值記錄 saveRecharge(orderNum, money, environment, transaction_id); return R.ok(); } }else{ return R.error(); } }else{ return R.error(); } }
后來發現大部分可以支付成功,但部分充值不成功,考慮可能是網絡的問題,然后利用定時任務實現多次請求。若第一次請求結果不是成功狀態則保存第一次請求的結果,並且每隔10秒請求一次,最多請求6次(一分鍾)。
就有了優化后的第二版:

/** * 接收iOS端發過來的購買憑證(方法已過時,被替代) * @param */ @RequestMapping("/setIapCertificate1") @ResponseBody public R setIapCertificate1(HttpServletRequest req){ String certificateCode = req.getParameter("certificateCode"); String money = req.getParameter("money");//充值金額 String type = req.getParameter("type");//1、正式 2、測試 String url = null; url = type.equals("1")?certificateUrl : certificateUrlTest; // if(StringUtils.isNotEmpty(certificateCode)){ String r = sendHttpsCoon(url, certificateCode); JSONObject params = JSONObject.parseObject(r); System.out.println("*****************"+params); String code = ""; String environment = ""; String transaction_id = ""; final String orderNum = DateUtil.getRechargeOrderNum();//根據當前時間生成訂單號,yyMMddHHmmssSSS + 0~1000000隨機數 if(params != null) { code = String.valueOf(params.get("status")); payError = new WfIospayErrorEntity(); payError.setCertificatecode(certificateCode); payError.setMoney(money); payError.setRebate(rebate); payError.setResultcode(code); payError.setType(type); payError.setUpdatetime(new Date()); payError.setRemark(params.toString()); iospayErrorService.save(payError); if("0".equals(code)) {//成功 environment = String.valueOf(params.get("environment")); String r_receipt = params.getString("receipt"); JSONObject returnJson = JSONObject.parseObject(r_receipt); System.out.println("*****************"+returnJson); String in_app = returnJson.getString("in_app"); if (in_app.endsWith("]")) { JSONObject in_appJson = JSONObject.parseObject(in_app.substring(1, in_app.length()-1)); transaction_id = in_appJson.getString("transaction_id"); // 訂單號 } //保存充值記錄 saveRecharge(rebate, orderNum, uid, money, environment, transaction_id); return R.ok(); }else{//失敗 final String url1 = url; final String certificateCode1 = certificateCode; final String environment1 = environment; final String transaction_id1 = transaction_id; final String money1 = money;//充值金額 final Timer timer = new Timer(); timer.schedule(new TimerTask() {//若失敗,則每10請求一次 @Override public void run() { if (map.get(certificateCode1) != null) { map.put(certificateCode1, map.get(certificateCode1) + 1); } else { map.put(certificateCode1, 1); } if (map.get(certificateCode1) >= 60) { map.remove(certificateCode1); timer.cancel(); } String r1 = sendHttpsCoon(url1, certificateCode1); if (JSONObject.parseObject(r1).getString("status").equals("0")) { saveRecharge( orderNum, money1, environment1, transaction_id1);//當成功時處理充值業務 map.remove(certificateCode1); timer.cancel(); } //System.out.println("設定指定任務task在指定延遲delay后執行"); } },1000, 10000); return R.error("68"); } }else{//保存失敗憑證 payError = new WfIospayErrorEntity(); payError.setCertificatecode(certificateCode); payError.setMoney(money); payError.setRebate(rebate); payError.setResultcode(code); payError.setType(type); payError.setUpdatetime(new Date()); if(params != null){ payError.setRemark("params為空"); }else { payError.setRemark(params.toString()); } iospayErrorService.save(payError); return R.error(); } }else{ return R.error(); } }
but,還是有掉單的問題,此時我崩潰了,想不明白怎么回事。直到我查了蘋果返回的結果發現,根據一個憑證查出了兩次支付的訂單信息:

{ "environment": "Production", "receipt": { "in_app": [ { "transaction_id": "470000347480489", "original_purchase_date": "2018-07-13 06:01:00 Etc/GMT", "quantity": "1", "original_transaction_id": "470000347480489", "purchase_date_pst": "2018-07-12 23:01:00 America/Los_Angeles", "original_purchase_date_ms": "1531461660000", "purchase_date_ms": "1531461660000", "product_id": "com.Beijxxxxxxxxation.fourc", "original_purchase_date_pst": "2018-07-12 23:01:00 America/Los_Angeles", "is_trial_period": "false", "purchase_date": "2018-07-13 06:01:00 Etc/GMT" }, { "transaction_id": "470000347479507", "original_purchase_date": "2018-07-13 05:56:51 Etc/GMT", "quantity": "1", "original_transaction_id": "470000347479507", "purchase_date_pst": "2018-07-12 22:56:51 America/Los_Angeles", "original_purchase_date_ms": "1531461411000", "purchase_date_ms": "1531461411000", "product_id": "com.Beixxxxxxxxxxxcation.fourb", "original_purchase_date_pst": "2018-07-12 22:56:51 America/Los_Angeles", "is_trial_period": "false", "purchase_date": "2018-07-13 05:56:51 Etc/GMT" } ], "adam_id": 1375992347, "receipt_creation_date": "2018-07-13 06:01:00 Etc/GMT", "original_application_version": "3", "app_item_id": 1375992347, "original_purchase_date_ms": "1531016326000", "request_date_ms": "1531461663282", "original_purchase_date_pst": "2018-07-07 19:18:46 America/Los_Angeles", "original_purchase_date": "2018-07-08 02:18:46 Etc/GMT", "receipt_creation_date_pst": "2018-07-12 23:01:00 America/Los_Angeles", "receipt_type": "Production", "bundle_id": "com.jxxxxxxxnmei.www.Goxxxxxxsrooms", "receipt_creation_date_ms": "1531461660000", "request_date": "2018-07-13 06:01:03 Etc/GMT", "version_external_identifier": 827702473, "request_date_pst": "2018-07-12 23:01:03 America/Los_Angeles", "download_id": 87033802813938, "application_version": "3" }, "status": 0 }
知道原因后,想解決辦法。讀取蘋果服務器返回的訂單數組,循環充值。但這樣的話,需要避免重復充值,所以現在需要用緩存給每次充值做個記錄(我選擇的redis),在充值之前判斷蘋果服務器返回的訂單中有沒有已經充過值的。
然后有了第三版:

/** * 接收iOS端發過來的購買憑證 * * @param */ @RequestMapping("/setIapCertificate") @ResponseBody public R setIapCertificate(HttpServletRequest req) { final Integer uid = WebUtils.getUid(req); String certificateCode = req.getParameter("certificateCode"); String money = req.getParameter("money");//充值金額 String type = req.getParameter("type");//1、正式 2、測試 String url = null; url = type.equals("1") ? certificateUrl : certificateUrlTest; // WfIospayErrorEntity payError = new WfIospayErrorEntity(); payError.setCertificatecode(certificateCode); payError.setMoney(money); payError.setRebate(rebate); payError.setResultcode("110"); payError.setType(type); payError.setUid(String.valueOf(uid)); payError.setUpdatetime(new Date()); payError.setRemark("params為空"); iospayErrorService.save(payError); if (StringUtils.isNotEmpty(certificateCode)) { String r = sendHttpsCoon(url, certificateCode); JSONObject params = JSONObject.parseObject(r); System.out.println("*****************" + params); String code = ""; String environment = ""; String transaction_id = ""; final String orderNum = DateUtil.getRechargeOrderNum();//根據當前時間生成訂單號,yyMMddHHmmssSSS + 0~1000000隨機數 if (params != null) { code = String.valueOf(params.get("status")); payError = new WfIospayErrorEntity(); payError.setCertificatecode(certificateCode); payError.setMoney(money); payError.setRebate(rebate); payError.setResultcode(code); payError.setType(type); payError.setUid(String.valueOf(uid)); payError.setUpdatetime(new Date()); payError.setRemark(params.toString()); iospayErrorService.save(payError); if ("0".equals(code)) {//成功 environment = String.valueOf(params.get("environment")); String r_receipt = params.getString("receipt"); JSONObject returnJson = JSONObject.parseObject(r_receipt); System.out.println("*****************" + returnJson); //String in_app = returnJson.getString("in_app"); JSONArray jsonArray = (JSONArray) returnJson.get("in_app");// 獲取返回結果中的訂單列表 JSONObject targetOrder = null; String product_id = null; for (int i = 0; i < jsonArray.size(); i++) { targetOrder = jsonArray.getJSONObject(i);//獲取訂單信息對象 product_id = targetOrder.getString("product_id");//獲取產品信息 transaction_id = targetOrder.getString("transaction_id");// transaction_id交易號 if (!ISsoLoginHelper.confirmPay(uid, transaction_id)) {//redis驗證訂單是否已經充值 //保存充值記錄 ISsoLoginHelper.savePay(uid, transaction_id, money);//用redis保存記錄 //進行充值 saveRecharge(rebate, orderNum, uid, money, environment, transaction_id); } } return R.ok(); } else {//失敗 final String url1 = url; final String certificateCode1 = certificateCode; final String environment1 = environment; final String transaction_id1 = transaction_id; final String money1 = money;//充值金額 final Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { if (map.get(certificateCode1) != null) { map.put(certificateCode1, map.get(certificateCode1) + 1); } else { map.put(certificateCode1, 1); } if (map.get(certificateCode1) >= 60) { map.remove(certificateCode1); timer.cancel(); } String r1 = sendHttpsCoon(url1, certificateCode1); if (JSONObject.parseObject(r1).getString("status").equals("0")) { if (!ISsoLoginHelper.confirmPay(uid, transaction_id1)) {//redis驗證訂單是否已經充值 //保存充值記錄 ISsoLoginHelper.savePay(uid, transaction_id1, money1); //進行充值 saveRecharge(rebate, orderNum, uid, money1, environment1, transaction_id1); } map.remove(certificateCode1); timer.cancel(); } //System.out.println("設定指定任務task在指定延遲delay后執行"); } }, 1000, 10000); return R.error("68"); } } else { payError = new WfIospayErrorEntity(); payError.setCertificatecode(certificateCode); payError.setMoney(money); payError.setRebate(rebate); payError.setResultcode("111"); payError.setType(type); payError.setUid(String.valueOf(uid)); payError.setUpdatetime(new Date()); payError.setRemark("params再次為空"); iospayErrorService.save(payError); return R.error(); } } else { return R.error(); } }
其中涉及的工具或方法:
發送請求:

/** * 重寫X509TrustManager */ private static TrustManager myX509TrustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }; /** * 發送請求 * @param url * @param * @return */ private String sendHttpsCoon(String url, String code){ if(url.isEmpty()){ return null; } try { //設置SSLContext SSLContext ssl = SSLContext.getInstance("SSL"); ssl.init(null, new TrustManager[]{myX509TrustManager}, null); //打開連接 HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection(); //設置套接工廠 conn.setSSLSocketFactory(ssl.getSocketFactory()); //加入數據 conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setRequestProperty("Content-type","application/json"); JSONObject obj = new JSONObject(); obj.put("receipt-data", code); BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream()); buffOutStr.write(obj.toString().getBytes()); buffOutStr.flush(); buffOutStr.close(); //獲取輸入流 BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line = null; StringBuffer sb = new StringBuffer(); while((line = reader.readLine())!= null){ sb.append(line); } return sb.toString(); } catch (Exception e) { return null; } }
redis保存|判斷充值記錄(安裝配置我就不多說了,不懂的請自行百度)

/** * 保存ios訂單支付信息 * * @param userid * @return */ public static void savePay(Integer userid,String transaction_id,String money) { Jedis jedis = myJedisPool.getResource(); try { jedis.setnx(userid +"_"+ transaction_id , money); } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage(), e); } finally { jedis.close(); } } /** * 判斷訂單是否已充值成功 * * @param userid * @return */ public static boolean confirmPay(Integer userid,String transaction_id) { Jedis jedis = myJedisPool.getResource(); boolean b = false; try { b = jedis.exists(userid +"_"+ transaction_id); } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage(), e); } finally { jedis.close(); } return b; }