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

手机沙盒购买成功。JAVA服务器向苹果服务器查询的时候总是失败

参考:http://blog.csdn.net/weiqingli190949353/article/details/19123095

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;
	}
	
}

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

--------------------------分割线------------------------------------------------------

--------------------------分割线------------------------------------------------------

这是购买成功后,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

/**
 * @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();
		}
		
	}
}


    {
"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";
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值