In-App Purchase iap 内付费 二次验证代码 (java 服务器端)

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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(nullnew 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. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 之前一个项目做的代码全部不见了,过了一段时间后想拿来用,发现早己不了,不得己,摸了半天才弄成功了,汗,不是做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

     

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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. }  
//--------------------------------------工具类----------------------------------------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值