• 此处给各位贴出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版)相关推荐

  1. Unity iOS内购

    前言:最近项目需要切换到iOS平台做一些提交审核和支付对接相关的工作,上一篇刚分享了最新的iOS10提交审核的一些坑,这篇分享一些内购相关的流程. Unity iOS内购 思路: Unity调用iOS ...

  2. 苹果内购验证(熟称苹果支付回调)java版

    简介: 苹果支付是直接由ios客户端调起苹果支付并支付完成后,java后台提供一个支付回调接口供ios客户端进行同步回调,只需要在该接口进行进行验证苹果支付是否支付成功,跟微信支付和支付宝支付不一样, ...

  3. java集成ios内购\与ios退款通知处理

    使用ios内购,需在项目数据库建立虚拟币相关表(虚拟币余额表.充值面额表.充值订单表等)上代码 苹果IAP内购验证工具类 IosVerifyUtil import javax.net.ssl.*; i ...

  4. iOS内购--java后台

    最近公司iOS发布了新版本,被拒,原因就是没有添加内购,并被严重警告,为此,不得已要加上iOS内购功能,以下就是我为了iOS内购所写的后台代码,首先看下支付的时序图吧: 简单说下,时序图的意思吧: 第 ...

  5. ios内购二次验证安全性问题_苹果IOS内购二次验证返回state为21002的坑

    项目是三四年前的老项目,之前有IOS内购二次验证的接口,貌似很久都没用了,然而最近IOS的妹子说接口用不了,让我看看啥问题.接口流程时很简单的,就是前端IOS在购买成功之后,接收到receipt后进行 ...

  6. IOS 内购IAP 自动订阅收据验证文档服务端翻译

    将收据数据发送到App Store: 提交此JSON对象作为HTTP POST请求的有效负载. 中文文档:https://help.apple.com/app-store-connect/#/dev7 ...

  7. ios内购php验证码,PHP (Laravel) 实现 iOS 内购服务端验证

    /** * @Author woann * @param Request $request * @return \Illuminate\Http\JsonResponse * @des ios内购支付 ...

  8. ios内购二次验证安全性问题_iOS 内购遇到的坑

    一.内购沙盒测试账号在支付成功后,再次购买相同 ID 的物品,会提示如下内容的弹窗.您以购买过此APP内购项目,此项目将免费恢复 您以购买过此APP内购项目,此项目将免费恢复.PNG 原因: 当使用内 ...

  9. ios内购二次验证安全性问题_iOS内购之二次验证

    开篇:关于iOS内购整体流程网上能找到很多.我抽丝剥茧,着重说一下二次验证及收据回传的数据问题. 二次验证 关于二次验证,其实有两种做法,第一种是在app端验证,第二种也是安全防盗的一种,在服务端进行 ...

最新文章

  1. LeetCode:937. Reorder Log Files
  2. pro*c 倒出数据库数据
  3. [转]Java中Runtime.exec的一些事
  4. pythonsubprocess执行多条shell命令_python中subprocess批量执行linux命令
  5. [攻防世界 pwn]——welpwn
  6. 搭建IIS并配置网站之旅
  7. CAS (1) —— Mac下配置CAS到Tomcat(服务端)(转)
  8. 【NOIP2017模拟6.25】小W的动漫
  9. 有php注入的源码,php注入3_php
  10. PHP用户名和密码登陆验证代码
  11. Atitit. 解决unterminated string literal 缺失引号
  12. matlab求函数在区间内最大值与最小值
  13. 搭建vue脚手架全教程
  14. python读取csv某一列 pandas_numpy和pandas实战:文件夹CSV文件中的第一列数据
  15. kali2020安装最新版本Java
  16. js对节点 属性的操作
  17. HTTP笔记1:网络模型与TCP协议
  18. ugui 转轮_(转)unity3D的FingerGestures插件
  19. 驭势科技无人驾驶技术走向海外,AI国潮获赞满满
  20. Bootloader的作用与实现

热门文章

  1. tableau -- 月销售额年同比增长
  2. Linux ln 命令是什么?C/C++代码实现
  3. 拍照相册和裁剪保存图片集合
  4. Android 7.0下拍照和裁剪图片
  5. 如何在电脑上添加蓝牙耳机设备
  6. VUE-17 图片的获取,购物车商品数量的添加与减少,计算整个购物车商品的价格(循环)
  7. 51单片机检测温湿度并且上传到阿里云
  8. 机械键盘按键失灵解决办法(亲测有效,不用换不用拆,5分钟搞定)
  9. 技巧 | 清理电脑垃圾
  10. 【01】什么是概率图模型?