<?php
/**
 * ApplePay.php UTF-8
 * 苹果支付
 *
 * @date    : 2018/6/8 12:30
 *
 * @license 这不是一个自由软件,未经授权不许任何使用和传播。
 * @author  : wuyonghong <wyh@huosdk.com>
 * @version : HUOSDK 8.0
 * https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW2
 */

namespace huo\controller\pay;

use huo\controller\common\Base;
use huo\controller\queue\ApplePayQueue;
use huo\controller\request\Request;
use huo\model\order\PayAppleErrorModel;
use huo\model\order\PayAppleModel;
use huolib\constant\OrderConst;
use huolib\constant\PaywayConst;
use huolib\status\OrderStatus;
use think\Log;

class ApplePay extends Base {
    const APPLE_STATUS_SUCCESS = 0; /* 0 表示验证成功 */
    const APPLE_STATUS_21000   = 21000; /* 21000 App Store无法读取你提供的JSON数据 */
    const APPLE_STATUS_21002   = 21002; /* 21002 收据数据不符合格式 */
    const APPLE_STATUS_21003   = 21003; /* 21003 收据无法被验证 */
    const APPLE_STATUS_21004   = 21004; /* 21004 你提供的共享密钥和账户的共享密钥不一致 */
    const APPLE_STATUS_21005   = 21005; /* 21005 收据服务器当前不可用 */
    const APPLE_STATUS_21006   = 21006; /* 21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中 */
    const APPLE_STATUS_21007   = 21007; /* 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证 */
    const APPLE_STATUS_21008   = 21008; /* 21008 收据信息是产品环境中使用,但却被发送到测试环境中验证 */
    private   $url_buy    = '';
    private   $sb_url_buy = '';
    protected $is_sandbox = false;
    private   $order_id   = '';

    /**
     * 构造函数
     *
     * @param bool $is_sandbox 是否是沙盒环境
     */
    public function __construct($is_sandbox = false) {
        $this->url_buy = "https://buy.itunes.apple.com/verifyReceipt";
        $this->sb_url_buy = "https://sandbox.itunes.apple.com/verifyReceipt";
        $this->is_sandbox = $is_sandbox;
    }

    /**
     * 校验苹果订单
     *
     * @param string $order_id     平台订单ID
     * @param string $trans_id     苹果订单ID
     * @param string $receipt_data 苹果支付票据
     * @param bool   $is_sandbox   是否是沙盒环境
     * @param string $idfv         苹果IDFV
     * @param string $apple_id     苹果应用ID
     *
     * @return bool
     */
    public function checkAppleOrder($order_id, $trans_id, $receipt_data = null, $is_sandbox = false, $idfv, $apple_id) {
        $_log_message = "func=".__FUNCTION__."&class=".__CLASS__.'&order_id='.$order_id.'&trans_id='.$trans_id
                        .'&receipt_data='.$receipt_data.'&idfv='.$idfv.'&apple_id='.$apple_id.'&step';
        if (empty($trans_id) || empty($receipt_data) || empty($apple_id) || empty($idfv)) {
            /* 不处理 Log 记录*/
            Log::write($_log_message.'1', LOG::LOG);

            return false;
        }
        $_oc_cache = SdkOrderCache::ins();
        $_apple_data = $_oc_cache->getAppleOrderByTransId($trans_id);
        if (!empty($order_id)) {
            /* 查询是否与trans_id对应 */
            if (empty($_apple_data)) {
                /* trans_id 与 order_id 未对应*/
                /* 获取并更新苹果订单数据 */
                $_apple_data = $this->getUpdateDataByOrderId($order_id, $trans_id, $receipt_data);
                if (false == $_apple_data) {
                    /* 订单Id错误 */
                    $this->code = OrderStatus::ORDER_ID_ERROR;
                    /* Log 记录 */
                    Log::write($_log_message.'2', LOG::LOG);

                    return false;
                }
            } elseif (!empty($_apple_data)) {
                /* trans_id 与 order_id 已对应 */
                if ($_apple_data['order_id'] != $order_id) {
                    /* 订单Id错误 */
                    $this->code = OrderStatus::ORDER_ID_ERROR;
                    /* Log 记录 */
                    Log::write($_log_message.'3', LOG::LOG);

                    return false;
                }
            }
        }
        if (!empty($_apple_data)) {
            /* 99%概率 order_id不丢 处理使用此流程 */
            $_order_id = $_apple_data['order_id'];
            $_order_data = $_oc_cache->getInfoByOrderId($_order_id);
            if (OrderConst::PAY_STATUS_SUC == $_order_data['status']) {
                /* 苹果支付已校验成功 */
                if (OrderConst::CP_STATUS_SUC == $_order_data['cp_status']) {
                    /* 已通知CP服务器 */
                    $this->code = OrderStatus::NO_ERROR;
                } else {
                    /* 未通知CP服务器 */
                    $_rs = (new Notify())->notify($_order_id);
                    $this->code = $_rs['code'];
                }

                return true;
            }
        }
        /* 苹果进行二次验证,防止收到的是伪造的数据 */
        $_rs = $this->checkReceiptData($receipt_data, $is_sandbox);
        if (false == $_rs) {
            /* 无法正确获取苹果服务器数据 */
            $this->code = OrderStatus::APPLE_CHECK_FAIL;
            /* Log 记录 */
            Log::write($_log_message.'4', LOG::LOG);

            return false;
        }
        if (self::APPLE_STATUS_SUCCESS != $_rs['status']) {
            $this->code = OrderStatus::RECEIPT_DATA_ERROR;
            /* Log 记录 */
            Log::write($_log_message.'5', LOG::LOG);

            return false;
        }
        if (!isset($_rs['receipt'])
            || !isset($_rs['receipt']['in_app'])
            || !isset($_rs['receipt']['in_app'][0])) {
            /* 无法正确获取苹果服务器数据 */
            $this->code = OrderStatus::RECEIPT_DATA_ERROR;
            /* Log 记录 */
            Log::write($_log_message.'6', LOG::LOG);

            return false;
        }
        /* 获取In-App Purchase Receipt Fields */
        $_receipt_data = $_rs['receipt']['in_app'];
        $_iap_data = [];
        if (!empty($_receipt_data)) {
            foreach ($_receipt_data as $_k => $_v) {
                if ($_v['transaction_id'] == $trans_id) {
                    $_iap_data = $_v;
                }
            }
        }
        $_iap_product_id = $_iap_data['product_id'];
        //$_iap_request_time = round($_iap_data['request_date_ms'] / 1000);
        $_iap_request_time = round($_rs['receipt']['request_date_ms'] / 1000);
        $_iap_purchase_time = round($_iap_data['purchase_date_ms'] / 1000);
        $_iap_transaction_id = $_iap_data['transaction_id'];
        if ($_iap_transaction_id != $trans_id) {
            $this->code = OrderStatus::TRANS_ID_ERROR;
            /* Log 记录 */
            Log::write($_log_message.'8', LOG::LOG);

            return false;
        }
        $_apple_data['payway'] = PaywayConst::PAYWAY_APPLE;
        if (true == $is_sandbox) {
            $_apple_data['payway'] = PaywayConst::PAYWAY_APPLE_TEST;
        }
        if (empty($_apple_data)) {
            /* 查找最近订单 */
            $_apple_data = (new PayAppleModel())->getLastOrderData(
                $_iap_request_time, $idfv, $apple_id, $_iap_product_id
            );
            if (empty($_apple_data)) {
                /* 异常订单记录 报警 */
                // TODO: wuyonghong 2018/6/11 收入异常记录
                /* Log 记录 */
                Log::write($_log_message.'9', LOG::ERROR);
                $_pae_data['trans_id'] = $_iap_transaction_id;
                $_pae_data['idfv'] = $idfv;
                $_pae_data['idfa'] = '';
                $_pae_data['apple_id'] = $apple_id;
                $_pae_data['product_id'] = $_iap_product_id;
                $_pae_data['status'] = OrderConst::PAY_STATUS_SUC;
                $_pae_data['payway'] = $_apple_data['payway'];
                $_pae_data['pay_time'] = $_iap_purchase_time;
                $_pae_data['check_cnt'] = 1;
                $_pae_data['last_check_time'] = time();
                $_pae_data['receipt_data'] = $receipt_data;
                $_pae_data['receipt_field'] = json_encode($_iap_data);
                (new PayAppleErrorModel())->addData($_pae_data);
                $this->code = OrderStatus::ORDER_NOT_EXISTS;

                /* 异常订单不再程序处理 */

                return true;
            }
        } else {
            /* 核对苹果支付信息 */
            if ($_iap_product_id != $_apple_data['product_id']) {
                $this->code = OrderStatus::PRODUCT_ID_ERROR;
                /* Log 记录 */
                Log::write($_log_message.'7', LOG::LOG);

                return false;
            }
        }
        $this->order_id = $_apple_data['order_id'];
        $_apple_data['status'] = OrderConst::PAY_STATUS_SUC;
        $_apple_data['check_cnt']++;
        $_apple_data['last_check_time'] = time();
        $_apple_data['pay_time'] = $_iap_purchase_time;
        $_oc_cache->updateAppleOrder($trans_id, $_apple_data);
        $_order_data = $_oc_cache->getInfoByOrderId($this->order_id);
        (new Notify())->payNotify(
            $_iap_product_id, $order_id, $trans_id, $_order_data['real_amount'], $_apple_data['payway']
        );
        $this->code = OrderStatus::NO_ERROR;

        return true;
    }

    /**
     * 订单校验
     *
     * @param string $order_id     平台订单ID
     * @param string $trans_id     苹果订单ID
     * @param string $receipt_data 苹果支付票据
     * @param bool   $is_sandbox   是否是沙盒环境
     * @param string $idfv         苹果IDFV
     * @param string $apple_id     苹果应用ID
     *
     * @return array
     */
    public function orderQuery(
        $order_id, $trans_id, $receipt_data = null, $is_sandbox = false, $idfv = '', $apple_id = ''
    ) {
        $_rdata = [
            'order_id'  => $order_id,
            'trans_id'  => $trans_id,
            'status'    => OrderConst::PAY_STATUS_NOT,
            'cp_status' => OrderConst::CP_STATUS_NOT,
        ];
        $this->order_id = $order_id;
        if (empty($trans_id) || empty($receipt_data)) {
            $_code = OrderStatus::INVALID_PARAMS;

            return $this->huoError($_code, OrderStatus::getMsg($_code), $_rdata);
        }
        $this->checkAppleOrder($order_id, $trans_id, $receipt_data, $is_sandbox, $idfv, $apple_id);
        $_code = $this->code;
        if (OrderStatus::APPLE_CHECK_FAIL == $_code) {
            /* 苹果验单失败 加入队列 */
            (new ApplePayQueue($apple_id))->setApplePayQueue(
                $order_id, $trans_id, $receipt_data, $is_sandbox, $idfv, $apple_id
            );

            return $this->huoError($_code, OrderStatus::getMsg($_code), $_rdata);
        }
        if (OrderStatus::NO_ERROR != $_code) {
            return $this->huoError($_code, OrderStatus::getMsg($_code), $_rdata);
        }
        /* 从订单中获取状态 */
        $_statues = (new Sdk())->getStatus($this->order_id);
        if (false == $_statues) {
            $_code = OrderStatus::ORDER_ID_ERROR;

            return $this->huoError($_code, OrderStatus::getMsg($_code), $_rdata);
        }
        $_rdata = [
            'order_id'  => $this->order_id,
            'trans_id'  => $trans_id,
            'status'    => $_statues['status'],
            'cp_status' => $_statues['cp_status']
        ];

        return $this->huoSuccess($_code, OrderStatus::getMsg($_code), $_rdata);
    }

    /**
     * 通过订单ID获取数据
     *
     * @param string $order_id
     * @param string $trans_id
     * @param string $receipt_data
     *
     * @return array|bool|false
     */
    private function getUpdateDataByOrderId($order_id, $trans_id, $receipt_data) {
        $_pa_model = new PayAppleModel();
        $_apple_data = $_pa_model->getInfoByOrderId($order_id);
        if (!empty($_apple_data['trans_id'])) {
            if ($_apple_data['trans_id'] == $trans_id) {
                return $_apple_data;
            }

            /* 已对应其他订单不处理 */

            return false;
        }
        $_apple_data['trans_id'] = $trans_id;
        $_apple_data['receipt_data'] = $receipt_data;
        $_rs = $_pa_model->updateData($_apple_data, $_apple_data['id']);
        if (false === $_rs) {
            return false;
        }
        SdkOrderCache::ins()->saveAppleOrder($trans_id, $_apple_data);

        return $_apple_data;
    }

    /**
     * 向苹果服务器校验ReceiptData
     *
     * @param      $receipt
     * @param bool $is_sandbox
     *
     * @return array|false   失败直接返回false 成功放回以下数组
     * array (
     *    'status' => 0,
     *    'environment' => 'Sandbox',
     *    'receipt' =>
     *        array (
     *            'receipt_type' => 'ProductionSandbox',
     *            'adam_id' => 0,
     *            'app_item_id' => 0,   //App Store用来标识程序的字符串
     *            'bundle_id' => 'com.abcde.www',     //iPhone程序的bundle标识
     *            'application_version' => '0.0.9',
     *            'download_id' => 0,
     *            'version_external_identifier' => 0,
     *            'receipt_creation_date' => '2016-07-13 18:22:19 Etc/GMT',
     *            'receipt_creation_date_ms' => '1468434139000',
     *            'receipt_creation_date_pst' => '2016-07-13 11:22:19 America/Los_Angeles',
     *            'request_date' => '2016-07-13 18:22:22 Etc/GMT',
     *            'request_date_ms' => '1468434142143',
     *            'request_date_pst' => '2016-07-13 11:22:22 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' =>
     *                array (
     *                    0 =>
     *                        array (
     *                            'quantity' => '1',                                  //购买商品的数量
     *                            'product_id' => 'price_1',              ////商品的标识
     *                            'transaction_id' => '1000000223463280',             //交易的标识
     *                            'original_transaction_id' => '1000000223463280',    //原始交易ID
     *                            'purchase_date' => '2016-07-13 18:22:19 Etc/GMT',   //购买时间
     *                            'purchase_date_ms' => '1468434139000',               //购买时间毫秒
     *                            'purchase_date_pst' => '2016-07-13 11:22:19 America/Los_Angeles',//太平洋标准时间
     *                            'original_purchase_date' => '2016-07-13 18:22:19 Etc/GMT',   //原始购买时间
     *                            'original_purchase_date_ms' => '1468434139000',              //原始购买时间毫秒
     *                            'original_purchase_date_pst' => '2016-07-13 11:22:19 America/Los_Angeles',//太平洋标准时间
     *                            'is_trial_period' => 'false',
     *                              ),
     *                        ),
     *              ),
     * )
     */
    protected function checkReceiptData($receipt, &$is_sandbox = false) {
        if ($is_sandbox) {
            $_url_buy = $this->sb_url_buy;
        } else {
            $_url_buy = $this->url_buy;
        }
        $_param = json_encode(['receipt-data' => $receipt]);
        $_rs = Request::httpJsonpost($_url_buy, $_param);
        if (false === $_rs) {
            /* 请求失败 */
            return false;
        }
        $_rs = json_decode($_rs, true);
        if (empty($_rs) || !isset($_rs['status'])) {
            return false;
        }
        switch ($_rs['status']) {
            case self::APPLE_STATUS_21007:
                /* 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证 苹果官网进行二次验证(沙盒) */
                $_rs = Request::httpJsonpost($this->sb_url_buy, $_param);
                $is_sandbox = false;
                break;
            case self::APPLE_STATUS_21008:
                /* 21008为正式充值去请求沙盒验单地址返回状态,增加确保正式充值验单成功 */
                $_rs = Request::httpJsonpost($this->url_buy, $_param);
                $is_sandbox = true;
                break;
            default:
                return $_rs;
        }
        if (false === $_rs) {
            /* 请求失败 */
            return false;
        }
        $_rs = json_decode($_rs, true);
        if (empty($_rs) || !isset($_rs['status'])) {
            return false;
        }

        return $_rs;
    }
}