In-App Purchase iap 內付費 二次驗證代碼 (java 服務器端)

In-App Purchase iap 內付費 二次驗證代碼 (java 服務器端)

2014年02月12日 17:09:03

閱讀數:17097

 


 
  1. package com.yichangmao.buyVerify.Comm.ios;

  2.  
  3. import java.io.BufferedOutputStream;

  4. import java.io.BufferedReader;

  5. import java.io.InputStream;

  6. import java.io.InputStreamReader;

  7. import java.net.URL;

  8. import java.security.MessageDigest;

  9. import java.security.cert.CertificateException;

  10. import java.security.cert.X509Certificate;

  11. import java.util.Locale;

  12.  
  13. import javax.net.ssl.HostnameVerifier;

  14. import javax.net.ssl.HttpsURLConnection;

  15. import javax.net.ssl.SSLContext;

  16. import javax.net.ssl.SSLSession;

  17. import javax.net.ssl.TrustManager;

  18. import javax.net.ssl.X509TrustManager;

  19.  
  20. import net.sf.json.JSONObject;

  21.  
  22. import sun.misc.BASE64Decoder;

  23.  
  24. import com.yichangmao.buyVerify.R;

  25. import com.yichangmao.buyVerify.Comm.FileUtil;

  26.  
  27.  
  28.  
  29.  
  30. public class IOS_Verify {

  31. private static class TrustAnyTrustManager implements X509TrustManager {

  32.  
  33. public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

  34. }

  35.  
  36. public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

  37. }

  38.  
  39. public X509Certificate[] getAcceptedIssuers() {

  40. return new X509Certificate[]{};

  41. }

  42. }

  43.  
  44. private static class TrustAnyHostnameVerifier implements HostnameVerifier {

  45. public boolean verify(String hostname, SSLSession session) {

  46. return true;

  47. }

  48. }

  49. private static final String url_sandbox="https://sandbox.itunes.apple.com/verifyReceipt";

  50. private static final String url_verify="https://buy.itunes.apple.com/verifyReceipt";

  51.  
  52.  
  53. /**

  54. * 蘋果服務器驗證

  55. * @param receipt 賬單

  56. * @url 要驗證的地址

  57. * @return null 或返回結果

  58. * 沙盒 https://sandbox.itunes.apple.com/verifyReceipt

  59. *

  60. */

  61. public static String buyAppVerify(String receipt,String verifyState)

  62. {

  63. String url=url_verify;

  64. if(verifyState!=null&&verifyState.equals("Sandbox")){

  65. url=url_sandbox;

  66. }

  67. String buyCode=getBASE64(receipt);

  68. try{

  69. SSLContext sc = SSLContext.getInstance("SSL");

  70. sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());

  71. URL console = new URL(url);

  72. HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();

  73. conn.setSSLSocketFactory(sc.getSocketFactory());

  74. conn.setHostnameVerifier(new TrustAnyHostnameVerifier());

  75. conn.setRequestMethod("POST");

  76. conn.setRequestProperty("content-type", "text/json");

  77. conn.setRequestProperty("Proxy-Connection", "Keep-Alive");

  78. conn.setDoInput(true);

  79. conn.setDoOutput(true);

  80. BufferedOutputStream hurlBufOus=new BufferedOutputStream(conn.getOutputStream());

  81.  
  82. String str= String.format(Locale.CHINA,"{\"receipt-data\":\"" + buyCode+"\"}");

  83. hurlBufOus.write(str.getBytes());

  84. hurlBufOus.flush();

  85.  
  86. InputStream is = conn.getInputStream();

  87. BufferedReader reader=new BufferedReader(new InputStreamReader(is));

  88. String line = null;

  89. StringBuffer sb = new StringBuffer();

  90. while((line = reader.readLine()) != null){

  91. sb.append(line);

  92. }

  93.  
  94. return sb.toString();

  95. }catch(Exception ex)

  96. {

  97. ex.printStackTrace();

  98. }

  99. return null;

  100. }

  101.  
  102. /**

  103. * 根據原始收據返回蘋果的驗證地址:

  104. * * 沙箱 https://sandbox.itunes.apple.com/verifyReceipt

  105. * 真正的地址 https://buy.itunes.apple.com/verifyReceipt

  106. * @param receipt

  107. * @return Sandbox 測試單 Real 正式單

  108. */

  109. public static String getEnvironment(String receipt)

  110. {

  111. try{

  112. JSONObject job = JSONObject.fromObject(receipt);

  113. if(job.containsKey("environment")){

  114. String evvironment=job.getString("environment");

  115. return evvironment;

  116. }

  117. }catch(Exception ex){

  118. ex.printStackTrace();

  119. }

  120. return "Real";

  121. }

  122.  
  123. /**

  124. * 用BASE64加密

  125. * @param str

  126. * @return

  127. */

  128. public static String getBASE64(String str) {

  129. byte[] b = str.getBytes();

  130. String s = null;

  131. if (b != null) {

  132. s = new sun.misc.BASE64Encoder().encode(b);

  133. }

  134. return s;

  135. }

  136.  
  137. /**

  138. * 解密BASE64字竄

  139. * @param s

  140. * @return

  141. */

  142. public static String getFromBASE64(String s) {

  143. byte[] b = null;

  144. if (s != null) {

  145. BASE64Decoder decoder = new BASE64Decoder();

  146. try {

  147. b = decoder.decodeBuffer(s);

  148. return new String(b);

  149. } catch (Exception e) {

  150. e.printStackTrace();

  151. }

  152. }

  153. return new String(b);

  154. }

  155.  
  156. /**

  157. * md5加密方法

  158. * @author: zhengsunlei

  159. * Jul 30, 2010 4:38:28 PM

  160. * @param plainText 加密字符串

  161. * @return String 返回32位md5加密字符串(16位加密取substring(8,24))

  162. * 每位工程師都有保持代碼優雅的義務

  163. * each engineer has a duty to keep the code elegant

  164. */

  165. public final static String md5(String plainText) {

  166. // 返回字符串

  167. String md5Str = null;

  168. try {

  169. // 操作字符串

  170. StringBuffer buf = new StringBuffer();

  171. /**

  172. * MessageDigest 類爲應用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。

  173. * 信息摘要是安全的單向哈希函數,它接收任意大小的數據,並輸出固定長度的哈希值。

  174. *

  175. * MessageDigest 對象開始被初始化。

  176. * 該對象通過使用 update()方法處理數據。

  177. * 任何時候都可以調用 reset()方法重置摘要。

  178. * 一旦所有需要更新的數據都已經被更新了,應該調用digest()方法之一完成哈希計算。

  179. *

  180. * 對於給定數量的更新數據,digest 方法只能被調用一次。

  181. * 在調用 digest 之後,MessageDigest 對象被重新設置成其初始狀態。

  182. */

  183. MessageDigest md = MessageDigest.getInstance("MD5");

  184.  
  185. // 添加要進行計算摘要的信息,使用 plainText 的 byte 數組更新摘要。

  186. md.update(plainText.getBytes());

  187. // 計算出摘要,完成哈希計算。

  188. byte b[] = md.digest();

  189. int i;

  190. for (int offset = 0; offset < b.length; offset++) {

  191. i = b[offset];

  192. if (i < 0) {

  193. i += 256;

  194. }

  195. if (i < 16) {

  196. buf.append("0");

  197. }

  198. // 將整型 十進制 i 轉換爲16位,用十六進制參數表示的無符號整數值的字符串表示形式。

  199. buf.append(Integer.toHexString(i));

  200. }

  201. // 32位的加密

  202. md5Str = buf.toString();

  203. // 16位的加密

  204. // md5Str = buf.toString().md5Strstring(8,24);

  205. } catch (Exception e) {

  206. e.printStackTrace();

  207. }

  208. return md5Str;

  209. }

  210.  
  211. }

 

   之前一個項目做的代碼全部不見了,過了一段時間後想拿來用,發現早己不了,不得己,摸了半天才弄成功了,汗,不是做IOS開發的,見笑。

 

1、   在IOS前端拿到的收據(Receipt)格式是:

--------------------------分割線------------------------------------------------------

    {
"signature" = "Am7vrfmY+FJq9g8gJDdYMGWOBJiKUUz80nAHooQFwYEZAL9wdXU7uaMiSZn75JQUjO3XfydLs2bwm9VPoKYKTGcft0LrISl7YNlQAWeVfA62F2E1qgTAGVFoTF1k0o3hJR1D/bLoum3i5PrQiScV90s0V77WVon2+B6vqUtHLsZUAAADVzCCA1MwggI7oAMCAQICCGUUkU3ZWAS1MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MDYxNTIyMDU1NloXDTE0MDYxNDIyMDU1NlowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrRjF2ct4IrSdiTChaI0g8pwv/cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ==";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE0LTAyLTEyIDAwOjQ1OjUzIEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJmNzFjODA0YmNkMDkwMDg1ZDE3Y2YwN2UyODA1YzFiMGRmYTA1M2VhIjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDEwMTI2NTU1MSI7CgkiYnZycyIgPSAiMS4wIjsKCSJ0cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDEwMTI2NTU1MSI7CgkicXVhbnRpdHkiID0gIjEiOwoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtbXMiID0gIjEzOTIxOTQ3NTMzNjgiOwoJInVuaXF1ZS12ZW5kb3ItaWRlbnRpZmllciIgPSAiRjYzRTdBMzUtMDQwNi00NDVGLUE1QUEtQ0M5OTc0RDRDQTlCIjsKCSJwcm9kdWN0LWlkIiA9ICJjb20ueWNtLnBubS53aTEiOwoJIml0ZW0taWQiID0gIjgwMjc5MzM1MiI7CgkiYmlkIiA9ICJjb20ueWNtLnBubSI7CgkicHVyY2hhc2UtZGF0ZS1tcyIgPSAiMTM5MjE5NDc1MzM2OCI7CgkicHVyY2hhc2UtZGF0ZSIgPSAiMjAxNC0wMi0xMiAwODo0NTo1MyBFdGMvR01UIjsKCSJwdXJjaGFzZS1kYXRlLXBzdCIgPSAiMjAxNC0wMi0xMiAwMDo0NTo1MyBBbWVyaWNhL0xvc19BbmdlbGVzIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDE0LTAyLTEyIDA4OjQ1OjUzIEV0Yy9HTVQiOwp9";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}

--------------------------分割線------------------------------------------------------

這是購買成功後,app store發回來的收據,我們需要把這個收據發給第三方服務器(我們的服務器)去驗證。

2、在跟蘋果服務器作二次驗證之前,需要將上面的這個原始收據用base64編碼過,然後組裝成JOSN格式:

{"receipt-data":"base64編碼過後的代碼"}

  * 沙箱   https://sandbox.itunes.apple.com/verifyReceipt
* 真正的地址   https://buy.itunes.apple.com/verifyReceipt

 

3、蘋果服務器返回的格式也是一個JSON格式,拿到裏面返回的值“status”,若爲0 說明驗證成功

      若成功,還會返回收據支付信息內容,格式如下:

      {"original_purchase_date_pst":"2014-02-12 00:45:53 America/Los_Angeles","purchase_date_ms":"1392194753368","unique_identifier":"f71c804bcd090085d17cf07e2805c1b0dfa053ea","original_transaction_id":"1000000101265551","bvrs":"1.0","transaction_id":"1000000101265551","quantity":"1","unique_vendor_identifier":"F63E7A35-0406-445F-A5AA-CC9974D4CA9B","item_id":"802793352","product_id":"com.ycm.pnm.wi1","purchase_date":"2014-02-12 08:45:53 Etc/GMT","original_purchase_date":"2014-02-12 08:45:53 Etc/GMT","purchase_date_pst":"2014-02-12 00:45:53 America/Los_Angeles","bid":"com.ycm.pnm","original_purchase_date_ms":"1392194753368"}

--------------------代碼爲具體代碼部分-----------------------------------------------------------------------------------------------

一、具體處理Action

     


 
  1. /**

  2. * @author qili

  3. *

  4. */

  5. public class IOSAction extends BaseAction{

  6. private static final long serialVersionUID = 1L;

  7.  
  8. /**

  9. * 客戶端向服務器驗證

  10. *

  11. *

  12. * * checkState A 驗證成功有效(返回收據)

  13. * B 賬單有效,但己經驗證過

  14. * C 服務器數據庫中沒有此賬單(無效賬單)

  15. * D 不處理

  16. *

  17. * @return

  18. * @throws IOException

  19. */

  20. public void IOSVerify() throws IOException

  21. {

  22.  
  23. HttpServletRequest request=ServletActionContext.getRequest();

  24. HttpServletResponse response=ServletActionContext.getResponse();

  25. System.out.println(new Date().toLocaleString()+" 來自蘋果端的驗證...");

  26. //蘋果客戶端傳上來的收據,是最原據的收據

  27. String receipt=request.getParameter("receipt");

  28. System.out.println(receipt);

  29. //拿到收據的MD5

  30. String md5_receipt=MD5.md5Digest(receipt);

  31. //默認是無效賬單

  32. String result=R.BuyState.STATE_C+"#"+md5_receipt;

  33. //查詢數據庫,看是否是己經驗證過的賬號

  34. boolean isExists=DbServiceImpl_PNM.isExistsIOSReceipt(md5_receipt);

  35. String verifyResult=null;

  36. if(!isExists){

  37. String verifyUrl=IOS_Verify.getVerifyURL();

  38. verifyResult=IOS_Verify.buyAppVerify(receipt, verifyUrl);

  39. //System.out.println(verifyResult);

  40. if(verifyResult==null){

  41. //蘋果服務器沒有返回驗證結果

  42. result=R.BuyState.STATE_D+"#"+md5_receipt;

  43. }else{

  44. //跟蘋果驗證有返回結果------------------

  45. JSONObject job = JSONObject.fromObject(verifyResult);

  46. String states=job.getString("status");

  47. if(states.equals("0"))//驗證成功

  48. {

  49. String r_receipt=job.getString("receipt");

  50. JSONObject returnJson = JSONObject.fromObject(r_receipt);

  51. //產品ID

  52. String product_id=returnJson.getString("product_id");

  53. //數量

  54. String quantity=returnJson.getString("quantity");

  55. //跟蘋果的服務器驗證成功

  56. result=R.BuyState.STATE_A+"#"+md5_receipt+"_"+product_id+"_"+quantity;

  57. //交易日期

  58. String purchase_date=returnJson.getString("purchase_date");

  59. //保存到數據庫

  60. DbServiceImpl_PNM.saveIOSReceipt(md5_receipt, product_id, purchase_date, r_receipt);

  61. }else{

  62. //賬單無效

  63. result=R.BuyState.STATE_C+"#"+md5_receipt;

  64. }

  65. //跟蘋果驗證有返回結果------------------

  66. }

  67. //傳上來的收據有購買信息==end=============

  68. }else{

  69. //賬單有效,但己驗證過

  70. result=R.BuyState.STATE_B+"#"+md5_receipt;

  71. }

  72. //返回結果

  73. try {

  74. System.out.println("驗證結果 "+result);

  75. System.out.println();

  76. response.getWriter().write(result);

  77. } catch (IOException e) {

  78. e.printStackTrace();

  79. }

  80.  
  81. }

  82. }

//--------------------------------------工具類----------------------------------------------------

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章