苹果内购:

只要你在苹果系统购买APP中虚拟物品(虚拟货币,VIP充值等),必须通过内购方式进行支付,苹果和商家进行三七开

验证模式有两种:

Validating Receipts With the App Store 通过访问苹果接口进行验证。
Validating Receipts Locally 本地代码解码进行验证

官方验证文档地址:https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1

官方文档说明

image.png

我这里主要说一下服务端验证模式,大致流程为

  • app进行支付,然后收到苹果的收据(一串很长的BASE64编码的字符串)
  • app请求服务端,将收据给到服务端,服务端拿到收据请求苹果服务器验证收据是否为真
  • 服务端验证收据真伪,验证当前支付的交易是否成功,成功则处理支付成功的业务逻辑

进行代码前,首先使用postman将收据发送给苹果服务器,熟悉一下返回的数据结构

image.png

重点说一下我的理解

在官方文档和各个私人博客中都没有明确说明要验证的内容,百度一整天得到的验证逻辑为
苹果服务器只验证了收据的真伪,而收据包含多个交易的信息。
所以,我们验证当status字段为0(即收据为真),且当前交易ID(app传递到后台)在收据交易列表中,即可认为交易支付成功
同时app传递当前支付产品的ID(我们内部的商品ID),处理该商品的订单

注意:这个接口可以多次请求,所以应当将交易ID与订单进行绑定,防止一个交易生成多个订单

上验证代码,首先来一个百度的工具类,功能为组装请求数据,发送http请求


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;/*** 苹果IAP内购验证工具类* Created by wangqichang on 2019/2/26.*/
public class IosVerifyUtil {private static class TrustAnyTrustManager implements X509TrustManager {public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[] {};}}private static class TrustAnyHostnameVerifier implements HostnameVerifier {public boolean verify(String hostname, SSLSession session) {return true;}}private static final String url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";private static final String url_verify = "https://buy.itunes.apple.com/verifyReceipt";/*** 苹果服务器验证** @param receipt*            账单* @url 要验证的地址* @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt**/public static String buyAppVerify(String receipt,int type) {//环境判断 线上/开发环境用不同的请求链接String url = "";if(type==0){url = url_sandbox; //沙盒测试}else{url = url_verify; //线上测试}//String url = EnvUtils.isOnline() ?url_verify : url_sandbox;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", "text/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 = null;StringBuffer sb = new StringBuffer();while ((line = reader.readLine()) != null) {sb.append(line);}return sb.toString();} catch (Exception ex) {System.out.println("苹果服务器异常");ex.printStackTrace();}return null;}/*** 用BASE64加密** @param str* @return*/public static String getBASE64(String str) {byte[] b = str.getBytes();String s = null;if (b != null) {s = new sun.misc.BASE64Encoder().encode(b);}return s;}}

验证逻辑代码

/*** 苹果内购校验* @param priceId 会员价格ID* @param transactionId 苹果内购交易ID* @param payload 校验体(base64字符串)* @return*/@PostMapping("/iospay")public Map<String, Object> iosPay(Long priceId,String transactionId, String payload) {log.info("苹果内购校验开始,交易ID:" + transactionId + " base64校验体:" + payload);Shipper shipper = getLoginShipper();if (shipper == null) {return failure("未登录");}//线上环境验证String verifyResult = IosVerifyUtil.buyAppVerify(payload, 1);if (verifyResult == null) {return failure("苹果验证失败,返回数据为空");} else {log.info("线上,苹果平台返回JSON:" + verifyResult);JSONObject appleReturn = JSONObject.parseObject(verifyResult);String states = appleReturn.getString("status");//无数据则沙箱环境验证if ("21007".equals(states)) {verifyResult = IosVerifyUtil.buyAppVerify(payload, 0);log.info("沙盒环境,苹果平台返回JSON:" + verifyResult);appleReturn = JSONObject.parseObject(verifyResult);states = appleReturn.getString("status");}log.info("苹果平台返回值:appleReturn" + appleReturn);// 前端所提供的收据是有效的    验证成功if (states.equals("0")) {String receipt = appleReturn.getString("receipt");JSONObject returnJson = JSONObject.parseObject(receipt);String inApp = returnJson.getString("in_app");List<HashMap> inApps = JSONObject.parseArray(inApp, HashMap.class);if (!CollectionUtils.isEmpty(inApps)) {ArrayList<String> transactionIds = new ArrayList<String>();for (HashMap app : inApps) {transactionIds.add((String) app.get("transaction_id"));}//交易列表包含当前交易,则认为交易成功if (transactionIds.contains(transactionId)) {//处理业务逻辑VipOrder vipOrder = vipOrderService.saveVipOrder(shipper, priceId, EnumPayType.APPLE_IN_APP_PURCHASES.getValue(),transactionId);vipOrderService.paySuccess(vipOrder.getOrderCode(),null);log.info("交易成功,新增并处理订单:{}",vipOrder.getOrderCode());return success("充值成功");}return failure("当前交易不在交易列表中");}return failure("未能获取获取到交易列表");} else {return failure("支付失败,错误码:" + states);}}}

同学们可以直接copy,注意删除我个人的业务代码即可
这个时候ios开发提出了一个问题,当支付完成,还没有发起验证,app闪退或关机时,岂不是无法生成订单?
这个时候可以先保存到本地,每次app启动判断本地是否有尚未验证的交易,有则发起验证请求。验证返回成功则删除本地记录

作者:老王_KICHUN
链接:https://www.jianshu.com/p/976fc6090cfa
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

苹果内购IAP服务端验证-java篇相关推荐

  1. 苹果内购IAP记录-1

    这段时间做了苹果内购IAP,做一个整理记录,主要是开发层面. 一.前期工作:在开发者账号中添加银行信息同意协议等,添加沙盒账号,添加内购商品 二.项目开发,因为项目需要支持iOS15一下的版本所以使用 ...

  2. Sign in with Apple(苹果授权登陆)服务端验证-测试通过版

    Sign in with Apple(苹果授权登陆)服务端验证-测试通过版 1.先引用2个jwt用到的jar包 2.算法的工具类 三方登录调用验证工具类 苹果登录方式有2种,这里介绍基于JWT算法验证 ...

  3. 苹果内购IAP记录-2 StoreKit新版

    苹果内购新版的StoreKit2只支持iOS15以上,新的nsync同步接口,简单的使用如下: @available(iOS 15.0, *) public class MXLiveIAPStoreV ...

  4. ios 内购正式环境_ios内购之服务端操作

    { "status": 0, "environment": "Sandbox", "receipt": { " ...

  5. 苹果授权登陆 服务端验证(java)

    需要的jar包: jose4j-0.6.4.jar:jwks-rsa-0.9.0.jar:jjwt-0.9.1.jar: jar包下载地址:https://download.csdn.net/down ...

  6. 苹果内购IAP流程(转载)

    一.In App Purchase概览 Store Kit代表App和App Store之间进行通信.程序将从App Store接收那些你想要提供的产品的信息,并将它们显示出来供用户购买. 当用户需要 ...

  7. PHP 苹果内购iap支付

    $receiptData = $_POST; // 验证参数 if (strlen($receiptData['receipt']) < 1000) {return; } $receipt = ...

  8. iOS_苹果内购详细步骤

    iOS苹果内购详细步骤 iOS开发支付的两种方式 1 Apple Pay + 调取外部支付,例如支付宝.微信.银联等 2 苹果内购IAP(In-App Purchase) 1 IAP规则详解 1.1 ...

  9. java(jfinal) 接入ios苹果内购(连续包月订阅),服务端将二次验证。

    大致流程: 1.ios端进行支付,然后收到苹果的一串数据(也叫收据),然后ios端将其转码为BASE64编码的字符串. 2.ios端请求服务端接口,将数据传给服务端,服务端拿到数据后,通过一系列处理后 ...

  10. postman关闭ssl验证_【第5期】springboot:苹果内购服务端验证

    ​苹果内购: 只要你在苹果系统购买APP中虚拟物品(虚拟货币,VIP充值等),必须通过内购方式进行支付,苹果和商家进行三七开 验证模式有两种: Validating Receipts With the ...

最新文章

  1. 怎样更好地使用快捷键?
  2. 计算机视觉与深度学习 | 基于边缘与形态学的细胞检测
  3. Linux编程 3 (初识bash shell与man查看手册)
  4. 一文了解树在前端中的应用,掌握数据结构中树的生命线
  5. Linux网络编程——tcp并发服务器(多进程)
  6. 操作系统概述 操作系统第一章知识点归纳总结
  7. Abp vnext Web应用程序开发教程 4 —— 集成测试
  8. EasyUI 搜索框
  9. 力扣--19删除链表中的倒数第n个节点
  10. Linux打开软盘镜像,怎样在Linux下制作软盘和光盘镜像
  11. 【Linux 编程】线程绑定 CPU
  12. CocosCreator接入穿山甲广告2-插屏广告
  13. JIRA中的史诗、故事、版本与冲刺
  14. ​PDF怎么转换成Word?试试这几种好用的转换方法
  15. Java语言程序设计数据结构基础篇第11版6.31(金融应用:信用卡号的合法性检验)信用卡号遵循某种模式。一个信用卡号必须是13-16位的整数 (java)
  16. 微服架构基础设施环境平台搭建 -(五)Docker常用命令
  17. 大连理工大学GlobalProtect使用方法
  18. BIM模型+实景模型融合应用
  19. 如何将wincc英文界面转化成中文
  20. git解决push错误failed to push some refs to的解决

热门文章

  1. Opencv之给图片加水印
  2. 《趣味知识博文》小W与小L带你聊天式备考CDA Level Ⅰ(三)
  3. jupyter改字体主题美化
  4. 8421BCD码与十进制的转换
  5. 显示Java国家列表
  6. Ubuntu20.04的一些功能设置记录(持续更新)
  7. It has been compressed and nested jar files must be stored without compression
  8. PPT导出高分辨率dpi图片
  9. php 检测是否是微信浏览器,PHP判断设备是否为微信浏览器或QQ浏览器
  10. luci开发小插件_luci框架-LUA的一个web框架使用