arctic_fox

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

IOS IAP APP内支付 Java服务端代码

场景:作为后台需要为app提供服务,在ios中,app内进行支付购买时需要进行二次验证。

基础:可以参考上一篇转载的博文In-App Purchase(iap)快速指南了解原理。

直接先上服务端测试通过的代码:

 

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
 
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
 
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.alibaba.fastjson.JSONObject;
 
@Controller
@RequestMapping("iap")
public class IapController {
	
	//购买凭证验证地址
	private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt";
	
	//测试的购买凭证验证地址 
	private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt";
	
	/**
	 * 重写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 {
			
		}
	};
	
	/**
	 * 接收iOS端发过来的购买凭证
	 * @param userId 
	 * @param receipt
	 * @param chooseEnv
	 */
	@RequestMapping("/setIapCertificate")
	public String setIapCertificate(String userId, String receipt, boolean chooseEnv){
		if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(receipt)){
			return null;
		}
		String url = null;
		url = chooseEnv == true? certificateUrl:certificateUrlTest;
		final String certificateCode = receipt;
		if(StringUtils.isNotEmpty(certificateCode)){
			return sendHttpsCoon(url, certificateCode);
		}else{
			return null;
		}
	}
	
	/**
	 * 发送请求
	 * @param url
	 * @param strings
	 * @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;
		}
	}
}

 

注意:
1. setIapCertificate()方法的输入输出参数自行与ios商定,这里是直接将apple返回的数据返给了app(个人推荐返回boolean,前台只要知道验证是否通过即可)。
2. 参数receipt:BASE64编码过的话
ewoJInNpZ25hdHVyZSIgPSAiQXAzbjVUZmQrVXIvQmFrTEU3VG9Dc3NOSGdxbmI2dnlrM0RLZlFxcnBxQStLV1AzVHVkZDN2N2w0OUEyc2NVY1g2cWNDeEx3bHAzR2RKMG9Ma3IzRzdTaEZFYU5UeTBrL25hRTNoWUIxY0kxajFGM1ppSmxTVC9kL1VDN0ZFY1BUMjR2SUFFZDE2WXFOdHNqWUpNYmJQVGRRR3g4NitFZWlDR21mVDJKS0VrK0FBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NCdXA0K1BBaG0vTE1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEUwTURZd056QXdNREl5TVZvWERURTJNRFV4T0RFNE16RXpNRm93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNbVRFdUxnamltTHdSSnh5MW9FZjBlc1VORFZFSWU2d0Rzbm5hbDE0aE5CdDF2MTk1WDZuOTNZTzdnaTNvclBTdXg5RDU1NFNrTXArU2F5Zzg0bFRjMzYyVXRtWUxwV25iMzRucXlHeDlLQlZUeTVPR1Y0bGpFMU93QytvVG5STStRTFJDbWVOeE1iUFpoUzQ3VCtlWnRERWhWQjl1c2szK0pNMkNvZ2Z3bzdBZ01CQUFHamNqQndNQjBHQTFVZERnUVdCQlNKYUVlTnVxOURmNlpmTjY4RmUrSTJ1MjJzc0RBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkRZZDZPS2RndElCR0xVeWF3N1hRd3VSV0VNNk1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQWVhSlYyVTUxcnhmY3FBQWU1QzIvZkVXOEtVbDRpTzRsTXV0YTdONlh6UDFwWkl6MU5ra0N0SUl3ZXlOajVVUllISytIalJLU1U5UkxndU5sMG5rZnhxT2JpTWNrd1J1ZEtTcTY5Tkluclp5Q0Q2NlI0Szc3bmI5bE1UQUJTU1lsc0t0OG9OdGxoZ1IvMWtqU1NSUWNIa3RzRGNTaVFHS01ka1NscDRBeVhmN3ZuSFBCZTR5Q3dZVjJQcFNOMDRrYm9pSjNwQmx4c0d3Vi9abEwyNk0ydWVZSEtZQ3VYaGRxRnd4VmdtNTJoM29lSk9PdC92WTRFY1FxN2VxSG02bTAzWjliN1BSellNMktHWEhEbU9Nazd2RHBlTVZsTERQU0dZejErVTNzRHhKemViU3BiYUptVDdpbXpVS2ZnZ0VZN3h4ZjRjemZIMHlqNXdOelNHVE92UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUyTFRBMExUSTRJREF6T2pFNE9qUTVJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluVnVhWEYxWlMxcFpHVnVkR2xtYVdWeUlpQTlJQ0prTkdVM01qRmxZelkzWldZeVptVmpZVGRtWW1SaVpESTFZVFExWTJaaU16ZGxNVEJsWVRkaUlqc0tDU0p2Y21sbmFXNWhiQzEwY21GdWMyRmpkR2x2YmkxcFpDSWdQU0FpTVRBd01EQXdNREl3T0RZeU1EUTNNQ0k3Q2draVluWnljeUlnUFNBaU1TNHhJanNLQ1NKMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaU1UQXdNREF3TURJd09EWXlNRFEzTUNJN0Nna2ljWFZoYm5ScGRIa2lJRDBnSWpFaU93b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGJYTWlJRDBnSWpFME5qRTRNemczTWpreU9EVWlPd29KSW5WdWFYRjFaUzEyWlc1a2IzSXRhV1JsYm5ScFptbGxjaUlnUFNBaU9FVXhPVVZGUXpRdE16TkVOeTAwTlRNMkxVSTJNa1V0TVRFeVFrRkROamhGUlVORUlqc0tDU0p3Y205a2RXTjBMV2xrSWlBOUlDSXhNalEwSWpzS0NTSnBkR1Z0TFdsa0lpQTlJQ0l4TVRBNE56azRNVFV4SWpzS0NTSmlhV1FpSUQwZ0ltTnZiUzVrYjJOMGIzSkllWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakUwTmpFNE16ZzNNamt5T0RVaU93b0pJbkIxY21Ob1lYTmxMV1JoZEdVaUlEMGdJakl3TVRZdE1EUXRNamdnTVRBNk1UZzZORGtnUlhSakwwZE5WQ0k3Q2draWNIVnlZMmhoYzJVdFpHRjBaUzF3YzNRaUlEMGdJakl3TVRZdE1EUXRNamdnTURNNk1UZzZORGtnUVcxbGNtbGpZUzlNYjNOZlFXNW5aV3hsY3lJN0Nna2liM0pwWjJsdVlXd3RjSFZ5WTJoaGMyVXRaR0YwWlNJZ1BTQWlNakF4Tmkwd05DMHlPQ0F4TURveE9EbzBPU0JGZEdNdlIwMVVJanNLZlE9PSI7CgkiZW52aXJvbm1lbnQiID0gIlNhbmRib3giOwoJInBvZCIgPSAiMTAwIjsKCSJzaWduaW5nLXN0YXR1cyIgPSAiMCI7Cn0=
未进行编码的话:
{
"signature" = "Ap3n5Tfd+Ur/BakLE7ToCssNHgqnb6vyk3DKfQqrpqA+KWP3Tudd3v7l49A2scUcX6qcCxLwlp3GdJ0oLkr3G7ShFEaNTy0k/naE3hYB1cI1j1F3ZiJlST/d/UC7FEcPT24vIAEd16YqNtsjYJMbbPTdQGx86+EeiCGmfT2JKEk+AAADVzCCA1MwggI7oAMCAQICCBup4+PAhm/LMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDYwNzAwMDIyMVoXDTE2MDUxODE4MzEzMFowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMmTEuLgjimLwRJxy1oEf0esUNDVEIe6wDsnnal14hNBt1v195X6n93YO7gi3orPSux9D554SkMp+Sayg84lTc362UtmYLpWnb34nqyGx9KBVTy5OGV4ljE1OwC+oTnRM+QLRCmeNxMbPZhS47T+eZtDEhVB9usk3+JM2Cogfwo7AgMBAAGjcjBwMB0GA1UdDgQWBBSJaEeNuq9Df6ZfN68Fe+I2u22ssDAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFDYd6OKdgtIBGLUyaw7XQwuRWEM6MA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAeaJV2U51rxfcqAAe5C2/fEW8KUl4iO4lMuta7N6XzP1pZIz1NkkCtIIweyNj5URYHK+HjRKSU9RLguNl0nkfxqObiMckwRudKSq69NInrZyCD66R4K77nb9lMTABSSYlsKt8oNtlhgR/1kjSSRQcHktsDcSiQGKMdkSlp4AyXf7vnHPBe4yCwYV2PpSN04kboiJ3pBlxsGwV/ZlL26M2ueYHKYCuXhdqFwxVgm52h3oeJOOt/vY4EcQq7eqHm6m03Z9b7PRzYM2KGXHDmOMk7vDpeMVlLDPSGYz1+U3sDxJzebSpbaJmT7imzUKfggEY7xxf4czfH0yj5wNzSGTOvQ==";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE2LTA0LTI4IDAzOjE4OjQ5IEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJkNGU3MjFlYzY3ZWYyZmVjYTdmYmRiZDI1YTQ1Y2ZiMzdlMTBlYTdiIjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDIwODYyMDQ3MCI7CgkiYnZycyIgPSAiMS4xIjsKCSJ0cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDIwODYyMDQ3MCI7CgkicXVhbnRpdHkiID0gIjEiOwoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtbXMiID0gIjE0NjE4Mzg3MjkyODUiOwoJInVuaXF1ZS12ZW5kb3ItaWRlbnRpZmllciIgPSAiOEUxOUVFQzQtMzNENy00NTM2LUI2MkUtMTEyQkFDNjhFRUNEIjsKCSJwcm9kdWN0LWlkIiA9ICIxMjQ0IjsKCSJpdGVtLWlkIiA9ICIxMTA4Nzk4MTUxIjsKCSJiaWQiID0gImNvbS5kb2N0b3JIeXMiOwoJInB1cmNoYXNlLWRhdGUtbXMiID0gIjE0NjE4Mzg3MjkyODUiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMTYtMDQtMjggMTA6MTg6NDkgRXRjL0dNVCI7CgkicHVyY2hhc2UtZGF0ZS1wc3QiID0gIjIwMTYtMDQtMjggMDM6MTg6NDkgQW1lcmljYS9Mb3NfQW5nZWxlcyI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZSIgPSAiMjAxNi0wNC0yOCAxMDoxODo0OSBFdGMvR01UIjsKfQ==";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}
3.  向苹果发起验证请求的参数一定要通过json格式发送:
conn.setRequestProperty("Content-type","application/json");
JSONObject obj = new JSONObject();
obj.put("receipt-data", code);
4.  App Store返回值也是一个JSON对象:
{
“status” : 0,
“receipt” : { … }
}
具体我这边是这样的:
<pre name="code" class="javascript">{
"receipt": {
"original_purchase_date_pst": "2016-04-28 03:18:49 America/Los_Angeles",
"purchase_date_ms": "1461838729285",
"unique_identifier": "d4e721ec67ef2feca7fbdbd25a45cfb37e10ea7b",
"original_transaction_id": "1000000208620470",
"bvrs": "1.1",
"transaction_id": "1000000208620470",
"quantity": "1",
"unique_vendor_identifier": "8E19EEC4-33D7-4536-B62E-112BAC68EECD",
"item_id": "1108798151",
"product_id": "1244",
"purchase_date": "2016-04-28 10:18:49 Etc/GMT",
"original_purchase_date": "2016-04-28 10:18:49 Etc/GMT",
"purchase_date_pst": "2016-04-28 03:18:49 America/Los_Angeles",
"bid": "com.doctorHys",
"original_purchase_date_ms": "1461838729285"
},
"status": 0
}

 

 

posted on 2019-06-08 21:24  月染霜华  阅读(1660)  评论(0编辑  收藏  举报