<?php
namespace liuniu;
use app\admin\model\Company;
use app\common\model\WechatContext;
use EasyWeChat\Factory;
use think\Hook;
use think\Request;

class MiniProgramService
{
    private static $instance = null;
    private static $app = null;
    public static function options($cid)
    {
        $info = Company::where('id',$cid)->find();
        $config = [
            'app_id' => isset($info['routine_appid']) ? trim($info['routine_appid']) : '',
            'secret' => isset($info['routine_appsecret']) ? trim($info['routine_appsecret']) : '',
            'token' => isset($info['wechat_token']) ? trim($info['wechat_token']) : '',
            'aes_key' => isset($info['wechat_encodingaeskey']) ? trim($info['wechat_encodingaeskey']) : ''
        ];
        if (isset($info['pay_routine_open']) && $info['pay_routine_open'] == 1) {
            $config1 = [
                'merchant_id' => trim($info['pay_routine_mchid']),
                'key' => trim($info['pay_routine_key']),
                'cert_path' => realpath('.' . $info['pay_routine_client_cert']),
                'key_path' => realpath('.' . $info['pay_routine_client_key']),
                'notify_url' => Request::instance()->domain(). "/api/routine/notify/".$cid
            ];
            $config = array_merge($config,$config1);
        }
        return $config;
    }
    public static function miniprogram($cache = false,$cid)
    {
        (self::$instance['cid'] === null || $cache === true) && (self::$instance['cid'] = Factory::miniProgram(self::options($cid)));
        return self::$instance['cid'];
    }
    /**
     * 支付接口
     * @param false $cache
     * @param int $cid
     * @return \EasyWeChat\Payment\Application|mixed
     */
    public static function payment($cache = false,$cid=0)
    {
        (self::$app[$cid] === null || $cache === true) && (self::$app[$cid] = Factory::payment(self::options($cid)));
        return self::$app[$cid];
    }
    /**
     * 获得用户信息 根据code 获取session_key
     * @param array|string $openid
     * @return $userInfo
     */
    public static function getUserInfo($cid,$code)
    {
        $userInfo = self::miniprogram(false,$cid)->auth->session($code);
        return $userInfo;
    }
    /**
     * 加密数据解密
     * @param $sessionKey
     * @param $iv
     * @param $encryptData
     * @return $userInfo
     */
    public static function encryptor($cid,$sessionKey, $iv, $encryptData)
    {
        return self::miniprogram(false,$cid)->encryptor->decryptData($sessionKey, $iv, $encryptData);
    }
    /**
     * 订阅模板消息接口
     * @return \crmeb\services\subscribe\ProgramSubscribe
     */
    public static function SubscribenoticeService($cid)
    {
        return self::miniprogram(false,$cid)->subscribe_message;
    }
    /**
     * 发送订阅消息
     * @param string $touser 接收者（用户）的 openid
     * @param string $templateId 所需下发的订阅模板id
     * @param array $data 模板内容，格式形如 { "key1": { "value": any }, "key2": { "value": any } }
     * @param string $link 击模板卡片后的跳转页面，仅限本小程序内的页面。支持带参数,（示例index?foo=bar）。该字段不填则模板无跳转。
     * @return \EasyWeChat\Support\Collection|null
     * @throws \EasyWeChat\Core\Exceptions\HttpException
     * @throws \EasyWeChat\Core\Exceptions\InvalidArgumentException
     */
    public static function sendSubscribeTemlate($cid,string $touser, string $templateId, array $data, string $link = '')
    {
        $msg = [
            'template_id' => $templateId, // 所需下发的订阅模板id
            'touser' => $touser,     // 接收者（用户）的 openid
            'page' => $link,       // 点击模板卡片后的跳转页面，仅限本小程序内的页面。支持带参数,（示例index?foo=bar）。该字段不填则模板无跳转。
            'data' => $data,
        ];
        return self::SubscribenoticeService($cid)->send($msg);
    }
    /**
     * 生成支付订单对象
     * @param $openid
     * @param $out_trade_no
     * @param $total_fee
     * @param $attach
     * @param $body
     * @param string $detail
     * @param string $trade_type
     * @param array $options
     * @return Order
     */
    public static function paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [],$cid=0)
    {
        $total_fee = bcmul($total_fee, 100, 0);
        $order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type'), $options);
//        if (!is_null($openid)) $order['openid'] = $openid;
        if ($order['detail'] == '') unset($order['detail']);
        $result = self::payment(false,$cid)->order->unify($order);
        return $result;
    }



    /**
     * 使用商户订单号退款
     * @param $orderNo
     * @param $opt
     */

    public static function payOrderRefund($cid,$orderNo, array $opt)
    {
        if (!isset($opt['pay_price'])) exception('缺少pay_price');
        $totalFee = floatval(bcmul($opt['pay_price'], 100, 0));
        $refundFee = isset($opt['refund_price']) ? floatval(bcmul($opt['refund_price'], 100, 0)) : null;
        $refundReason = isset($opt['desc']) ? $opt['desc'] : '';
        $refundNo = isset($opt['refund_id']) ? $opt['refund_id'] : $orderNo;
        $opUserId = isset($opt['op_user_id']) ? $opt['op_user_id'] : null;
        $type = isset($opt['type']) ? $opt['type'] : 'out_trade_no';
        /*仅针对老资金流商户使用
        REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款（默认使用未结算资金退款）
        REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款*/
        $refundAccount = isset($opt['refund_account']) ? $opt['refund_account'] : 'REFUND_SOURCE_UNSETTLED_FUNDS';
        try {
            $res = self::payment('false',$cid)->byOutTradeNumber($orderNo,$refundNo,$totalFee,$refundFee,['refund_desc'=>$refundReason]);
            if ($res->return_code == 'FAIL') exception('退款失败:' . $res->return_msg);
            if (isset($res->err_code)) exception('退款失败:' . $res->err_code_des);
        } catch (\Exception $e) {
            exception($e->getMessage());
        }
        return true;
    }

    /**
     * 微信支付成功回调接口
     */
    public static function handleNotify($cid)
    {
        $response  = self::payment(true,$cid)->handlePaidNotify(function ($notify, $successful) use($cid){
            if ($successful && isset($notify['out_trade_no'])) {
                if (isset($notify['attach']) && $notify['attach']) {
                    if (($count = strpos($notify['out_trade_no'], '_')) !== false) {
                        $notify['out_trade_no'] = substr($notify['out_trade_no'], $count + 1);
                    }
                    $params = [$cid,$notify['out_trade_no']];
                    Hook::exec("\\liuniu\\repositories\\PaymentRepositories","wechat".ucfirst($notify['attach']),$params);
                }
                $data = ['eventkey' => 'notify', 'command' => '', 'refreshtime' => time(), 'openid' => $notify['openid'],'message'=>json_encode($notify)];
                $wechatContext = WechatContext::create($data, true);
                return true;
            }
        });
        $response->send();
    }
    public static function getToken($cid)
    {
        $token = self::miniprogram(false,$cid)->access_token->getToken();
        return $token;

    }
    /**
     * 生成支付签约订单对象
     * @param $openid
     * @param $out_trade_no
     * @param $total_fee
     * @param $attach
     * @param $body
     * @param string $detail
     * @param string $trade_type
     * @param array $options
     * @return Order
     */
    public static function paysignedOrder($openid, $out_trade_no, $total_fee, $attach, $body,$contract_code,$plan_id,$spbill_create_ip, $detail = '', $trade_type = 'JSAPI', $options = [],$cid=0,$contract_display_account='',$contract_notify_url='')
    {
        $total_fee = bcmul($total_fee, 100, 0);
        $order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type','contract_code','spbill_create_ip','plan_id','contract_display_account','contract_notify_url'), $options);
        if (!is_null($openid)) $order['openid'] = $openid;
        if ($order['detail'] == '') unset($order['detail']);
        $result = self::payment(false,$cid)->order->unify($order,true);
        return $result;
    }

}
