苹果支付的逻辑如下
首先客户端先请求苹果支付中心,支付中心返回给客户端一堆加密的数据。
然后客户端把这段加密的数据
base64
之后传给后端。最后由后端再去请求苹果支付中心来验证这次购买是否成功。验证通过,服务器端对业务逻辑进行处理(增加应用内的金币等)。
代码实现
服务器端请求验证通过之后的数据应该类似于
{ environment = Sandbox; receipt = { "adam_id" = 0; "app_item_id" = 0; "application_version" = 201512181924; "bundle_id" = "me.topit.awesome.t16.pk"; "download_id" = 0; "in_app" = ( { "is_trial_period" = false; "original_purchase_date" = "2016-03-29 07:55:05 Etc/GMT"; "original_purchase_date_ms" = 1459238105000; "original_purchase_date_pst" = "2016-03-29 00:55:05 America/Los_Angeles"; "original_transaction_id" = 1000000202075562; "product_id" = "me.topit.sixteenpk.6"; "purchase_date" = "2016-03-29 07:55:05 Etc/GMT"; "purchase_date_ms" = 1459238105000; "purchase_date_pst" = "2016-03-29 00:55:05 America/Los_Angeles"; quantity = 1; "transaction_id" = 1000000202075562; } ); "original_application_version" = "1.0"; "original_purchase_date" = "2013-08-01 07:00:00 Etc/GMT"; "original_purchase_date_ms" = 1375340400000; "original_purchase_date_pst" = "2013-08-01 00:00:00 America/Los_Angeles"; "receipt_creation_date" = "2016-03-29 07:55:06 Etc/GMT"; "receipt_creation_date_ms" = 1459238106000; "receipt_creation_date_pst" = "2016-03-29 00:55:06 America/Los_Angeles"; "receipt_type" = ProductionSandbox; "request_date" = "2016-03-29 07:55:08 Etc/GMT"; "request_date_ms" = 1459238108417; "request_date_pst" = "2016-03-29 00:55:08 America/Los_Angeles"; "version_external_identifier" = 0; }; status = 0; }
首先验证status
是否为0,0表示正常。然后才是其他业务逻辑的判断。
String endpoint; if(TopitConfig.getBoolean("app.debug")){ endpoint = "https://sandbox.itunes.apple.com/verifyReceipt"; }else{ endpoint = "https://buy.itunes.apple.com/verifyReceipt"; } JSONObject jsonObject = new JSONObject(); jsonObject.put("receipt-data",data); BasicHeader[] basicHeaders = new BasicHeader[1]; basicHeaders[0] = new BasicHeader("Content-Type","application/json"); String res = HttpRequestUtil.post(endpoint, jsonObject.toString(), basicHeaders, true); JSONObject resObject = new JSONObject(res); try{ int status = Integer.parseInt(resObject.get("status").toString()); if(status == 0){ JSONObject inApp = (JSONObject) resObject.getJSONObject("receipt").getJSONArray("in_app").get(0); int ts = (int) (inApp.getLong("purchase_date_ms")/1000); rechargeService.applePay(loginId,inApp.get("product_id").toString(),inApp.get("transaction_id").toString(),ts); return success(); }else{ return error("充值失败"); } }catch (Exception e){ e.printStackTrace(); return error("充值失败"); }
上面用到HTTPS POST 直接发送实体内容的方法,如下
import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.scheme.Scheme; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.apache.http.conn.ssl.SSLSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; /** * Created by zhoumengkang on 19/1/16. */ public class HttpRequestUtil { public static String post(String url, String data, Header[] headers, boolean ssl) { HttpClient httpClient = getClient(ssl); String responseText = ""; HttpResponse httpResponse = null; try { HttpPost httpPost = new HttpPost(url); RequestConfig requestConfig = RequestConfig.custom(). setSocketTimeout(5000). setConnectTimeout(5000). build(); httpPost.setConfig(requestConfig); httpPost.setHeaders(headers); httpPost.setEntity(new StringEntity(data, "UTF-8")); httpResponse = httpClient.execute(httpPost); if (httpResponse.getStatusLine().getStatusCode() == 200) { responseText = EntityUtils.toString(httpResponse.getEntity()); } } catch (Exception e) { e.printStackTrace(); } return responseText; } public static HttpClient getClient(boolean isSSL) { HttpClient httpClient = new DefaultHttpClient(); if (isSSL) { X509TrustManager xtm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; try { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[]{xtm}, null); SSLSocketFactory socketFactory = new SSLSocketFactory(ctx); httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory)); } catch (Exception e) { throw new RuntimeException(); } } return httpClient; } }
是不是苹果测试服务器不稳定?偶尔会返回错误,但是实际我请求都是购买同一个商品
{"status":21002, "exception":"java.lang.IllegalArgumentException"}
同样的请求多次操作,发现有成功有失败,不知道是什么原因。
The data in the receipt-data property was malformed or missing.