java环境对接苹果内购

流程

step1

iphone下单成功->ios返回recepit凭据->iphone获取凭据请求java服务端->java服务端请求苹果进行二次验证->验证通过返回产品和交易号->java服务器存储并返回给前端金额提示消费成功

step2

直接上代码

// 正式  购买凭证验证地址
private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt";

// 沙箱 购买凭证验证地址
private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt";
===========================================核心代码块=====================================================
if (StringUtils.isEmpty(certificateCode )) {
    return GeneralResponse.error("缺少参数");
}
String url = certificateUrl;
try {
    String sendHttpsCoon = sendHttpsCoon(url, certificateCode);
    if(StringUtils.isBlank(sendHttpsCoon)){
        for(int i=0; i<5; i++){
            sendHttpsCoon = sendHttpsCoon(url, certificateCode);
            if(StringUtils.isNotBlank(sendHttpsCoon)){
                break;
            }
        }
    }
    JSONObject json = JSONObject.parseObject(sendHttpsCoon);
    if ("21007".equals(json.get("status").toString())) {
        url = certificateUrlTest;
        sendHttpsCoon = sendHttpsCoon(url, certificateCode);  //发送请求
        if(StringUtils.isBlank(sendHttpsCoon)){
            for(int i=0; i<5; i++){
                sendHttpsCoon = sendHttpsCoon(url, certificateCode);
                if(StringUtils.isNotBlank(sendHttpsCoon)){
                    break;
                }
            }
        }
    }
    JSONObject jsonObject = JSONObject.parseObject(sendHttpsCoon);
    log.info("凭证:{},certificateCode:{},returnJson:{}", jsonObject.toJSONString(), certificateCode, jsonObject.toJSONString());
    JifenLogEntity jifenLogEntity = new JifenLogEntity();
    if ("0".equals(jsonObject.get("status").toString())) { //苹果服务器向返回status结果
        JSONObject parseObject =jsonObject.getJSONObject("receipt");
        String inApp = parseObject.getString("in_app");
        List inApps = JSONObject.parseArray(inApp, HashMap.class);
        if (CollectionUtils.isNotEmpty(inApps)) { //如果订单状态成功在判断in_app这个字段有没有,没有直接就返回失败了。如果存在的话,遍历整个数组,通过客户端给的transaction_id 来比较
            for (HashMap app : inApps) {
                String productId = app.get("product_id").toString();
                String transactionId = app.get("transaction_id").toString();
                List recRecharge = recRechargeRepository.findByTransactionId("A" + transactionId);
                if (StringUtils.isNotBlank(productId) && StringUtils.isNotBlank(transactionId) && CollectionUtils.isEmpty(recRecharge)) {//判重,避免重复分发内购商品。收到客户端上报的transaction_id后,直接MD5后去数据库查,能查到说明是重复订单就不做处理
                    //处理自己的逻辑,将transaction_id存入数据库,完成订单
                    RecRechargeEntity recRechargeEntity = new RecRechargeEntity();
                    recRechargeEntity.setTransactionId("A" + transactionId);
                    recRechargeEntity.setCertificateCode(certificateCode);
                    recRechargeEntity.setUsername(user.getUsername());
                    recRechargeEntity.setProductId(productId);
                    if("com.DuduTravel.duduShuxue10".equals(productId)){
                        recRechargeEntity.setPrice("12");
                        user.setJifen(user.getJifen() + 100);
                        jifenLogEntity.setJifen(100);
                    } else if("com.DuduTravel.duduShuxue30".equals(productId)){
                        recRechargeEntity.setPrice("111");
                        user.setJifen(user.getJifen() + 200);
                        jifenLogEntity.setJifen(200);
                    } else if("com.DuduTravel.duduShuxue60".equals(productId)){
                        recRechargeEntity.setPrice("222");
                        user.setJifen(user.getJifen() + 300);
                        jifenLogEntity.setJifen(300);
                    } else {
                        recRechargeEntity.setPrice("0");
                    }
                    recRechargeEntity.setJson(jsonObject.toJSONString());
                    recRechargeRepository.save(recRechargeEntity);

                    userRepository.saveAndFlush(user);
                    jifenLogEntity.setTotalJifen(user.getJifen());
                    jifenLogEntity.setUserId(user.getId());
                    jifenLogEntity.setUsername(user.getUsername());
                    jifenLogEntity.setReason("用户购买,时间:"
                            + app.get("purchase_date_pst").toString() +",金额"
                            + recRechargeEntity.getPrice());
                    jifenLogRepository.save(jifenLogEntity);
                } else {
                    log.error("A{}重复消费", transactionId);
                }
            }
        }
    } else {
        return GeneralResponse.error(jsonObject.toJSONString());
    }
    return GeneralResponse.success(user.getJifen());
} catch (Exception e) {
    log.error("购买凭证验证错误", e);
    return GeneralResponse.error("购买凭证验证错误");
}

=====================================工具代码块==========================================

/**
 * 重写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.replace(" ", "+"));

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

step3

问题:

1.java传输是+号会转换为空格

使用URLEncoder.encoder(recept,"IOS8859-1")请求苹果无效

2.使用replace("+", "%2B")替换所有加号请求苹果无效

3.使用replace(" ","+")替换所有空格有效

4.其他博客贡献者总会默默地给你埋坑,增加开发时间,请主动标记他进入黑名单,谢谢!

step4

返回格式:

{

    "environment": "Sandbox",

    "receipt": {

        "adam_id": 0,

        "app_item_id": 0,

        "application_version": "3",

        "bundle_id": "com.xx.xx.xx",

        "download_id": 0,

        "in_app": [

            {

                "is_trial_period": "false",

                "original_purchase_date": "2020-06-10 03:43:15 Etc/GMT",

                "original_purchase_date_ms": "1591760595000",

                "original_purchase_date_pst": "2020-06-09 20:43:15 America/Los_Angeles",

                "original_transaction_id": "100002923929292",

                "product_id": "com.xx.xx.xx",

                "purchase_date": "2020-06-10 03:43:15 Etc/GMT",

                "purchase_date_ms": "1591760595000",

                "purchase_date_pst": "2020-06-09 20:43:15 America/Los_Angeles",

                "quantity": "1",

                "transaction_id": "1010101010101010"

            },

            {

                "is_trial_period": "false",

                "original_purchase_date": "2020-06-11 09:29:44 Etc/GMT",

                "original_purchase_date_ms": "1591867784000",

                "original_purchase_date_pst": "2020-06-11 02:29:44 America/Los_Angeles",

                "original_transaction_id": "100002923929292",

                "product_id": "com.xx.xx.xx",

                "purchase_date": "2020-06-11 09:29:44 Etc/GMT",

                "purchase_date_ms": "1591867784000",

                "purchase_date_pst": "2020-06-11 02:29:44 America/Los_Angeles",

                "quantity": "1",

                "transaction_id": "18i18181811111"

            }

        ],

        "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": "2020-06-11 09:41:10 Etc/GMT",

        "receipt_creation_date_ms": "1591868470000",

        "receipt_creation_date_pst": "2020-06-11 02:41:10 America/Los_Angeles",

        "receipt_type": "ProductionSandbox",

        "request_date": "2020-06-12 07:09:52 Etc/GMT",

        "request_date_ms": "1591945792375",

        "request_date_pst": "2020-06-12 00:09:52 America/Los_Angeles",

        "version_external_identifier": 0

    },

    "status": 0

}

 

productId:产品id,用这个可以获取对应的价格套餐

transaction_id:单笔消费唯一主键,用于重复消费判断

 

step5

  • 21000App Store无法读取你提供的JSON数据
  • 21002 收据数据不符合格式
  • 21003 收据无法被验证
  • 21004 你提供的共享密钥和账户的共享密钥不一致
  • 21005 收据服务器当前不可用
  • 21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
  • 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
  • 21008 收据信息是产品环境中使用,但却被发送到测试环境中验证

step6

java环境对接苹果内购_第1张图片

产品和id和金额需要存储到DB或者枚举类,目的是为了判断用户购买了那个套餐以及花了多少钱

  •  

你可能感兴趣的:(java)