IOS内购验证 (Java版)
此处给各位贴出apple官方文档
App 内购买项目配置流程
apple 收据文档
apple 收据responseBody字段释义IOS内购逻辑图
IOS内购验证相关代码
package xxxxx;/*** @description 苹果验证返回结果状态码枚举* @Author xc* @Date 2021/3/12 18:23**/
public enum IosStatusCodeEnum {CODE_SUCCESS("0", "收据是有效, 验证成功"),CODE_NULL("-1", "苹果服务器没有返回验证结果"),CODE_21000("21000", "没有使用HTTP POST请求方法向App Store发出请求"),CODE_21001("21001", "这个状态码不再由App Store发送"),CODE_21002("21002", "receipt-data属性中的数据格式错误或服务遇到临时问题"),CODE_21003("21003", "这张收据无法证实真伪"),CODE_21004("21004", "您提供的共享秘密与您的帐户文件中的共享秘密不匹配"),CODE_21005("21005", "收据服务器暂时无法提供收据"),CODE_21006("21006", "订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中"),CODE_21007("21007", "这个收据来自测试环境,但是它被发送到生产环境进行验证"),CODE_21008("21008", "这个收据来自生产环境,但是它被发送到测试环境进行验证"),CODE_21009("21009", "内部数据访问错误"),CODE_21010("21010", "用户帐户找不到或已被删除");public String code;public String desc;IosStatusCodeEnum(String code, String desc) {this.code = code;this.desc = desc;}public static IosStatusCodeEnum getByCode(String code) {for (IosStatusCodeEnum value : values()) {if(value.code.equals(code)) return value;}return null;}}
package xxxxx;import lombok.Data;/*** @description 所有应用内购买交易的应用内购买收据* @Author xc* @Date 2021/3/12 18:03**/
@Data
public class IosVerifyDTO {private String transaction_id; // 交易的唯一标识符private String original_purchase_date;private String quantity; // 购买的消费品数量private String original_transaction_id;private String purchase_date_pst; // 应用内购买的时间private String original_purchase_date_ms;private String purchase_date_ms;private String product_id; // IOS内购商品Idprivate String original_purchase_date_pst;private String is_trial_period;private String purchase_date;}
package xxxxx;import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import xxxxx.IosStatusCodeEnum;
import xxxxx.ServiceException;
import xxxxx.EnvironmentUtil;
import xxxxx.JsonUtils;
import xxxxx.IosVerifyDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;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.X509Certificate;
import java.util.Locale;
import java.util.Optional;/***
/*** @description IOS内购验证业务层* @Author xc* @Date 2021/3/12 18:45**/
@Service
public class IosVerifyService {// IOS内购 - 沙箱环境private static final String URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";// IOS内购 - 线上环境private static final String URL_VERIFY = "https://buy.itunes.apple.com/verifyReceipt";private static class TrustAnyTrustManager implements X509TrustManager {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[] {};}}private static class TrustAnyHostnameVerifier implements HostnameVerifier {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}}/*** 苹果支付验证(接口返回null则未查询到收据信息)** @param receipt // 收据* @param transactionId // 交易的唯一标识符* @return*/public IosVerifyDTO applePayVerify(String receipt, String transactionId) {// 当前交易标识符收据信息IosVerifyDTO iosVerifyDTO = null;String url;/*** 注 :* EnvironmentUtil 为环境工具类,可根据各位实际情况而定,如没有多环境的项目,* 可以看下文章最末尾代码片段,无环境写法*/if(EnvironmentUtil.isProd()){url = URL_VERIFY;}else {url = URL_SANDBOX;}String verifyResult = buyAppVerify(receipt, url);JSONObject resultJob = JSONObject.parseObject(verifyResult);String status = resultJob.getString("status"); // 苹果验证返回结果状态码/*** 注 :* 此处写法原因 :项目上线至 App Store 审核员支付审核使用的是沙箱支付;* 如没有此处代码,支付失败审核拒绝则被打回,需重新提交审核*/if (IosStatusCodeEnum.CODE_21007.code.equals(status)) {verifyResult = buyAppVerify(receipt, URL_SANDBOX);resultJob = JSONObject.parseObject(verifyResult);status = resultJob.getString("status");}// 收据有效, 验证成功if (!IosStatusCodeEnum.CODE_SUCCESS.code.equals(status)) {log.error("【苹果服务器验证失败】返回验证结果。 transactionId: {}, status: {}, msg: {}", transactionId, status, IosStatusCodeEnum.getByCode(status).desc);return iosVerifyDTO;}String resultReceipt = resultJob.getString("receipt");JSONObject receiptJson = JSONObject.parseObject(resultReceipt);String in_app = receiptJson.getString("in_app"); // 苹果收据列表if(StringUtils.isEmpty(in_app)) {log.error("【苹果服务器验证失败】未查询到收据信息。 transactionId: {}, status: {}, msg: {}", transactionId, status, IosStatusCodeEnum.getByCode(status).desc);return iosVerifyDTO;}JSONArray in_app_array = JSONArray.parseArray(in_app);Optional<Object> optional = in_app_array.stream().filter(r ->(JsonUtils.fromJson(r.toString(), IosVerifyDTO.class).getTransaction_id().equals(transactionId))).findFirst();if(optional.isPresent()) iosVerifyDTO = JsonUtils.fromJson(optional.get().toString(), IosVerifyDTO.class);return iosVerifyDTO;}/*** 苹果服务器验证** @param receipt 账单* @param url IOS内购核验环境地址* @url 要验证的地址* @return null 或返回结果*/private String buyAppVerify(String receipt, String url) {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","application/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\":\"" + receipt + "\"}");hurlBufOus.write(str.getBytes());hurlBufOus.flush();InputStream is = conn.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is));String line;StringBuffer sb = new StringBuffer();while ((line = reader.readLine()) != null) {sb.append(line);}return sb.toString();} catch (Exception e) {log.error("苹果服务器异常 errMsg: {}", e);/*** 注 :* 此处为自定义业务异常类*/throw new ServiceException("苹果服务器异常");}}}
/*** 注 :* 此处代码为无多环境写法* 可以将 IosVerifyService 类中 72-95行代码进行替换*/
String verifyResult = buyAppVerify(receipt, URL_VERIFY);
JSONObject resultJob = JSONObject.parseObject(verifyResult);
String status = resultJob.getString("status"); // 苹果验证返回结果状态码
if (IosStatusCodeEnum.CODE_21007.code.equals(status)) {verifyResult = buyAppVerify(receipt, URL_SANDBOX);resultJob = JSONObject.parseObject(verifyResult);status = resultJob.getString("status");
}
IOS内购验证 (Java版)相关推荐
- Unity iOS内购
前言:最近项目需要切换到iOS平台做一些提交审核和支付对接相关的工作,上一篇刚分享了最新的iOS10提交审核的一些坑,这篇分享一些内购相关的流程. Unity iOS内购 思路: Unity调用iOS ...
- 苹果内购验证(熟称苹果支付回调)java版
简介: 苹果支付是直接由ios客户端调起苹果支付并支付完成后,java后台提供一个支付回调接口供ios客户端进行同步回调,只需要在该接口进行进行验证苹果支付是否支付成功,跟微信支付和支付宝支付不一样, ...
- java集成ios内购\与ios退款通知处理
使用ios内购,需在项目数据库建立虚拟币相关表(虚拟币余额表.充值面额表.充值订单表等)上代码 苹果IAP内购验证工具类 IosVerifyUtil import javax.net.ssl.*; i ...
- iOS内购--java后台
最近公司iOS发布了新版本,被拒,原因就是没有添加内购,并被严重警告,为此,不得已要加上iOS内购功能,以下就是我为了iOS内购所写的后台代码,首先看下支付的时序图吧: 简单说下,时序图的意思吧: 第 ...
- ios内购二次验证安全性问题_苹果IOS内购二次验证返回state为21002的坑
项目是三四年前的老项目,之前有IOS内购二次验证的接口,貌似很久都没用了,然而最近IOS的妹子说接口用不了,让我看看啥问题.接口流程时很简单的,就是前端IOS在购买成功之后,接收到receipt后进行 ...
- IOS 内购IAP 自动订阅收据验证文档服务端翻译
将收据数据发送到App Store: 提交此JSON对象作为HTTP POST请求的有效负载. 中文文档:https://help.apple.com/app-store-connect/#/dev7 ...
- ios内购php验证码,PHP (Laravel) 实现 iOS 内购服务端验证
/** * @Author woann * @param Request $request * @return \Illuminate\Http\JsonResponse * @des ios内购支付 ...
- ios内购二次验证安全性问题_iOS 内购遇到的坑
一.内购沙盒测试账号在支付成功后,再次购买相同 ID 的物品,会提示如下内容的弹窗.您以购买过此APP内购项目,此项目将免费恢复 您以购买过此APP内购项目,此项目将免费恢复.PNG 原因: 当使用内 ...
- ios内购二次验证安全性问题_iOS内购之二次验证
开篇:关于iOS内购整体流程网上能找到很多.我抽丝剥茧,着重说一下二次验证及收据回传的数据问题. 二次验证 关于二次验证,其实有两种做法,第一种是在app端验证,第二种也是安全防盗的一种,在服务端进行 ...
最新文章
- LeetCode:937. Reorder Log Files
- pro*c 倒出数据库数据
- [转]Java中Runtime.exec的一些事
- pythonsubprocess执行多条shell命令_python中subprocess批量执行linux命令
- [攻防世界 pwn]——welpwn
- 搭建IIS并配置网站之旅
- CAS (1) —— Mac下配置CAS到Tomcat(服务端)(转)
- 【NOIP2017模拟6.25】小W的动漫
- 有php注入的源码,php注入3_php
- PHP用户名和密码登陆验证代码
- Atitit. 解决unterminated string literal 缺失引号
- matlab求函数在区间内最大值与最小值
- 搭建vue脚手架全教程
- python读取csv某一列 pandas_numpy和pandas实战:文件夹CSV文件中的第一列数据
- kali2020安装最新版本Java
- js对节点 属性的操作
- HTTP笔记1:网络模型与TCP协议
- ugui 转轮_(转)unity3D的FingerGestures插件
- 驭势科技无人驾驶技术走向海外,AI国潮获赞满满
- Bootloader的作用与实现