-
2019-11-29 16:38:38
controller代码
@PostMapping("chongzhi") @ApiOperation("苹果内购支付返回") public void iosConfirmOrder(String receipt_data,Integer payMoney) { //首先调用正式环境查看验证结果; String verifyResult = IosVerifyUtil.buyAppVerify(receipt_data, 1);//type:1->正式环境,发送平台验证 if (verifyResult == null) { //如果验证结果不存在;提示前端;订单信息不存在; return ResultModel.error("充值验证不通过!"); } JSONObject job = JSONObject.parseObject(verifyResult); String states = job.getString("status"); //states为21007表示,收据信息是沙盒测试用;需要请求沙盒测试地址对凭证进行校验 if ("21007".equals(states)) { //如果正式环境校验不成功,则请求沙盒测试地址对凭证进行校验 verifyResult = IosVerifyUtil.buyAppVerify(receipt_data, 0);//type:0->沙盒测试,发送平台验证 job = JSONObject.parseObject(verifyResult); states = job.getString("status"); } if (states.equals("0")) { // 前端所提供的收据是有效的,验证成功 String r_receipt = job.getString("receipt"); JSONObject returnJson = JSONObject.parseObject(r_receipt); String in_app = returnJson.getString("in_app"); JSONArray jsonArray = JSONArray.parseArray(in_app); JSONObject in_appJson = jsonArray.getJSONObject(jsonArray.size() - 1); System.out.println("IOS支付验证结果3:" + in_appJson); String product_id = in_appJson.getString("product_id");//产品ID String transaction_id = in_appJson.getString("transaction_id"); // 订单号 //业务代码 //支付成功 } else { //支付失败 } }
工具类
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; 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; } }
更多相关内容 -
Java对接iOS内购,回调地址验证.zip
2020-04-28 18:09:48主要是Java对接iOS内购,回调验证相关的方法, 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号,二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的... -
java 苹果支付(内购)
2022-05-04 14:29:591、app配置苹果内购项目(iOS系统app端负责) https://help.apple.com/app-store-connect/#/devb57be10e7 APP store 配置APP项目 项目下添加内购项目 添加沙盒测试账号 2.iOS-iOS内购流程(手把手图文...1、app配置苹果内购项目(iOS系统app端负责)
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 }
⑤错误码
-
-
java对接IOS 内购
2019-12-19 16:54:18一、首先我们先简单理一下整个内购的核心流程: ①客户端发起支付订单 ②客户端监听购买结果 ③苹果回调订单购买成功时,客户端把苹果给的receipt_data和一些订单信息上报给服务器 ④后台服务器拿receipt_data向苹果...一、首先我们先简单理一下整个内购的核心流程:
①客户端发起支付订单
②客户端监听购买结果
③苹果回调订单购买成功时,客户端把苹果给的receipt_data和一些订单信息上报给服务器
④后台服务器拿receipt_data向苹果服务器校验
⑤苹果服务器向返回status结果,含义如下,其中为0时表示成功。
21000 App Store无法读取你提供的JSON数据
21002 收据数据不符合格式
21003 收据无法被验证
21004 你提供的共享密钥和账户的共享密钥不一致
21005 收据服务器当前不可用
21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
21008 收据信息是产品环境中使用,但却被发送到测试环境中验证
⑥服务器发现订单校验成功后,会把这笔订单存起来,transaction_id用MD5值映射下,保存到数据库,防止同一笔订单,多次发放内购商品。苹果服务器返回给我们数据:
{ "environment": "Sandbox", "receipt": { "adam_id": 0, "app_item_id": 0, "application_version": "2.0.6", "bundle_id": "com.appstoreMJB.mobao", "download_id": 0, "in_app": [{ "is_trial_period": "false", "original_purchase_date": "2019-10-25 01:07:14 Etc/GMT", "original_purchase_date_ms": "1571965634000", "original_purchase_date_pst": "2019-10-24 18:07:14 America/Los_Angeles", "original_transaction_id": "1000000583857816", "product_id": "com.wha.***.6", "purchase_date": "2019-10-25 01:07:14 Etc/GMT", "purchase_date_ms": "1571965634000", "purchase_date_pst": "2019-10-24 18:07:14 America/Los_Angeles", "quantity": "1", "transaction_id": "1000000583857816" }], "original_application_version": "1.0", "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", "receipt_creation_date": "2019-10-25 01:07:14 Etc/GMT", "receipt_creation_date_ms": "1571965634000", "receipt_creation_date_pst": "2019-10-24 18:07:14 America/Los_Angeles", "receipt_type": "ProductionSandbox", "request_date": "2019-10-25 01:07:16 Etc/GMT", "request_date_ms": "1571965636457", "request_date_pst": "2019-10-24 18:07:16 America/Los_Angeles", "version_external_identifier": 0 }, "status": 0 }
验证代码:
package com.wha.controller; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.wha.dao.RecRechargeMapper; import com.wha.model.RecRecharge; import com.wha.model.sys.RequestLog; import com.wha.service.UserService; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.wha.model.ReturnData; import com.wha.service.PayService; import com.wha.util.CommonUtils; @Controller @RequestMapping("iap") public class IapController { private Logger logger = Logger.getLogger(this.getClass()); @Autowired private PayService payService; @Autowired private RecRechargeMapper recRechargeMapper; // 正式 购买凭证验证地址 private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt"; // 沙箱 购买凭证验证地址 private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt"; /** * 接收iOS端发过来的购买凭证 * * @param userId * @param certificateCode * @param * @throws IOException */ @RequestMapping("/setIapCertificate") public void setIapCertificate(String userId, String certificateCode , HttpServletResponse response, HttpServletRequest request) throws IOException { if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(certificateCode )) { CommonUtils.returnCode(response, new ReturnData(1, "缺少参数", null)); return; } String url = certificateUrl; try { String sendHttpsCoon = sendHttpsCoon(url, certificateCode); JSONObject json = JSONObject.parseObject(sendHttpsCoon); if ("21007".equals(json.get("status").toString())) { url = certificateUrlTest; sendHttpsCoon = sendHttpsCoon(url, certificateCode); //发送请求 } JSONObject jsonObject = JSONObject.parseObject(sendHttpsCoon); if ("0".equals(jsonObject.get("status").toString())) { //苹果服务器向返回status结果 JSONArray inapp = (JSONArray) ((JSONObject) jsonObject.get("receipt")).get("in_app"); if (inapp.size() > 0) { //如果订单状态成功在判断in_app这个字段有没有,没有直接就返回失败了。如果存在的话,遍历整个数组,通过客户端给的transaction_id 来比较 JSONObject parseObject = (JSONObject) inapp.get(0); String product_id = parseObject.get("product_id").toString(); String transaction_id = parseObject.get("transaction_id").toString(); RecRecharge recRecharge = recRechargeMapper.selectByTransactionId(transaction_id); if (!CommonUtils.isEmptyString(product_id) && !CommonUtils.isEmptyString(transaction_id) && null == recRecharge) {//判重,避免重复分发内购商品。收到客户端上报的transaction_id后,直接MD5后去数据库查,能查到说明是重复订单就不做处理 String[] split = product_id.split("com.wha.***."); //获取订单价格 payService.payReturn("A" + transaction_id, userId, split[1]);//处理自己的逻辑,将transaction_id存入数据库,完成订单 } } } CommonUtils.returnCode(response, new ReturnData(0, "ok", jsonObject)); } catch (Exception e) { logger.error("购买凭证验证错误", e); CommonUtils.returnCode(response, new ReturnData(1, "购买凭证验证错误", null)); } } /** * 重写X509TrustManager */ private static TrustManager myX509TrustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }; /** * 发送请求 * * @param url * @param * @return */ private String sendHttpsCoon(String url, String code) { if (url.isEmpty()) { return null; } try { // 设置SSLContext SSLContext ssl = SSLContext.getInstance("SSL"); ssl.init(null, new TrustManager[]{myX509TrustManager}, null); // 打开连接 HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection(); // 设置套接工厂 conn.setSSLSocketFactory(ssl.getSocketFactory()); // 加入数据 conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setRequestProperty("Content-type", "application/json"); JSONObject obj = new JSONObject(); obj.put("receipt-data", code); BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream()); buffOutStr.write(obj.toString().getBytes()); buffOutStr.flush(); buffOutStr.close(); // 获取输入流 BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line = null; StringBuffer sb = new StringBuffer(); while ((line = reader.readLine()) != null) { sb.append(line); } return sb.toString(); } catch (Exception e) { return null; } } }
丢单:
引起内购丢单的主要操作其实是当用户点击内购商品时,苹果服务器太慢了,支付页面一直不出来。结果用户退出或者杀死App,这时候在Home页面,支付框又弹出来了,然后用户点击支付,成功后在打开App发现丢单。
一般这种只要你在Appdelegate的didFinishLaunchingWithOptions方法就开始对苹果内购回调做监听,然后把所有相关内购的东西抽出来做一个单例即可解决丢单 -
java服务端处理ios内购回调通知示例
2021-06-17 20:37:40有没有大佬有java服务端处理ios内购回调通知示例 -
苹果内购demo
2017-12-09 14:27:10苹果内购demo -
iOS内购实现及测试CheckList
2021-03-23 14:12:38iOS内购实现及测试CheckList.免费+应用内购买的模式已经被证明了是最有效的盈利模式,所以实现内购功能可能是很多开发者必做的工作和必备的技能了。但是鉴于内购这块坑不算少,另外因为sandbox测试所需要特定的配置... -
iOS内购--java后台
2019-02-15 09:49:15最近公司iOS发布了新版本,被拒,原因就是没有添加内购,并被严重警告,为此,不得已要加上iOS内购功能,以下就是我为了iOS内购所写的后台代码,首先看下支付的时序图吧: 简单说下,时序图的意思吧: 第一...最近公司iOS发布了新版本,被拒,原因就是没有添加内购,并被严重警告,为此,不得已要加上iOS内购功能,以下就是我为了iOS内购所写的后台代码,首先看下支付的时序图吧:
简单说下,时序图的意思吧:
第一步: 客户端请求java服务器,在数据创建一个订单号,返回给客户端;
第二步: 客户端请求支付,apple返回结果值receipt-data;
第三步: 客户端拿着返回的receipt-data给java服务端,java服务端去apple服务器验证是否正确之类的,最后返回给客户端验证结果。
下面是我的java后台代码:
首先在自己服务器创建一条订单记录,这个完全是根据自己这边的业务需求去写就好了,不需要说很多,记得把你这边生成的订单号返回给前端,不然后面没有办法拿到唯一标识,因为苹果那边必须要走到最后一步才能拿到流水号,因此,我们这边需要创建订单号,当做唯一标识,传给前端,唉,其实,这样也是会有问题的,因为有可能会被搞。
下面是我们的服务器拿到前端给的receipt-data去苹果服务器验证的代码:
后端的验证代码:
public ResultVO iPayNotify(HttpServletRequest request, String productId, String receipt, String orderNo) { int userId = JWTUtil.getUserIdFromHeader(request); String verifyResult = IosVerifyUtil.buyAppVerify(receipt, 1); //1.先线上测试 发送平台验证 if (verifyResult == null) { // 苹果服务器没有返回验证结果 return ResultVOUtil.error(ResultEnum.ORDER_NOT_EX); } else { // 苹果验证有返回结果 JSONObject job = JSONObject.parseObject(verifyResult); String states = job.getString("status"); if ("21007".equals(states)) { //是沙盒环境,应沙盒测试,否则执行下面 verifyResult = IosVerifyUtil.buyAppVerify(receipt, 0); //2.再沙盒测试 发送平台验证 job = JSONObject.parseObject(verifyResult); states = job.getString("status"); } if (states.equals("0")) { // 前端所提供的收据是有效的 验证成功 String r_receipt = job.getString("receipt"); JSONObject returnJson = JSONObject.parseObject(r_receipt); String in_app = returnJson.getString("in_app"); JSONArray jsonArray = JSONArray.parseArray(in_app); for (int i = 0; i < jsonArray.size(); i++) { if (productId.equals(jsonArray.getJSONObject(i).get("product_id") == null ? null : jsonArray.getJSONObject(i).get("product_id").toString())) { Map<String, Object> paraMap = new HashMap<>(); String transaction_id = jsonArray.getJSONObject(i).get("transaction_id") == null ? null : jsonArray.getJSONObject(i).get("transaction_id").toString(); SysOrder order = sysOrderService.getOrderInfoBySeq(transaction_id); if (!StringUtils.isEmpty(order) && order.getUserId() != userId) { return ResultVOUtil.error(ResultEnum.APPID_ALREADY_BINDING_USER); } paraMap.put("userId", userId); paraMap.put("orderNo", orderNo); SysOrder sysOrder = sysOrderService.getOrderInfoByOrderNo(paraMap); if (StringUtils.isEmpty(sysOrder)) { return ResultVOUtil.error(ResultEnum.ORDER_NOT_EX); } sysOrder.setPaySeq(transaction_id); sysOrder.setPayStatus(PayStatusEnum.PAID.getCode()); paraMap.clear(); //更改用户的优惠券状态 paraMap.put("userId", sysOrder.getUserId()); paraMap.put("id", sysOrder.getGoodsId()); Goods goods = goodsService.getGoodsInfo(paraMap); // paraMap.put("userId", userId); paraMap.put("type", goods.getType()); List<CouponVO> couponList = couponService.getCouponListByGoodsType(paraMap); if (!couponList.isEmpty()) { couponService.updateCouponByMap(couponList, sysOrder.getId()); } sysOrder.setUpdateTime(new Date()); sysOrderService.saveOrUpdate(sysOrder, sysOrder.getId()); //可以做你们自己业务的一些操作吧,这边就给出伪代码了 } } } else { return ResultVOUtil.error(ResultEnum.RECEIPT_DATA_ERROR); } } return ResultVOUtil.success(); }
其中用到的工具类IosVerifyUtil :
package com.zhima.utils; 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; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; /** * 苹果IAP内购验证工具类 * @ClassName: IosVerify * @Description:Apple Pay */ 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("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 = 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; } }
以上,就是我这次为iOS写的java后台,其中有考虑不全的地方,如果各位大佬有好的想法或者看法建议,可以留言一起讨论一下。
-
java对接ios内购流程
2019-05-30 16:56:361.流程图: 2.java代码(可增加对iosData使用记录的查询,防止重复使用,... //走ios内购验证 //iosData = {"receipt-data" : "MIIVDAYJKoZIhvcNAQcCoIIU/T..."} // 苹果支付沙箱验证地址,可以真机模拟支付 ... -
java集成Google Pay内购
2021-09-15 12:03:13@RequiresAuthentication @ApiOperation("google内购校验") @PostMapping("/google_pay") public ResultVo checkOrder(@RequestBody GooglePayForm googlePayForm, HttpServletRequest request) throws IOException... -
java集成ios内购\与ios退款通知处理
2021-09-01 18:19:31使用ios内购,需在项目数据库建立虚拟币相关表(虚拟币余额表、充值面额表、充值订单表等)上代码 苹果IAP内购验证工具类 IosVerifyUtil import javax.net.ssl.*; import java.io.BufferedOutputStream; import ... -
IOS苹果内购JAVA(服务端)二次验证代码示例及步骤
2021-08-04 17:17:46import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.security.cert.CertificateException; ... -
Android内购封装-GoogleBilling.zip
2019-12-10 10:01:45这是基于新版Google内购封装, 不需要Key,包含各种回调, 支持消耗型内购,一次性内购和订阅功能. -
【IOS一气呵成】之IAP集成:内购和内购恢复 DEMO
2014-04-19 17:27:33//不支持内购 -(void)iapNotSupported:(RMIAPHelper*)sender; @end @interface RMIAPHelper : NSObject +(RMIAPHelper*)sharedInstance; @property(nonatomic,assign) id<RMIAPHelperDelegate> delegate; -(void... -
苹果内购验证工具类1.0
2018-09-28 15:20:15开发过程中碰到的问题,需要进行苹果内部支付,亲测可用 -
苹果IOS内购验单完整流程,in_app购买收据处理
2019-10-11 14:40:45// 收据格式处理 val data = JsonObject() ...总结了自己认为较为完善的内购验单流程,由于技术有限,流程仅供参考,内部方法,类之类的就不贴出来了,如果有遗漏或者错误的地方,欢迎指正。 -
ios内购java服务器端需要做些什么
2016-03-28 09:29:31苹果内购 JAVA服务端接口应该怎么写,公司做了一个APP卖的是虚拟产品,第三方支付走不同,要走苹果内购,可是没有接触过苹果内购这个东西,一直都是做JAVA的 简单看了一些文档,需要做挺多的 希望做过的大神可以贴下... -
支付宝、微信、ios内购支付.doc
2019-07-16 09:35:02原生支付与第三方支付比较及流程 使用第三方支付如ping++,EasyPay; -
苹果内购后台(java)验证订单
2019-03-28 16:55:10苹果内购:前端购买 -> 下单支付 -> 拿到苹果服务器返回的receipt-data,再把receipt-data传递给自己的服务器进行验证操作,自己的服务器拿到receipt-data后,请求苹果服务器进行验证,然后处理本地业务逻辑,... -
苹果内购IAP服务端验证-java篇
2020-05-19 09:17:06参考地址:https://www.jianshu.com/p/976fc6090cfa -
JAVA项目之苹果IAP内购JAVA服务器验证流程详解
2018-02-21 19:58:291.前言 本博客是经历过多个项目检验的, 绝对真实, 适应于对苹果iap内购稍微有些了解的JAVA开发人员, 认真看, 定能完美解决苹果内购问题. 苹果IAP内购支付实际上是"将客户端支付后的一些信息传给后台, 后台拿... -
java(jfinal) 接入ios苹果内购(连续包月订阅),服务端将二次验证。
2019-07-19 19:11:15大致流程: 1、ios端进行支付,然后收到苹果的一串数据(也叫收据),然后ios端将其转码为BASE64编码的字符串。...关于苹果内购的文章,本人也是参考了网上的一些资料,在这里主要的参考文章贴一下:https... -
什么是恢复内购
2014-06-24 14:56:54恢复内购就是恢复已购买内置购买项目, 并不是说把原来购买的项目恢复成未购买。 形象的说,比如您在iPhone上用你的帐号在游戏中够买了2000宝石, 然后您在iPad上登陆同一个帐号,选择恢复购买,也会得到相同的... -
苹果内购验证(熟称苹果支付回调)java版
2020-05-12 10:53:45苹果支付是直接由ios客户端调起苹果支付并支付完成后,java后台提供一个支付回调接口供ios客户端进行同步回调,只需要在该接口进行进行验证苹果支付是否支付成功,跟微信支付和支付宝支付不一样,不需要统一下单接口... -
App Store内部购买产品总结(app store回调)--Java版(二)
2021-11-29 20:39:15app store 单次购买与续费 -
App Store内部购买产品总结(获取app store token)--Java版(三)
2021-11-30 18:26:58import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.... -
Java版 Steam API源码
2018-04-12 19:24:33Java 针对 Steam API 接口的开发,欢迎拿去学习,主要针对Steam社区、steamid操作等…… -
IOS内购服务器二次验证 JAVA版
2016-08-24 17:04:50String itunes_request = ""; itunes_request = Https.sendPost("https://buy.itunes.apple.com/verifyReceipt", "{\"receipt-data\":\"" + app_receipt+"\"}"); /** * 向指定 URL 发送POST方法的请求 * -
pubSubImplementation:使用Spring Boot Java实现Google Pub sub的实现
2021-04-02 14:30:31pubSubImplementation 使用Spring Boot Java实现Google Pub sub的实现 -
苹果内购 订单验证 21002 坑
2021-12-02 11:48:11苹果官方文档上说: 要从设备上的 app 检索收据数据,请使用 NSBundle(英文) 的 appStoreReceiptURL(英文) 方法来找到 app 的收据,再对该数据进行 Base64 编码。接着将这个以 Base64 编码的数据发送到您的服务器。...