package com.sonic.lion.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sonic.lion.enums.BizType;
import com.sonic.lion.enums.PayChannel;
import com.sonic.lion.dao.*;
import com.sonic.lion.domain.entity.*;
import com.sonic.lion.domain.enums.TradeEvent;
import com.sonic.lion.domain.input.IOSReceiptInput;
import com.sonic.lion.domain.req.IapUploadReceiptReq;
import com.sonic.lion.service.*;
import com.sonic.lion.utils.IOSConsumptionUtil;
import com.sonic.lion.utils.NetRetryUtils;
import com.sonic.lion.enums.ToastResultCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Objects;

/**
 * @author: code
 * @date: 2025/06/22
 * @Description:
 * @version: 1.0.0
 */
@Slf4j
@Service("iapService")
public class IapServiceImpl implements IapService, InitializingBean {

    @Autowired
    private PayTradeService payTradeService;

    @Autowired
    private PayTradeDao payTradeDao;

    @Autowired
    private IapRecordService iapRecordService;

    @Autowired
    private IapUploadReceiptService iapUploadReceiptService;

    @Autowired
    private PayChargeService payChargeService;

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Autowired
    private PayConfigService payConfigService;

    @Value("${spring.profiles.active}")
    private String active;

    @Autowired
    private UserSubscriptionService userSubscriptionService;

    @Autowired
    private UserSubscriptionDao userSubscriptionDao;

    @Autowired
    private NetRetryUtils netRetryUtils;

    @Autowired
    private AccountBuffBillDao accountBuffBillDao;

    @Autowired
    private AccountBuffService accountBuffService;

    @Autowired
    private IOSConsumptionUtil iosConsumptionUtil;

    private static final int SUCC_STATUS = 0;

    private static final int SANDBOX_RECIPT = 21007;

    private static final String PRODUCT_VERIFY_RECEIPT_URL = "https://buy.itunes.apple.com/verifyReceipt";

    private static final String SANDBOX_VERIFY_RECEIPT_URL = "https://sandbox.itunes.apple.com/verifyReceipt";

    private static final BigDecimal MINUS_ONE = new BigDecimal(-1);

    @Value("${apple.boundId}")
    private String BUNDLE_ID;

    @Value("${apple.password}")
    private String password;

    @Value("${apple.verifyReceiptUrl}")
    private String verifyReceiptUrl;

    @Autowired
    private AppleRefundRecordDao appleRefundRecordDao;

    @Autowired
    private UserSubscriptionNotifyDao userSubscriptionNotifyDao;

    @Autowired
    private ChannelBlacklistService channelBlacklistService;

    @Override
    public Long calculatePaymentFee(@NonNull Long productAmount) {
        //蹩脚设计  为了兼容之前不传productId 版本。明明可以直接从productId取得
        String iosFeeRatio = payConfigService.getIOSFeeRatio();
        BigDecimal number1 = new BigDecimal(iosFeeRatio.split("/")[0]);
        BigDecimal number2 = new BigDecimal(iosFeeRatio.split("/")[1]);
        return new BigDecimal(productAmount).multiply(number1).divide(number2, 0, BigDecimal.ROUND_HALF_UP).add(MINUS_ONE).longValue();
    }


    @Override
    public void uploadReceipt(@NonNull IapUploadReceiptReq req, Long currentUserId) {
        JSONObject jsonObject = req.getTransactions();
        String tradeNo = null;
        for (String key : jsonObject.keySet()) {
            tradeNo = jsonObject.getString(key);
            break;
        }
        //先保存票据
        IapUploadReceipt iapUploadReceipt = IapUploadReceipt.builder()
                .transactionsJsonStr(req.getTransactions().toJSONString())
                .receipt(req.getReceipt())
                .processed(Boolean.FALSE)
                .userId(currentUserId)
                .tradeNo(tradeNo)
                .build();
        try {
            iapUploadReceiptService.save(iapUploadReceipt);
        } catch (DuplicateKeyException e) {
            log.warn("该iap收据已保存");
            return;
        }

        req.setReceiptId(iapUploadReceipt.getId());
        boolean processed = processReceipt(req);
        //处理完成后更新处理状态
        iapUploadReceiptService.updateProcessed(iapUploadReceipt.getId(), processed);
    }

    @Override
    public void processReceipt() {
        LocalDateTime startCreateTime = LocalDateTime.now().minusHours(24L);
        List<IapUploadReceipt> list = iapUploadReceiptService.list(Boolean.FALSE, startCreateTime, 20);
        for (IapUploadReceipt iapUploadReceipt : list) {
            if (iapUploadReceipt.getUserId() != null) {
                log.info("iap收据补偿订阅, receiptId: {}", iapUploadReceipt.getId());
                handReceiptSubscribe(null, iapUploadReceipt.getId(), iapUploadReceipt.getUserId());
            } else {
                try {
                    log.info("iap 收据补偿处理, receiptId: {}", iapUploadReceipt.getId());
                    JSONObject transactions = JSON.parseObject(iapUploadReceipt.getTransactionsJsonStr());
                    IapUploadReceiptReq req = IapUploadReceiptReq.builder()
                            .receiptId(iapUploadReceipt.getId())
                            .transactions(transactions)
                            .receipt(iapUploadReceipt.getReceipt())
                            .build();

                    boolean processed = processReceipt(req);
                    //处理完成后更新处理状态
                    iapUploadReceiptService.updateProcessed(iapUploadReceipt.getId(), processed);
                } catch (Exception e) {
                    log.error("iap收据处理失败, receiptId: {}", iapUploadReceipt.getId());
                }
            }
        }
    }

    @Override
    public void afterPropertiesSet() {
        if ("product".equals(active)) {
            verifyReceiptUrl = PRODUCT_VERIFY_RECEIPT_URL;
        } else {
            verifyReceiptUrl = SANDBOX_VERIFY_RECEIPT_URL;
        }
    }


    @Override
    public boolean handReceiptSubscribe(String productId, Long receiptId, Long userId) {
        String receipt = iapUploadReceiptService.getById(receiptId).getReceipt();
        JSONObject latestReceiptInfo = checkAndGeLatestReceiptInfo(receipt);
        log.info("handReceiptSubscribe latestReceiptInfo{}", latestReceiptInfo.toJSONString());
        /**
         *       "quantity": "1",
         *       "product_id": "com.sonic.autorenew_0001",
         *       "transaction_id": "1000000863749566",
         *       "original_transaction_id": "1000000860235555",
         *       "purchase_date": "2021-08-21 08:41:34 Etc/GMT",
         *       "purchase_date_ms": "1629535294000",
         *       "purchase_date_pst": "2021-08-21 01:41:34 America/Los_Angeles",
         *       "original_purchase_date": "2021-08-16 13:05:35 Etc/GMT",
         *       "original_purchase_date_ms": "1629119135000",
         *       "original_purchase_date_pst": "2021-08-16 06:05:35 America/Los_Angeles",
         *       "expires_date": "2021-08-21 08:46:34 Etc/GMT",
         *       "expires_date_ms": "1629535594000",
         *       "expires_date_pst": "2021-08-21 01:46:34 America/Los_Angeles",
         *       "web_order_line_item_id": "1000000065173763",
         *       "is_trial_period": "false",
         *       "is_in_intro_offer_period": "false",
         *       "in_app_ownership_type": "PURCHASED",
         *       "subscription_group_identifier": "20867190"
         */
        String originalTransactionId = latestReceiptInfo.getString("original_transaction_id");
        String transactionId = latestReceiptInfo.getString("transaction_id");
        String product_id = latestReceiptInfo.getString("product_id");
        //处理兼容前端没有传商品ID的情况
        if(StringUtils.isEmpty(productId)) {
            log.info("===> handReceiptSubscribe apple org productId : {}, after productId : {}", productId, product_id);
            productId =  product_id;
        }

        //过期时间
        Long expiresDateMs = Long.valueOf(latestReceiptInfo.getString("expires_date_ms"));
        //订购时间
        Long purchase_date_ms = Long.valueOf(latestReceiptInfo.getString("purchase_date_ms"));

        IOSReceiptInput iosReceiptInput = IOSReceiptInput.builder().originalTransactionId(originalTransactionId).transactionId(transactionId)
                .productId(product_id).expires_date_ms(expiresDateMs).purchase_date_ms(purchase_date_ms).build();

        userSubscriptionService.bind(iosReceiptInput.getSubscriptionId(), UserSubscription.Platform.APPLE, iosReceiptInput.getExpiresDate(), iosReceiptInput.getPurchaseDate(), productId, userId, null, null);
        iapUploadReceiptService.updateProcessed(receiptId, true);
        return true;
    }


    /**
     * https://developer.apple.com/documentation/appstoreservernotifications/notification_type 相关链接.
     *
     * @param receiptId
     * @param playload
     * @return
     */
    @Override
    public boolean handSubscribeWebhook(Long receiptId, String playload) {
        //将处理订阅的异常给吃掉，不能影响后面的业务逻辑的执行
        try {
            doWhenStatusChange(receiptId, playload);
        } catch (Exception e) {
            log.error("处理IOS回调失败notifyId2:"+ receiptId, e);
        }
        doWhenCancel(receiptId, playload);
        doWhenPay(receiptId, playload);
        doWhenPayFail(receiptId, playload);
        //处理收到退款回调
        doWhenRefundV2(receiptId, playload);
        doWhenConsumptionRequest(receiptId, playload);
        return true;
    }


    //当状态变更的时候 更新套餐 和 续订状态。
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doWhenStatusChange(Long receiptId, String playload) {
        JSONObject result = JSON.parseObject(playload);
        String original_transaction_id = result.getString("original_transaction_id");
        if (original_transaction_id == null) {
            return;
        }
        String productId = result.getString("auto_renew_product_id");
        Boolean autoRenew = !"false".equals(result.getString("auto_renew_status"));
        userSubscriptionService.updateStatusByThird(original_transaction_id, UserSubscription.Platform.APPLE , productId,autoRenew);
        if (!autoRenew) {
            userSubscriptionService.cancel(original_transaction_id, UserSubscription.Platform.APPLE, null);
        }
        userSubscriptionService.completeNotify(receiptId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doWhenConsumptionRequest(Long receiptId, String playload) {
        JSONObject result = JSON.parseObject(playload);
        String notification_type = result.getString("notification_type");
        if (!"CONSUMPTION_REQUEST".equals(notification_type)) {
            return;
        }
        //校验是IOS服务器
        if (!result.getString("password").equals(password)) {
            return;
        }
        String original_transaction_id = result.getString("original_transaction_id");
        IapRecord record = iapRecordService.getByTransactionId(original_transaction_id);
        if (record == null) {
            log.info("===> doWhenConsumptionRequest IapRecord not found , receiptId={}", receiptId);
            return;
        }
        int count = appleRefundRecordDao.selectCount(Wrappers.<AppleRefundRecord>lambdaQuery()
                .eq(AppleRefundRecord::getTransactionId, original_transaction_id));
        //已经存在的数据 不再处理
        if (count > 0) {
            log.info("===> doWhenConsumptionRequest IapRecord data found , receiptId={}", receiptId);
            return;
        }
        String tradeNo = record.getTradeNo();
        PayTrade payTrade = payTradeService.getByTradeNo(tradeNo);

        int consumptionStatus = 0;
        //购买得到的buff
        Long getBuffAmount = payTrade.getAmount();
        //这期间 消费了的buff
        Long sumConsumptionTotal = accountBuffBillDao.sumConsumption(payTrade.getPayTime(), LocalDateTime.now(), payTrade.getSrcAccountId());
        if (sumConsumptionTotal == 0) {   //没有消费 扣除buff  并发送系统消息。
            consumptionStatus = 1;    //不消耗应用内购买
        } else if (sumConsumptionTotal > 0 && sumConsumptionTotal < getBuffAmount) {
            consumptionStatus = 2;    //应用内购买被部分消费
        } else {
            consumptionStatus = 3;    //应用内购买已完全消耗
        }


        /**
         * {"transaction_id":"200001174368839","original_purchase_date":"2021-12-25 15:42:33 Etc/GMT",
         * "in_app_ownership_type":"PURCHASED","quantity":"1","original_transaction_id":"200001174368839",
         * "purchase_date_pst":"2021-12-25 07:42:33 America/Los_Angeles","original_purchase_date_ms":
         * "1640446953000","purchase_date_ms":"1640446953000","product_id":"gg.epal.buff_0004",
         * "original_purchase_date_pst":"2021-12-25 07:42:33 America/Los_Angeles",
         * "is_trial_period":"false","purchase_date":"2021-12-25 15:42:33 Etc/GMT"}
         */

        JSONObject latestReceiptInfo = JSONObject.parseObject(record.getResult());
        //订购时间
        Long purchase_date_ms = Long.valueOf(latestReceiptInfo.getString("purchase_date_ms"));
        LocalDateTime purchaseDate = LocalDateTime.ofEpochSecond(purchase_date_ms / 1000, 0, ZoneOffset.ofHours(8));


        Integer totalBuy = payTradeDao.countTotalBuy(payTrade.getSrcAccountId());
        Integer totalRefund = payTradeDao.countTotalRefund(payTrade.getSrcAccountId());
        //发送IOS 消费.
        String json = iosConsumptionUtil.sendConsumption(original_transaction_id, 1, consumptionStatus, totalBuy, totalRefund, 1);

//        accountBuffService.fronzenBalanceAndSendMessage(user.getId(), getBuffAmount); //冻结 金额并 发送消息

        //开始处理 冻结
        AppleRefundRecord.FronzenStatus fronzenStatus = accountBuffService.fronzenBalanceAndSendMessage(payTrade.getSrcAccountId(), payTrade.getAmount());

        AppleRefundRecord appleRefundRecord = AppleRefundRecord.builder().productId(latestReceiptInfo.getString("product_id"))
                .platform(AppleRefundRecord.Platform.IOS).bizType(BizType.CHARGE)
                .content(playload)
                .transactionId(original_transaction_id).cancellationDate(LocalDateTime.now()).purchaseDate(purchaseDate).build();

        appleRefundRecord.setAmount(payTrade.getOccurAmount());
        appleRefundRecord.setUserId(payTrade.getSrcAccountId());
        appleRefundRecord.setTradeNo(tradeNo);
        appleRefundRecord.setBuff(payTrade.getAmount());

        appleRefundRecord.setFronzenStatus(fronzenStatus);
        AccountBuff accountBuff = accountBuffService.getByUid(payTrade.getSrcAccountId());
        appleRefundRecord.setAfterFronzenBuff(accountBuff.getBalance());
        appleRefundRecord.setInsertTime(LocalDateTime.now());
        appleRefundRecordDao.insert(appleRefundRecord);
        //将用户加入渠道黑名单中
        channelBlacklistService.addBlacklist(appleRefundRecord.getUserId(), PayChannel.STRIPE.name());

        userSubscriptionService.updateNotifyExtend(receiptId, appleRefundRecord.getId(), json);
        userSubscriptionService.completeNotify(receiptId);
    }


    private void doWhenRefund(Long receiptId, String playload) {
        JSONObject result = JSON.parseObject(playload);
        String notification_type = result.getString("notification_type");
        if (!"REFUND".equals(notification_type)) {
            return;
        }
        //校验是IOS服务器
        if (!result.getString("password").equals(password)) {
            return;
        }

        JSONObject latestReceiptInfo = result.getJSONObject("unified_receipt").getJSONArray("latest_receipt_info").getJSONObject(0);


        Long cancellation_date_ms = Long.valueOf(latestReceiptInfo.getString("cancellation_date_ms"));
        LocalDateTime cancellation_date = LocalDateTime.ofEpochSecond(cancellation_date_ms / 1000, 0, ZoneOffset.ofHours(8));


        Long purchase_date_ms = Long.valueOf(latestReceiptInfo.getString("purchase_date_ms"));
        LocalDateTime purchase_date = LocalDateTime.ofEpochSecond(purchase_date_ms / 1000, 0, ZoneOffset.ofHours(8));

        String transaction_id = latestReceiptInfo.getString("transaction_id");
        AppleRefundRecord appleRefundRecord = AppleRefundRecord.builder().productId(latestReceiptInfo.getString("product_id"))
                .platform(AppleRefundRecord.Platform.IOS).bizType(BizType.CHARGE)
                .content(playload)
                .transactionId(transaction_id).cancellationDate(cancellation_date).purchaseDate(purchase_date).build();


        IapRecord record = iapRecordService.getByTransactionId(transaction_id);
        if (record != null) {
            String tradeNo = record.getTradeNo();
            PayTrade payTrade = payTradeService.getByTradeNo(tradeNo);
            appleRefundRecord.setAmount(payTrade.getOccurAmount());
            appleRefundRecord.setUserId(payTrade.getSrcAccountId());
            appleRefundRecord.setTradeNo(tradeNo);
            appleRefundRecord.setBuff(payTrade.getAmount());
        }
        appleRefundRecord.setInsertTime(LocalDateTime.now());
        appleRefundRecordDao.insert(appleRefundRecord);
        //将用户加入渠道黑名单中
        channelBlacklistService.addBlacklist(appleRefundRecord.getUserId(), PayChannel.STRIPE.name());
        userSubscriptionService.completeNotify(receiptId);
    }

    /**
     * 处理收到渠道方的退款回调数据
     * @param payload
 */
    private void doWhenRefundV2(Long receiptId, String payload) {
        try {
            JSONObject result = JSON.parseObject(payload);
            String notification_type = result.getString("notification_type");
            if (!"REFUND".equals(notification_type)) {
                return;
            }
            //校验是IOS服务器
            if (!result.getString("password").equals(password)) {
                return;
            }
            //获取退款对应的原始交易号
            String transaction_id = result.getString("original_transaction_id");
            //根据交易号查询出对应的退款数据
            AppleRefundRecord appleRefundRecord = appleRefundRecordDao.selectOne(Wrappers.<AppleRefundRecord>lambdaQuery()
                    .select(AppleRefundRecord::getId)
                    .eq(AppleRefundRecord::getTransactionId, transaction_id));
            if(appleRefundRecord == null) {
                log.info("===> doWhenRefundV2 data not found");
                return;
            }
            //将请求的数据给更新过来
            userSubscriptionService.updateNotifyExtend(receiptId, appleRefundRecord.getId(), null);
        } catch (Exception e) {
            log.info("===> doWhenRefundV2 error");
            e.printStackTrace();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doWhenPayFail(Long receiptId, String playlod) {
        JSONObject result = JSON.parseObject(playlod);
        String notification_type = result.getString("notification_type");
        if (!"DID_FAIL_TO_RENEW".equals(notification_type)) {
            return;
        }
        String latest_receipt = result.getJSONObject("unified_receipt").getString("latest_receipt");
        String env = result.getString("environment");
        JSONObject latestReceiptInfo = checkAndGeLatestReceiptInfoByEnv(latest_receipt, env);

        String originalTransactionId = latestReceiptInfo.getString("original_transaction_id");
        userSubscriptionNotifyDao.setSubscriptionId(receiptId, originalTransactionId);
        userSubscriptionService.payFailSystemMessage(originalTransactionId, UserSubscription.Platform.APPLE);
        userSubscriptionService.completeNotify(receiptId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doWhenPay(Long receiptId, String playlod) {
        JSONObject result = JSON.parseObject(playlod);
        String notification_type = result.getString("notification_type");
//        if(!"DID_RENEW".equals(notification_type)||"INTERACTIVE_RENEWAL".equals(notification_type) || "DID_RECOVER".equals(notification_type)){
//            return ;
//        }
        //处理订阅升级立马扣费的情况， 这个时候需要处理计算buff发放的情况  TODO 这儿是否会导致处理buff发放的问题？？
        if (!"DID_RENEW".equals(notification_type) && !"INTERACTIVE_RENEWAL".equals(notification_type)) {
            return;
        }

        String latest_receipt = result.getJSONObject("unified_receipt").getString("latest_receipt");
        String env = result.getString("environment");
        JSONObject latestReceiptInfo = checkAndGeLatestReceiptInfoByEnv(latest_receipt, env);

        log.info("===> latestReceiptInfo : {}", latestReceiptInfo);

        String originalTransactionId = latestReceiptInfo.getString("original_transaction_id");
        String transactionId = latestReceiptInfo.getString("transaction_id");
        userSubscriptionNotifyDao.setSubscriptionId(receiptId, originalTransactionId);

        String productId = latestReceiptInfo.getString("product_id");
        //过期时间
        Long expiresDateMs = Long.valueOf(latestReceiptInfo.getString("expires_date_ms"));
        //订购时间
        Long purchase_date_ms = Long.valueOf(latestReceiptInfo.getString("purchase_date_ms"));

        IOSReceiptInput iosReceiptInput = IOSReceiptInput.builder()
                .originalTransactionId(originalTransactionId)
                .transactionId(transactionId)
                .productId(productId)
                .expires_date_ms(expiresDateMs)
                .purchase_date_ms(purchase_date_ms).build();

        userSubscriptionService.bind(iosReceiptInput.getSubscriptionId(), UserSubscription.Platform.APPLE, iosReceiptInput.getExpiresDate(), iosReceiptInput.getPurchaseDate(), productId);
        userSubscriptionService.completeNotify(receiptId);
    }


    //IOS 退订还没有调通
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doWhenCancel(Long receiptId, String playlod) {
        JSONObject result = JSON.parseObject(playlod);
        String notification_type = result.getString("notification_type");
        //  String  auto_renew_status  = result.getString("auto_renew_status");
        if (!"CANCEL".equals(notification_type)) {
            return;
        }
        String latest_receipt = result.getJSONObject("unified_receipt").getString("latest_receipt");
        String env = result.getString("environment");
        JSONObject latestReceiptInfo = checkAndGeLatestReceiptInfoByEnv(latest_receipt, env);
        String originalTransactionId = latestReceiptInfo.getString("original_transaction_id");
        Long expiresDateMs = Long.valueOf(latestReceiptInfo.getString("expires_date_ms"));
//        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(expiresDateMs, 0, ZoneOffset.ofHours(8));
        userSubscriptionNotifyDao.setSubscriptionId(receiptId, originalTransactionId);
        userSubscriptionService.cancel(originalTransactionId, UserSubscription.Platform.APPLE, expiresDateMs);
        userSubscriptionService.completeNotify(receiptId);
    }


    public JSONObject checkAndGeLatestReceiptInfoByEnv(String receipt, String environment) {
        String url = "";
        if ("Sandbox".equals(environment)) {
            url = SANDBOX_VERIFY_RECEIPT_URL;
        } else {
            url = PRODUCT_VERIFY_RECEIPT_URL;
        }
        JSONObject request = new JSONObject();
        request.put("receipt-data", receipt);
        request.put("password", password);
        String str = netRetryUtils.postForObject(url, request, String.class);
        log.info("iap verify receipt result: {}, receiptId: {}", str, receipt);
        JSONObject result = JSON.parseObject(str);
        int status = result.getInteger("status");

        ToastResultCode.IAP_RECEIPT_STATUS_ERROR.check(status != SUCC_STATUS);
        JSONObject receiptObj = result.getJSONObject("receipt");
        ToastResultCode.IAP_BUNDLE_ID_ERROR.check(!BUNDLE_ID.equals(receiptObj.getString("bundle_id")));
        JSONArray latestReceiptInfoArray = result.getJSONArray("latest_receipt_info");
        JSONObject latestReceiptInfo = latestReceiptInfoArray.getJSONObject(0);
        return latestReceiptInfo;
    }


    public JSONObject checkAndGeLatestReceiptInfo(String receipt) {
        JSONObject request = new JSONObject();
        request.put("receipt-data", receipt);
        request.put("password", password);
        String str = netRetryUtils.postForObject(verifyReceiptUrl, request, String.class);
        log.info("iap verify receipt result: {}, receiptId: {}", str, receipt);
        JSONObject result = JSON.parseObject(str);

        //生产环境沙盒充值标志
//        boolean sandBoxCharge = false;
        int status = result.getInteger("status");
        //审核期间才会在生产环境校验沙盒收据
        if (status == SANDBOX_RECIPT) {
            log.warn("生产环境沙盒充值校验逻辑");
            str = netRetryUtils.postForObject(SANDBOX_VERIFY_RECEIPT_URL, request, String.class);
            log.warn("生产环境校验沙盒环境收据结果: {}", str);
            result = JSON.parseObject(str);
            status = result.getInteger("status");
//            sandBoxCharge = true;
        }
        ToastResultCode.IAP_RECEIPT_STATUS_ERROR.check(status != SUCC_STATUS);
        JSONObject receiptObj = result.getJSONObject("receipt");
        ToastResultCode.IAP_BUNDLE_ID_ERROR.check(!BUNDLE_ID.equals(receiptObj.getString("bundle_id")));
        JSONArray latestReceiptInfoArray = result.getJSONArray("latest_receipt_info");
        JSONObject latestReceiptInfo = latestReceiptInfoArray.getJSONObject(0);
        return latestReceiptInfo;
    }

    /**
     * 处理收据
     * 21000 App Store无法读取你提供的JSON数据
     * 21002 收据数据不符合格式
     * 21003 收据无法被验证
     * 21004 你提供的共享密钥和账户的共享密钥不一致
     * 21005 收据服务器当前不可用
     * 21006 收据是有效的，但订阅服务已经过期。当收到这个信息时，解码后的收据信息也包含在返回内容中
     * 21007 收据信息是测试用（sandbox），但却被发送到产品环境中验证
     * 21008 收据信息是产品环境中使用，但却被发送到测试环境中验证
     *
     * @param req
     * @return 是否处理完成
     */
    private boolean processReceipt(IapUploadReceiptReq req) {
        JSONObject request = new JSONObject();
        request.put("receipt-data", req.getReceipt());
        request.put("password", password);
        String str = netRetryUtils.postForObject(verifyReceiptUrl, request, String.class);
        log.info("iap verify receipt result: {}, receiptId: {}", str, req.getReceiptId());
        JSONObject result = JSON.parseObject(str);

        //生产环境沙盒充值标志
//        boolean sandBoxCharge = false;
        int status = result.getInteger("status");
        //审核期间才会在生产环境校验沙盒收据
        if (status == SANDBOX_RECIPT) {
            log.warn("生产环境沙盒充值校验逻辑");
            str = netRetryUtils.postForObject(SANDBOX_VERIFY_RECEIPT_URL, request, String.class);
            log.warn("生产环境校验沙盒环境收据结果: {}", str);
            result = JSON.parseObject(str);
            status = result.getInteger("status");
//            sandBoxCharge = true;
        }

        if (status != SUCC_STATUS) {
            log.info("收据不是成功状态{},{}", status, req.getReceiptId());
            //这里不应该是 return false才对吗？
            return true;
        }

        JSONObject receipt = result.getJSONObject("receipt");
        if (!BUNDLE_ID.equals(receipt.getString("bundle_id"))) {
            log.error("收据应用ID对应不上");
            return true;
        }
        JSONArray inApps = receipt.getJSONArray("in_app");

        for (int i = 0; i < inApps.size(); i++) {
            JSONObject inApp = inApps.getJSONObject(i);
            String productId = inApp.getString("product_id");
            String transactionId = inApp.getString("transaction_id");
            log.info("transactionId: {}", transactionId);
            String tradeNo = req.getTransactions().getString(transactionId);
            log.info("tradeNo: {}", tradeNo);

            //找不到相应的tradeNo
            if (StringUtils.isBlank(tradeNo)) {
                continue;
            }

            PayCharge payCharge = payChargeService.getByBundleIdAndProductIdAndPlatform(BUNDLE_ID, productId, "iOS");
            PayTrade payTrade = payTradeService.getByTradeNo(tradeNo);

            //沙盒环境 记录这条数据是由沙盒产生的
            if (SANDBOX_RECIPT == result.getInteger("status")) {
                payTradeDao.setStanbox(1, payTrade.getId());
            }

//            if (sandBoxCharge) {
//                List<Long> reviewAccountList = payConfigService.listReviewAccount();
//                if (reviewAccountList == null || !reviewAccountList.contains(payTrade.getSrcAccountId())) {
//                    log.error("生产环境校验沙盒环境收据，但交易发起账户不是iOS提审账户");
//                    break;
//                }
//                //提审账号最大可充值金额校验
//                if (payTradeService.sumChargeOccurAmount(payTrade.getSrcAccountId()) > AccountConstant.REVIEW_ACCOUNT_MAX_CHARGE_AMOUNT) {
//                    log.error("The top-up amount on the day cannot exceed $1000.00");
//                    break;
//                }
//            }

            if ((payCharge == null || payCharge.getPayAmount() == null)
                    || (payTrade == null || payTrade.getOccurAmount() == null)
                    || !Objects.equals(payCharge.getPayAmount(), payTrade.getOccurAmount())) {
                log.error("付款金额不匹配");
                break;
            }

            IapRecord iapRecord = iapRecordService.getByTransactionId(transactionId);
            if (iapRecord == null) {
                //事务
                TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
                try {
                    iapRecord = IapRecord.builder()
                            .tradeNo(tradeNo)
                            .transactionId(transactionId)
                            .receiptId(req.getReceiptId())
                            .bundleId(BUNDLE_ID)
                            .productId(productId)
                            .result(inApp.toJSONString())
                            .build();
                    iapRecordService.save(iapRecord);

                    //未处理的收据，可开始交易处理
                    payTradeService.tradeHandle(payTrade, TradeEvent.PAYMENT);
                    transactionManager.commit(transactionStatus);
                } catch (Exception e) {
                    transactionManager.rollback(transactionStatus);
                    log.error("iap receipt process error", e);
                }
            }
        }
        return true;
    }


    /**
     * 生产环境沙盒充值逻辑, 提审账号校验
     *
     * @param accountId
     */
    private void sandBoxChargeCheck(Long accountId) {
//        List<Long> reviewAccountList = payConfigService.listReviewAccount();
//        SysExceptionUtils.check(reviewAccountList == null || !reviewAccountList.contains(accountId), "", "生产环境校验沙盒环境收据，但交易发起账户不是iOS提审账户");
//        //提审账号最大可充值金额校验
//        BizExceptionUtils.check(payTradeService.sumChargeOccurAmount(accountId) > AccountConstant.REVIEW_ACCOUNT_MAX_CHARGE_AMOUNT, "", "The top-up amount on the day cannot exceed $1000.00");
    }
}
