您的位置:首页 > 移动开发

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

2016-10-24 20:28 701 查看
[java] view
plain copy

 





package com.yichangmao.buyVerify.Comm.ios;  

  

import java.io.BufferedOutputStream;  

import java.io.BufferedReader;  

import java.io.InputStream;  

import java.io.InputStreamReader;  

import java.net.URL;  

import java.security.MessageDigest;  

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;  

  

import net.sf.json.JSONObject;  

  

import sun.misc.BASE64Decoder;  

  

import com.yichangmao.buyVerify.R;  

import com.yichangmao.buyVerify.Comm.FileUtil;  

  

  

  

  

public class IOS_Verify {  

    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 账单 

     * @url 要验证的地址 

     * @return null 或返回结果 

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

     *  

     */  

    public static String buyAppVerify(String receipt,String verifyState)  

    {  

       String url=url_verify;  

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

           url=url_sandbox;  

       }  

       String buyCode=getBASE64(receipt);  

       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());  

             

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

           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)  

       {  

           ex.printStackTrace();  

       }  

       return null;  

    }  

      

    /** 

     * 根据原始收据返回苹果的验证地址: 

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

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

     * @param receipt 

     * @return Sandbox 测试单   Real 正式单 

     */  

    public static String getEnvironment(String receipt)  

    {  

        try{  

            JSONObject job = JSONObject.fromObject(receipt);  

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

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

                return evvironment;  

            }  

        }catch(Exception ex){  

            ex.printStackTrace();  

        }  

        return "Real";  

    }  

      

    /** 

     * 用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;  

    }  

  

    /** 

     * 解密BASE64字窜 

     * @param s 

     * @return 

     */  

    public static String getFromBASE64(String s) {  

        byte[] b = null;  

        if (s != null) {  

            BASE64Decoder decoder = new BASE64Decoder();  

            try {  

                b = decoder.decodeBuffer(s);  

                return new String(b);  

            } catch (Exception e) {  

                e.printStackTrace();  

            }  

        }  

        return new String(b);  

    }  

      

    /** 

    * md5加密方法 

    * @author: zhengsunlei 

    * Jul 30, 2010 4:38:28 PM 

    * @param plainText 加密字符串 

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

    * 每位工程师都有保持代码优雅的义务 

    * each engineer has a duty to keep the code elegant 

    */  

    public final static String md5(String plainText) {  

       // 返回字符串  

       String md5Str = null;  

       try {  

        // 操作字符串  

        StringBuffer buf = new StringBuffer();  

       /** 

        * MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。 

        * 信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。 

        *  

        * MessageDigest 对象开始被初始化。 

        * 该对象通过使用 update()方法处理数据。 

        * 任何时候都可以调用 reset()方法重置摘要。 

        * 一旦所有需要更新的数据都已经被更新了,应该调用digest()方法之一完成哈希计算。  

        *  

        * 对于给定数量的更新数据,digest 方法只能被调用一次。 

        * 在调用 digest 之后,MessageDigest 对象被重新设置成其初始状态。 

        */   

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

         

        // 添加要进行计算摘要的信息,使用 plainText 的 byte 数组更新摘要。  

        md.update(plainText.getBytes());  

        // 计算出摘要,完成哈希计算。  

        byte b[] = md.digest();  

        int i;  

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

         i = b[offset];  

         if (i < 0) {  

          i += 256;  

         }  

         if (i < 16) {  

          buf.append("0");  

         }  

         // 将整型 十进制 i 转换为16位,用十六进制参数表示的无符号整数值的字符串表示形式。  

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

        }  

        // 32位的加密  

        md5Str = buf.toString();  

        // 16位的加密  

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

       } catch (Exception e) {  

        e.printStackTrace();  

       }  

       return md5Str;  

    }  

      

}  

[java] view
plain copy

 





之前一个项目做的代码全部不见了,过了一段时间后想拿来用,发现早己不了,不得己,摸了半天才弄成功了,汗,不是做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

 





/** 

 * @author qili 

 * 

 */  

public class IOSAction extends BaseAction{  

    private static final long serialVersionUID = 1L;  

      

    /** 

     * 客户端向服务器验证 

     *  

     *  

     *   * checkState  A  验证成功有效(返回收据) 

     *             B  账单有效,但己经验证过 

     *             C  服务器数据库中没有此账单(无效账单) 

     *             D  不处理 

     *  

     * @return 

     * @throws IOException  

     */  

    public void IOSVerify() throws IOException  

    {  

          

        HttpServletRequest request=ServletActionContext.getRequest();  

        HttpServletResponse response=ServletActionContext.getResponse();  

        System.out.println(new  Date().toLocaleString()+"  来自苹果端的验证...");  

        //苹果客户端传上来的收据,是最原据的收据  

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

        System.out.println(receipt);  

        //拿到收据的MD5  

        String md5_receipt=MD5.md5Digest(receipt);  

        //默认是无效账单  

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

        //查询数据库,看是否是己经验证过的账号  

        boolean isExists=DbServiceImpl_PNM.isExistsIOSReceipt(md5_receipt);  

        String verifyResult=null;  

        if(!isExists){  

            String verifyUrl=IOS_Verify.getVerifyURL();  

            verifyResult=IOS_Verify.buyAppVerify(receipt, verifyUrl);  

            //System.out.println(verifyResult);  

            if(verifyResult==null){  

                //苹果服务器没有返回验证结果  

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

            }else{  

                //跟苹果验证有返回结果------------------  

                JSONObject job = JSONObject.fromObject(verifyResult);  

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

                if(states.equals("0"))//验证成功  

                {  

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

                    JSONObject returnJson = JSONObject.fromObject(r_receipt);  

                    //产品ID  

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

                    //数量  

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

                    //跟苹果的服务器验证成功  

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

                    //交易日期  

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

                    //保存到数据库  

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

                }else{  

                    //账单无效  

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

                }  

                //跟苹果验证有返回结果------------------  

            }  

            //传上来的收据有购买信息==end=============  

        }else{  

            //账单有效,但己验证过  

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

        }  

        //返回结果  

        try {  

            System.out.println("验证结果     "+result);  

            System.out.println();  

            response.getWriter().write(result);  

        } catch (IOException e) {  

            e.printStackTrace();  

        }  

          

    }  

}  



//--------------------------------------工具类----------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: