精华内容
下载资源
问答
  • Java 苹果内购
    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内购,回调验证相关的方法, 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号,二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的...
  • java 苹果支付(内购)

    2022-05-04 14:29:59
    1、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内购回调通知示例 
  • 苹果内购demo

    2017-12-09 14:27:10
    苹果内购demo
  • iOS内购实现及测试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:36
    1.流程图: 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 ...
  • 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; ...
  • 这是基于新版Google内购封装, 不需要Key,包含各种回调, 支持消耗型内购,一次性内购和订阅功能.
  • //不支持内购 -(void)iapNotSupported:(RMIAPHelper*)sender; @end @interface RMIAPHelper : NSObject +(RMIAPHelper*)sharedInstance; @property(nonatomic,assign) id<RMIAPHelperDelegate> delegate; -(void...
  • 开发过程中碰到的问题,需要进行苹果内部支付,亲测可用
  • // 收据格式处理 val data = JsonObject() ...总结了自己认为较为完善的内购验单流程,由于技术有限,流程仅供参考,内部方法,类之类的就不贴出来了,如果有遗漏或者错误的地方,欢迎指正。
  • 苹果内购 JAVA服务端接口应该怎么写,公司做了一个APP卖的是虚拟产品,第三方支付走不同,要走苹果内购,可是没有接触过苹果内购这个东西,一直都是做JAVA的 简单看了一些文档,需要做挺多的 希望做过的大神可以贴下...
  • 原生支付与第三方支付比较及流程 使用第三方支付如ping++,EasyPay;
  • 苹果内购后台(java)验证订单

    千次阅读 2019-03-28 16:55:10
    苹果内购:前端购买 -> 下单支付 -> 拿到苹果服务器返回的receipt-data,再把receipt-data传递给自己的服务器进行验证操作,自己的服务器拿到receipt-data后,请求苹果服务器进行验证,然后处理本地业务逻辑,...
  • 参考地址:https://www.jianshu.com/p/976fc6090cfa
  • 1.前言 本博客是经历过多个项目检验的, 绝对真实, 适应于对苹果iap内购稍微有些了解的JAVA开发人员, 认真看, 定能完美解决苹果内购问题. 苹果IAP内购支付实际上是"将客户端支付后的一些信息传给后台, 后台拿...
  • 大致流程: 1、ios端进行支付,然后收到苹果的一串数据(也叫收据),然后ios端将其转码为BASE64编码的字符串。...关于苹果内购的文章,本人也是参考了网上的一些资料,在这里主要的参考文章贴一下:https...
  • 什么是恢复内购

    千次阅读 2014-06-24 14:56:54
    恢复内购就是恢复已购买内置购买项目, 并不是说把原来购买的项目恢复成未购买。 形象的说,比如您在iPhone上用你的帐号在游戏中够买了2000宝石, 然后您在iPad上登陆同一个帐号,选择恢复购买,也会得到相同的...
  • 苹果支付是直接由ios客户端调起苹果支付并支付完成后,java后台提供一个支付回调接口供ios客户端进行同步回调,只需要在该接口进行进行验证苹果支付是否支付成功,跟微信支付和支付宝支付不一样,不需要统一下单接口...
  • app store 单次购买与续费
  • import 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:33
    Java 针对 Steam API 接口的开发,欢迎拿去学习,主要针对Steam社区、steamid操作等……
  • IOS内购服务器二次验证 JAVA

    千次阅读 2016-08-24 17:04:50
    String 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的实现
  • 苹果内购 订单验证 21002 坑

    千次阅读 2021-12-02 11:48:11
    苹果官方文档上说: 要从设备上的 app 检索收据数据,请使用 NSBundle(英文) 的 appStoreReceiptURL(英文) 方法来找到 app 的收据,再对该数据进行 Base64 编码。接着将这个以 Base64 编码的数据发送到您的服务器。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 89,133
精华内容 35,653
关键字:

java内购

java 订阅