https://help.apple.com/app-store-connect/#/devb57be10e7
APP store 配置APP项目
项目下添加内购项目
添加沙盒测试账号
2.iOS-iOS内购流程(手把手图文教程)_CC__超的博客-CSDN博客_ios内购流程//这个地址有ios系统app申请账号权限等教程;
3.requestBody | Apple Developer Documentation//苹果内购官网验证接口请求参数
4.verifyReceipt | Apple Developer Documentation//苹果官网内购验证接口返回参数及调用地址
5.status | Apple Developer Documentation//官网状态码
一.苹果支付
①逻辑流程图
二,苹果支付流程介绍
①户在app端购买产品下发支付请求指令app;
②app获取用户购买的信息及指令请求苹果系统服务后台进行支付扣,;
③苹果系统服务器扣款成功后返回receipt_data加密数据和支付订单号order_id给app端;
④app端直接将返回的数据及订单号,请求java后台的验证接口;
⑤java后端直接通过HttpsURLConnection将app端携带来的参数以及验证地址url请求苹果系统服务器验证用户在app端支付的结果;(注:receipt_data加密数据不需要在java后台解密,直接传给苹果服务器)
⑥苹果系统服务器将验证的结果及订单产品信息返回给java后台服务器,java后台服务器根据返回的结果处理自己的业务;
⑦java后端处理后自己的业务后,将验证结果以及自己所要返回的内容返回给app端;
②app端在请求苹果系统服务器检查java服务端服务是否已经验证;
③苹果服务告知app端,java服务是否验证成功;
⑧app端根据苹果服务器返回的验证结果通知提示用户订单是否结束;
注:苹果支付内购,价格是以6的倍数定价,且产品订单是唯一的;
三.java代码
①调用验证工具类校验app端返回的结果是否正确
/**
* TransactionID:苹果配置的产品id
* String receipt_data:需要客户端传过来的参数2
* 苹果内购支付
*
* @Title: doIosRequest
* @Description:Ios客户端内购支付
*/
@RequestMapping("/applePay")
public Result doIosRequest(HttpServletRequest request, @RequestBody ApplePayVo applePayVo) {
Result ext = new Result();
String receipt_data = applePayVo.getReceipt_data();
String transactionID = applePayVo.getTransaction_id();
String verifyResult = iosVerifyUtil.buyAppVerify(receipt_data,applePayVo.getType());
//苹果服务器没有返回验证结果
if (verifyResult == null) {
JSONObject jsonObject= new JSONObject();
jsonObject.put("verify_status",-1);
ext.setData(jsonObject);
ext.setMessage("无订单信息!");
ext.setCode(-1);
return ext;
} else {
// 苹果验证有返回结果
//log.info("线上,苹果平台返回JSON:" + verifyResult);
JSONObject job = JSONObject.parseObject(verifyResult);
String states = job.getString("status");
//获取苹果系统服务器验证结果数据处理自己的业务
Result appPay = applePayService.getAppPay(states, job, transactionID, request, applePayVo);
return appPay;
}
}
②获取验证后的数据处理自己的业务逻辑
/**
* 苹果支付验证后
**/
public Result getAppPay(String states, JSONObject job, String transactionID, HttpServletRequest request, ApplePayVo applePayVo) {
Result ext = new Result();
//log.info("苹果平台返回值:job" + job);
//判断是否验证成功
if ("0".equals(states)) {
//app端所提供的收据是有效的 验证成功
String r_receipt = job.getString("receipt");
JSONObject returnJson = JSONObject.parseObject(r_receipt);
String in_app = returnJson.getString("in_app");
JSONObject in_appJson = JSONObject.parseObject(in_app.substring(1, in_app.length() - 1));
String transactionId = in_appJson.getString("transaction_id");
//产品id
String productId = in_appJson.getString("product_id");
//苹果支付订单状态
String in_app_ownership_type = in_appJson.getString("in_app_ownership_type");
//如果获取验证后的支付订单单号与app端传来的支付订单号一致并且状态为已支付状态则处理自己的业务
if (transactionID.equals(transactionId) && in_app_ownership_type.equals("PURCHASED")) {
===================处理自己的业务============================
}
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("verify_status", -1);
ext.setMessage("receipt数据有问题");
ext.setData(jsonObject);
ext.setCode(-1);
return ext;
}
③发送苹果系统服务器支付结果验证工具类
package com.asftek.pay.common.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.net.ssl.*;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Locale;
@Component
public class IosVerifyUtil {
private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
@Value("${applepay.verifyUrl}")
private String url_apple_pay;
@Value("${applepay.verifyTestUrl}")
private String test_url_apple_pay;
/**
* 苹果服务器验证
*
* @param receipt 账单
* @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
* @url 要验证的地址
*/
public String buyAppVerify(String receipt,int type) {
//环境判断 线上/开发环境用不同的请求链接
try {
String url=null;
if (type==0){
url=test_url_apple_pay; //沙盒环境,测试
}else {
//生成
url=url_apple_pay;
}
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);
conn.setConnectTimeout(3000);
BufferedOutputStream hurlBufOus = new BufferedOutputStream(conn.getOutputStream());
//拼成固定的格式传给平台
String str = String.format(Locale.CHINA, "{\"receipt-data\":\"" + receipt + "\"}");
// 直接将receipt当参数发到苹果验证就行,不用拼格
// String str = String.format(Locale.CHINA, receipt);
hurlBufOus.write(str.getBytes());
hurlBufOus.flush();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (Exception ex) {
System.out.println("苹果服务器异常");
ex.printStackTrace();
}
return null;
}
}
④java调用验证后苹果系统服务器返回参数
新版IOS返回(7.0以后)
{
"receipt": {
"receipt_type": "ProductionSandbox",
"adam_id": 0,
"app_item_id": 0,
"bundle_id": "com.xxxx.xxxx",
"application_version": "1",
"download_id": 0,
"version_external_identifier": 0,
"receipt_creation_date": "2021-11-01 09:20:51 Etc/GMT",
"receipt_creation_date_ms": "1635758451000",
"receipt_creation_date_pst": "2021-11-01 02:20:51 America/Los_Angeles",
"request_date": "2021-11-01 09:20:52 Etc/GMT",
"request_date_ms": "1635758452973",
"request_date_pst": "2021-11-01 02:20:52 America/Los_Angeles",
"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",
"original_application_version": "1.0",
"in_app": [{
"quantity": "1",
"product_id": "",//唯一的
"transaction_id": "1000000901786189",
"original_transaction_id": "1000000901786189",
"purchase_date": "2021-11-01 09:13:33 Etc/GMT",
"purchase_date_ms": "1635758013000",
"purchase_date_pst": "2021-11-01 02:13:33 America/Los_Angeles",
"original_purchase_date": "2021-11-01 09:13:33 Etc/GMT",
"original_purchase_date_ms": "1635758013000",
"original_purchase_date_pst": "2021-11-01 02:13:33 America/Los_Angeles",
"is_trial_period": "false",
"in_app_ownership_type": "PURCHASED"
}]
},
"environment": "Sandbox",
"status": 0
}
⑤错误码