java后台接入IOS内购

参考文档

  • 官方文档
  • https://www.cnblogs.com/TheYouth/p/6847014.html?utm_source=itdadao&utm_medium=referral
  • https://www.cnblogs.com/lazydays/p/12078006.html

说明

后台处理:将购买凭证(接收IOS端)发送到苹果的服务器验证,并将验证结果返回给客户端。

代码

工具类


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内购验证工具类
 *
 * 官网:https://developer.apple.com/documentation/storekit/in-app_purchase
 * 参考:https://blog.csdn.net/lbd_123/article/details/87276204
 * @author xiaoqiang
 * @date 2020-05-11 14:37
 */
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;
        }
    }

    /**
     * 苹果服务器验证
     *
     * @param receipt
     *            账单
     * @url 要验证的地址
     * @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
     *
     *  21000App Store无法读取你提供的JSON数据
     *  21002 订单数据不符合格式
     *  21003 订单无法被验证
     *  21004 你提供的共享密钥和账户的共享密钥不一致
     *  21005 订单服务器当前不可用
     *  21006 订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
     *  21007 订单信息是测试用(sandbox),但却被发送到产品环境中验证
     *  21008 订单信息是产品环境中使用,但却被发送到测试环境中验证
     */
    public static String buyAppVerify(String receipt,int type) {
        String url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
        String url_verify = "https://buy.itunes.apple.com/verifyReceipt";
        // 环境判断 线上/开发环境用不同的请求链接
        String url = "";
        if(type==0){
            // 沙盒测试
            url = url_sandbox;
        }else{
            // 线上测试
            url = url_verify;
        }

        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 = 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;
    }

}

业务代码片段
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;// 日志用了lombok,报错可以将log相关删除
/**
*@param productId 商品ID
*@param receipt 购买凭证
*/
public Result iosInAppPurchase(String productId, String receipt) {
        if (StringUtils.isBlank(receipt)){
            return Result.fail("receipt不能为空");
        }
        if (StringUtils.isBlank(productId)){
            return Result.fail("productId不能为空");
        }
        // 0沙盒 非0即线上
        String verifyResult = IosVerifyUtil.buyAppVerify(receipt, 0);
        if (StringUtils.isBlank(verifyResult)) {// 苹果服务器没有返回验证结果
            log.info("IOS内购(充值)=>苹果服务器没有返回验证结果");
            return Result.error("苹果服务器没有返回验证结果");

        } else {// 苹果验证有返回结果
            JSONObject job = JSONObject.parseObject(verifyResult);
            log.info("IOS内购(充值)=>苹果服务器返回验证结果:{}", job);
            String states = job.getString("status");
            //是沙盒环境,应沙盒测试,否则执行下面
            if ("21007".equals(states)) {
                //2.在沙盒测试  发送平台验证
                verifyResult = IosVerifyUtil.buyAppVerify(receipt, 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);
                log.info("IOS内购(充值)=>苹果服务器返回验证结果订单数量:{}", jsonArray.size());
                for (int i = 0; i < jsonArray.size(); i++) {
                    if (rechargeBallCoinIosVo.getBallCoinRechargeId().equals(jsonArray.getJSONObject(i).get("product_id") == null ? null : jsonArray.getJSONObject(i).get("product_id").toString())) {
                        // 订单号
                        String transactionId = jsonArray.getJSONObject(i).get("transaction_id") == null ? null : jsonArray.getJSONObject(i).get("transaction_id").toString();
                     
                        /* 自己的业务处理 */
                        // 1.创建订单信息
                        // 2. 增加用户虚拟币数量
                        // 3. 添加用户消费记录
                    }
                }
            } else {
                return Result.error(job);
            }
        }
        return Result.success();
    }

统一返回结果



import java.util.HashMap;
import java.util.Objects;

/**
 * @author xiaoqiang
 * @Description 返回结果信息
 * @date 2019/3/21 16:50
 */
public class Result extends HashMap {

    public static String CODE_KEY = "code";
    public static String MSG_KEY = "msg";
    public static String DATA_KEY = "data";


    /**
     * 调用结果(-1 异常 0 失败 1 成功)
     */
    public enum CallResult {
        /**
         * 异常
         */
        ERROR(-1, "异常"),
        /**
         * 失败
         */
        FAIL(0, "失败"),
        /**
         * 成功
         */
        SUCCESS(1, "成功");
        /**
         * code码
         */
        private Integer code;
        /**
         * 提示信息
         */
        private String msg;

        private CallResult(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }

        public Integer getCode() {
            return code;
        }

        public String getMsg() {
            return msg;
        }
    }

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    /**
     * 成功
     *
     * @return
     */
    public static Result success() {
        Result result = new Result();
        result.put(CODE_KEY, Result.CallResult.SUCCESS.getCode());
        result.put(MSG_KEY, Result.CallResult.SUCCESS.getMsg());
        result.put(DATA_KEY, null);
        return result;
    }

    public static Result success(String msg) {
        Result result = success();
        result.put(MSG_KEY, msg);
        return result;
    }

    public static Result success(ResultCode code, Object obj) {
        Result result = success();
        result.put(CODE_KEY, code.code());
        result.put(MSG_KEY, code.msg());
        result.put(DATA_KEY, obj);
        return result;
    }

    public static Result success(Object obj) {
        Result result = success();
        result.put(DATA_KEY, obj);
        return result;
    }

    public static Result success(String msg, Object obj) {
        Result result = success();
        result.put(MSG_KEY, msg);
        result.put(DATA_KEY, obj);
        return result;
    }

    /**
     * 失败
     *
     * @return
     */
    public static Result fail() {
        Result result = new Result();
        result.put(CODE_KEY, Result.CallResult.FAIL.getCode());
        result.put(MSG_KEY, Result.CallResult.FAIL.getMsg());
        result.put(DATA_KEY, null);
        return result;
    }

    public static Result fail(String msg) {
        Result result = fail();
        result.put(MSG_KEY, msg);
        return result;
    }

    public static Result fail(ResultCode code, Object obj) {
        Result result = fail();
        result.put(CODE_KEY, code.code());
        result.put(MSG_KEY, code.msg());
        result.put(DATA_KEY, obj);
        return result;
    }

    public static Result fail(Object obj) {
        Result result = fail();
        result.put(DATA_KEY, obj);
        return result;
    }

    public static Result fail(String msg, Object obj) {
        Result result = fail();
        result.put(MSG_KEY, msg);
        result.put(DATA_KEY, obj);
        return result;
    }

    /**
     * 异常
     *
     * @return
     */
    public static Result error() {
        Result result = new Result();
        result.put(CODE_KEY, Result.CallResult.ERROR.getCode());
        result.put(MSG_KEY, Result.CallResult.ERROR.getMsg());
        result.put(DATA_KEY, null);
        return result;
    }

    public static Result error(String msg) {
        Result result = error();
        result.put(MSG_KEY, msg);
        return result;
    }

    public static Result error(Object obj) {
        Result result = error();
        result.put(DATA_KEY, obj);
        return result;
    }

    public static Result error(String msg, Object obj) {
        Result result = error();
        result.put(MSG_KEY, msg);
        result.put(DATA_KEY, obj);
        return result;
    }

    /**
     * 自定义
     *
     * @param code
     * @param msg
     * @param obj
     * @return
     */
    public static Result custom(Object code, Object msg, Object obj) {
        Result result = new Result();
        result.put(CODE_KEY, code);
        result.put(MSG_KEY, msg);
        result.put(DATA_KEY, obj);
        return result;
    }

    /**
     * 自定义返回
     * @param code
     * @param obj
     * @return
     */
    public static Result custom(ResultCode code, Object obj) {
        Result result = new Result();
        result.put(CODE_KEY, code.code());
        result.put(MSG_KEY, code.msg());
        result.put(DATA_KEY, obj);
        return result;
    }


    /**
     *
     * @return
     */
    public static Result notLogin(){
        Result result = new Result();
        result.put(CODE_KEY, ResultCode.NOT_LOGIN.code());
        result.put(MSG_KEY, ResultCode.NOT_LOGIN.msg());
        result.put(DATA_KEY, null);
        return result;
    }

    /**
     * 验证成功
     * @param result
     * @return
     */
    public static boolean isSuccess(Result result) {
        return !Objects.isNull(result.get(CODE_KEY)) && (Integer) result.get(CODE_KEY) == 1;
    }

}

购买凭证解析后JSON数据样列

{
    "environment": "Sandbox",
    "receipt": {
        "in_app": [
            {
                "transaction_id": "1000000665290365",
                "original_purchase_date": "2020-05-15 09:08:45 Etc/GMT",
                "quantity": "1",
                "original_transaction_id": "1000000665290365",
                "purchase_date_pst": "2020-05-15 02:08:45 America/Los_Angeles",
                "original_purchase_date_ms": "1589533725000",
                "purchase_date_ms": "1589533725000",
                "product_id": "1237265852472238082",
                "original_purchase_date_pst": "2020-05-15 02:08:45 America/Los_Angeles",
                "is_trial_period": "false",
                "purchase_date": "2020-05-15 09:08:45 Etc/GMT"
            }
        ],
        "adam_id": 0,
        "receipt_creation_date": "2020-05-15 09:08:46 Etc/GMT",
        "original_application_version": "1.0",
        "app_item_id": 0,
        "original_purchase_date_ms": "1375340400000",
        "request_date_ms": "1589533728625",
        "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
        "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
        "receipt_creation_date_pst": "2020-05-15 02:08:46 America/Los_Angeles",
        "receipt_type": "ProductionSandbox",
        "bundle_id": "com.bzsports.bz",
        "receipt_creation_date_ms": "1589533726000",
        "request_date": "2020-05-15 09:08:48 Etc/GMT",
        "version_external_identifier": 0,
        "request_date_pst": "2020-05-15 02:08:48 America/Los_Angeles",
        "download_id": 0,
        "application_version": "6338"
    },
    "status": 0
}

你可能感兴趣的:(java后台接入IOS内购)