苹果内购IAP服务端验证-java篇
苹果内购:
只要你在苹果系统购买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篇相关推荐
- 苹果内购IAP记录-1
这段时间做了苹果内购IAP,做一个整理记录,主要是开发层面. 一.前期工作:在开发者账号中添加银行信息同意协议等,添加沙盒账号,添加内购商品 二.项目开发,因为项目需要支持iOS15一下的版本所以使用 ...
- Sign in with Apple(苹果授权登陆)服务端验证-测试通过版
Sign in with Apple(苹果授权登陆)服务端验证-测试通过版 1.先引用2个jwt用到的jar包 2.算法的工具类 三方登录调用验证工具类 苹果登录方式有2种,这里介绍基于JWT算法验证 ...
- 苹果内购IAP记录-2 StoreKit新版
苹果内购新版的StoreKit2只支持iOS15以上,新的nsync同步接口,简单的使用如下: @available(iOS 15.0, *) public class MXLiveIAPStoreV ...
- ios 内购正式环境_ios内购之服务端操作
{ "status": 0, "environment": "Sandbox", "receipt": { " ...
- 苹果授权登陆 服务端验证(java)
需要的jar包: jose4j-0.6.4.jar:jwks-rsa-0.9.0.jar:jjwt-0.9.1.jar: jar包下载地址:https://download.csdn.net/down ...
- 苹果内购IAP流程(转载)
一.In App Purchase概览 Store Kit代表App和App Store之间进行通信.程序将从App Store接收那些你想要提供的产品的信息,并将它们显示出来供用户购买. 当用户需要 ...
- PHP 苹果内购iap支付
$receiptData = $_POST; // 验证参数 if (strlen($receiptData['receipt']) < 1000) {return; } $receipt = ...
- iOS_苹果内购详细步骤
iOS苹果内购详细步骤 iOS开发支付的两种方式 1 Apple Pay + 调取外部支付,例如支付宝.微信.银联等 2 苹果内购IAP(In-App Purchase) 1 IAP规则详解 1.1 ...
- java(jfinal) 接入ios苹果内购(连续包月订阅),服务端将二次验证。
大致流程: 1.ios端进行支付,然后收到苹果的一串数据(也叫收据),然后ios端将其转码为BASE64编码的字符串. 2.ios端请求服务端接口,将数据传给服务端,服务端拿到数据后,通过一系列处理后 ...
- postman关闭ssl验证_【第5期】springboot:苹果内购服务端验证
苹果内购: 只要你在苹果系统购买APP中虚拟物品(虚拟货币,VIP充值等),必须通过内购方式进行支付,苹果和商家进行三七开 验证模式有两种: Validating Receipts With the ...
最新文章
- 怎样更好地使用快捷键?
- 计算机视觉与深度学习 | 基于边缘与形态学的细胞检测
- Linux编程 3 (初识bash shell与man查看手册)
- 一文了解树在前端中的应用,掌握数据结构中树的生命线
- Linux网络编程——tcp并发服务器(多进程)
- 操作系统概述 操作系统第一章知识点归纳总结
- Abp vnext Web应用程序开发教程 4 —— 集成测试
- EasyUI 搜索框
- 力扣--19删除链表中的倒数第n个节点
- Linux打开软盘镜像,怎样在Linux下制作软盘和光盘镜像
- 【Linux 编程】线程绑定 CPU
- CocosCreator接入穿山甲广告2-插屏广告
- JIRA中的史诗、故事、版本与冲刺
- ​PDF怎么转换成Word?试试这几种好用的转换方法
- Java语言程序设计数据结构基础篇第11版6.31(金融应用:信用卡号的合法性检验)信用卡号遵循某种模式。一个信用卡号必须是13-16位的整数 (java)
- 微服架构基础设施环境平台搭建 -(五)Docker常用命令
- 大连理工大学GlobalProtect使用方法
- BIM模型+实景模型融合应用
- 如何将wincc英文界面转化成中文
- git解决push错误failed to push some refs to的解决