Ver Fonte

拉去服务器代码

Cherry há 2 anos atrás
commit
7baaecd777
100 ficheiros alterados com 12154 adições e 0 exclusões
  1. 10 0
      .bowerrc
  2. 19 0
      .env.bak20220729
  3. 19 0
      .env.bak2023412
  4. 28 0
      .env.sample
  5. 16 0
      .gitignore
  6. 7 0
      404.html
  7. 14 0
      LICENSE
  8. 1 0
      a.html
  9. 1 0
      addons/.gitkeep
  10. 95 0
      addons/aliyunsms/Aliyunsms.php
  11. 148 0
      addons/aliyunsms/config.php
  12. 15 0
      addons/aliyunsms/controller/Index.php
  13. 8 0
      addons/aliyunsms/info.ini
  14. 153 0
      addons/aliyunsms/library/Aliyunsms.php
  15. 1 0
      addons/areapicker/.addonrc
  16. 31 0
      addons/areapicker/Areapicker.php
  17. 27 0
      addons/areapicker/bootstrap.js
  18. 3 0
      addons/areapicker/config.php
  19. 119 0
      addons/areapicker/controller/Index.php
  20. 10 0
      addons/areapicker/info.ini
  21. 2754 0
      addons/areapicker/view/index/index.html
  22. 1 0
      addons/command/.addonrc
  23. 69 0
      addons/command/Command.php
  24. 4 0
      addons/command/config.php
  25. 15 0
      addons/command/controller/Index.php
  26. 10 0
      addons/command/info.ini
  27. 12 0
      addons/command/install.sql
  28. 28 0
      addons/command/library/Output.php
  29. 1 0
      addons/epay/.addonrc
  30. 69 0
      addons/epay/Epay.php
  31. 43 0
      addons/epay/certs/alipayCertPublicKey.crt
  32. 88 0
      addons/epay/certs/alipayRootCert.crt
  33. 24 0
      addons/epay/certs/apiclient_cert.pem
  34. 28 0
      addons/epay/certs/apiclient_key.pem
  35. 24 0
      addons/epay/certs/appCertPublicKey.crt
  36. 366 0
      addons/epay/config.html
  37. 67 0
      addons/epay/config.php
  38. 243 0
      addons/epay/controller/Api.php
  39. 111 0
      addons/epay/controller/Index.php
  40. 10 0
      addons/epay/info.ini
  41. 18 0
      addons/epay/library/Collection.php
  42. 16 0
      addons/epay/library/OrderException.php
  43. 1856 0
      addons/epay/library/QRCode.php
  44. 58 0
      addons/epay/library/RedirectResponse.php
  45. 26 0
      addons/epay/library/Response.php
  46. 325 0
      addons/epay/library/Service.php
  47. 110 0
      addons/epay/library/Wechat.php
  48. 83 0
      addons/epay/library/Yansongda/Pay/Contracts/GatewayApplicationInterface.php
  49. 20 0
      addons/epay/library/Yansongda/Pay/Contracts/GatewayInterface.php
  50. 98 0
      addons/epay/library/Yansongda/Pay/Events.php
  51. 31 0
      addons/epay/library/Yansongda/Pay/Events/ApiRequested.php
  52. 31 0
      addons/epay/library/Yansongda/Pay/Events/ApiRequesting.php
  53. 40 0
      addons/epay/library/Yansongda/Pay/Events/Event.php
  54. 33 0
      addons/epay/library/Yansongda/Pay/Events/MethodCalled.php
  55. 31 0
      addons/epay/library/Yansongda/Pay/Events/PayStarted.php
  56. 23 0
      addons/epay/library/Yansongda/Pay/Events/PayStarting.php
  57. 25 0
      addons/epay/library/Yansongda/Pay/Events/RequestReceived.php
  58. 25 0
      addons/epay/library/Yansongda/Pay/Events/SignFailed.php
  59. 19 0
      addons/epay/library/Yansongda/Pay/Exceptions/BusinessException.php
  60. 44 0
      addons/epay/library/Yansongda/Pay/Exceptions/Exception.php
  61. 20 0
      addons/epay/library/Yansongda/Pay/Exceptions/GatewayException.php
  62. 19 0
      addons/epay/library/Yansongda/Pay/Exceptions/InvalidArgumentException.php
  63. 19 0
      addons/epay/library/Yansongda/Pay/Exceptions/InvalidConfigException.php
  64. 19 0
      addons/epay/library/Yansongda/Pay/Exceptions/InvalidGatewayException.php
  65. 19 0
      addons/epay/library/Yansongda/Pay/Exceptions/InvalidSignException.php
  66. 422 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay.php
  67. 38 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/AppGateway.php
  68. 40 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/Gateway.php
  69. 46 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/MiniGateway.php
  70. 47 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/PosGateway.php
  71. 21 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/RefundGateway.php
  72. 41 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/ScanGateway.php
  73. 452 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/Support.php
  74. 49 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/TransferGateway.php
  75. 26 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/WapGateway.php
  76. 104 0
      addons/epay/library/Yansongda/Pay/Gateways/Alipay/WebGateway.php
  77. 366 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat.php
  78. 62 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/AppGateway.php
  79. 88 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/Gateway.php
  80. 57 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/GroupRedpackGateway.php
  81. 35 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/MiniappGateway.php
  82. 59 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/MpGateway.php
  83. 44 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/PosGateway.php
  84. 61 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/RedpackGateway.php
  85. 50 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/RefundGateway.php
  86. 44 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/ScanGateway.php
  87. 449 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/Support.php
  88. 80 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/TransferGateway.php
  89. 47 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/WapGateway.php
  90. 86 0
      addons/epay/library/Yansongda/Pay/Gateways/Wechat/WebGateway.php
  91. 20 0
      addons/epay/library/Yansongda/Pay/LICENSE
  92. 114 0
      addons/epay/library/Yansongda/Pay/Listeners/KernelLogSubscriber.php
  93. 49 0
      addons/epay/library/Yansongda/Pay/Log.php
  94. 131 0
      addons/epay/library/Yansongda/Pay/Pay.php
  95. 605 0
      addons/epay/library/Yansongda/Supports/Arr.php
  96. 363 0
      addons/epay/library/Yansongda/Supports/Collection.php
  97. 7 0
      addons/epay/library/Yansongda/Supports/Config.php
  98. 20 0
      addons/epay/library/Yansongda/Supports/LICENSE
  99. 91 0
      addons/epay/library/Yansongda/Supports/Log.php
  100. 240 0
      addons/epay/library/Yansongda/Supports/Logger.php

+ 10 - 0
.bowerrc

@@ -0,0 +1,10 @@
+{
+  "directory": "public/assets/libs",
+  "ignoredDependencies": [
+    "es6-promise",
+    "file-saver",
+    "html2canvas",
+    "jspdf",
+    "jspdf-autotable"
+  ]
+}

+ 19 - 0
.env.bak20220729

@@ -0,0 +1,19 @@
+[app]
+debug = false
+trace = false
+
+[database]
+hostname = 127.0.0.1
+database = mmlh_vip
+username = mmlh_vip
+password = LyP657cPXr3eYz28
+hostport = 3306
+prefix = 
+debug = false
+
+[redis]
+password=
+prefix=beauti-no
+
+[sms]
+testcode=1111

+ 19 - 0
.env.bak2023412

@@ -0,0 +1,19 @@
+[app]
+debug = false
+trace = false
+
+[database]
+hostname = pc-wz93t26u8943k8m63.rwlb.rds.aliyuncs.com
+database = mmlh_vip
+username = mmlh_vip
+password = LyP657cPXr3eYz28
+hostport = 3306
+prefix = 
+debug = false
+
+[redis]
+password=
+prefix=beauti-no
+
+[sms]
+testcode=6536

+ 28 - 0
.env.sample

@@ -0,0 +1,28 @@
+[app]
+debug = false
+trace = false
+
+[database]
+hostname = 127.0.0.1
+database = fastadmin
+username = root
+password = root
+hostport = 3306
+prefix = fa_
+
+[redis]
+password=123456
+prefix=videopoint
+
+[sms]
+testcode=1111
+
+[company_wechat]
+webhook=
+
+[api_config]
+minsheng_key=aallsoJJDAG87ADAnna
+minsheng_secret=adakda2313BAALPOjjada12
+
+guangzhou_liantong_source=
+guangzhou_liantong_password=

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+/nbproject/
+/runtime/*
+/public/uploads/*
+.idea
+composer.lock
+*.log
+*.css.map
+!.gitkeep
+.env
+.svn
+.vscode
+node_modules
+/public/.user.ini
+/application/extra/site.php
+.DS_Store
+.htaccess

+ 7 - 0
404.html

@@ -0,0 +1,7 @@
+<html>
+<head><title>404 Not Found</title></head>
+<body>
+<center><h1>404 Not Found</h1></center>
+<hr><center>nginx</center>
+</body>
+</html>

+ 14 - 0
LICENSE

@@ -0,0 +1,14 @@
+外网面板地址: http://120.79.95.107:8888/ac3aba12
+内网面板地址: http://172.25.148.46:8888/ac3aba12
+username: ndfsuxxl
+password: 23c9d581
+
+测试验证码:1111
+正式验证码:6536
+
+抖音账号:
+账户:mmlh666@163.com
+密码:qx6609..
+开发者用户名mmlh888
+tt70240699ef1aabfb01
+4884922d0e8ebbbcd6de387f2cb8f7e8804af45d

+ 1 - 0
a.html

@@ -0,0 +1 @@
+15.33.20

+ 1 - 0
addons/.gitkeep

@@ -0,0 +1 @@
+

+ 95 - 0
addons/aliyunsms/Aliyunsms.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace addons\aliyunsms;
+
+use app\common\library\Menu;
+use app\common\model\Sms;
+use think\Addons;
+
+/**
+ * Aliyunsms插件
+ */
+class Aliyunsms extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+
+        return true;
+    }
+
+    /**
+     * 插件启用方法
+     * @return bool
+     */
+    public function enable()
+    {
+
+        return true;
+    }
+
+    /**
+     * 插件禁用方法
+     * @return bool
+     */
+    public function disable()
+    {
+
+        return true;
+    }
+
+    /**
+     * 短信发送
+     * @param Sms $params
+     * @return mixed
+     * @throws \AlibabaCloud\Client\Exception\ClientException
+     * @throws \AlibabaCloud\Client\Exception\ServerException
+     */
+    public function smsSend(&$params)
+    {
+        $smsbao = new library\Aliyunsms();
+        return $smsbao->mobile($params['mobile'])
+            ->tempCode($params['temp'])
+            ->code($params['code'])
+            ->bind($params['params'])
+            ->send();
+    }
+
+    /**
+     * 短信发送通知(msg参数直接构建实际短信内容即可)
+     * @param array $params
+     * @return  boolean
+     * @throws \AlibabaCloud\Client\Exception\ClientException
+     * @throws \AlibabaCloud\Client\Exception\ServerException
+     */
+    public function smsNotice(&$params)
+    {
+        $smsbao = new library\Aliyunsms();
+        $result = $smsbao->mobile($params['mobile'])->msg($params['msg'])->send();
+        return $result;
+    }
+
+    /**
+     * 检测验证是否正确
+     * @param Sms $params
+     * @return  boolean
+     */
+    public function smsCheck(&$params)
+    {
+        return TRUE;
+    }
+}

+ 148 - 0
addons/aliyunsms/config.php

@@ -0,0 +1,148 @@
+<?php
+
+return [
+    [
+        'name' => 'accessKeyId',
+        'title' => 'accessKeyId',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'LTAI5tNGGy12coykFRCLz6iN',
+        'rule' => 'required',
+        'msg' => '',
+        'tip' => '',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'accessSecret',
+        'title' => 'accessSecret',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'Pot8LoSmQ7fC5ijX2oxMyXlLYsXwDF',
+        'rule' => 'required',
+        'msg' => '',
+        'tip' => '',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'regionId',
+        'title' => 'regionId',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'cn-hangzhou',
+        'rule' => 'required',
+        'msg' => '',
+        'tip' => 'cn-hangzhou',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'signName',
+        'title' => 'signName',
+        'type' => 'string',
+        'content' => [],
+        'value' => '美美靓号',
+        'rule' => 'required',
+        'msg' => '',
+        'tip' => 'signName',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_common',
+        'title' => '普通短信模版',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'SMS_229335244',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '普通短信模版',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_send_order',
+        'title' => '发货通知模版',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'SMS_243945777',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '发货通知模版',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_submit_order',
+        'title' => '下单短信通知模板',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'SMS_243925740',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '下单短信通知模板',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_payed_order',
+        'title' => '订购通知模板',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'SMS_243925740',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '订购通知模板',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_refund_order',
+        'title' => '订单退款通知模板',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'SMS_229635697',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '订单退款通知模板',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_down',
+        'title' => '下架通知模板',
+        'type' => 'string',
+        'content' => [],
+        'value' => 'SMS_236561776',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '下架通知模板',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_unpay',
+        'title' => '未支付通知模板',
+        'type' => 'string',
+        'content' => [],
+        'value' => '',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '未支付通知模板',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'temp_supply_notice',
+        'title' => '供应商订购通知',
+        'type' => 'string',
+        'content' => [],
+        'value' => '',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '用户付款供应商收到通知',
+        'ok' => '',
+        'extend' => '',
+    ],
+];

+ 15 - 0
addons/aliyunsms/controller/Index.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace addons\smsbao\controller;
+
+use think\addons\Controller;
+
+class Index extends Controller
+{
+
+    public function index()
+    {
+        $this->error("当前插件暂无前台页面");
+    }
+
+}

+ 8 - 0
addons/aliyunsms/info.ini

@@ -0,0 +1,8 @@
+name = aliyunsms
+title = 阿里云短信
+intro = 快速接入、使用方便、价格低廉的短信服务
+author = xianghua_we
+website = https://www.fastadmin.net
+version = 1.0.0
+state = 1
+url = /addons/aliyunsms.html

+ 153 - 0
addons/aliyunsms/library/Aliyunsms.php

@@ -0,0 +1,153 @@
+<?php
+
+namespace addons\aliyunsms\library;
+
+use AlibabaCloud\Client\AlibabaCloud;
+
+class Aliyunsms
+{
+    private $_params = [];
+    protected $error = '';
+    protected $config = [];
+    protected static $instance = null;
+    protected $statusStr = array(
+        "0" => "短信发送成功",
+        "-1" => "参数不全",
+        "-2" => "服务器空间不支持,请确认支持curl或者fsocket,联系您的空间商解决或者更换空间!",
+        "30" => "密码错误",
+        "40" => "账号不存在",
+        "41" => "余额不足",
+        "42" => "帐户已过期",
+        "43" => "IP地址限制",
+        "50" => "内容含有敏感词"
+    );
+    protected $bindParams=[];
+    protected $tempCode;
+    protected $code;
+
+
+    /**
+     * Aliyunsms constructor.
+     * @param array $options
+     * @throws \AlibabaCloud\Client\Exception\ClientException
+     */
+    public function __construct($options = [])
+    {
+        if ($config = get_addon_config('aliyunsms')) {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+        AlibabaCloud::accessKeyClient($this->config['accessKeyId'], $this->config['accessSecret'])
+            ->regionId($this->config['regionId'])
+            ->asDefaultClient();
+    }
+
+    /**
+     * 单例
+     * @param array $options 参数
+     * @return Aliyunsms
+     */
+    public static function instance($options = [])
+    {
+        if (is_null(self::$instance)) {
+            self::$instance = new static($options);
+        }
+        return self::$instance;
+    }
+
+    /**
+     * 立即发送短信
+     *
+     * @return boolean
+     * @throws \AlibabaCloud\Client\Exception\ClientException
+     * @throws \AlibabaCloud\Client\Exception\ServerException
+     */
+    public function send()
+    {
+        $this->error = '';
+        $params = $this->_params();
+
+
+        $params_post = [
+            'code' => $this->code,
+        ];
+        $params_post=array_merge($params_post,$this->bindParams);
+
+
+        $result = AlibabaCloud::rpc()
+            ->product('Dysmsapi')
+            // ->scheme('https') // https | http
+            ->version('2017-05-25')
+            ->action('SendSms')
+            ->method('POST')
+            ->host('dysmsapi.aliyuncs.com')
+            ->options([
+                'query' => $allParams=[
+                    'RegionId' => $this->config['regionId'],
+                    'PhoneNumbers' => $this->_params['mobile'],
+                    'SignName' => $this->config['signName'],
+                    'TemplateCode' => $this->config[$this->tempCode]??'',
+                    'TemplateParam' => json_encode($params_post),
+                ],
+            ])
+            ->request();
+
+        $result = $result->toArray();
+        user_log('sms',compact('allParams','result'));
+        if ($result['Code'] == "OK") {
+            return true;
+        } else {
+            $this->error = $result['Message'];
+        }
+        return false;
+    }
+
+    private function _params()
+    {
+        return $this->_params;
+    }
+
+    /**
+     * 获取错误信息
+     * @return string
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * 接收手机
+     * @param string $mobile 手机号码
+     * @return Aliyunsms
+     */
+    public function mobile($mobile = '')
+    {
+        $this->_params['mobile'] = $mobile;
+        return $this;
+    }
+
+    /**
+     * 短信内容
+     * @param string $msg 短信内容
+     * @return Aliyunsms
+     */
+    public function msg($msg = '')
+    {
+        $this->_params['msg'] = $msg;
+        return $this;
+    }
+
+    public function bind($params){
+        $this->bindParams = $params;
+        return $this;
+    }
+    public function code($code){
+        $this->code = $code;
+        return $this;
+    }
+    public function tempCode($tmpCode){
+        $this->tempCode = $tmpCode;
+        return $this;
+    }
+}

+ 1 - 0
addons/areapicker/.addonrc

@@ -0,0 +1 @@
+{"files":[],"license":"regular","licenseto":"50893","licensekey":"vmVWbfw5S4Kl1HPZ yBTETNIHdIFqfZ+LrfjB\/fhDWh72XtImTBiz4Ug7uzc=","domains":[],"licensecodes":[],"validations":[]}

+ 31 - 0
addons/areapicker/Areapicker.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace addons\areapicker;
+
+use think\Addons;
+
+/**
+ * 区域选择
+ * @author [xiaoyu5062] <[xiaoyu5062@qq.com]>
+ */
+class Areapicker extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+        return true;
+    }
+}

+ 27 - 0
addons/areapicker/bootstrap.js

@@ -0,0 +1,27 @@
+require([], function () {
+    $(document).on('click', "[data-toggle='areapicker']", function () {
+        var iscustomer =$(this).data('iscustomer')?$(this).data('iscustomer'):false;
+        var that = this;
+        var callback = $(that).data('callback');
+        var input_name = $(that).data("input-name") ? $(that).data("input-name") : "";
+        var input_val = $(that).data("input-val") ? $(that).data("input-val") : "";
+        var name = input_name ? $("#" + input_name).val() : '';
+        var val = input_val ? $("#" + input_val).val() : '';
+        var datasource = $(that).data("datasource") ? $(that).data("datasource") : "";
+        var url = "/addons/areapicker/index?iscustomer="+iscustomer+"&ds="+datasource;
+        url += val ? '&val=' + val : '';
+        Fast.api.open(url, '区域选择', {
+            callback: function (res) {
+                input_name && $("#" + input_name).val(res.name);
+                input_val && $("#" + input_val).val(res.val);
+                try {
+                    if (typeof callback === 'function') {
+                        callback.call(that, res);
+                    }
+                } catch (e) {
+
+                }
+            }
+        });
+    });
+});

+ 3 - 0
addons/areapicker/config.php

@@ -0,0 +1,3 @@
+<?php
+
+return array();

+ 119 - 0
addons/areapicker/controller/Index.php

@@ -0,0 +1,119 @@
+<?php
+/*
+ * @Description: 区域插件
+ * @Author: xiaoyu5062
+ * @QQ: 170515071
+ * @E-mail: xiaoyu5062@qq.com
+ * @Date: 2020-12-23 18:19:22
+ * @Remark:
+ * 【自定义数据源使用方法】
+ * 1、在前台html元素中增加 自定义属性data-datasource
+ * 2、在该控制器中的datasource方法中根据前台设置的属性值判断(107行)返回遵循示例数据源格式(46行)的数据
+ */
+
+namespace addons\areapicker\controller;
+
+use think\addons\Controller;
+use app\admin\model\Area;
+
+class Index extends Controller
+{
+
+
+    public function index()
+    {
+        $val = $this->request->get('val', 100001);
+        $iscustomer = $this->request->get('iscustomer', 'false');
+        if ($iscustomer == 'true') {
+            $func = $this->request->get('ds', false);
+            $this->view->assign('data', $this->datasource($func));
+        }
+        $this->view->assign('val', $val);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 自定义数据源
+     * 当元素data-iscustomer='true'时,将会使用此数据源
+     * 请根据自身数据按照固定结构生成数据源
+     * 请根据传入的参数source,自定义相应数据源
+     * @return void
+     */
+    protected function datasource($source = false)
+    {
+        // 如果没有指定数据源,则使用示例数据源
+        if ($source == false) {
+            //示例数据源格式
+            $data = [ //第一级
+                [
+                    'id' => '1000001',
+                    'name' => '华北',
+                    'children' => [ //第二级
+                        [
+                            'id' => '16842752',
+                            'name' => '北京',
+                            'children' => [] //第三级
+                        ],
+                        [
+                            'id' => '16908288',
+                            'name' => '天津',
+                            'children' => []
+                        ]
+                    ]
+                ],
+                [
+                    'id' => '1000002',
+                    'name' => '华东',
+                    'children' => [
+                        [
+                            'id' => '17432576',
+                            'name' => '江苏',
+                            'children' => [
+                                [
+                                    'id' => '17432832',
+                                    'name' => '南京'
+                                ],
+                                [
+                                    'id' => '17434112',
+                                    'name' => '盐城'
+                                ]
+                            ]
+                        ]
+                    ]
+                ]
+            ];
+            for ($i = 2; $i < 10; $i++) {
+                $data[$i] = [
+                    'id' => 'id_' . $i,
+                    'name' => 'name_' . $i,
+                    'children' => []
+                ];
+                for ($j = 0; $j < 10; $j++) {
+                    $data[$i]['children'][$j] = [
+                        'id' => 'id_' . $i . '_' . $j,
+                        'name' => 'name_' . $i . '_' . $j,
+                        'children' => []
+                    ];
+                    for ($k = 0; $k < 20; $k++) {
+                        $data[$i]['children'][$j]['children'][$k] = [
+                            'id' => 'id_' . $i . '_' . $j . '_' . $k,
+                            'name' => 'name_' . $i . '_' . $j . '_' . $k,
+                            'children' => []
+                        ];
+                    }
+                }
+            }
+            return $data;
+        } else {
+            // TODO: 请在此处根据source传入参数返回对应数据源
+            if ($source == "mysource") {
+                return [];
+            }
+
+            $list = Area::getAreaList(0);
+
+            return $list;
+        }
+        return [];
+    }
+}

+ 10 - 0
addons/areapicker/info.ini

@@ -0,0 +1,10 @@
+name = areapicker
+title = 区域选择
+intro = 区域选择插件,按华北、华东、华中、华南、西南、西北、东西及其它划分大区
+author = xiaoyu5062
+website = http://www.fastadmin.net
+version = 1.2.0
+state = 1
+url = /addons/areapicker
+license = regular
+licenseto = 50893

+ 2754 - 0
addons/areapicker/view/index/index.html

@@ -0,0 +1,2754 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <link rel="stylesheet" href="__CDN__/assets/css/bootstrap.min.css" />
+    <link rel="stylesheet" href="__CDN__/assets/css/fastadmin.min.css" />
+    <link rel="stylesheet" href="__CDN__/assets/libs/font-awesome/css/font-awesome.min.css" />
+    <style>
+        .region-picker-panel {
+            background: #fff;
+        }
+
+        .region-picker-panel .all-region {
+            background: #f8f8f8;
+            height: 30px;
+        }
+
+        .region-picker-panel ul {
+            list-style-type: none;
+            line-height: 0;
+            margin-right: 50px;
+        }
+
+        .region-picker-panel .area-li {
+            margin-top: 5px;
+        }
+
+        .region-picker-panel .area-li,
+        .region-picker-panel .all-region {
+            padding-left: 50px;
+            position: relative;
+            line-height: 26px;
+        }
+
+        .region-picker-panel .area-li .area-content,
+        .region-picker-panel .all-region .area-content {
+            position: absolute;
+            left: 10px;
+            top: 3px;
+        }
+
+        .region-picker-panel .area-li li,
+        .region-picker-panel .all-region li {
+            display: inline-block;
+            line-height: 26px;
+            position: relative;
+            color: #333;
+            width: 100px;
+            font-size: 12px;
+            box-sizing: border-box;
+        }
+
+        .region-picker-panel .area-content,
+        .region-picker-panel .city-content,
+        .region-picker-panel .pro-content {
+            cursor: pointer;
+            display: block;
+            position: relative;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
+
+        .region-picker-panel .area-content input,
+        .region-picker-panel .city-content input,
+        .region-picker-panel .pro-content input {
+            margin-right: 5px;
+            cursor: pointer;
+            position: relative;
+            top: 1px;
+            width: 14px;
+            height: 14px;
+            vertical-align: baseline;
+        }
+
+        .region-picker-panel .area-content span,
+        .region-picker-panel .city-content span,
+        .region-picker-panel .pro-content span {
+            width: 26px;
+            white-space: pre;
+            font-size: 12px;
+        }
+
+        .region-picker-panel .area-content .num-tip,
+        .region-picker-panel .city-content .num-tip,
+        .region-picker-panel .pro-content .num-tip {
+            position: absolute;
+            /* right: -10px; */
+            min-width: 20px;
+            top: 0px;
+            display: inline-block;
+            line-height: 1.5em;
+            z-index: 1;
+            background: #dedede75;
+            border: 1px solid #cccccc8a;
+            padding: 3px 5px;
+            transform: scale(0.7);
+            color: #999;
+            white-space: unset;
+        }
+
+        .region-picker-panel .li-province {
+            display: inline-block;
+            border: 1px solid #fff;
+        }
+
+        .region-picker-panel .ul-city {
+            position: absolute;
+            background: #fff;
+            width: 310px;
+            left: -1px;
+            top: 100%;
+            z-index: 2;
+            /* line-height: 26px; */
+            border: 1px solid #e4e4e4;
+            padding-top: 6px;
+        }
+
+        .region-picker-panel .city-content {
+            padding: 0 10px;
+        }
+
+        .region-picker-panel .li-province {
+            border: 1px solid #fff;
+        }
+
+        .region-picker-panel .li-province .pro-parent {
+            border: 1px solid #fff;
+            display: block;
+            position: relative;
+            /* padding-left: 10px; */
+        }
+
+        .region-picker-panel .li-province .pro-parent .pro-shadow {
+            width: 100%;
+            height: 9px;
+            background: #fff;
+            top: 90%;
+            position: absolute;
+            z-index: 9;
+            left: 0;
+            display: none;
+        }
+
+        .region-picker-panel .li-province .pro-parent:hover {
+            border: 1px solid #e4e4e4;
+            background: #fff;
+            z-index: 9;
+            box-shadow: 0px 0px 2px #e4e4e4;
+        }
+
+        .region-picker-panel .li-province .pro-parent:hover .ul-city {
+            box-shadow: 0px 0px 2px #e4e4e4;
+        }
+
+        .region-picker-panel input.checked {
+            background-position: 0px 0 !important;
+        }
+
+        .region-picker-panel input.indeter {
+            background-position: -45px 0 !important;
+        }
+
+        .block {
+            font-size: 20px;
+            color: #333;
+            font-weight: bold;
+            border-bottom: 1px solid #2f88ff;
+            padding-bottom: 10px;
+            /* width: 100px; */
+        }
+
+        .footer {
+            padding: 8px 16px;
+            bottom: 0;
+            position: absolute;
+            border-top: 1px solid #e4e4e4;
+            width: 100%;
+            box-sizing: border-box;
+        }
+
+        .footer .default-bottom {
+            text-align: center;
+            min-width: 100px;
+        }
+
+        .footer .button {
+            margin-left: 5px;
+            cursor: pointer;
+            width: 56px;
+            min-width: 20px;
+            line-height: 24px;
+        }
+
+        .footer .cancel-button {
+            padding: 0 5px;
+            border: 1px solid #FFF;
+            min-width: 30px;
+            display: inline-block;
+            background: #fff;
+            outline-color: #fff;
+            background-image: none;
+            margin-left: 2px;
+            margin-right: 0px;
+        }
+    </style>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+    <title>区域选择器</title>
+</head>
+
+<body>
+    <div id="area_dg">
+        <div class="layui-layer-content" id="areacontent">
+            <div class="region-picker-panel">
+                <div class="all-region">
+                    <div class="area-content"><label><input id="d1" type="checkbox" class="checked"
+                                value="100001">所有地域</label>
+                    </div>
+                </div>
+                <ul>
+                    {if empty($data)}
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000001">华北:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1">
+                                            <span>北京</span></label>
+                                        <div class="num-tip">1</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2"
+                                                        class="checked">
+                                                    <span title="北京">北京</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="19">
+                                            <span>天津</span></label>
+                                        <div class="num-tip">1</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="20"
+                                                        class="checked">
+                                                    <span title="天津">天津</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="37">
+                                            <span>河北</span></label>
+                                        <div class="num-tip">11</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="38"
+                                                        class="checked">
+                                                    <span title="石家庄">石家庄</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="61"
+                                                        class="checked">
+                                                    <span title="唐山">唐山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="76"
+                                                        class="checked">
+                                                    <span title="秦皇岛">秦皇岛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="84"
+                                                        class="checked">
+                                                    <span title="邯郸">邯郸</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="104"
+                                                        class="checked">
+                                                    <span title="邢台">邢台</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="124"
+                                                        class="checked">
+                                                    <span title="保定">保定</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="150"
+                                                        class="checked">
+                                                    <span title="张家口">张家口</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="168"
+                                                        class="checked">
+                                                    <span title="承德">承德</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="180"
+                                                        class="checked">
+                                                    <span title="沧州">沧州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="197"
+                                                        class="checked">
+                                                    <span title="廊坊">廊坊</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="208"
+                                                        class="checked">
+                                                    <span title="衡水">衡水</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="220">
+                                            <span>山西</span></label>
+                                        <div class="num-tip">11</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="221"
+                                                        class="checked">
+                                                    <span title="太原">太原</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="232"
+                                                        class="checked">
+                                                    <span title="大同">大同</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="244"
+                                                        class="checked">
+                                                    <span title="阳泉">阳泉</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="250"
+                                                        class="checked">
+                                                    <span title="长治">长治</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="264"
+                                                        class="checked">
+                                                    <span title="晋城">晋城</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="271"
+                                                        class="checked">
+                                                    <span title="朔州">朔州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="278"
+                                                        class="checked">
+                                                    <span title="晋中">晋中</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="290"
+                                                        class="checked">
+                                                    <span title="运城">运城</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="304"
+                                                        class="checked">
+                                                    <span title="忻州">忻州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="319"
+                                                        class="checked">
+                                                    <span title="临汾">临汾</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="337"
+                                                        class="checked">
+                                                    <span title="吕梁">吕梁</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="351">
+                                            <span>内蒙古</span></label>
+                                        <div class="num-tip">12</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="352"
+                                                        class="checked">
+                                                    <span title="呼和浩特">呼和浩特</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="362"
+                                                        class="checked">
+                                                    <span title="包头">包头</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="372"
+                                                        class="checked">
+                                                    <span title="乌海">乌海</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="376"
+                                                        class="checked">
+                                                    <span title="赤峰">赤峰</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="389"
+                                                        class="checked">
+                                                    <span title="通辽">通辽</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="398"
+                                                        class="checked">
+                                                    <span title="鄂尔多斯">鄂尔多斯</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="407"
+                                                        class="checked">
+                                                    <span title="呼伦贝尔">呼伦贝尔</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="422"
+                                                        class="checked">
+                                                    <span title="巴彦淖尔">巴彦淖尔</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="430"
+                                                        class="checked">
+                                                    <span title="乌兰察布">乌兰察布</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="442"
+                                                        class="checked">
+                                                    <span title="兴安盟">兴安盟</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="449"
+                                                        class="checked">
+                                                    <span title="锡林郭勒盟">锡林郭勒盟</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="462"
+                                                        class="checked">
+                                                    <span title="阿拉善盟">阿拉善盟</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000002">华东:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="801">
+                                            <span>上海</span></label>
+                                        <div class="num-tip">1</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="802"
+                                                        class="checked">
+                                                    <span title="上海">上海</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="820">
+                                            <span>江苏</span></label>
+
+                                        <div class="num-tip">13</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="821"
+                                                        class="checked">
+                                                    <span title="南京">南京</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="833"
+                                                        class="checked">
+                                                    <span title="无锡">无锡</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="842"
+                                                        class="checked">
+                                                    <span title="徐州">徐州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="853"
+                                                        class="checked">
+                                                    <span title="常州">常州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="861"
+                                                        class="checked">
+                                                    <span title="苏州">苏州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="871"
+                                                        class="checked">
+                                                    <span title="南通">南通</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="880"
+                                                        class="checked">
+                                                    <span title="连云港">连云港</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="887"
+                                                        class="checked">
+                                                    <span title="淮安">淮安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="896"
+                                                        class="checked">
+                                                    <span title="盐城">盐城</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="906"
+                                                        class="checked">
+                                                    <span title="扬州">扬州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="913"
+                                                        class="checked">
+                                                    <span title="镇江">镇江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="920"
+                                                        class="checked">
+                                                    <span title="泰州">泰州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="927"
+                                                        class="checked">
+                                                    <span title="宿迁">宿迁</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="933">
+                                            <span>浙江</span></label>
+
+                                        <div class="num-tip">12</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="934"
+                                                        class="checked">
+                                                    <span title="杭州">杭州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="948"
+                                                        class="checked">
+                                                    <span title="宁波">宁波</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="960"
+                                                        class="checked">
+                                                    <span title="温州">温州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="972"
+                                                        class="checked">
+                                                    <span title="嘉兴">嘉兴</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="980"
+                                                        class="checked">
+                                                    <span title="湖州">湖州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="986"
+                                                        class="checked">
+                                                    <span title="绍兴">绍兴</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="993"
+                                                        class="checked">
+                                                    <span title="金华">金华</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1003"
+                                                        class="checked">
+                                                    <span title="衢州">衢州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1010"
+                                                        class="checked">
+                                                    <span title="舟山">舟山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1015"
+                                                        class="checked">
+                                                    <span title="台州">台州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1025"
+                                                        class="checked">
+                                                    <span title="丽水">丽水</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1035"
+                                                        class="checked">
+                                                    <span title="舟山新区">舟山新区</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1046">
+                                            <span>安徽</span></label>
+
+                                        <div class="num-tip">16</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1047"
+                                                        class="checked">
+                                                    <span title="合肥">合肥</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1057"
+                                                        class="checked">
+                                                    <span title="芜湖">芜湖</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1066"
+                                                        class="checked">
+                                                    <span title="蚌埠">蚌埠</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1074"
+                                                        class="checked">
+                                                    <span title="淮南">淮南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1081"
+                                                        class="checked">
+                                                    <span title="马鞍山">马鞍山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1088"
+                                                        class="checked">
+                                                    <span title="淮北">淮北</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1093"
+                                                        class="checked">
+                                                    <span title="铜陵">铜陵</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1098"
+                                                        class="checked">
+                                                    <span title="安庆">安庆</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1110"
+                                                        class="checked">
+                                                    <span title="黄山">黄山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1118"
+                                                        class="checked">
+                                                    <span title="滁州">滁州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1127"
+                                                        class="checked">
+                                                    <span title="阜阳">阜阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1136"
+                                                        class="checked">
+                                                    <span title="宿州">宿州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1142"
+                                                        class="checked">
+                                                    <span title="六安">六安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1150"
+                                                        class="checked">
+                                                    <span title="亳州">亳州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1155"
+                                                        class="checked">
+                                                    <span title="池州">池州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1160"
+                                                        class="checked">
+                                                    <span title="宣城">宣城</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1168">
+                                            <span>福建</span></label>
+
+                                        <div class="num-tip">9</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1169"
+                                                        class="checked">
+                                                    <span title="福州">福州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1183"
+                                                        class="checked">
+                                                    <span title="厦门">厦门</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1190"
+                                                        class="checked">
+                                                    <span title="莆田">莆田</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1196"
+                                                        class="checked">
+                                                    <span title="三明">三明</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1209"
+                                                        class="checked">
+                                                    <span title="泉州">泉州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1222"
+                                                        class="checked">
+                                                    <span title="漳州">漳州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1234"
+                                                        class="checked">
+                                                    <span title="南平">南平</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1245"
+                                                        class="checked">
+                                                    <span title="龙岩">龙岩</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1253"
+                                                        class="checked">
+                                                    <span title="宁德">宁德</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1263">
+                                            <span>江西</span></label>
+
+                                        <div class="num-tip">11</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1264"
+                                                        class="checked">
+                                                    <span title="南昌">南昌</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1274"
+                                                        class="checked">
+                                                    <span title="景德镇">景德镇</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1279"
+                                                        class="checked">
+                                                    <span title="萍乡">萍乡</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1285"
+                                                        class="checked">
+                                                    <span title="九江">九江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1299"
+                                                        class="checked">
+                                                    <span title="新余">新余</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1302"
+                                                        class="checked">
+                                                    <span title="鹰潭">鹰潭</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1306"
+                                                        class="checked">
+                                                    <span title="赣州">赣州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1325"
+                                                        class="checked">
+                                                    <span title="吉安">吉安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1339"
+                                                        class="checked">
+                                                    <span title="宜春">宜春</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1350"
+                                                        class="checked">
+                                                    <span title="抚州">抚州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1362"
+                                                        class="checked">
+                                                    <span title="上饶">上饶</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1375">
+                                            <span>山东</span></label>
+
+                                        <div class="num-tip">17</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1376"
+                                                        class="checked">
+                                                    <span title="济南">济南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1387"
+                                                        class="checked">
+                                                    <span title="青岛">青岛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1399"
+                                                        class="checked">
+                                                    <span title="淄博">淄博</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1408"
+                                                        class="checked">
+                                                    <span title="枣庄">枣庄</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1415"
+                                                        class="checked">
+                                                    <span title="东营">东营</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1421"
+                                                        class="checked">
+                                                    <span title="烟台">烟台</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1434"
+                                                        class="checked">
+                                                    <span title="潍坊">潍坊</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1447"
+                                                        class="checked">
+                                                    <span title="济宁">济宁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1459"
+                                                        class="checked">
+                                                    <span title="泰安">泰安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1466"
+                                                        class="checked">
+                                                    <span title="威海">威海</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1471"
+                                                        class="checked">
+                                                    <span title="日照">日照</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1476"
+                                                        class="checked">
+                                                    <span title="莱芜">莱芜</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1479"
+                                                        class="checked">
+                                                    <span title="临沂">临沂</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1492"
+                                                        class="checked">
+                                                    <span title="德州">德州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1504"
+                                                        class="checked">
+                                                    <span title="聊城">聊城</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1513"
+                                                        class="checked">
+                                                    <span title="滨州">滨州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1522"
+                                                        class="checked">
+                                                    <span title="菏泽">菏泽</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000003">华中:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1532">
+                                            <span>河南</span></label>
+                                        <div class="num-tip">18</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1533"
+                                                        class="checked">
+                                                    <span title="郑州">郑州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1546"
+                                                        class="checked">
+                                                    <span title="开封">开封</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1556"
+                                                        class="checked">
+                                                    <span title="洛阳">洛阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1572"
+                                                        class="checked">
+                                                    <span title="平顶山">平顶山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1583"
+                                                        class="checked">
+                                                    <span title="安阳">安阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1593"
+                                                        class="checked">
+                                                    <span title="鹤壁">鹤壁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1599"
+                                                        class="checked">
+                                                    <span title="新乡">新乡</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1612"
+                                                        class="checked">
+                                                    <span title="焦作">焦作</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1623"
+                                                        class="checked">
+                                                    <span title="濮阳">濮阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1630"
+                                                        class="checked">
+                                                    <span title="许昌">许昌</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1637"
+                                                        class="checked">
+                                                    <span title="漯河">漯河</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1643"
+                                                        class="checked">
+                                                    <span title="三门峡">三门峡</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1650"
+                                                        class="checked">
+                                                    <span title="南阳">南阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1664"
+                                                        class="checked">
+                                                    <span title="商丘">商丘</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1674"
+                                                        class="checked">
+                                                    <span title="信阳">信阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1685"
+                                                        class="checked">
+                                                    <span title="周口">周口</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1696"
+                                                        class="checked">
+                                                    <span title="驻马店">驻马店</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1707"
+                                                        class="checked">
+                                                    <span title=" "> </span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1709">
+                                            <span>湖北</span></label>
+
+                                        <div class="num-tip">14</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1710"
+                                                        class="checked">
+                                                    <span title="武汉">武汉</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1724"
+                                                        class="checked">
+                                                    <span title="黄石">黄石</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1731"
+                                                        class="checked">
+                                                    <span title="十堰">十堰</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1740"
+                                                        class="checked">
+                                                    <span title="宜昌">宜昌</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1754"
+                                                        class="checked">
+                                                    <span title="襄阳">襄阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1764"
+                                                        class="checked">
+                                                    <span title="鄂州">鄂州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1768"
+                                                        class="checked">
+                                                    <span title="荆门">荆门</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1774"
+                                                        class="checked">
+                                                    <span title="孝感">孝感</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1782"
+                                                        class="checked">
+                                                    <span title="荆州">荆州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1791"
+                                                        class="checked">
+                                                    <span title="黄冈">黄冈</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1802"
+                                                        class="checked">
+                                                    <span title="咸宁">咸宁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1809"
+                                                        class="checked">
+                                                    <span title="随州">随州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1813"
+                                                        class="checked">
+                                                    <span title="恩施">恩施</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1822"
+                                                        class="checked">
+                                                    <span title=" "> </span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1827">
+                                            <span>湖南</span></label>
+
+                                        <div class="num-tip">14</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1828"
+                                                        class="checked">
+                                                    <span title="长沙">长沙</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1838"
+                                                        class="checked">
+                                                    <span title="株洲">株洲</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1848"
+                                                        class="checked">
+                                                    <span title="湘潭">湘潭</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1854"
+                                                        class="checked">
+                                                    <span title="衡阳">衡阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1867"
+                                                        class="checked">
+                                                    <span title="邵阳">邵阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1880"
+                                                        class="checked">
+                                                    <span title="岳阳">岳阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1890"
+                                                        class="checked">
+                                                    <span title="常德">常德</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1900"
+                                                        class="checked">
+                                                    <span title="张家界">张家界</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1905"
+                                                        class="checked">
+                                                    <span title="益阳">益阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1912"
+                                                        class="checked">
+                                                    <span title="郴州">郴州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1924"
+                                                        class="checked">
+                                                    <span title="永州">永州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1936"
+                                                        class="checked">
+                                                    <span title="怀化">怀化</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1949"
+                                                        class="checked">
+                                                    <span title="娄底">娄底</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1955"
+                                                        class="checked">
+                                                    <span title="湘西">湘西</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000004">华南:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="1964">
+                                            <span>广东</span></label>
+                                        <div class="num-tip">21</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1965"
+                                                        class="checked">
+                                                    <span title="广州">广州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1977"
+                                                        class="checked">
+                                                    <span title="韶关">韶关</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1988"
+                                                        class="checked">
+                                                    <span title="深圳">深圳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="1999"
+                                                        class="checked">
+                                                    <span title="珠海">珠海</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2003"
+                                                        class="checked">
+                                                    <span title="汕头">汕头</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2011"
+                                                        class="checked">
+                                                    <span title="佛山">佛山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2017"
+                                                        class="checked">
+                                                    <span title="江门">江门</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2025"
+                                                        class="checked">
+                                                    <span title="湛江">湛江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2035"
+                                                        class="checked">
+                                                    <span title="茂名">茂名</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2041"
+                                                        class="checked">
+                                                    <span title="肇庆">肇庆</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2050"
+                                                        class="checked">
+                                                    <span title="惠州">惠州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2056"
+                                                        class="checked">
+                                                    <span title="梅州">梅州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2065"
+                                                        class="checked">
+                                                    <span title="汕尾">汕尾</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2070"
+                                                        class="checked">
+                                                    <span title="河源">河源</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2077"
+                                                        class="checked">
+                                                    <span title="阳江">阳江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2082"
+                                                        class="checked">
+                                                    <span title="清远">清远</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2091"
+                                                        class="checked">
+                                                    <span title="东莞">东莞</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2123"
+                                                        class="checked">
+                                                    <span title="中山">中山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2146"
+                                                        class="checked">
+                                                    <span title="潮州">潮州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2150"
+                                                        class="checked">
+                                                    <span title="揭阳">揭阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2156"
+                                                        class="checked">
+                                                    <span title="云浮">云浮</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2162">
+                                            <span>广西</span></label>
+                                        <div class="num-tip">14</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2163"
+                                                        class="checked">
+                                                    <span title="南宁">南宁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2177"
+                                                        class="checked">
+                                                    <span title="柳州">柳州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2189"
+                                                        class="checked">
+                                                    <span title="桂林">桂林</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2207"
+                                                        class="checked">
+                                                    <span title="梧州">梧州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2215"
+                                                        class="checked">
+                                                    <span title="北海">北海</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2220"
+                                                        class="checked">
+                                                    <span title="防城港">防城港</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2225"
+                                                        class="checked">
+                                                    <span title="钦州">钦州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2230"
+                                                        class="checked">
+                                                    <span title="贵港">贵港</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2236"
+                                                        class="checked">
+                                                    <span title="玉林">玉林</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2245"
+                                                        class="checked">
+                                                    <span title="百色">百色</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2258"
+                                                        class="checked">
+                                                    <span title="贺州">贺州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2264"
+                                                        class="checked">
+                                                    <span title="河池">河池</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2276"
+                                                        class="checked">
+                                                    <span title="来宾">来宾</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2283"
+                                                        class="checked">
+                                                    <span title="崇左">崇左</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2291">
+                                            <span>海南</span></label>
+                                        <div class="num-tip">4</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2292"
+                                                        class="checked">
+                                                    <span title="海口">海口</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2297"
+                                                        class="checked">
+                                                    <span title="三亚">三亚</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2302"
+                                                        class="checked">
+                                                    <span title="三沙">三沙</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2306"
+                                                        class="checked">
+                                                    <span title=" "> </span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000005">西南:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2323">
+                                            <span>重庆</span></label>
+                                        <div class="num-tip">2</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2324"
+                                                        class="checked">
+                                                    <span title="重庆">重庆</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2363"
+                                                        class="checked">
+                                                    <span title="两江新区">两江新区</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2367">
+                                            <span>四川</span></label>
+                                        <div class="num-tip">21</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2368"
+                                                        class="checked">
+                                                    <span title="成都">成都</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2388"
+                                                        class="checked">
+                                                    <span title="自贡">自贡</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2395"
+                                                        class="checked">
+                                                    <span title="攀枝花">攀枝花</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2401"
+                                                        class="checked">
+                                                    <span title="泸州">泸州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2409"
+                                                        class="checked">
+                                                    <span title="德阳">德阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2416"
+                                                        class="checked">
+                                                    <span title="绵阳">绵阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2426"
+                                                        class="checked">
+                                                    <span title="广元">广元</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2434"
+                                                        class="checked">
+                                                    <span title="遂宁">遂宁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2440"
+                                                        class="checked">
+                                                    <span title="内江">内江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2446"
+                                                        class="checked">
+                                                    <span title="乐山">乐山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2458"
+                                                        class="checked">
+                                                    <span title="南充">南充</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2468"
+                                                        class="checked">
+                                                    <span title="眉山">眉山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2475"
+                                                        class="checked">
+                                                    <span title="宜宾">宜宾</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2486"
+                                                        class="checked">
+                                                    <span title="广安">广安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2493"
+                                                        class="checked">
+                                                    <span title="达州">达州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2501"
+                                                        class="checked">
+                                                    <span title="雅安">雅安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2510"
+                                                        class="checked">
+                                                    <span title="巴中">巴中</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2516"
+                                                        class="checked">
+                                                    <span title="资阳">资阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2521"
+                                                        class="checked">
+                                                    <span title="阿坝">阿坝</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2535"
+                                                        class="checked">
+                                                    <span title="甘孜">甘孜</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2554"
+                                                        class="checked">
+                                                    <span title="凉山">凉山</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2572">
+                                            <span>贵州</span></label>
+                                        <div class="num-tip">9</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2573"
+                                                        class="checked">
+                                                    <span title="贵阳">贵阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2584"
+                                                        class="checked">
+                                                    <span title="六盘水">六盘水</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2589"
+                                                        class="checked">
+                                                    <span title="遵义">遵义</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2604"
+                                                        class="checked">
+                                                    <span title="安顺">安顺</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2611"
+                                                        class="checked">
+                                                    <span title="毕节">毕节</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2620"
+                                                        class="checked">
+                                                    <span title="铜仁">铜仁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2631"
+                                                        class="checked">
+                                                    <span title="黔西南">黔西南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2640"
+                                                        class="checked">
+                                                    <span title="黔东南">黔东南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2657"
+                                                        class="checked">
+                                                    <span title="黔南">黔南</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2670">
+                                            <span>云南</span></label>
+                                        <div class="num-tip">16</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2671"
+                                                        class="checked">
+                                                    <span title="昆明">昆明</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2686"
+                                                        class="checked">
+                                                    <span title="曲靖">曲靖</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2696"
+                                                        class="checked">
+                                                    <span title="玉溪">玉溪</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2706"
+                                                        class="checked">
+                                                    <span title="保山">保山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2712"
+                                                        class="checked">
+                                                    <span title="昭通">昭通</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2724"
+                                                        class="checked">
+                                                    <span title="丽江">丽江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2730"
+                                                        class="checked">
+                                                    <span title="普洱">普洱</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2741"
+                                                        class="checked">
+                                                    <span title="临沧">临沧</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2750"
+                                                        class="checked">
+                                                    <span title="楚雄">楚雄</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2761"
+                                                        class="checked">
+                                                    <span title="红河">红河</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2775"
+                                                        class="checked">
+                                                    <span title="文山">文山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2784"
+                                                        class="checked">
+                                                    <span title="西双版纳">西双版纳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2788"
+                                                        class="checked">
+                                                    <span title="大理">大理</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2801"
+                                                        class="checked">
+                                                    <span title="德宏">德宏</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2807"
+                                                        class="checked">
+                                                    <span title="怒江">怒江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2812"
+                                                        class="checked">
+                                                    <span title="迪庆">迪庆</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2816">
+                                            <span>西藏</span></label>
+                                        <div class="num-tip">7</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2817"
+                                                        class="checked">
+                                                    <span title="拉萨">拉萨</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2826"
+                                                        class="checked">
+                                                    <span title="日喀则">日喀则</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2845"
+                                                        class="checked">
+                                                    <span title="昌都">昌都</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2857"
+                                                        class="checked">
+                                                    <span title="山南">山南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2870"
+                                                        class="checked">
+                                                    <span title="那曲">那曲</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2882"
+                                                        class="checked">
+                                                    <span title="阿里">阿里</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2890"
+                                                        class="checked">
+                                                    <span title="林芝">林芝</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000006">西北:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="2898">
+                                            <span>陕西</span></label>
+                                        <div class="num-tip">11</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2899"
+                                                        class="checked">
+                                                    <span title="西安">西安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2913"
+                                                        class="checked">
+                                                    <span title="铜川">铜川</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2918"
+                                                        class="checked">
+                                                    <span title="宝鸡">宝鸡</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2931"
+                                                        class="checked">
+                                                    <span title="咸阳">咸阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2946"
+                                                        class="checked">
+                                                    <span title="渭南">渭南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2958"
+                                                        class="checked">
+                                                    <span title="延安">延安</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2972"
+                                                        class="checked">
+                                                    <span title="汉中">汉中</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2984"
+                                                        class="checked">
+                                                    <span title="榆林">榆林</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="2997"
+                                                        class="checked">
+                                                    <span title="安康">安康</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3008"
+                                                        class="checked">
+                                                    <span title="商洛">商洛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3016"
+                                                        class="checked">
+                                                    <span title="西咸">西咸</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3022">
+                                            <span>甘肃</span></label>
+
+                                        <div class="num-tip">14</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3023"
+                                                        class="checked">
+                                                    <span title="兰州">兰州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3032"
+                                                        class="checked">
+                                                    <span title="嘉峪关">嘉峪关</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3036"
+                                                        class="checked">
+                                                    <span title="金昌">金昌</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3039"
+                                                        class="checked">
+                                                    <span title="白银">白银</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3045"
+                                                        class="checked">
+                                                    <span title="天水">天水</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3053"
+                                                        class="checked">
+                                                    <span title="武威">武威</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3058"
+                                                        class="checked">
+                                                    <span title="张掖">张掖</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3065"
+                                                        class="checked">
+                                                    <span title="平凉">平凉</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3073"
+                                                        class="checked">
+                                                    <span title="酒泉">酒泉</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3081"
+                                                        class="checked">
+                                                    <span title="庆阳">庆阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3090"
+                                                        class="checked">
+                                                    <span title="定西">定西</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3098"
+                                                        class="checked">
+                                                    <span title="陇南">陇南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3108"
+                                                        class="checked">
+                                                    <span title="临夏">临夏</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3117"
+                                                        class="checked">
+                                                    <span title="甘南">甘南</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3126">
+                                            <span>青海</span></label>
+
+                                        <div class="num-tip">8</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3127"
+                                                        class="checked">
+                                                    <span title="西宁">西宁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3135"
+                                                        class="checked">
+                                                    <span title="海东">海东</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3142"
+                                                        class="checked">
+                                                    <span title="海北">海北</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3147"
+                                                        class="checked">
+                                                    <span title="黄南">黄南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3152"
+                                                        class="checked">
+                                                    <span title="海南">海南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3158"
+                                                        class="checked">
+                                                    <span title="果洛">果洛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3165"
+                                                        class="checked">
+                                                    <span title="玉树">玉树</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3172"
+                                                        class="checked">
+                                                    <span title="海西">海西</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3178">
+                                            <span>宁夏</span></label>
+
+                                        <div class="num-tip">5</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3179"
+                                                        class="checked">
+                                                    <span title="银川">银川</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3186"
+                                                        class="checked">
+                                                    <span title="石嘴山">石嘴山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3190"
+                                                        class="checked">
+                                                    <span title="吴忠">吴忠</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3196"
+                                                        class="checked">
+                                                    <span title="固原">固原</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3202"
+                                                        class="checked">
+                                                    <span title="中卫">中卫</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3206">
+                                            <span>新疆</span></label>
+
+                                        <div class="num-tip">15</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3207"
+                                                        class="checked">
+                                                    <span title="乌鲁木齐">乌鲁木齐</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3216"
+                                                        class="checked">
+                                                    <span title="克拉玛依">克拉玛依</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3221"
+                                                        class="checked">
+                                                    <span title="吐鲁番">吐鲁番</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3225"
+                                                        class="checked">
+                                                    <span title="哈密">哈密</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3229"
+                                                        class="checked">
+                                                    <span title="昌吉">昌吉</span></label></div>
+                                        </li>
+
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3237"
+                                                        class="checked">
+                                                    <span title="博尔塔拉">博尔塔拉</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3242"
+                                                        class="checked">
+                                                    <span title="巴音郭楞">巴音郭楞</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3252"
+                                                        class="checked">
+                                                    <span title="阿克苏">阿克苏</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3262"
+                                                        class="checked">
+                                                    <span title="克孜勒苏">克孜勒苏</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3267"
+                                                        class="checked">
+                                                    <span title="喀什">喀什</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3280"
+                                                        class="checked">
+                                                    <span title="和田">和田</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3289"
+                                                        class="checked">
+                                                    <span title="伊犁">伊犁</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3301"
+                                                        class="checked">
+                                                    <span title="塔城">塔城</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3309"
+                                                        class="checked">
+                                                    <span title="阿勒泰">阿勒泰</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3317"
+                                                        class="checked">
+                                                    <span title="直辖县级">直辖县级</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000007">东北:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="466">
+                                            <span>辽宁</span></label>
+                                        <div class="num-tip">15</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="467"
+                                                        class="checked">
+                                                    <span title="沈阳">沈阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="481"
+                                                        class="checked">
+                                                    <span title="大连">大连</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="492"
+                                                        class="checked">
+                                                    <span title="鞍山">鞍山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="500"
+                                                        class="checked">
+                                                    <span title="抚顺">抚顺</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="508"
+                                                        class="checked">
+                                                    <span title="本溪">本溪</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="515"
+                                                        class="checked">
+                                                    <span title="丹东">丹东</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="522"
+                                                        class="checked">
+                                                    <span title="锦州">锦州</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="530"
+                                                        class="checked">
+                                                    <span title="营口">营口</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="537"
+                                                        class="checked">
+                                                    <span title="阜新">阜新</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="545"
+                                                        class="checked">
+                                                    <span title="辽阳">辽阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="553"
+                                                        class="checked">
+                                                    <span title="盘锦">盘锦</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="558"
+                                                        class="checked">
+                                                    <span title="铁岭">铁岭</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="566"
+                                                        class="checked">
+                                                    <span title="朝阳">朝阳</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="574"
+                                                        class="checked">
+                                                    <span title="葫芦岛">葫芦岛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="581"
+                                                        class="checked">
+                                                    <span title="金普新区">金普新区</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="585">
+                                            <span>吉林</span></label>
+                                        <div class="num-tip">9</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="586"
+                                                        class="checked">
+                                                    <span title="长春">长春</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="597"
+                                                        class="checked">
+                                                    <span title="吉林">吉林</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="607"
+                                                        class="checked">
+                                                    <span title="四平">四平</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="614"
+                                                        class="checked">
+                                                    <span title="辽源">辽源</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="619"
+                                                        class="checked">
+                                                    <span title="通化">通化</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="627"
+                                                        class="checked">
+                                                    <span title="白山">白山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="634"
+                                                        class="checked">
+                                                    <span title="松原">松原</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="640"
+                                                        class="checked">
+                                                    <span title="白城">白城</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="646"
+                                                        class="checked">
+                                                    <span title="延边">延边</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="655">
+                                            <span>黑龙江</span></label>
+                                        <div class="num-tip">13</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="656"
+                                                        class="checked">
+                                                    <span title="哈尔滨">哈尔滨</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="675"
+                                                        class="checked">
+                                                    <span title="齐齐哈尔">齐齐哈尔</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="692"
+                                                        class="checked">
+                                                    <span title="鸡西">鸡西</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="702"
+                                                        class="checked">
+                                                    <span title="鹤岗">鹤岗</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="711"
+                                                        class="checked">
+                                                    <span title="双鸭山">双鸭山</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="720"
+                                                        class="checked">
+                                                    <span title="大庆">大庆</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="730"
+                                                        class="checked">
+                                                    <span title="伊春">伊春</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="748"
+                                                        class="checked">
+                                                    <span title="佳木斯">佳木斯</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="759"
+                                                        class="checked">
+                                                    <span title="七台河">七台河</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="764"
+                                                        class="checked">
+                                                    <span title="牡丹江">牡丹江</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="775"
+                                                        class="checked">
+                                                    <span title="黑河">黑河</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="782"
+                                                        class="checked">
+                                                    <span title="绥化">绥化</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="793"
+                                                        class="checked">
+                                                    <span title="大兴安岭">大兴安岭</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="1000008">其它:</label>
+                        </div>
+                        <ul>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3716">
+                                            <span>香港</span></label>
+                                        <div class="num-tip">3</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3717"
+                                                        class="checked">
+                                                    <span title="香港岛">香港岛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3722"
+                                                        class="checked">
+                                                    <span title="九龙">九龙</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3728"
+                                                        class="checked">
+                                                    <span title="新界">新界</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3738">
+                                            <span>澳门</span></label>
+                                        <div class="num-tip">3</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3739"
+                                                        class="checked">
+                                                    <span title="澳门半岛">澳门半岛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3745"
+                                                        class="checked">
+                                                    <span title="氹仔岛">氹仔岛</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3747"
+                                                        class="checked">
+                                                    <span title="路环岛">路环岛</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked" value="3325">
+                                            <span>台湾</span></label>
+                                        <div class="num-tip">22</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3326"
+                                                        class="checked">
+                                                    <span title="台北">台北</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3339"
+                                                        class="checked">
+                                                    <span title="高雄">高雄</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3378"
+                                                        class="checked">
+                                                    <span title="基隆">基隆</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3386"
+                                                        class="checked">
+                                                    <span title="台中">台中</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3416"
+                                                        class="checked">
+                                                    <span title="台南">台南</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3454"
+                                                        class="checked">
+                                                    <span title="新竹">新竹</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3458"
+                                                        class="checked">
+                                                    <span title="嘉义">嘉义</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3461"
+                                                        class="checked">
+                                                    <span title="新北">新北</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3491"
+                                                        class="checked">
+                                                    <span title="宜兰">宜兰</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3504"
+                                                        class="checked">
+                                                    <span title="桃园">桃园</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3518"
+                                                        class="checked">
+                                                    <span title="新竹">新竹</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3532"
+                                                        class="checked">
+                                                    <span title="苗栗">苗栗</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3551"
+                                                        class="checked">
+                                                    <span title="彰化">彰化</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3578"
+                                                        class="checked">
+                                                    <span title="南投">南投</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3592"
+                                                        class="checked">
+                                                    <span title="云林">云林</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3613"
+                                                        class="checked">
+                                                    <span title="嘉义">嘉义</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3632"
+                                                        class="checked">
+                                                    <span title="屏东">屏东</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3666"
+                                                        class="checked">
+                                                    <span title="台东">台东</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3683"
+                                                        class="checked">
+                                                    <span title="花莲">花莲</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3697"
+                                                        class="checked">
+                                                    <span title="澎湖">澎湖</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3704"
+                                                        class="checked">
+                                                    <span title="金门">金门</span></label></div>
+                                        </li>
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="3711"
+                                                        class="checked">
+                                                    <span title="连江">连江</span></label></div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </li>
+                    {else /}
+                    {volist name="data" id="vo"}
+                    <li class="area-li">
+                        <div class="area-content">
+                            <label><input type="checkbox" class="checked" value="{$vo.id}">{$vo.name}:</label>
+                        </div>
+                        <ul>
+                            {volist name="vo.children" id="p"}
+                            <li class="li-province">
+                                <div class="pro-parent">
+                                    <div class="pro-shadow"></div>
+                                    <div class="pro-content"><label><input type="checkbox" class="checked"
+                                                value="{$p.id}">
+                                            <span>{$p.name}</span></label>
+                                        <div class="num-tip">{size($p.children)}</div>
+                                    </div>
+                                    <ul class="ul-city" style="display: none;">
+                                        {volist name="p.children" id="c"}
+                                        <li class="li-city">
+                                            <div class="city-content"><label><input type="checkbox" value="{$c.id}"
+                                                        class="checked">
+                                                    <span title="{$c.name}">{$c.name}</span></label></div>
+                                        </li>
+                                        {/volist}
+                                    </ul>
+                                </div>
+                            </li>
+                            {/volist}
+                        </ul>
+                    </li>
+                    {/volist}
+                    {/if}
+
+                </ul>
+            </div>
+        </div>
+        <div class="footer layer-footer hidden">
+            <div class="default-bottom text-center">
+                <button class="btn btn-success primary ensure-button">确定</button>
+                <button class="btn btn-default plain cancel-button">取消</button>
+            </div>
+        </div>
+    </div>
+</body>
+<script src="__CDN__/assets/libs/jquery/dist/jquery.min.js"></script>
+<script>
+    var close = function (data) {
+        var index = parent.Layer.getFrameIndex(window.name);
+        var callback = parent.$("#layui-layer" + index).data("callback");
+        parent.Layer.close(index);
+        if (typeof callback === 'function') {
+            callback.call(undefined, data);
+        }
+    };
+
+    function getareavalue() {
+        var txt = "";
+        var val = "";
+        $("#areacontent .all-region").each(function () {
+            var cka = $(this).find(":checkbox");
+            var labela = cka.parent().text().trim();
+            if (cka.is(":checked")) {
+                txt += labela + ";";
+                val += cka.val() + ";";
+            } else {
+                $("#areacontent .pro-content").each(function () {
+                    var ck = $(this).find(":checkbox");
+                    var label = ck.parent().text().trim();
+                    if (ck.is(":checked")) {
+                        txt += label + ";";
+                        val += ck.val() + ";";
+                    } else {
+                        $(this).next("ul").find(":checked").each(function () {
+                            txt += label + "-" + $(this).parent().text().trim() + ";";
+                            val += $(this).val() + ";";
+                        });
+                    }
+                });
+            }
+        });
+        if (txt && val) {
+            txt = txt.substring(0, txt.length - 1);
+            val = val.substring(0, val.length - 1);
+        }
+        // $("#areaname").html(txt);
+        $("#areaname").val(txt);
+        $("#areaval").val(val);
+        // $('#area_div').hide();
+        var data = {
+            name: txt,
+            val: val
+        };
+        close(data);
+    }
+
+
+    function showarea() {
+        $("#areacontent :checkbox").each(function () {
+            this.checked = false;
+            $(this).attr("class", "");
+        });
+        var areaval = '{$val}'; // $("#areaval").val();
+        if (areaval) {
+            var vals = areaval.split(";");
+            for (var i = 0; i < vals.length; i++) {
+                $("#areacontent :checkbox[value='" + vals[i] + "']").trigger("click");
+            }
+        }
+        var tp = ($(window).height() - $("#area_dg").height()) / 2;
+        if (tp < 0) {
+            tp = 0;
+        }
+        $("#area_dg").css("top", tp);
+    }
+
+    $(".li-province").hover(function () {
+        var $this = $(this);
+        var ul = $this.find("ul");
+        var li = ul.find("li");
+        if (li && li.length) {
+            ul.show();
+        }
+    }, function () {
+        var $this = $(this);
+        var ul = $this.find("ul");
+        ul.hide();
+    });
+
+    $(function () {
+        $(document).on("click", ".ensure-button", function () {
+            getareavalue();
+        });
+        $(document).on("click", ".cancel-button", function () {
+            var index = parent.Layer.getFrameIndex(window.name);
+            var callback = parent.$("#layui-layer" + index).data("callback");
+            parent.Layer.close(index);
+        });
+        $("#areacontent").find(":checkbox").on("click", function () {
+            var $this = $(this);
+            var ckd = $this.is(":checked");
+            $this.attr("class", ckd ? "checked" : "");
+            var $he = $this.parent().parent().parent();
+            var lv = $he.attr("class");
+            var $ul;
+            if (lv == "all-region") {
+                $ul = $he.next().eq(0);
+            } else if (lv == "area-li") {
+                $ul = $he.find("ul").eq(0);
+            } else if (lv == "pro-parent") {
+                $ul = $he.find("ul").eq(0);
+            } else if (lv == "li-city") {}
+            if ($ul) {
+                $ul.find(":checkbox").each(function () {
+                    this.checked = ckd;
+                    $(this).attr("class", ckd ? "checked" : "");
+                });
+            }
+            $("#areacontent .pro-content").each(function () {
+                var l = $(this).next("ul").find(".li-city").length;
+                var cl = $(this).next("ul").find(":checked").length;
+                $(this).find(".num-tip").remove();
+                var ck = $(this).find(":checkbox");
+                if (cl > 0) {
+                    $(this).append($("<div class='num-tip'>" + cl + "</div>"));
+                    if (l == cl) {
+                        ck[0].checked = true;
+                        ck.attr("class", "checked");
+                    } else {
+                        ck[0].checked = false;
+                        ck.attr("class", "indeter");
+                    }
+                } else {
+                    if (l > 0) {
+                        ck[0].checked = false;
+                        ck.attr("class", "");
+                    } else {
+
+                    }
+                }
+            });
+            $("#areacontent .area-li .area-content").each(function () {
+                var l = $(this).next("ul").find(".li-province").length;
+                var cl = $(this).next("ul").find(".pro-content").find(":checked").length;
+                var ck = $(this).find(":checkbox");
+                if (cl > 0) {
+                    if (l == cl) {
+                        ck[0].checked = true;
+                        ck.attr("class", "checked");
+                    } else {
+                        ck[0].checked = false;
+                        ck.attr("class", "indeter");
+                    }
+                } else {
+                    if (l > 0) {
+                        ck[0].checked = false;
+                        ck.attr("class", "");
+                    }
+                }
+            });
+            $("#areacontent .all-region").each(function () {
+                var l = $(this).next("ul").find(".area-li").length;
+                var cl = $(this).next("ul").find(".area-content").find(":checked").length;
+                var ck = $(this).find(":checkbox");
+                if (cl > 0) {
+                    if (l == cl) {
+                        ck[0].checked = true;
+                        ck.attr("class", "checked");
+                    } else {
+                        ck[0].checked = false;
+                        ck.attr("class", "indeter");
+                    }
+                } else {
+                    if (l > 0) {
+                        ck[0].checked = false;
+                        ck.attr("class", "");
+                    }
+                }
+            });
+
+        });
+        showarea();
+    });
+</script>
+
+</html>

+ 1 - 0
addons/command/.addonrc

@@ -0,0 +1 @@
+{"license":"regular","licenseto":"39487","licensekey":"YNMEsQF8LKjIvDAt sEV\/cCS3B6Y8jnDDnvhNww==","menus":["command","command\/index","command\/add","command\/detail","command\/execute","command\/del","command\/multi"],"files":["application\\admin\\controller\\Command.php","application\\admin\\lang\\zh-cn\\command.php","application\\admin\\model\\Command.php","application\\admin\\validate\\Command.php","application\\admin\\view\\command\\add.html","application\\admin\\view\\command\\detail.html","application\\admin\\view\\command\\index.html","public\\assets\\js\\backend\\command.js"]}

+ 69 - 0
addons/command/Command.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace addons\command;
+
+use app\common\library\Menu;
+use think\Addons;
+
+/**
+ * 在线命令插件
+ */
+class Command extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+        $menu = [
+            [
+                'name'    => 'command',
+                'title'   => '在线命令管理',
+                'icon'    => 'fa fa-terminal',
+                'sublist' => [
+                    ['name' => 'command/index', 'title' => '查看'],
+                    ['name' => 'command/add', 'title' => '添加'],
+                    ['name' => 'command/detail', 'title' => '详情'],
+                    ['name' => 'command/execute', 'title' => '运行'],
+                    ['name' => 'command/del', 'title' => '删除'],
+                    ['name' => 'command/multi', 'title' => '批量更新'],
+                ]
+            ]
+        ];
+        Menu::create($menu);
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+        Menu::delete('command');
+        return true;
+    }
+
+    /**
+     * 插件启用方法
+     * @return bool
+     */
+    public function enable()
+    {
+        Menu::enable('command');
+        return true;
+    }
+
+    /**
+     * 插件禁用方法
+     * @return bool
+     */
+    public function disable()
+    {
+        Menu::disable('command');
+        return true;
+    }
+
+}

+ 4 - 0
addons/command/config.php

@@ -0,0 +1,4 @@
+<?php
+
+return [
+];

+ 15 - 0
addons/command/controller/Index.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace addons\command\controller;
+
+use think\addons\Controller;
+
+class Index extends Controller
+{
+
+    public function index()
+    {
+        $this->error("当前插件暂无前台页面");
+    }
+
+}

+ 10 - 0
addons/command/info.ini

@@ -0,0 +1,10 @@
+name = command
+title = 在线命令
+intro = 可在线执行FastAdmin的命令行相关命令
+author = Karson
+website = https://www.fastadmin.net
+version = 1.0.6
+state = 1
+url = /addons/command
+license = regular
+licenseto = 39487

+ 12 - 0
addons/command/install.sql

@@ -0,0 +1,12 @@
+CREATE TABLE IF NOT EXISTS `__PREFIX__command`  (
+  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `type` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '类型',
+  `params` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '参数',
+  `command` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '命令',
+  `content` text COMMENT '返回结果',
+  `executetime` int(10) UNSIGNED DEFAULT NULL COMMENT '执行时间',
+  `createtime` int(10) UNSIGNED DEFAULT NULL COMMENT '创建时间',
+  `updatetime` int(10) UNSIGNED DEFAULT NULL COMMENT '更新时间',
+  `status` enum('successed','failured') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'failured' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线命令表';

+ 28 - 0
addons/command/library/Output.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace addons\command\library;
+
+/**
+ * Class Output
+ */
+class Output extends \think\console\Output
+{
+
+    protected $message = [];
+
+    public function __construct($driver = 'console')
+    {
+        parent::__construct($driver);
+    }
+
+    protected function block($style, $message)
+    {
+        $this->message[] = $message;
+    }
+
+    public function getMessage()
+    {
+        return $this->message;
+    }
+
+}

+ 1 - 0
addons/epay/.addonrc

@@ -0,0 +1 @@
+{"files":["application\\admin\\controller\\Epay.php","public\\assets\\addons\\epay\\css\\common.css","public\\assets\\addons\\epay\\css\\epay.css","public\\assets\\addons\\epay\\images\\alipay.png","public\\assets\\addons\\epay\\images\\expired.png","public\\assets\\addons\\epay\\images\\logo-alipay.png","public\\assets\\addons\\epay\\images\\logo-wechat.png","public\\assets\\addons\\epay\\images\\paid.png","public\\assets\\addons\\epay\\images\\scan.png","public\\assets\\addons\\epay\\images\\screenshot-alipay.png","public\\assets\\addons\\epay\\images\\screenshot-wechat.png","public\\assets\\addons\\epay\\images\\wechat.png","public\\assets\\addons\\epay\\js\\common.js","public\\assets\\addons\\epay\\less\\common.less","public\\assets\\addons\\epay\\less\\epay.less"],"license":"regular","licenseto":"39487","licensekey":"yTFP1xvADpzRu7r6 iEt0ic5YODzSq2Qa57DnCw==","domains":[],"licensecodes":[],"validations":[]}

+ 69 - 0
addons/epay/Epay.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace addons\epay;
+
+use think\Addons;
+use think\Config;
+use think\Loader;
+
+/**
+ * 微信支付宝整合插件
+ */
+class Epay extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+
+        return true;
+    }
+
+    /**
+     * 插件启用方法
+     * @return bool
+     */
+    public function enable()
+    {
+
+        return true;
+    }
+
+    /**
+     * 插件禁用方法
+     * @return bool
+     */
+    public function disable()
+    {
+
+        return true;
+    }
+
+    /**
+     * 添加命名空间
+     */
+    public function appInit()
+    {
+        //添加命名空间
+        if (!class_exists('\Yansongda\Pay\Pay')) {
+            Loader::addNamespace('Yansongda\Pay', ADDON_PATH . 'epay' . DS . 'library' . DS . 'Yansongda' . DS . 'Pay' . DS);
+        }
+        if (!class_exists('\Yansongda\Supports\Logger')) {
+            Loader::addNamespace('Yansongda\Supports', ADDON_PATH . 'epay' . DS . 'library' . DS . 'Yansongda' . DS . 'Supports' . DS);
+        }
+    }
+
+}

+ 43 - 0
addons/epay/certs/alipayCertPublicKey.crt

@@ -0,0 +1,43 @@
+-----BEGIN CERTIFICATE-----
+MIIDsjCCApqgAwIBAgIQICISB9eg0+a73GWggHv7NzANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
+BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
+YXNzIDIgUjEwHhcNMjIxMjA3MTMzMDA2WhcNMjcxMjA2MTMzMDA2WjCBkjELMAkGA1UEBhMCQ04x
+LTArBgNVBAoMJOmprOmejeWxseW4guavheWKm+WVhui0uOaciemZkOWFrOWPuDEPMA0GA1UECwwG
+QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
+uC0yMDg4MzQxMzkxNzM4NjA1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl4ngaPi4
+xQouYpdIs92/MsmUShlXAkXZeDO3YhGFJq/hYHWPOsxyhQFp/O5yHXQrNx5z9hW+JHuOg7R5m5ml
+cWR+6UZzzFryqM/w2se+ZHWn6viXthG+GV/fw19vKBhNP4FCYXDetb1w8v731klgTqKZPBe4b/xp
+Il9Seih75QA8eEh4hFPS9iAy9mltjs7h6yuSLB6Ps+exdumzFZEz7Dyz5Nkr7sPBJpZhR+bOOJFR
+E1SC3FYgA/N45lXc63X0SDhpgcgPLSCozhb3M6NWJ+0qtP4XAOOJkY6485RD++/yrx5tPIsrBIh2
+4WOjgD/T1+SKZidx1q1vx7VgblReEwIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
+AQELBQADggEBAJIR37IfLECjtmSweuhq4QvSSLcS22aGrxSwZ4AUB6Ytc/J4mv9T96FSPbD/rP59
+h+9Bxisbb4ttKFfJbI4T7rEAD8Fvx1raJ1kpANG1PczwACck+mF9zD6mFgzyV9DrFz2L+MX5S6P/
+lAISjY4FsUwkD+3SaE6vYM2EjV2IWa1eCPlS2BJgx3qebNG/WBHvlId+GDrRjUCuxqU+/lq0mqrn
+nbynayo1FVbkXVhalBhbPFOhKlR/aUtWeZljgIT30lzK2lEN0/hwknu/0Kluj4Q9Iihxh8eaWdgh
+ZXbX6f+3P/O851FNvWpJ+WneN56Zxpr2xeZavJjLbfXCDfylfvk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU
+BgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEw
+LwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMy
+MjE0MzQxNVoXDTM3MTEyNjE0MzQxNVowgYIxCzAJBgNVBAYTAkNOMRYwFAYDVQQKDA1BbnQgRmlu
+YW5jaWFsMSAwHgYDVQQLDBdDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTE5MDcGA1UEAwwwQW50IEZp
+bmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDbGFzcyAyIFIxMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAsLMfYaoRoPRbmDcAfXPCmKf43pWRN5yTXa/KJWO0l+mrgQvs89bA
+NEvbDUxlkGwycwtwi5DgBuBgVhLliXu+R9CYgr2dXs8D8Hx/gsggDcyGPLmVrDOnL+dyeauheARZ
+fA3du60fwEwwbGcVIpIxPa/4n3IS/ElxQa6DNgqxh8J9Xwh7qMGl0JK9+bALuxf7B541Gr4p0WEN
+G8fhgjBV4w4ut9eQLOoa1eddOUSZcy46Z7allwowwgt7b5VFfx/P1iKJ3LzBMgkCK7GZ2kiLrL7R
+iqV+h482J7hkJD+ardoc6LnrHO/hIZymDxok+VH9fVeUdQa29IZKrIDVj65THQIDAQABo2MwYTAf
+BgNVHSMEGDAWgBRfdLQEwE8HWurlsdsio4dBspzhATAdBgNVHQ4EFgQUSqHkYINtUSAtDPnS8Xoy
+oP9p7qEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB
+AIQ8TzFy4bVIVb8+WhHKCkKNPcJe2EZuIcqvRoi727lZTJOfYy/JzLtckyZYfEI8J0lasZ29wkTt
+a1IjSo+a6XdhudU4ONVBrL70U8Kzntplw/6TBNbLFpp7taRALjUgbCOk4EoBMbeCL0GiYYsTS0mw
+7xdySzmGQku4GTyqutIGPQwKxSj9iSFw1FCZqr4VP4tyXzMUgc52SzagA6i7AyLedd3tbS6lnR5B
+L+W9Kx9hwT8L7WANAxQzv/jGldeuSLN8bsTxlOYlsdjmIGu/C9OWblPYGpjQQIRyvs4Cc/mNhrh+
+14EQgwuemIIFDLOgcD+iISoN8CqegelNcJndFw1PDN6LkVoiHz9p7jzsge8RKay/QW6C03KNDpWZ
+EUCgCUdfHfo8xKeR+LL1cfn24HKJmZt8L/aeRZwZ1jwePXFRVtiXELvgJuM/tJDIFj2KD337iV64
+fWcKQ/ydDVGqfDZAdcU4hQdsrPWENwPTQPfVPq2NNLMyIH9+WKx9Ed6/WzeZmIy5ZWpX1TtTolo6
+OJXQFeItMAjHxW/ZSZTok5IS3FuRhExturaInnzjYpx50a6kS34c5+c8hYq7sAtZ/CNLZmBnBCFD
+aMQqT8xFZJ5uolUaSeXxg7JFY1QsYp5RKvj4SjFwCGKJ2+hPPe9UyyltxOidNtxjaknOCeBHytOr
+-----END CERTIFICATE-----

+ 88 - 0
addons/epay/certs/alipayRootCert.crt

@@ -0,0 +1,88 @@
+-----BEGIN CERTIFICATE-----
+MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
+EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
+MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
+UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
+MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
+V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
+W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
+MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
+53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
+pDoiVhsLwg==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
+BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
+MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
+BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
+aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
+rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
+xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
+dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
+vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
+YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
+Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
+DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
+SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
+PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
+9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
+AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
+tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
+nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
+tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
+JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
+IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
+05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
+T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
+kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
+PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
+1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
+jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
+77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
+kT9qhqn+lw==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
+EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
+WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
+CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
+YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
+WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
+rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
+4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
+zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
+wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
+Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
+BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
+E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
+MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
+MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
+bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
+b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
+nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
+4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
+wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
+WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
+z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
+KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
+uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
+emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
+U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
+UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
+DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
+1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
+Yf4Zr0fJsGuv
+-----END CERTIFICATE-----

+ 24 - 0
addons/epay/certs/apiclient_cert.pem

@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID8DCCAtigAwIBAgIUQhD8BZdapRqZZ0vNM6S+FY54zmUwDQYJKoZIhvcNAQEL
+BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
+FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
+Q0EwHhcNMjIwODI3MDIwODI0WhcNMjcwODI2MDIwODI0WjCBgTETMBEGA1UEAwwK
+MTYyMzQyOTQ3NzEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL
+DCTpqazpno3lsbHluILmr4XlipvllYbotLjmnInpmZDlhazlj7gxCzAJBgNVBAYM
+AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAKng1J/tMdJjIid0vxueVJDjK1vVRslEAwoXSCTLDKBAuuxkC6z1OG1E
+xMoiAYhb7kLqvs6OzSf5vtBqJCZffRd1CzIcO6UYtFmmYzkobN12CJtrfLDOt2e9
+88EQnpCSNE4Z+gfTb8zTlE1jMiSLPO3s1vKUVdDBvfxl6nSyt+lahcNRJEGtBWAQ
+Y4j3b1GCMgJHBG+VyVUAGKWSg33v+/ZYerivT75+L7eAoMe11y0dDzfLcJGd2sAB
+BSAEzIQu0kzJwGXZQvE31WdcdYiHeRvxy8XLJPHlZruSyN/TqzDDVDWrYgTPQxzt
+nhSi21Qin+i0uE6e2hXO6GltE8aMw2UCAwEAAaOBgTB/MAkGA1UdEwQCMAAwCwYD
+VR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0cnVzLmNv
+bS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1
+NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAO+gBjxbmlQ04zFER
+TEkS5EFbKgF+JfHtGxcG/KJhy2DJ6crUbTlpawJMBVdTHQAkvpKBVmSvnn2b5nQI
+pqd/TYTR1KlHO84rB/QQzR34WDXn1lgeCqRv2pAwe8x5KKkx5s999/z8dRt4dALL
+DIej1cYy9VyX3cvM8NKWjO7js9YiY5Y5wEmnKwNhnf61RClMCbJD8WNpHXiNBKH4
+A9bFDS4doipSClbbFDanoRbwjI7CtBMWLpdOZrya3OIAHCNhpBB7tOzuHqwgaxqc
+TVLQnValwr8eVNzEPuyzdFJiViXeZJq4LcoqmSORcKBmdocBclYB6zxL0rjCV8ij
+AD3ocg==
+-----END CERTIFICATE-----

+ 28 - 0
addons/epay/certs/apiclient_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCp4NSf7THSYyIn
+dL8bnlSQ4ytb1UbJRAMKF0gkywygQLrsZAus9ThtRMTKIgGIW+5C6r7Ojs0n+b7Q
+aiQmX30XdQsyHDulGLRZpmM5KGzddgiba3ywzrdnvfPBEJ6QkjROGfoH02/M05RN
+YzIkizzt7NbylFXQwb38Zep0srfpWoXDUSRBrQVgEGOI929RgjICRwRvlclVABil
+koN97/v2WHq4r0++fi+3gKDHtdctHQ83y3CRndrAAQUgBMyELtJMycBl2ULxN9Vn
+XHWIh3kb8cvFyyTx5Wa7ksjf06sww1Q1q2IEz0Mc7Z4UottUIp/otLhOntoVzuhp
+bRPGjMNlAgMBAAECggEAfvNV8m3/3YnX7ESCzND1wZECxg4MaDw7ST882sIjHV9C
+UYYyc36OPJo3thbTrmOFGtzy2so85rLstjOb8FFL2kvNohUXPbnIjXaakAx1zsp2
+hC8/b6ILzatWjeEtnjykeflPsKmB00AKhAqtXkHvtp5Xw9C2kNs4b3Z6T9t/NRMJ
+Q3yTl9K0N+tHdmH7v6AgO5gBbhAkwH086hBI5owsI69a+0famRYcYCo5Mk8i3mCO
+758cFzILFEQhTq/1knIuqMIev/nq67O06UngZ4ZHYdIEitOI5QbFeYN0+8t12J8v
+DbWLFrBDKvKdeIolYT3ADMce/waVUo/C/nsH0okJwQKBgQDUsBz5vdw0uUAGZ6dU
+2oxIDnRYrlMfLL/uQCv8aqos6JRGmhiHH7pj2j6RtabGuWvUFsgl1jMVOY1v7E3r
+APbXaSjsEilfYyyJ8pk9cyvOMEvJ8g6Vfz8pruU/tCdbsR5/7AbGmbmGqSx9qx/R
+0HKQZluaoMUMqalvARQ8vYglZwKBgQDMePRRNsHIJjB2C0SicXZf5aVbhPPzlWGM
+dY5nrWGk/LqXwRKc9X0DsrzzPSt47BsKXr58CuDD/KD3X0fLVlnYdXrSJDjSjEuF
+g1IuNRqF/wCMVuoJgcnlEPrnbb2dLBGDA0imDzJe5GCna7rqLRyGjBh9aBJmK/k0
+Fc/RBsFlUwKBgQCLWbQyYRX1KWbsi19UNjjxQR8zt0vYwh4igMMRB+XltKDpvS7S
+4ZPitmyGVWknDPe8Jw0N1VD02Bd2PHI0xhX0vv7D5YZb7AOTgnKqk1ohJv2RG9o4
+Uv3UVrX3Thb3eY3NLJyn5LJsq0zuLX+UnaliON5N6oYQrNzJUQCsZ1XbgwKBgQDA
+Q5u4Ifzk7sPyu3W2KWpdkdqCy+l3yacIJCHbazS9psbtmyadYFhE+fSh44rVEW37
+ukry6Geg4hOZh4ZCtNeWqiDrKBQmJrV8vZawzE48YSNq0u8OMXReaDEFHDOHbsCo
+qJXPT8rVX3li8/G18OU5vtHoWcuzNvYE+92Fdwyy8QKBgQCjAAj4/jAu7lDY/Uvq
+SzkSmOJn8C3wHUTK2rKE1WCDjmHdzNocHo7/XxcN1GchzSM6SiUNLan5IxxI1bMc
+fpeJlM0lI4DxZwJCoNr3B/evXEDP3o9PDrScJY2875hnpVqfL0vtBAkrcme/ABKr
+tEc8/EIpIJs1C1T4bSNjfuUySw==
+-----END PRIVATE KEY-----

+ 24 - 0
addons/epay/certs/appCertPublicKey.crt

@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEsTCCA5mgAwIBAgIQICISBwplyMuqrY6L6O5sfTANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
+BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
+YXNzIDEgUjEwHhcNMjIxMjA3MTMzMDA1WhcNMjcxMjA2MTMzMDA1WjB5MQswCQYDVQQGEwJDTjEt
+MCsGA1UECgwk6ams6Z6N5bGx5biC5q+F5Yqb5ZWG6LS45pyJ6ZmQ5YWs5Y+4MQ8wDQYDVQQLDAZB
+bGlwYXkxKjAoBgNVBAMMITIwODgzNDEzOTE3Mzg2MDUtMjAyMTAwMzExMDYwMDEyMTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJfjaa5dwJ6uSAeK64iobBCwe58lNzYIx22Qp1okJElm
+XHboVA0gT0sX+QciksACnnyfusHH2LT3Wa9F0jH/Kdx9LCJ3ZkToY2Ni8h0m9HqKalUA0lQP9u+S
+D0jruNnU6KMEQltiiAPmljsxUu+VZQ8YeNEdUXc6UKeKVvhKqZ+H+Frex7JXyv+aGrnSPEb5GoMS
+DwMiTUgbXzhjBQVISWUGulAyNCcu0QrPSfkFvz9HKK8liTcQhZ8oCMdOWvvWMsT8mWNb4Q0gPFSw
+T8X1kqAFq6a5AHe3n2ZAZvxBPY2SQkBKj4ZBRvrWZtHvcM80qeQNSiqFQQ1kpZmTu3l+eNkCAwEA
+AaOCASkwggElMB8GA1UdIwQYMBaAFHEH4gRhFuTl8mXrMQ/J4PQ8mtWRMB0GA1UdDgQWBBT1+r/s
+Fdh4WYC8EOfx5u8oWbnc1TBABgNVHSAEOTA3MDUGB2CBHAFuAQEwKjAoBggrBgEFBQcCARYcaHR0
+cDovL2NhLmFsaXBheS5jb20vY3BzLnBkZjAOBgNVHQ8BAf8EBAMCBsAwLwYDVR0fBCgwJjAkoCKg
+IIYeaHR0cDovL2NhLmFsaXBheS5jb20vY3JsNzIuY3JsMGAGCCsGAQUFBwEBBFQwUjAoBggrBgEF
+BQcwAoYcaHR0cDovL2NhLmFsaXBheS5jb20vY2E2LmNlcjAmBggrBgEFBQcwAYYaaHR0cDovL2Nh
+LmFsaXBheS5jb206ODM0MC8wDQYJKoZIhvcNAQELBQADggEBAMtC8XDsgq9u4TaPxB7PbSykBFEf
+3G8b3ZXr5Tf0CuYn1NE7dshEdG8Kp+hEw+ZJyztS5IqHwDY+EYs2Ax9lV/zyLu36AKRwMExH66Rm
+qkSSBKL8qIM/SWKmfKxJVDiQseR5Rh+n+tI73dvF9xINCT0jDJUGQbo8mFbpwpWlkxVIsKHm37QX
+tnQvDzovU5vgeHaSh7QDCfHH5dVG1F+N/Aq0w1JBQGltLMw3S2YXJWaEeq4zs4loq+6AP3tL0uQ5
+vU7htOhFidz/PncxUkaeFBsyfqPvrcVwj0kABTzGW9hs+XO6hXKLtlTuVqgumpoWyjU7fjRpAE1q
+H9nt/hF0DPg=
+-----END CERTIFICATE-----

+ 366 - 0
addons/epay/config.html

@@ -0,0 +1,366 @@
+<form id="config-form" class="edit-form form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="panel panel-default panel-intro">
+        <div class="panel-heading">
+            <ul class="nav nav-tabs nav-group">
+                <li class="active"><a href="#wechat" data-toggle="tab">微信支付</a></li>
+                <li><a href="#alipay" data-toggle="tab">支付宝</a></li>
+            </ul>
+        </div>
+
+        <div class="panel-body">
+            <div id="myTabContent" class="tab-content">
+                {foreach $addon.config as $item}
+                {if $item.name=='wechat'}
+                <div class="tab-pane fade active in" id="wechat">
+                    <table class="table table-striped table-config">
+                        <tbody>
+                        <tr>
+                            <td width="15%">APP appid</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][appid]" value="{$item.value.appid|default=''}" class="form-control" data-rule="" data-tip="APP应用中支付时使用"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>公众号的app_id</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip="公众号支付必须"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>公众号的app_secret</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][app_secret]" value="{$item.value.app_secret|default=''}" class="form-control" data-rule="" data-tip="仅在需要获取Openid时使用,一般情况下为空"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>小程序的app_id</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][miniapp_id]" value="{$item.value.miniapp_id|default=''}" class="form-control" data-rule="" data-tip="仅在小程序支付时使用"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>微信支付商户号ID</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][mch_id]" value="{$item.value.mch_id|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>微信支付商户的密钥</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][key]" value="{$item.value.key|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>支付模式</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        {:Form::radios('row[wechat][mode]',['normal'=>'正式环境','dev'=>'沙箱环境','service'=>'服务商模式'],$item.value.mode??'normal')}
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
+                            <td>子商户商户号ID</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][sub_mch_id]" value="{$item.value.sub_mch_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
+                            <td>子商户 APP appid</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][sub_appid]" value="{$item.value.sub_appid|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
+                            <td>子商户公众号的appid</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][sub_app_id]" value="{$item.value.sub_app_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
+                            <td>子商户小程序的appid</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][sub_miniapp_id]" value="{$item.value.sub_miniapp_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>回调通知地址</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[wechat][notify_url]" value="{$item.value.notify_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>微信支付API证书cert</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <div class="input-group">
+                                            <input id="c-cert_client" class="form-control" size="50" name="row[wechat][cert_client]" type="text" value="{$item.value.cert_client|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-cert_client" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_client"}' data-mimetype="pem" data-input-id="c-cert_client" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-cert_client"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>微信支付API证书key</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <div class="input-group">
+                                            <input id="c-cert_key" class="form-control" size="50" name="row[wechat][cert_key]" type="text" value="{$item.value.cert_key|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-cert_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_key"}' data-mimetype="pem" data-input-id="c-cert_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-cert_key"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+
+                        <tr>
+                            <td>记录日志</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        {:Form::radios('row[wechat][log]',['1'=>'开启','0'=>'关闭'],$item.value.log)}
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                {elseif $item.name=='alipay'}
+                <div class="tab-pane fade" id="alipay">
+                    <table class="table table-striped table-config">
+                        <tbody>
+                        <tr>
+                            <td width="15%">应用ID(app_id)</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[alipay][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip=""/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>支付模式</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        {:Form::radios('row[alipay][mode]',['normal'=>'正式环境','dev'=>'沙箱环境'],$item.value.mode??'normal')}
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>回调通知地址</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[alipay][notify_url]" value="{$item.value.notify_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>支付跳转地址</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[alipay][return_url]" value="{$item.value.return_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>应用私钥(private_key)</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <input type="text" name="row[alipay][private_key]" value="{$item.value.private_key|default=''}" class="form-control" data-rule="" />
+                                        <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用私钥?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>支付宝公钥路径(ali_public_key)</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <div class="input-group">
+                                            <input id="c-ali_public_key" class="form-control" size="50" name="row[alipay][ali_public_key]" type="text" value="{$item.value.ali_public_key|default=''|htmlentities}" placeholder="公钥请直接粘贴,公钥证书请点击右侧的上传">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-ali_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"ali_public_key"}' data-mimetype="crt" data-input-id="c-ali_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-ali_public_key"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="javascript:" data-toggle="tooltip" data-title="如果要使用转账、提现功能,则必须使用公钥证书"> <i class="fa fa-info-circle"></i> 公钥和公钥证书说明</a> <a href="https://opensupport.alipay.com/support/helpcenter/207/201602471154" target="_blank"><i class="fa fa-question-circle"></i> 如何获取支付宝公钥证书?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>应用公钥证书路径(app_cert_public_key)</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <div class="input-group">
+                                            <input id="c-app_cert_public_key" class="form-control" size="50" name="row[alipay][app_cert_public_key]" type="text" value="{$item.value.app_cert_public_key|default=''|htmlentities}">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-app_cert_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"app_cert_public_key"}' data-mimetype="crt" data-input-id="c-app_cert_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-app_cert_public_key"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用公钥证书?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>支付宝根证书路径(alipay_root_cert)</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        <div class="input-group">
+                                            <input id="c-alipay_root_cert" class="form-control" size="50" name="row[alipay][alipay_root_cert]" type="text" value="{$item.value.alipay_root_cert|default=''|htmlentities}">
+                                            <div class="input-group-addon no-border no-padding">
+                                                <span><button type="button" id="faupload-alipay_root_cert" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"alipay_root_cert"}' data-mimetype="crt" data-input-id="c-alipay_root_cert" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                                            </div>
+                                            <span class="msg-box n-right" for="c-alipay_root_cert"></span>
+                                        </div>
+                                        <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取支付宝证书?</a></div>
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+
+                        <tr>
+                            <td>记录日志</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        {:Form::radios('row[alipay][log]',['1'=>'开启','0'=>'关闭'],$item.value.log)}
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+
+                        <tr>
+                            <td>PC端使用扫码支付</td>
+                            <td>
+                                <div class="row">
+                                    <div class="col-sm-8 col-xs-12">
+                                        {:Form::radios('row[alipay][scanpay]',['1'=>'开启','0'=>'关闭'],$item.value.scanpay??0)}
+                                    </div>
+                                    <div class="col-sm-4"></div>
+                                </div>
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                {/if}
+                {/foreach}
+                <div class="form-group layer-footer">
+                    <label class="control-label col-xs-12 col-sm-2"></label>
+                    <div class="col-xs-12 col-sm-8">
+                        <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
+                        <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</form>
+<script>
+    document.querySelectorAll("input[name='row[wechat][mode]']").forEach(function (i, j) {
+        i.addEventListener("click", function () {
+            document.querySelectorAll("#wechat table tr[data-type]").forEach(function (m, n) {
+                m.classList.add("hidden");
+            });
+            document.querySelectorAll("#wechat table tr[data-type='" + this.value + "']").forEach(function (m, n) {
+                m.classList.remove("hidden");
+            });
+        });
+    });
+</script>

+ 67 - 0
addons/epay/config.php

@@ -0,0 +1,67 @@
+<?php
+
+return [
+    [
+        'name' => 'wechat',
+        'title' => '微信',
+        'type' => 'array',
+        'content' => [],
+        'value' => [
+            'appid' => 'wx799d545a856277fb',
+            'app_id' => 'wx799d545a856277fb',
+            'app_secret' => 'wx799d545a856277fb',
+            'miniapp_id' => 'wx799d545a856277fb',
+            'mch_id' => '1623429477',
+            'key' => '6002C8899C37EDA24131ACD40F681851',
+            'mode' => 'normal',
+            'sub_mch_id' => '',
+            'sub_appid' => '',
+            'sub_app_id' => '',
+            'sub_miniapp_id' => '',
+            'notify_url' => '/addons/epay/api/notifyx/type/wechat',
+            'cert_client' => '/addons/epay/certs/apiclient_cert.pem',
+            'cert_key' => '/addons/epay/certs/apiclient_key.pem',
+            'log' => '1',
+        ],
+        'rule' => '',
+        'msg' => '',
+        'tip' => '微信参数配置',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => 'alipay',
+        'title' => '支付宝',
+        'type' => 'array',
+        'content' => [],
+        'value' => [
+            'app_id' => '2021003110600121',
+            'mode' => 'normal',
+            'notify_url' => '/addons/epay/api/notifyx/type/alipay',
+            'return_url' => '/addons/epay/api/returnx/type/alipay',
+            'private_key' => 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCX42muXcCerkgHiuuIqGwQsHufJTc2CMdtkKdaJCRJZlx26FQNIE9LF/kHIpLAAp58n7rBx9i091mvRdIx/yncfSwid2ZE6GNjYvIdJvR6impVANJUD/bvkg9I67jZ1OijBEJbYogD5pY7MVLvlWUPGHjRHVF3OlCnilb4Sqmfh/ha3seyV8r/mhq50jxG+RqDEg8DIk1IG184YwUFSEllBrpQMjQnLtEKz0n5Bb8/RyivJYk3EIWfKAjHTlr71jLE/JljW+ENIDxUsE/F9ZKgBaumuQB3t59mQGb8QT2NkkJASo+GQUb61mbR73DPNKnkDUoqhUENZKWZk7t5fnjZAgMBAAECggEAHKXXA/1I3GUQzuNj42Wo5Ej1OCIj1Lw5KTA0cGsKsRFudTsCx2sFqas+3BqoRTD5RZY2+l/APvm8X7Oynp16/sxZK7MLhgmy3UcHVXpIeGhs/9Vk1lgggjUxTtavCd7/defSwDvIXbv7L/TwqKGr6vn5p6948xz7o24TU0d50M3hICSj655irCKJ3azGztwbSbrDTfY4x3obvtv3/LFfnECl/i+M3+vRi1vyL08NZopkWNvp1YscXOXCX9gOpBnOoMBYdzFqkx1XkDLnQZzG0E1AUK83q6Wzcw3E47vzqEgj2rVLkGPkR/eOrXDv5R7m2/URxEp4JUDG84G3+PKEAQKBgQDKTsX4plmVD9dL8tLG/zWnSAq5adPCx/ZTor20sUa6hKEu38gVL95icQ6JUbqrEbb3I53DBdR4XfegkN0+xg6UYshIBqjk9Ef2xEZRfjnPu2Z9c1LCmFgSM01rcShTVpJEPYxbLKeWBwFsaZDBcBDxgcoET1lyEJ/ktnt2YQDvWQKBgQDAMwmApys/4y95gjSnIzYpUaSreC4dMqYrWtWdBoAssl9+AGbo3Gr5FjDjNrUJ6gKz/neVyGu/4Xv5LBk0pRvqS8aRx9tQARI0giKN1VQ+Djo5x3eMU3JgDj74SApo2n3DV3HsPkYnFkF8nQX/IDSER5bi1S5Tf2PYgvwykR4lgQKBgHMtG7pvAmtBxIHhsaxHWYVuF2Tt4fhvnUaKTVSU5L6FibpE/wVI5mPHF86H8C06fZ7/8O454HfSj4sjTkJRwbUq8/lFiVwn0tyja6Ezqrggab4cLM7KqsFZ4ca8eofPrqAZJlI1/wcD4ASXqSjno03LZXbEshilbFJqral8mmqpAoGAW4Lxb1Qo/45IOG6Ka1oUm6/H2+yo6DYJcHMbalyCAsYbOX8vgIgSVu797/FIWaQWDWWHwsVeqEnQIV+h0LMci33w2dBy17Dq3h1oHmgIaFOV3ZTfkuzYdGvUB5slea8uWif0kitEc6/hFlgdVtAasTD04qPmHtN00s3rDpiR5gECgYBeSsvN/ZmJYApIXddUQBfhJ/STm/zE944Laj425NwzqkJd7tVevehPrqDLYD40W7civJo50tf0mpEw/LAcbHbFR6KIfu/IcXIYnSeVSgMLUvI0t2ThvFBgyUV9WUPFqYE2Zo/eutnO4sd62yRwqFRxnWGWpmUUsU0UsMZIsKdX3Q==',
+            'ali_public_key' => '/addons/epay/certs/alipayCertPublicKey.crt',
+            'app_cert_public_key' => '/addons/epay/certs/appCertPublicKey.crt',
+            'alipay_root_cert' => '/addons/epay/certs/alipayRootCert.crt',
+            'log' => '1',
+            'scanpay' => '0',
+        ],
+        'rule' => 'required',
+        'msg' => '',
+        'tip' => '支付宝参数配置',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => '__tips__',
+        'title' => '温馨提示',
+        'type' => 'array',
+        'content' => [],
+        'value' => '请注意微信支付证书路径位于/addons/epay/certs目录下,请替换成你自己的证书<br>appid:APP的appid<br>app_id:公众号的appid<br>app_secret:公众号的secret<br>miniapp_id:小程序ID<br>mch_id:微信商户ID<br>key:微信商户支付的密钥',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '微信参数配置',
+        'ok' => '',
+        'extend' => '',
+    ],
+];

+ 243 - 0
addons/epay/controller/Api.php

@@ -0,0 +1,243 @@
+<?php
+
+namespace addons\epay\controller;
+
+use addons\epay\library\QRCode;
+use addons\epay\library\Service;
+use addons\epay\library\Wechat;
+use addons\third\model\Third;
+use app\common\library\Auth;
+use think\addons\Controller;
+use think\Response;
+use think\Session;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Pay;
+
+/**
+ * API接口控制器
+ *
+ * @package addons\epay\controller
+ */
+class Api extends Controller
+{
+
+    protected $layout = 'default';
+    protected $config = [];
+
+    /**
+     * 默认方法
+     */
+    public function index()
+    {
+        return;
+    }
+
+    /**
+     * 外部提交
+     */
+    public function submit()
+    {
+        $this->request->filter('trim');
+        $out_trade_no = $this->request->request("out_trade_no");
+        $title = $this->request->request("title");
+        $amount = $this->request->request('amount');
+        $type = $this->request->request('type');
+        $method = $this->request->request('method', 'web');
+        $openid = $this->request->request('openid', '');
+        $auth_code = $this->request->request('auth_code', '');
+        $notifyurl = $this->request->request('notifyurl', '');
+        $returnurl = $this->request->request('returnurl', '');
+
+        if (!$amount || $amount < 0) {
+            $this->error("支付金额必须大于0");
+        }
+
+        if (!$type || !in_array($type, ['alipay', 'wechat'])) {
+            $this->error("支付类型错误");
+        }
+
+        $params = [
+            'type'         => $type,
+            'out_trade_no' => $out_trade_no,
+            'title'        => $title,
+            'amount'       => $amount,
+            'method'       => $method,
+            'openid'       => $openid,
+            'auth_code'    => $auth_code,
+            'notifyurl'    => $notifyurl,
+            'returnurl'    => $returnurl,
+        ];
+        return Service::submitOrder($params);
+    }
+
+    /**
+     * 微信支付(公众号支付&PC扫码支付)
+     * @return string
+     */
+    public function wechat()
+    {
+        $config = Service::getConfig('wechat');
+
+        $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
+        $isMobile = $this->request->isMobile();
+        $this->view->assign("isWechat", $isWechat);
+        $this->view->assign("isMobile", $isMobile);
+
+        //发起PC支付(Scan支付)(PC扫码模式)
+        if ($this->request->isAjax()) {
+            $pay = Pay::wechat($config);
+            $orderid = $this->request->post("orderid");
+            try {
+                $result = $pay->find($orderid);
+                if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
+                    $this->success("", "", ['status' => $result['trade_state']]);
+                } else {
+                    $this->error("查询失败");
+                }
+            } catch (GatewayException $e) {
+                $this->error("查询失败");
+            }
+        }
+
+        $orderData = Session::get("wechatorderdata");
+        if (!$orderData) {
+            $this->error("请求参数错误");
+        }
+        if ($isWechat) {
+            //发起公众号(jsapi支付),openid必须
+
+            //如果没有openid,则自动去获取openid
+            if (!isset($orderData['openid']) || !$orderData['openid']) {
+                $orderData['openid'] = Service::getOpenid();
+            }
+
+            $orderData['method'] = 'mp';
+            $type = 'jsapi';
+            $payData = Service::submitOrder($orderData);
+            if (!isset($payData['paySign'])) {
+                $this->error("创建订单失败,请返回重试", "");
+            }
+        } else {
+            $orderData['method'] = 'scan';
+            $type = 'pc';
+            $payData = Service::submitOrder($orderData);
+            if (!isset($payData['code_url'])) {
+                $this->error("创建订单失败,请返回重试", "");
+            }
+        }
+        $this->view->assign("orderData", $orderData);
+        $this->view->assign("payData", $payData);
+        $this->view->assign("type", $type);
+
+        $this->view->assign("title", "微信支付");
+        return $this->view->fetch();
+    }
+
+    /**
+     * 支付宝支付(PC扫码支付)
+     * @return string
+     */
+    public function alipay()
+    {
+        $config = Service::getConfig('alipay');
+
+        $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
+        $isMobile = $this->request->isMobile();
+        $this->view->assign("isWechat", $isWechat);
+        $this->view->assign("isMobile", $isMobile);
+
+        if ($this->request->isAjax()) {
+            $orderid = $this->request->post("orderid");
+            $pay = Pay::alipay($config);
+            try {
+                $result = $pay->find($orderid);
+                if ($result['code'] == '10000' && $result['trade_status'] == 'TRADE_SUCCESS') {
+                    $this->success("", "", ['status' => $result['trade_status']]);
+                } else {
+                    $this->error("查询失败");
+                }
+            } catch (GatewayException $e) {
+                $this->error("查询失败");
+            }
+        }
+
+        //发起PC支付(Scan支付)(PC扫码模式)
+        $orderData = Session::get("alipayorderdata");
+        if (!$orderData) {
+            $this->error("请求参数错误");
+        }
+
+        $orderData['method'] = 'scan';
+        $payData = Service::submitOrder($orderData);
+        if (!isset($payData['qr_code'])) {
+            $this->error("创建订单失败,请返回重试");
+        }
+
+        $type = 'pc';
+        $this->view->assign("orderData", $orderData);
+        $this->view->assign("payData", $payData);
+        $this->view->assign("type", $type);
+        $this->view->assign("title", "支付宝支付");
+        return $this->view->fetch();
+    }
+
+    /**
+     * 支付成功回调
+     */
+    public function notifyx()
+    {
+        $type = $this->request->param('type');
+        if (!Service::checkNotify($type)) {
+            echo '签名错误';
+            return;
+        }
+
+        //你可以在这里你的业务处理逻辑,比如处理你的订单状态、给会员加余额等等功能
+        //下面这句必须要执行,且在此之前不能有任何输出
+        echo "success";
+        return;
+    }
+
+    /**
+     * 支付成功返回
+     */
+    public function returnx()
+    {
+        $type = $this->request->param('type');
+        if (Service::checkReturn($type)) {
+            echo '签名错误';
+            return;
+        }
+
+        //你可以在这里定义你的提示信息,但切记不可在此编写逻辑
+        $this->success("恭喜你!支付成功!", addon_url("epay/index/index"));
+
+        return;
+    }
+
+    /**
+     * 生成二维码
+     */
+    public function qrcode()
+    {
+        $text = $this->request->get('text', 'hello world');
+
+        //如果有安装二维码插件,则调用插件的生成方法
+        if (class_exists("\addons\qrcode\library\Service") && get_addon_info('qrcode')['state']) {
+            $qrCode = \addons\qrcode\library\Service::qrcode(['text' => $text]);
+            $response = Response::create()->header("Content-Type", "image/png");
+
+            header('Content-Type: ' . $qrCode->getContentType());
+            $response->content($qrCode->writeString());
+            return $response;
+        } else {
+            $qr = QRCode::getMinimumQRCode($text);
+            $im = $qr->createImage(8, 5);
+            header("Content-type: image/png");
+            imagepng($im);
+            imagedestroy($im);
+            return;
+        }
+    }
+
+}

+ 111 - 0
addons/epay/controller/Index.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace addons\epay\controller;
+
+use addons\epay\library\Service;
+use fast\Random;
+use think\addons\Controller;
+use Exception;
+
+/**
+ * 微信支付宝插件首页
+ *
+ * 此控制器仅用于开发展示说明和体验,建议自行添加一个新的控制器进行处理返回和回调事件,同时删除此控制器文件
+ *
+ * Class Index
+ * @package addons\epay\controller
+ */
+class Index extends Controller
+{
+
+    protected $layout = 'default';
+
+    protected $config = [];
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        if (!config("app_debug")) {
+            $this->error("仅在开发环境下查看");
+        }
+    }
+
+    public function index()
+    {
+        $this->view->assign("title", "微信支付宝整合插件");
+        return $this->view->fetch();
+    }
+
+    /**
+     * 体验,仅供开发测试
+     */
+    public function experience()
+    {
+        $amount = $this->request->request('amount');
+        $type = $this->request->request('type');
+        $method = $this->request->request('method');
+
+        if (!$amount || $amount < 0) {
+            $this->error("支付金额必须大于0");
+        }
+
+        if (!$type || !in_array($type, ['alipay', 'wechat'])) {
+            $this->error("支付类型不能为空");
+        }
+
+        //订单号
+        $out_trade_no = date("YmdHis") . mt_rand(100000, 999999);
+
+        //订单标题
+        $title = '测试订单';
+
+        //回调链接
+        $notifyurl = $this->request->root(true) . '/addons/epay/index/notifyx/paytype/' . $type;
+        $returnurl = $this->request->root(true) . '/addons/epay/index/returnx/paytype/' . $type . '/out_trade_no/' . $out_trade_no;
+
+        $response = Service::submitOrder($amount, $out_trade_no, $type, $title, $notifyurl, $returnurl, $method);
+
+        return $response;
+    }
+
+    /**
+     * 支付成功,仅供开发测试
+     */
+    public function notifyx()
+    {
+        $paytype = $this->request->param('paytype');
+        $pay = Service::checkNotify($paytype);
+        if (!$pay) {
+            echo '签名错误';
+            return;
+        }
+        $data = $pay->verify();
+        try {
+            $payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
+            $out_trade_no = $data['out_trade_no'];
+
+            //你可以在此编写订单逻辑
+        } catch (Exception $e) {
+        }
+        echo $pay->success();
+    }
+
+    /**
+     * 支付返回,仅供开发测试
+     */
+    public function returnx()
+    {
+        $paytype = $this->request->param('paytype');
+        $out_trade_no = $this->request->param('out_trade_no');
+        $pay = Service::checkReturn($paytype);
+        if (!$pay) {
+            $this->error('签名错误', '');
+        }
+
+        //你可以在这里通过out_trade_no去验证订单状态
+        //但是不可以在此编写订单逻辑!!!
+
+        $this->success("请返回网站查看支付结果", addon_url("epay/index/index"));
+    }
+
+}

+ 10 - 0
addons/epay/info.ini

@@ -0,0 +1,10 @@
+name = epay
+title = 微信支付宝整合
+intro = 可用于快速整合微信、支付宝支付功能
+author = FastAdmin
+website = https://www.fastadmin.net
+version = 1.2.5
+state = 1
+url = /addons/epay
+license = regular
+licenseto = 39487

+ 18 - 0
addons/epay/library/Collection.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace addons\epay\library;
+
+class Collection extends \Yansongda\Supports\Collection
+{
+
+    /**
+     * 创建 Collection 实例
+     * @access public
+     * @param  array $items 数据
+     * @return static
+     */
+    public static function make($items = [])
+    {
+        return new static($items);
+    }
+}

+ 16 - 0
addons/epay/library/OrderException.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace addons\epay\library;
+
+use think\Exception;
+
+class OrderException extends Exception
+{
+    public function __construct($message = "", $code = 0, $data = [])
+    {
+        $this->message = $message;
+        $this->code = $code;
+        $this->data = $data;
+    }
+
+}

+ 1856 - 0
addons/epay/library/QRCode.php

@@ -0,0 +1,1856 @@
+<?php
+
+namespace addons\epay\library;
+
+//---------------------------------------------------------------
+// QRCode for PHP5
+//
+// Copyright (c) 2009 Kazuhiko Arase
+//
+// URL: http://www.d-project.com/
+//
+// Licensed under the MIT license:
+//   http://www.opensource.org/licenses/mit-license.php
+//
+// The word "QR Code" is registered trademark of
+// DENSO WAVE INCORPORATED
+//   http://www.denso-wave.com/qrcode/faqpatent-e.html
+//
+//---------------------------------------------------------------------
+
+//---------------------------------------------------------------
+// QRCode
+//---------------------------------------------------------------
+
+define("QR_PAD0", 0xEC);
+define("QR_PAD1", 0x11);
+
+class QRCode
+{
+
+    var $typeNumber;
+
+    var $modules;
+
+    var $moduleCount;
+
+    var $errorCorrectLevel;
+
+    var $qrDataList;
+
+    function __construct()
+    {
+        $this->typeNumber = 1;
+        $this->errorCorrectLevel = QR_ERROR_CORRECT_LEVEL_H;
+        $this->qrDataList = array();
+    }
+
+    function getTypeNumber()
+    {
+        return $this->typeNumber;
+    }
+
+    function setTypeNumber($typeNumber)
+    {
+        $this->typeNumber = $typeNumber;
+    }
+
+    function getErrorCorrectLevel()
+    {
+        return $this->errorCorrectLevel;
+    }
+
+    function setErrorCorrectLevel($errorCorrectLevel)
+    {
+        $this->errorCorrectLevel = $errorCorrectLevel;
+    }
+
+    function addData($data, $mode = 0)
+    {
+
+        if ($mode == 0) {
+            $mode = QRUtil::getMode($data);
+        }
+
+        switch ($mode) {
+
+            case QR_MODE_NUMBER :
+                $this->addDataImpl(new QRNumber($data));
+                break;
+
+            case QR_MODE_ALPHA_NUM :
+                $this->addDataImpl(new QRAlphaNum($data));
+                break;
+
+            case QR_MODE_8BIT_BYTE :
+                $this->addDataImpl(new QR8BitByte($data));
+                break;
+
+            case QR_MODE_KANJI :
+                $this->addDataImpl(new QRKanji($data));
+                break;
+
+            default :
+                trigger_error("mode:$mode", E_USER_ERROR);
+        }
+    }
+
+    function clearData()
+    {
+        $this->qrDataList = array();
+    }
+
+    function addDataImpl($qrData)
+    {
+        $this->qrDataList[] = $qrData;
+    }
+
+    function getDataCount()
+    {
+        return count($this->qrDataList);
+    }
+
+    function getData($index)
+    {
+        return $this->qrDataList[$index];
+    }
+
+    function isDark($row, $col)
+    {
+        if ($this->modules[$row][$col] !== null) {
+            return $this->modules[$row][$col];
+        } else {
+            return false;
+        }
+    }
+
+    function getModuleCount()
+    {
+        return $this->moduleCount;
+    }
+
+    // used for converting fg/bg colors (e.g. #0000ff = 0x0000FF)
+    // added 2015.07.27 ~ DoktorJ
+    function hex2rgb($hex = 0x0)
+    {
+        return array(
+            'r' => floor($hex / 65536),
+            'g' => floor($hex / 256) % 256,
+            'b' => $hex % 256
+        );
+    }
+
+    function make()
+    {
+        $this->makeImpl(false, $this->getBestMaskPattern());
+    }
+
+    function getBestMaskPattern()
+    {
+
+        $minLostPoint = 0;
+        $pattern = 0;
+
+        for ($i = 0; $i < 8; $i++) {
+
+            $this->makeImpl(true, $i);
+
+            $lostPoint = QRUtil::getLostPoint($this);
+
+            if ($i == 0 || $minLostPoint > $lostPoint) {
+                $minLostPoint = $lostPoint;
+                $pattern = $i;
+            }
+        }
+
+        return $pattern;
+    }
+
+    function createNullArray($length)
+    {
+        $nullArray = array();
+        for ($i = 0; $i < $length; $i++) {
+            $nullArray[] = null;
+        }
+        return $nullArray;
+    }
+
+    function makeImpl($test, $maskPattern)
+    {
+
+        $this->moduleCount = $this->typeNumber * 4 + 17;
+
+        $this->modules = array();
+        for ($i = 0; $i < $this->moduleCount; $i++) {
+            $this->modules[] = QRCode::createNullArray($this->moduleCount);
+        }
+
+        $this->setupPositionProbePattern(0, 0);
+        $this->setupPositionProbePattern($this->moduleCount - 7, 0);
+        $this->setupPositionProbePattern(0, $this->moduleCount - 7);
+
+        $this->setupPositionAdjustPattern();
+        $this->setupTimingPattern();
+
+        $this->setupTypeInfo($test, $maskPattern);
+
+        if ($this->typeNumber >= 7) {
+            $this->setupTypeNumber($test);
+        }
+
+        $dataArray = $this->qrDataList;
+
+        $data = QRCode::createData($this->typeNumber, $this->errorCorrectLevel, $dataArray);
+
+        $this->mapData($data, $maskPattern);
+    }
+
+    function mapData(&$data, $maskPattern)
+    {
+
+        $inc = -1;
+        $row = $this->moduleCount - 1;
+        $bitIndex = 7;
+        $byteIndex = 0;
+
+        for ($col = $this->moduleCount - 1; $col > 0; $col -= 2) {
+
+            if ($col == 6) $col--;
+
+            while (true) {
+
+                for ($c = 0; $c < 2; $c++) {
+
+                    if ($this->modules[$row][$col - $c] === null) {
+
+                        $dark = false;
+
+                        if ($byteIndex < count($data)) {
+                            $dark = ((($data[$byteIndex] >> $bitIndex) & 1) == 1);
+                        }
+
+                        if (QRUtil::getMask($maskPattern, $row, $col - $c)) {
+                            $dark = !$dark;
+                        }
+
+                        $this->modules[$row][$col - $c] = $dark;
+                        $bitIndex--;
+
+                        if ($bitIndex == -1) {
+                            $byteIndex++;
+                            $bitIndex = 7;
+                        }
+                    }
+                }
+
+                $row += $inc;
+
+                if ($row < 0 || $this->moduleCount <= $row) {
+                    $row -= $inc;
+                    $inc = -$inc;
+                    break;
+                }
+            }
+        }
+    }
+
+    function setupPositionAdjustPattern()
+    {
+
+        $pos = QRUtil::getPatternPosition($this->typeNumber);
+
+        for ($i = 0; $i < count($pos); $i++) {
+
+            for ($j = 0; $j < count($pos); $j++) {
+
+                $row = $pos[$i];
+                $col = $pos[$j];
+
+                if ($this->modules[$row][$col] !== null) {
+                    continue;
+                }
+
+                for ($r = -2; $r <= 2; $r++) {
+
+                    for ($c = -2; $c <= 2; $c++) {
+                        $this->modules[$row + $r][$col + $c] =
+                            $r == -2 || $r == 2 || $c == -2 || $c == 2 || ($r == 0 && $c == 0);
+                    }
+                }
+            }
+        }
+    }
+
+    function setupPositionProbePattern($row, $col)
+    {
+
+        for ($r = -1; $r <= 7; $r++) {
+
+            for ($c = -1; $c <= 7; $c++) {
+
+                if ($row + $r <= -1 || $this->moduleCount <= $row + $r
+                    || $col + $c <= -1 || $this->moduleCount <= $col + $c) {
+                    continue;
+                }
+
+                $this->modules[$row + $r][$col + $c] =
+                    (0 <= $r && $r <= 6 && ($c == 0 || $c == 6))
+                    || (0 <= $c && $c <= 6 && ($r == 0 || $r == 6))
+                    || (2 <= $r && $r <= 4 && 2 <= $c && $c <= 4);
+            }
+        }
+    }
+
+    function setupTimingPattern()
+    {
+
+        for ($i = 8; $i < $this->moduleCount - 8; $i++) {
+
+            if ($this->modules[$i][6] !== null || $this->modules[6][$i] !== null) {
+                continue;
+            }
+
+            $this->modules[$i][6] = ($i % 2 == 0);
+            $this->modules[6][$i] = ($i % 2 == 0);
+        }
+    }
+
+    function setupTypeNumber($test)
+    {
+
+        $bits = QRUtil::getBCHTypeNumber($this->typeNumber);
+
+        for ($i = 0; $i < 18; $i++) {
+            $mod = (!$test && (($bits >> $i) & 1) == 1);
+            $this->modules[(int)floor($i / 3)][$i % 3 + $this->moduleCount - 8 - 3] = $mod;
+            $this->modules[$i % 3 + $this->moduleCount - 8 - 3][floor($i / 3)] = $mod;
+        }
+    }
+
+    function setupTypeInfo($test, $maskPattern)
+    {
+
+        $data = ($this->errorCorrectLevel << 3) | $maskPattern;
+        $bits = QRUtil::getBCHTypeInfo($data);
+
+        for ($i = 0; $i < 15; $i++) {
+
+            $mod = (!$test && (($bits >> $i) & 1) == 1);
+
+            if ($i < 6) {
+                $this->modules[$i][8] = $mod;
+            } else if ($i < 8) {
+                $this->modules[$i + 1][8] = $mod;
+            } else {
+                $this->modules[$this->moduleCount - 15 + $i][8] = $mod;
+            }
+
+            if ($i < 8) {
+                $this->modules[8][$this->moduleCount - $i - 1] = $mod;
+            } else if ($i < 9) {
+                $this->modules[8][15 - $i - 1 + 1] = $mod;
+            } else {
+                $this->modules[8][15 - $i - 1] = $mod;
+            }
+        }
+
+        $this->modules[$this->moduleCount - 8][8] = !$test;
+    }
+
+    function createData($typeNumber, $errorCorrectLevel, $dataArray)
+    {
+
+        $rsBlocks = QRRSBlock::getRSBlocks($typeNumber, $errorCorrectLevel);
+
+        $buffer = new QRBitBuffer();
+
+        for ($i = 0; $i < count($dataArray); $i++) {
+            /** @var \QRData $data */
+            $data = $dataArray[$i];
+            $buffer->put($data->getMode(), 4);
+            $buffer->put($data->getLength(), $data->getLengthInBits($typeNumber));
+            $data->write($buffer);
+        }
+
+        $totalDataCount = 0;
+        for ($i = 0; $i < count($rsBlocks); $i++) {
+            $totalDataCount += $rsBlocks[$i]->getDataCount();
+        }
+
+        if ($buffer->getLengthInBits() > $totalDataCount * 8) {
+            trigger_error("code length overflow. ("
+                . $buffer->getLengthInBits()
+                . ">"
+                . $totalDataCount * 8
+                . ")", E_USER_ERROR);
+        }
+
+        // end code.
+        if ($buffer->getLengthInBits() + 4 <= $totalDataCount * 8) {
+            $buffer->put(0, 4);
+        }
+
+        // padding
+        while ($buffer->getLengthInBits() % 8 != 0) {
+            $buffer->putBit(false);
+        }
+
+        // padding
+        while (true) {
+
+            if ($buffer->getLengthInBits() >= $totalDataCount * 8) {
+                break;
+            }
+            $buffer->put(QR_PAD0, 8);
+
+            if ($buffer->getLengthInBits() >= $totalDataCount * 8) {
+                break;
+            }
+            $buffer->put(QR_PAD1, 8);
+        }
+
+        return QRCode::createBytes($buffer, $rsBlocks);
+    }
+
+    /**
+     * @param \QRBitBuffer $buffer
+     * @param \QRRSBlock[] $rsBlocks
+     *
+     * @return array
+     */
+    function createBytes(&$buffer, &$rsBlocks)
+    {
+
+        $offset = 0;
+
+        $maxDcCount = 0;
+        $maxEcCount = 0;
+
+        $dcdata = QRCode::createNullArray(count($rsBlocks));
+        $ecdata = QRCode::createNullArray(count($rsBlocks));
+
+        $rsBlockCount = count($rsBlocks);
+        for ($r = 0; $r < $rsBlockCount; $r++) {
+
+            $dcCount = $rsBlocks[$r]->getDataCount();
+            $ecCount = $rsBlocks[$r]->getTotalCount() - $dcCount;
+
+            $maxDcCount = max($maxDcCount, $dcCount);
+            $maxEcCount = max($maxEcCount, $ecCount);
+
+            $dcdata[$r] = QRCode::createNullArray($dcCount);
+            $dcDataCount = count($dcdata[$r]);
+            for ($i = 0; $i < $dcDataCount; $i++) {
+                $bdata = $buffer->getBuffer();
+                $dcdata[$r][$i] = 0xff & $bdata[$i + $offset];
+            }
+            $offset += $dcCount;
+
+            $rsPoly = QRUtil::getErrorCorrectPolynomial($ecCount);
+            $rawPoly = new QRPolynomial($dcdata[$r], $rsPoly->getLength() - 1);
+
+            $modPoly = $rawPoly->mod($rsPoly);
+            $ecdata[$r] = QRCode::createNullArray($rsPoly->getLength() - 1);
+
+            $ecDataCount = count($ecdata[$r]);
+            for ($i = 0; $i < $ecDataCount; $i++) {
+                $modIndex = $i + $modPoly->getLength() - count($ecdata[$r]);
+                $ecdata[$r][$i] = ($modIndex >= 0) ? $modPoly->get($modIndex) : 0;
+            }
+        }
+
+        $totalCodeCount = 0;
+        for ($i = 0; $i < $rsBlockCount; $i++) {
+            $totalCodeCount += $rsBlocks[$i]->getTotalCount();
+        }
+
+        $data = QRCode::createNullArray($totalCodeCount);
+
+        $index = 0;
+
+        for ($i = 0; $i < $maxDcCount; $i++) {
+            for ($r = 0; $r < $rsBlockCount; $r++) {
+                if ($i < count($dcdata[$r])) {
+                    $data[$index++] = $dcdata[$r][$i];
+                }
+            }
+        }
+
+        for ($i = 0; $i < $maxEcCount; $i++) {
+            for ($r = 0; $r < $rsBlockCount; $r++) {
+                if ($i < count($ecdata[$r])) {
+                    $data[$index++] = $ecdata[$r][$i];
+                }
+            }
+        }
+
+        return $data;
+    }
+
+    static function getMinimumQRCode($data, $errorCorrectLevel = QR_ERROR_CORRECT_LEVEL_L)
+    {
+
+        $mode = QRUtil::getMode($data);
+
+        $qr = new QRCode();
+        $qr->setErrorCorrectLevel($errorCorrectLevel);
+        $qr->addData($data, $mode);
+
+        $qrData = $qr->getData(0);
+        $length = $qrData->getLength();
+
+        for ($typeNumber = 1; $typeNumber <= 40; $typeNumber++) {
+            if ($length <= QRUtil::getMaxLength($typeNumber, $mode, $errorCorrectLevel)) {
+                $qr->setTypeNumber($typeNumber);
+                break;
+            }
+        }
+
+        $qr->make();
+
+        return $qr;
+    }
+
+    // added $fg (foreground), $bg (background), and $bgtrans (use transparent bg) parameters
+    // also added some simple error checking on parameters
+    // updated 2015.07.27 ~ DoktorJ
+    function createImage($size = 2, $margin = 2, $fg = 0x000000, $bg = 0xFFFFFF, $bgtrans = false)
+    {
+
+        // size/margin EC
+        if (!is_numeric($size)) $size = 2;
+        if (!is_numeric($margin)) $margin = 2;
+        if ($size < 1) $size = 1;
+        if ($margin < 0) $margin = 0;
+
+        $image_size = $this->getModuleCount() * $size + $margin * 2;
+
+        $image = imagecreatetruecolor($image_size, $image_size);
+
+        // fg/bg EC
+        if ($fg < 0 || $fg > 0xFFFFFF) $fg = 0x0;
+        if ($bg < 0 || $bg > 0xFFFFFF) $bg = 0xFFFFFF;
+
+        // convert hexadecimal RGB to arrays for imagecolorallocate
+        $fgrgb = $this->hex2rgb($fg);
+        $bgrgb = $this->hex2rgb($bg);
+
+        // replace $black and $white with $fgc and $bgc
+        $fgc = imagecolorallocate($image, $fgrgb['r'], $fgrgb['g'], $fgrgb['b']);
+        $bgc = imagecolorallocate($image, $bgrgb['r'], $bgrgb['g'], $bgrgb['b']);
+        if ($bgtrans) imagecolortransparent($image, $bgc);
+
+        // update $white to $bgc
+        imagefilledrectangle($image, 0, 0, $image_size, $image_size, $bgc);
+
+        for ($r = 0; $r < $this->getModuleCount(); $r++) {
+            for ($c = 0; $c < $this->getModuleCount(); $c++) {
+                if ($this->isDark($r, $c)) {
+
+                    // update $black to $fgc
+                    imagefilledrectangle($image,
+                        $margin + $c * $size,
+                        $margin + $r * $size,
+                        $margin + ($c + 1) * $size - 1,
+                        $margin + ($r + 1) * $size - 1,
+                        $fgc);
+                }
+            }
+        }
+
+        return $image;
+    }
+
+    function printHTML($size = "2px")
+    {
+
+        $style = "border-style:none;border-collapse:collapse;margin:0px;padding:0px;";
+
+        print("<table style='$style'>");
+
+        for ($r = 0; $r < $this->getModuleCount(); $r++) {
+
+            print("<tr style='$style'>");
+
+            for ($c = 0; $c < $this->getModuleCount(); $c++) {
+                $color = $this->isDark($r, $c) ? "#000000" : "#ffffff";
+                print("<td style='$style;width:$size;height:$size;background-color:$color'></td>");
+            }
+
+            print("</tr>");
+        }
+
+        print("</table>");
+    }
+}
+
+//---------------------------------------------------------------
+// QRUtil
+//---------------------------------------------------------------
+
+define("QR_G15", (1 << 10) | (1 << 8) | (1 << 5)
+    | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0));
+
+define("QR_G18", (1 << 12) | (1 << 11) | (1 << 10)
+    | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0));
+
+define("QR_G15_MASK", (1 << 14) | (1 << 12) | (1 << 10)
+    | (1 << 4) | (1 << 1));
+
+class QRUtil
+{
+
+    static $QR_MAX_LENGTH = array(
+        array(array(41, 25, 17, 10), array(34, 20, 14, 8), array(27, 16, 11, 7), array(17, 10, 7, 4)),
+        array(array(77, 47, 32, 20), array(63, 38, 26, 16), array(48, 29, 20, 12), array(34, 20, 14, 8)),
+        array(array(127, 77, 53, 32), array(101, 61, 42, 26), array(77, 47, 32, 20), array(58, 35, 24, 15)),
+        array(array(187, 114, 78, 48), array(149, 90, 62, 38), array(111, 67, 46, 28), array(82, 50, 34, 21)),
+        array(array(255, 154, 106, 65), array(202, 122, 84, 52), array(144, 87, 60, 37), array(106, 64, 44, 27)),
+        array(array(322, 195, 134, 82), array(255, 154, 106, 65), array(178, 108, 74, 45), array(139, 84, 58, 36)),
+        array(array(370, 224, 154, 95), array(293, 178, 122, 75), array(207, 125, 86, 53), array(154, 93, 64, 39)),
+        array(array(461, 279, 192, 118), array(365, 221, 152, 93), array(259, 157, 108, 66), array(202, 122, 84, 52)),
+        array(array(552, 335, 230, 141), array(432, 262, 180, 111), array(312, 189, 130, 80), array(235, 143, 98, 60)),
+        array(array(652, 395, 271, 167), array(513, 311, 213, 131), array(364, 221, 151, 93), array(288, 174, 119, 74))
+    );
+
+    static $QR_PATTERN_POSITION_TABLE = array(
+        array(),
+        array(6, 18),
+        array(6, 22),
+        array(6, 26),
+        array(6, 30),
+        array(6, 34),
+        array(6, 22, 38),
+        array(6, 24, 42),
+        array(6, 26, 46),
+        array(6, 28, 50),
+        array(6, 30, 54),
+        array(6, 32, 58),
+        array(6, 34, 62),
+        array(6, 26, 46, 66),
+        array(6, 26, 48, 70),
+        array(6, 26, 50, 74),
+        array(6, 30, 54, 78),
+        array(6, 30, 56, 82),
+        array(6, 30, 58, 86),
+        array(6, 34, 62, 90),
+        array(6, 28, 50, 72, 94),
+        array(6, 26, 50, 74, 98),
+        array(6, 30, 54, 78, 102),
+        array(6, 28, 54, 80, 106),
+        array(6, 32, 58, 84, 110),
+        array(6, 30, 58, 86, 114),
+        array(6, 34, 62, 90, 118),
+        array(6, 26, 50, 74, 98, 122),
+        array(6, 30, 54, 78, 102, 126),
+        array(6, 26, 52, 78, 104, 130),
+        array(6, 30, 56, 82, 108, 134),
+        array(6, 34, 60, 86, 112, 138),
+        array(6, 30, 58, 86, 114, 142),
+        array(6, 34, 62, 90, 118, 146),
+        array(6, 30, 54, 78, 102, 126, 150),
+        array(6, 24, 50, 76, 102, 128, 154),
+        array(6, 28, 54, 80, 106, 132, 158),
+        array(6, 32, 58, 84, 110, 136, 162),
+        array(6, 26, 54, 82, 110, 138, 166),
+        array(6, 30, 58, 86, 114, 142, 170)
+    );
+
+    static function getPatternPosition($typeNumber)
+    {
+        return self::$QR_PATTERN_POSITION_TABLE[$typeNumber - 1];
+    }
+
+    static function getMaxLength($typeNumber, $mode, $errorCorrectLevel)
+    {
+
+        $t = $typeNumber - 1;
+        $e = 0;
+        $m = 0;
+
+        switch ($errorCorrectLevel) {
+            case QR_ERROR_CORRECT_LEVEL_L :
+                $e = 0;
+                break;
+            case QR_ERROR_CORRECT_LEVEL_M :
+                $e = 1;
+                break;
+            case QR_ERROR_CORRECT_LEVEL_Q :
+                $e = 2;
+                break;
+            case QR_ERROR_CORRECT_LEVEL_H :
+                $e = 3;
+                break;
+            default :
+                trigger_error("e:$errorCorrectLevel", E_USER_ERROR);
+        }
+
+        switch ($mode) {
+            case QR_MODE_NUMBER    :
+                $m = 0;
+                break;
+            case QR_MODE_ALPHA_NUM :
+                $m = 1;
+                break;
+            case QR_MODE_8BIT_BYTE :
+                $m = 2;
+                break;
+            case QR_MODE_KANJI     :
+                $m = 3;
+                break;
+            default :
+                trigger_error("m:$mode", E_USER_ERROR);
+        }
+
+        return self::$QR_MAX_LENGTH[$t][$e][$m];
+    }
+
+    static function getErrorCorrectPolynomial($errorCorrectLength)
+    {
+
+        $a = new QRPolynomial(array(1));
+
+        for ($i = 0; $i < $errorCorrectLength; $i++) {
+            $a = $a->multiply(new QRPolynomial(array(1, QRMath::gexp($i))));
+        }
+
+        return $a;
+    }
+
+    static function getMask($maskPattern, $i, $j)
+    {
+
+        switch ($maskPattern) {
+
+            case QR_MASK_PATTERN000 :
+                return ($i + $j) % 2 == 0;
+            case QR_MASK_PATTERN001 :
+                return $i % 2 == 0;
+            case QR_MASK_PATTERN010 :
+                return $j % 3 == 0;
+            case QR_MASK_PATTERN011 :
+                return ($i + $j) % 3 == 0;
+            case QR_MASK_PATTERN100 :
+                return (floor($i / 2) + floor($j / 3)) % 2 == 0;
+            case QR_MASK_PATTERN101 :
+                return ($i * $j) % 2 + ($i * $j) % 3 == 0;
+            case QR_MASK_PATTERN110 :
+                return (($i * $j) % 2 + ($i * $j) % 3) % 2 == 0;
+            case QR_MASK_PATTERN111 :
+                return (($i * $j) % 3 + ($i + $j) % 2) % 2 == 0;
+
+            default :
+                trigger_error("mask:$maskPattern", E_USER_ERROR);
+        }
+    }
+
+    /**
+     * @param \QRCode $qrCode
+     *
+     * @return float|int
+     */
+    static function getLostPoint($qrCode)
+    {
+
+        $moduleCount = $qrCode->getModuleCount();
+
+        $lostPoint = 0;
+
+
+        // LEVEL1
+
+        for ($row = 0; $row < $moduleCount; $row++) {
+
+            for ($col = 0; $col < $moduleCount; $col++) {
+
+                $sameCount = 0;
+                $dark = $qrCode->isDark($row, $col);
+
+                for ($r = -1; $r <= 1; $r++) {
+
+                    if ($row + $r < 0 || $moduleCount <= $row + $r) {
+                        continue;
+                    }
+
+                    for ($c = -1; $c <= 1; $c++) {
+
+                        if (($col + $c < 0 || $moduleCount <= $col + $c) || ($r == 0 && $c == 0)) {
+                            continue;
+                        }
+
+                        if ($dark == $qrCode->isDark($row + $r, $col + $c)) {
+                            $sameCount++;
+                        }
+                    }
+                }
+
+                if ($sameCount > 5) {
+                    $lostPoint += (3 + $sameCount - 5);
+                }
+            }
+        }
+
+        // LEVEL2
+
+        for ($row = 0; $row < $moduleCount - 1; $row++) {
+            for ($col = 0; $col < $moduleCount - 1; $col++) {
+                $count = 0;
+                if ($qrCode->isDark($row, $col)) $count++;
+                if ($qrCode->isDark($row + 1, $col)) $count++;
+                if ($qrCode->isDark($row, $col + 1)) $count++;
+                if ($qrCode->isDark($row + 1, $col + 1)) $count++;
+                if ($count == 0 || $count == 4) {
+                    $lostPoint += 3;
+                }
+            }
+        }
+
+        // LEVEL3
+
+        for ($row = 0; $row < $moduleCount; $row++) {
+            for ($col = 0; $col < $moduleCount - 6; $col++) {
+                if ($qrCode->isDark($row, $col)
+                    && !$qrCode->isDark($row, $col + 1)
+                    && $qrCode->isDark($row, $col + 2)
+                    && $qrCode->isDark($row, $col + 3)
+                    && $qrCode->isDark($row, $col + 4)
+                    && !$qrCode->isDark($row, $col + 5)
+                    && $qrCode->isDark($row, $col + 6)) {
+                    $lostPoint += 40;
+                }
+            }
+        }
+
+        for ($col = 0; $col < $moduleCount; $col++) {
+            for ($row = 0; $row < $moduleCount - 6; $row++) {
+                if ($qrCode->isDark($row, $col)
+                    && !$qrCode->isDark($row + 1, $col)
+                    && $qrCode->isDark($row + 2, $col)
+                    && $qrCode->isDark($row + 3, $col)
+                    && $qrCode->isDark($row + 4, $col)
+                    && !$qrCode->isDark($row + 5, $col)
+                    && $qrCode->isDark($row + 6, $col)) {
+                    $lostPoint += 40;
+                }
+            }
+        }
+
+        // LEVEL4
+
+        $darkCount = 0;
+
+        for ($col = 0; $col < $moduleCount; $col++) {
+            for ($row = 0; $row < $moduleCount; $row++) {
+                if ($qrCode->isDark($row, $col)) {
+                    $darkCount++;
+                }
+            }
+        }
+
+        $ratio = abs(100 * $darkCount / $moduleCount / $moduleCount - 50) / 5;
+        $lostPoint += $ratio * 10;
+
+        return $lostPoint;
+    }
+
+    static function getMode($s)
+    {
+        if (QRUtil::isAlphaNum($s)) {
+            if (QRUtil::isNumber($s)) {
+                return QR_MODE_NUMBER;
+            }
+            return QR_MODE_ALPHA_NUM;
+        } else if (QRUtil::isKanji($s)) {
+            return QR_MODE_KANJI;
+        } else {
+            return QR_MODE_8BIT_BYTE;
+        }
+    }
+
+    static function isNumber($s)
+    {
+        for ($i = 0; $i < strlen($s); $i++) {
+            $c = ord($s[$i]);
+            if (!(QRUtil::toCharCode('0') <= $c && $c <= QRUtil::toCharCode('9'))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    static function isAlphaNum($s)
+    {
+        for ($i = 0; $i < strlen($s); $i++) {
+            $c = ord($s[$i]);
+            if (!(QRUtil::toCharCode('0') <= $c && $c <= QRUtil::toCharCode('9'))
+                && !(QRUtil::toCharCode('A') <= $c && $c <= QRUtil::toCharCode('Z'))
+                && strpos(" $%*+-./:", $s[$i]) === false) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    static function isKanji($s)
+    {
+
+        $data = $s;
+
+        $i = 0;
+
+        while ($i + 1 < strlen($data)) {
+
+            $c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
+
+            if (!(0x8140 <= $c && $c <= 0x9FFC) && !(0xE040 <= $c && $c <= 0xEBBF)) {
+                return false;
+            }
+
+            $i += 2;
+        }
+
+        if ($i < strlen($data)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    static function toCharCode($s)
+    {
+        return ord($s[0]);
+    }
+
+    static function getBCHTypeInfo($data)
+    {
+        $d = $data << 10;
+        while (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G15) >= 0) {
+            $d ^= (QR_G15 << (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G15)));
+        }
+        return (($data << 10) | $d) ^ QR_G15_MASK;
+    }
+
+    static function getBCHTypeNumber($data)
+    {
+        $d = $data << 12;
+        while (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G18) >= 0) {
+            $d ^= (QR_G18 << (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G18)));
+        }
+        return ($data << 12) | $d;
+    }
+
+    static function getBCHDigit($data)
+    {
+
+        $digit = 0;
+
+        while ($data != 0) {
+            $digit++;
+            $data >>= 1;
+        }
+
+        return $digit;
+    }
+}
+
+//---------------------------------------------------------------
+// QRRSBlock
+//---------------------------------------------------------------
+
+class QRRSBlock
+{
+
+    var $totalCount;
+    var $dataCount;
+
+    static $QR_RS_BLOCK_TABLE = array(
+
+        // L
+        // M
+        // Q
+        // H
+
+        // 1
+        array(1, 26, 19),
+        array(1, 26, 16),
+        array(1, 26, 13),
+        array(1, 26, 9),
+
+        // 2
+        array(1, 44, 34),
+        array(1, 44, 28),
+        array(1, 44, 22),
+        array(1, 44, 16),
+
+        // 3
+        array(1, 70, 55),
+        array(1, 70, 44),
+        array(2, 35, 17),
+        array(2, 35, 13),
+
+        // 4
+        array(1, 100, 80),
+        array(2, 50, 32),
+        array(2, 50, 24),
+        array(4, 25, 9),
+
+        // 5
+        array(1, 134, 108),
+        array(2, 67, 43),
+        array(2, 33, 15, 2, 34, 16),
+        array(2, 33, 11, 2, 34, 12),
+
+        // 6
+        array(2, 86, 68),
+        array(4, 43, 27),
+        array(4, 43, 19),
+        array(4, 43, 15),
+
+        // 7
+        array(2, 98, 78),
+        array(4, 49, 31),
+        array(2, 32, 14, 4, 33, 15),
+        array(4, 39, 13, 1, 40, 14),
+
+        // 8
+        array(2, 121, 97),
+        array(2, 60, 38, 2, 61, 39),
+        array(4, 40, 18, 2, 41, 19),
+        array(4, 40, 14, 2, 41, 15),
+
+        // 9
+        array(2, 146, 116),
+        array(3, 58, 36, 2, 59, 37),
+        array(4, 36, 16, 4, 37, 17),
+        array(4, 36, 12, 4, 37, 13),
+
+        // 10
+        array(2, 86, 68, 2, 87, 69),
+        array(4, 69, 43, 1, 70, 44),
+        array(6, 43, 19, 2, 44, 20),
+        array(6, 43, 15, 2, 44, 16),
+
+        // 11
+        array(4, 101, 81),
+        array(1, 80, 50, 4, 81, 51),
+        array(4, 50, 22, 4, 51, 23),
+        array(3, 36, 12, 8, 37, 13),
+
+        // 12
+        array(2, 116, 92, 2, 117, 93),
+        array(6, 58, 36, 2, 59, 37),
+        array(4, 46, 20, 6, 47, 21),
+        array(7, 42, 14, 4, 43, 15),
+
+        // 13
+        array(4, 133, 107),
+        array(8, 59, 37, 1, 60, 38),
+        array(8, 44, 20, 4, 45, 21),
+        array(12, 33, 11, 4, 34, 12),
+
+        // 14
+        array(3, 145, 115, 1, 146, 116),
+        array(4, 64, 40, 5, 65, 41),
+        array(11, 36, 16, 5, 37, 17),
+        array(11, 36, 12, 5, 37, 13),
+
+        // 15
+        array(5, 109, 87, 1, 110, 88),
+        array(5, 65, 41, 5, 66, 42),
+        array(5, 54, 24, 7, 55, 25),
+        array(11, 36, 12, 7, 37, 13),
+
+        // 16
+        array(5, 122, 98, 1, 123, 99),
+        array(7, 73, 45, 3, 74, 46),
+        array(15, 43, 19, 2, 44, 20),
+        array(3, 45, 15, 13, 46, 16),
+
+        // 17
+        array(1, 135, 107, 5, 136, 108),
+        array(10, 74, 46, 1, 75, 47),
+        array(1, 50, 22, 15, 51, 23),
+        array(2, 42, 14, 17, 43, 15),
+
+        // 18
+        array(5, 150, 120, 1, 151, 121),
+        array(9, 69, 43, 4, 70, 44),
+        array(17, 50, 22, 1, 51, 23),
+        array(2, 42, 14, 19, 43, 15),
+
+        // 19
+        array(3, 141, 113, 4, 142, 114),
+        array(3, 70, 44, 11, 71, 45),
+        array(17, 47, 21, 4, 48, 22),
+        array(9, 39, 13, 16, 40, 14),
+
+        // 20
+        array(3, 135, 107, 5, 136, 108),
+        array(3, 67, 41, 13, 68, 42),
+        array(15, 54, 24, 5, 55, 25),
+        array(15, 43, 15, 10, 44, 16),
+
+        // 21
+        array(4, 144, 116, 4, 145, 117),
+        array(17, 68, 42),
+        array(17, 50, 22, 6, 51, 23),
+        array(19, 46, 16, 6, 47, 17),
+
+        // 22
+        array(2, 139, 111, 7, 140, 112),
+        array(17, 74, 46),
+        array(7, 54, 24, 16, 55, 25),
+        array(34, 37, 13),
+
+        // 23
+        array(4, 151, 121, 5, 152, 122),
+        array(4, 75, 47, 14, 76, 48),
+        array(11, 54, 24, 14, 55, 25),
+        array(16, 45, 15, 14, 46, 16),
+
+        // 24
+        array(6, 147, 117, 4, 148, 118),
+        array(6, 73, 45, 14, 74, 46),
+        array(11, 54, 24, 16, 55, 25),
+        array(30, 46, 16, 2, 47, 17),
+
+        // 25
+        array(8, 132, 106, 4, 133, 107),
+        array(8, 75, 47, 13, 76, 48),
+        array(7, 54, 24, 22, 55, 25),
+        array(22, 45, 15, 13, 46, 16),
+
+        // 26
+        array(10, 142, 114, 2, 143, 115),
+        array(19, 74, 46, 4, 75, 47),
+        array(28, 50, 22, 6, 51, 23),
+        array(33, 46, 16, 4, 47, 17),
+
+        // 27
+        array(8, 152, 122, 4, 153, 123),
+        array(22, 73, 45, 3, 74, 46),
+        array(8, 53, 23, 26, 54, 24),
+        array(12, 45, 15, 28, 46, 16),
+
+        // 28
+        array(3, 147, 117, 10, 148, 118),
+        array(3, 73, 45, 23, 74, 46),
+        array(4, 54, 24, 31, 55, 25),
+        array(11, 45, 15, 31, 46, 16),
+
+        // 29
+        array(7, 146, 116, 7, 147, 117),
+        array(21, 73, 45, 7, 74, 46),
+        array(1, 53, 23, 37, 54, 24),
+        array(19, 45, 15, 26, 46, 16),
+
+        // 30
+        array(5, 145, 115, 10, 146, 116),
+        array(19, 75, 47, 10, 76, 48),
+        array(15, 54, 24, 25, 55, 25),
+        array(23, 45, 15, 25, 46, 16),
+
+        // 31
+        array(13, 145, 115, 3, 146, 116),
+        array(2, 74, 46, 29, 75, 47),
+        array(42, 54, 24, 1, 55, 25),
+        array(23, 45, 15, 28, 46, 16),
+
+        // 32
+        array(17, 145, 115),
+        array(10, 74, 46, 23, 75, 47),
+        array(10, 54, 24, 35, 55, 25),
+        array(19, 45, 15, 35, 46, 16),
+
+        // 33
+        array(17, 145, 115, 1, 146, 116),
+        array(14, 74, 46, 21, 75, 47),
+        array(29, 54, 24, 19, 55, 25),
+        array(11, 45, 15, 46, 46, 16),
+
+        // 34
+        array(13, 145, 115, 6, 146, 116),
+        array(14, 74, 46, 23, 75, 47),
+        array(44, 54, 24, 7, 55, 25),
+        array(59, 46, 16, 1, 47, 17),
+
+        // 35
+        array(12, 151, 121, 7, 152, 122),
+        array(12, 75, 47, 26, 76, 48),
+        array(39, 54, 24, 14, 55, 25),
+        array(22, 45, 15, 41, 46, 16),
+
+        // 36
+        array(6, 151, 121, 14, 152, 122),
+        array(6, 75, 47, 34, 76, 48),
+        array(46, 54, 24, 10, 55, 25),
+        array(2, 45, 15, 64, 46, 16),
+
+        // 37
+        array(17, 152, 122, 4, 153, 123),
+        array(29, 74, 46, 14, 75, 47),
+        array(49, 54, 24, 10, 55, 25),
+        array(24, 45, 15, 46, 46, 16),
+
+        // 38
+        array(4, 152, 122, 18, 153, 123),
+        array(13, 74, 46, 32, 75, 47),
+        array(48, 54, 24, 14, 55, 25),
+        array(42, 45, 15, 32, 46, 16),
+
+        // 39
+        array(20, 147, 117, 4, 148, 118),
+        array(40, 75, 47, 7, 76, 48),
+        array(43, 54, 24, 22, 55, 25),
+        array(10, 45, 15, 67, 46, 16),
+
+        // 40
+        array(19, 148, 118, 6, 149, 119),
+        array(18, 75, 47, 31, 76, 48),
+        array(34, 54, 24, 34, 55, 25),
+        array(20, 45, 15, 61, 46, 16)
+
+    );
+
+    function __construct($totalCount, $dataCount)
+    {
+        $this->totalCount = $totalCount;
+        $this->dataCount = $dataCount;
+    }
+
+    function getDataCount()
+    {
+        return $this->dataCount;
+    }
+
+    function getTotalCount()
+    {
+        return $this->totalCount;
+    }
+
+    static function getRSBlocks($typeNumber, $errorCorrectLevel)
+    {
+
+        $rsBlock = QRRSBlock::getRsBlockTable($typeNumber, $errorCorrectLevel);
+        $length = count($rsBlock) / 3;
+
+        $list = array();
+
+        for ($i = 0; $i < $length; $i++) {
+
+            $count = $rsBlock[$i * 3 + 0];
+            $totalCount = $rsBlock[$i * 3 + 1];
+            $dataCount = $rsBlock[$i * 3 + 2];
+
+            for ($j = 0; $j < $count; $j++) {
+                $list[] = new QRRSBlock($totalCount, $dataCount);
+            }
+        }
+
+        return $list;
+    }
+
+    static function getRsBlockTable($typeNumber, $errorCorrectLevel)
+    {
+
+        switch ($errorCorrectLevel) {
+            case QR_ERROR_CORRECT_LEVEL_L :
+                return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 0];
+            case QR_ERROR_CORRECT_LEVEL_M :
+                return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 1];
+            case QR_ERROR_CORRECT_LEVEL_Q :
+                return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 2];
+            case QR_ERROR_CORRECT_LEVEL_H :
+                return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 3];
+            default :
+                trigger_error("tn:$typeNumber/ecl:$errorCorrectLevel", E_USER_ERROR);
+        }
+    }
+}
+
+//---------------------------------------------------------------
+// QRNumber
+//---------------------------------------------------------------
+
+class QRNumber extends QRData
+{
+
+    function __construct($data)
+    {
+        parent::__construct(QR_MODE_NUMBER, $data);
+    }
+
+    function write(&$buffer)
+    {
+
+        $data = $this->getData();
+
+        $i = 0;
+
+        while ($i + 2 < strlen($data)) {
+            $num = QRNumber::parseInt(substr($data, $i, 3));
+            $buffer->put($num, 10);
+            $i += 3;
+        }
+
+        if ($i < strlen($data)) {
+
+            if (strlen($data) - $i == 1) {
+                $num = QRNumber::parseInt(substr($data, $i, $i + 1));
+                $buffer->put($num, 4);
+            } else if (strlen($data) - $i == 2) {
+                $num = QRNumber::parseInt(substr($data, $i, $i + 2));
+                $buffer->put($num, 7);
+            }
+        }
+    }
+
+    static function parseInt($s)
+    {
+
+        $num = 0;
+        for ($i = 0; $i < strlen($s); $i++) {
+            $num = $num * 10 + QRNumber::parseIntAt(ord($s[$i]));
+        }
+        return $num;
+    }
+
+    static function parseIntAt($c)
+    {
+
+        if (QRUtil::toCharCode('0') <= $c && $c <= QRUtil::toCharCode('9')) {
+            return $c - QRUtil::toCharCode('0');
+        }
+
+        trigger_error("illegal char : $c", E_USER_ERROR);
+    }
+}
+
+//---------------------------------------------------------------
+// QRKanji
+//---------------------------------------------------------------
+
+class QRKanji extends QRData
+{
+
+    function __construct($data)
+    {
+        parent::__construct(QR_MODE_KANJI, $data);
+    }
+
+    function write(&$buffer)
+    {
+
+        $data = $this->getData();
+
+        $i = 0;
+
+        while ($i + 1 < strlen($data)) {
+
+            $c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
+
+            if (0x8140 <= $c && $c <= 0x9FFC) {
+                $c -= 0x8140;
+            } else if (0xE040 <= $c && $c <= 0xEBBF) {
+                $c -= 0xC140;
+            } else {
+                trigger_error("illegal char at " . ($i + 1) . "/$c", E_USER_ERROR);
+            }
+
+            $c = (($c >> 8) & 0xff) * 0xC0 + ($c & 0xff);
+
+            $buffer->put($c, 13);
+
+            $i += 2;
+        }
+
+        if ($i < strlen($data)) {
+            trigger_error("illegal char at " . ($i + 1), E_USER_ERROR);
+        }
+    }
+
+    function getLength()
+    {
+        return floor(strlen($this->getData()) / 2);
+    }
+}
+
+//---------------------------------------------------------------
+// QRAlphaNum
+//---------------------------------------------------------------
+
+class QRAlphaNum extends QRData
+{
+
+    function __construct($data)
+    {
+        parent::__construct(QR_MODE_ALPHA_NUM, $data);
+    }
+
+    function write(&$buffer)
+    {
+
+        $i = 0;
+        $c = $this->getData();
+
+        while ($i + 1 < strlen($c)) {
+            $buffer->put(QRAlphaNum::getCode(ord($c[$i])) * 45
+                + QRAlphaNum::getCode(ord($c[$i + 1])), 11);
+            $i += 2;
+        }
+
+        if ($i < strlen($c)) {
+            $buffer->put(QRAlphaNum::getCode(ord($c[$i])), 6);
+        }
+    }
+
+    static function getCode($c)
+    {
+
+        if (QRUtil::toCharCode('0') <= $c
+            && $c <= QRUtil::toCharCode('9')) {
+            return $c - QRUtil::toCharCode('0');
+        } else if (QRUtil::toCharCode('A') <= $c
+            && $c <= QRUtil::toCharCode('Z')) {
+            return $c - QRUtil::toCharCode('A') + 10;
+        } else {
+            switch ($c) {
+                case QRUtil::toCharCode(' ') :
+                    return 36;
+                case QRUtil::toCharCode('$') :
+                    return 37;
+                case QRUtil::toCharCode('%') :
+                    return 38;
+                case QRUtil::toCharCode('*') :
+                    return 39;
+                case QRUtil::toCharCode('+') :
+                    return 40;
+                case QRUtil::toCharCode('-') :
+                    return 41;
+                case QRUtil::toCharCode('.') :
+                    return 42;
+                case QRUtil::toCharCode('/') :
+                    return 43;
+                case QRUtil::toCharCode(':') :
+                    return 44;
+                default :
+                    trigger_error("illegal char : $c", E_USER_ERROR);
+            }
+        }
+
+    }
+}
+
+//---------------------------------------------------------------
+// QR8BitByte
+//---------------------------------------------------------------
+
+class QR8BitByte extends QRData
+{
+
+    function __construct($data)
+    {
+        parent::__construct(QR_MODE_8BIT_BYTE, $data);
+    }
+
+    function write(&$buffer)
+    {
+
+        $data = $this->getData();
+        for ($i = 0; $i < strlen($data); $i++) {
+            $buffer->put(ord($data[$i]), 8);
+        }
+    }
+
+}
+
+//---------------------------------------------------------------
+// QRData
+//---------------------------------------------------------------
+
+abstract class QRData
+{
+
+    var $mode;
+
+    var $data;
+
+    function __construct($mode, $data)
+    {
+        $this->mode = $mode;
+        $this->data = $data;
+    }
+
+    function getMode()
+    {
+        return $this->mode;
+    }
+
+    function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * @return int
+     */
+    function getLength()
+    {
+        return strlen($this->getData());
+    }
+
+    /**
+     * @param \QRBitBuffer $buffer
+     */
+    abstract function write(&$buffer);
+
+    function getLengthInBits($type)
+    {
+
+        if (1 <= $type && $type < 10) {
+
+            // 1 - 9
+
+            switch ($this->mode) {
+                case QR_MODE_NUMBER     :
+                    return 10;
+                case QR_MODE_ALPHA_NUM     :
+                    return 9;
+                case QR_MODE_8BIT_BYTE    :
+                    return 8;
+                case QR_MODE_KANJI      :
+                    return 8;
+                default :
+                    trigger_error("mode:$this->mode", E_USER_ERROR);
+            }
+
+        } else if ($type < 27) {
+
+            // 10 - 26
+
+            switch ($this->mode) {
+                case QR_MODE_NUMBER     :
+                    return 12;
+                case QR_MODE_ALPHA_NUM     :
+                    return 11;
+                case QR_MODE_8BIT_BYTE    :
+                    return 16;
+                case QR_MODE_KANJI      :
+                    return 10;
+                default :
+                    trigger_error("mode:$this->mode", E_USER_ERROR);
+            }
+
+        } else if ($type < 41) {
+
+            // 27 - 40
+
+            switch ($this->mode) {
+                case QR_MODE_NUMBER     :
+                    return 14;
+                case QR_MODE_ALPHA_NUM    :
+                    return 13;
+                case QR_MODE_8BIT_BYTE    :
+                    return 16;
+                case QR_MODE_KANJI      :
+                    return 12;
+                default :
+                    trigger_error("mode:$this->mode", E_USER_ERROR);
+            }
+
+        } else {
+            trigger_error("mode:$this->mode", E_USER_ERROR);
+        }
+    }
+
+}
+
+//---------------------------------------------------------------
+// QRMath
+//---------------------------------------------------------------
+
+class QRMath
+{
+
+    static $QR_MATH_EXP_TABLE = null;
+    static $QR_MATH_LOG_TABLE = null;
+
+    static function init()
+    {
+
+        self::$QR_MATH_EXP_TABLE = QRMath::createNumArray(256);
+
+        for ($i = 0; $i < 8; $i++) {
+            self::$QR_MATH_EXP_TABLE[$i] = 1 << $i;
+        }
+
+        for ($i = 8; $i < 256; $i++) {
+            self::$QR_MATH_EXP_TABLE[$i] = self::$QR_MATH_EXP_TABLE[$i - 4]
+                ^ self::$QR_MATH_EXP_TABLE[$i - 5]
+                ^ self::$QR_MATH_EXP_TABLE[$i - 6]
+                ^ self::$QR_MATH_EXP_TABLE[$i - 8];
+        }
+
+        self::$QR_MATH_LOG_TABLE = QRMath::createNumArray(256);
+
+        for ($i = 0; $i < 255; $i++) {
+            self::$QR_MATH_LOG_TABLE[self::$QR_MATH_EXP_TABLE[$i]] = $i;
+        }
+    }
+
+    static function createNumArray($length)
+    {
+        $num_array = array();
+        for ($i = 0; $i < $length; $i++) {
+            $num_array[] = 0;
+        }
+        return $num_array;
+    }
+
+    static function glog($n)
+    {
+
+        if ($n < 1) {
+            trigger_error("log($n)", E_USER_ERROR);
+        }
+
+        return self::$QR_MATH_LOG_TABLE[$n];
+    }
+
+    static function gexp($n)
+    {
+
+        while ($n < 0) {
+            $n += 255;
+        }
+
+        while ($n >= 256) {
+            $n -= 255;
+        }
+
+        return self::$QR_MATH_EXP_TABLE[$n];
+    }
+}
+
+// init static table
+QRMath::init();
+
+//---------------------------------------------------------------
+// QRPolynomial
+//---------------------------------------------------------------
+
+class QRPolynomial
+{
+
+    var $num;
+
+    function __construct($num, $shift = 0)
+    {
+
+        $offset = 0;
+
+        while ($offset < count($num) && $num[$offset] == 0) {
+            $offset++;
+        }
+
+        $this->num = QRMath::createNumArray(count($num) - $offset + $shift);
+        for ($i = 0; $i < count($num) - $offset; $i++) {
+            $this->num[$i] = $num[$i + $offset];
+        }
+    }
+
+    function get($index)
+    {
+        return $this->num[$index];
+    }
+
+    function getLength()
+    {
+        return count($this->num);
+    }
+
+    // PHP5
+    function __toString()
+    {
+        return $this->toString();
+    }
+
+    function toString()
+    {
+
+        $buffer = "";
+
+        for ($i = 0; $i < $this->getLength(); $i++) {
+            if ($i > 0) {
+                $buffer .= ",";
+            }
+            $buffer .= $this->get($i);
+        }
+
+        return $buffer;
+    }
+
+    function toLogString()
+    {
+
+        $buffer = "";
+
+        for ($i = 0; $i < $this->getLength(); $i++) {
+            if ($i > 0) {
+                $buffer .= ",";
+            }
+            $buffer .= QRMath::glog($this->get($i));
+        }
+
+        return $buffer;
+    }
+
+    /**
+     * @param \QRPolynomial $e
+     *
+     * @return \QRPolynomial
+     */
+    function multiply($e)
+    {
+
+        $num = QRMath::createNumArray($this->getLength() + $e->getLength() - 1);
+
+        for ($i = 0; $i < $this->getLength(); $i++) {
+            $vi = QRMath::glog($this->get($i));
+
+            for ($j = 0; $j < $e->getLength(); $j++) {
+                $num[$i + $j] ^= QRMath::gexp($vi + QRMath::glog($e->get($j)));
+            }
+        }
+
+        return new QRPolynomial($num);
+    }
+
+    /**
+     * @param \QRPolynomial $e
+     *
+     * @return $this|\QRPolynomial
+     */
+    function mod($e)
+    {
+
+        if ($this->getLength() - $e->getLength() < 0) {
+            return $this;
+        }
+
+        $ratio = QRMath::glog($this->get(0)) - QRMath::glog($e->get(0));
+
+        $num = QRMath::createNumArray($this->getLength());
+        for ($i = 0; $i < $this->getLength(); $i++) {
+            $num[$i] = $this->get($i);
+        }
+
+        for ($i = 0; $i < $e->getLength(); $i++) {
+            $num[$i] ^= QRMath::gexp(QRMath::glog($e->get($i)) + $ratio);
+        }
+
+        $newPolynomial = new QRPolynomial($num);
+        return $newPolynomial->mod($e);
+    }
+}
+
+//---------------------------------------------------------------
+// Mode
+//---------------------------------------------------------------
+
+define("QR_MODE_NUMBER", 1 << 0);
+define("QR_MODE_ALPHA_NUM", 1 << 1);
+define("QR_MODE_8BIT_BYTE", 1 << 2);
+define("QR_MODE_KANJI", 1 << 3);
+
+//---------------------------------------------------------------
+// MaskPattern
+//---------------------------------------------------------------
+
+define("QR_MASK_PATTERN000", 0);
+define("QR_MASK_PATTERN001", 1);
+define("QR_MASK_PATTERN010", 2);
+define("QR_MASK_PATTERN011", 3);
+define("QR_MASK_PATTERN100", 4);
+define("QR_MASK_PATTERN101", 5);
+define("QR_MASK_PATTERN110", 6);
+define("QR_MASK_PATTERN111", 7);
+
+//---------------------------------------------------------------
+// ErrorCorrectLevel
+
+// 7%.
+define("QR_ERROR_CORRECT_LEVEL_L", 1);
+// 15%.
+define("QR_ERROR_CORRECT_LEVEL_M", 0);
+// 25%.
+define("QR_ERROR_CORRECT_LEVEL_Q", 3);
+// 30%.
+define("QR_ERROR_CORRECT_LEVEL_H", 2);
+
+
+//---------------------------------------------------------------
+// QRBitBuffer
+//---------------------------------------------------------------
+
+class QRBitBuffer
+{
+
+    var $buffer;
+    var $length;
+
+    function __construct()
+    {
+        $this->buffer = array();
+        $this->length = 0;
+    }
+
+    function getBuffer()
+    {
+        return $this->buffer;
+    }
+
+    function getLengthInBits()
+    {
+        return $this->length;
+    }
+
+    function __toString()
+    {
+        $buffer = "";
+        for ($i = 0; $i < $this->getLengthInBits(); $i++) {
+            $buffer .= $this->get($i) ? '1' : '0';
+        }
+        return $buffer;
+    }
+
+    function get($index)
+    {
+        $bufIndex = (int)floor($index / 8);
+        return (($this->buffer[$bufIndex] >> (7 - $index % 8)) & 1) == 1;
+    }
+
+    function put($num, $length)
+    {
+
+        for ($i = 0; $i < $length; $i++) {
+            $this->putBit((($num >> ($length - $i - 1)) & 1) == 1);
+        }
+    }
+
+    function putBit($bit)
+    {
+
+        $bufIndex = (int)floor($this->length / 8);
+        if (count($this->buffer) <= $bufIndex) {
+            $this->buffer[] = 0;
+        }
+
+        if ($bit) {
+            $this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
+        }
+
+        $this->length++;
+    }
+}
+

+ 58 - 0
addons/epay/library/RedirectResponse.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace addons\epay\library;
+
+class RedirectResponse extends \Symfony\Component\HttpFoundation\RedirectResponse implements \JsonSerializable, \Serializable
+{
+    public function __toString()
+    {
+        return $this->getContent();
+    }
+
+    public function setTargetUrl($url)
+    {
+        if ('' === ($url ?? '')) {
+            throw new \InvalidArgumentException('无法跳转到空页面');
+        }
+
+        $this->targetUrl = $url;
+
+        $this->setContent(
+            sprintf('<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8" />
+        <meta http-equiv="refresh" content="0;url=\'%1$s\'" />
+
+        <title>正在跳转支付 %1$s</title>
+    </head>
+    <body>
+        <div id="redirect" style="display:none;">正在跳转支付 <a href="%1$s">%1$s</a></div>
+        <script type="text/javascript">
+            setTimeout(function(){
+                document.getElementById("redirect").style.display = "block";
+            }, 1000);
+        </script>
+    </body>
+</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
+
+        $this->headers->set('Location', $url);
+
+        return $this;
+    }
+
+    public function jsonSerialize()
+    {
+        return $this->getContent();
+    }
+
+    public function serialize()
+    {
+        return serialize($this->content);
+    }
+
+    public function unserialize($serialized)
+    {
+        return $this->content = unserialize($serialized);
+    }
+}

+ 26 - 0
addons/epay/library/Response.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace addons\epay\library;
+
+class Response extends \Symfony\Component\HttpFoundation\Response implements \JsonSerializable, \Serializable
+{
+    public function __toString()
+    {
+        return $this->getContent();
+    }
+
+    public function jsonSerialize()
+    {
+        return $this->getContent();
+    }
+
+    public function serialize()
+    {
+        return serialize($this->content);
+    }
+
+    public function unserialize($serialized)
+    {
+        return $this->content = unserialize($serialized);
+    }
+}

+ 325 - 0
addons/epay/library/Service.php

@@ -0,0 +1,325 @@
+<?php
+
+namespace addons\epay\library;
+
+use addons\third\model\Third;
+use app\common\library\Auth;
+use Exception;
+use think\Session;
+use Yansongda\Pay\Pay;
+use Yansongda\Supports\Str;
+
+/**
+ * 订单服务类
+ *
+ * @package addons\epay\library
+ */
+class Service
+{
+
+    /**
+     * 提交订单
+     * @param array|float $amount    订单金额
+     * @param string      $orderid   订单号
+     * @param string      $type      支付类型,可选alipay或wechat
+     * @param string      $title     订单标题
+     * @param string      $notifyurl 通知回调URL
+     * @param string      $returnurl 跳转返回URL
+     * @param string      $method    支付方法
+     * @return Response|RedirectResponse|Collection
+     * @throws Exception
+     */
+    public static function submitOrder($amount, $orderid = null, $type = null, $title = null, $notifyurl = null, $returnurl = null, $method = null, $openid = '')
+    {
+        if (!is_array($amount)) {
+            $params = [
+                'amount'    => $amount,
+                'orderid'   => $orderid,
+                'type'      => $type,
+                'title'     => $title,
+                'notifyurl' => $notifyurl,
+                'returnurl' => $returnurl,
+                'method'    => $method,
+                'openid'    => $openid,
+            ];
+        } else {
+            $params = $amount;
+        }
+        $type = isset($params['type']) && in_array($params['type'], ['alipay', 'wechat']) ? $params['type'] : 'wechat';
+        $method = isset($params['method']) ? $params['method'] : 'web';
+        $orderid = isset($params['orderid']) ? $params['orderid'] : date("YmdHis") . mt_rand(100000, 999999);
+        $amount = isset($params['amount']) ? $params['amount'] : 1;
+        $title = isset($params['title']) ? $params['title'] : "支付";
+        $auth_code = isset($params['auth_code']) ? $params['auth_code'] : '';
+        $openid = isset($params['openid']) ? $params['openid'] : '';
+
+        $request = request();
+        $notifyurl = isset($params['notifyurl']) ? $params['notifyurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'notify';
+        $returnurl = isset($params['returnurl']) ? $params['returnurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'return/out_trade_no/' . $orderid;
+        $html = '';
+        $config = Service::getConfig($type);
+        $config['notify_url'] = $notifyurl;
+        $config['return_url'] = $returnurl;
+        $isWechat = strpos($request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
+
+        $result = null;
+        if ($type == 'alipay') {
+            //如果是PC支付,判断当前环境,进行跳转
+            if ($method == 'web') {
+                //如果是微信环境或后台配置PC使用扫码支付
+                if ($isWechat || $config['scanpay']) {
+                    Session::set("alipayorderdata", $params);
+                    $url = addon_url('epay/api/alipay', [], true, true);
+                    return RedirectResponse::create($url);
+                } elseif ($request->isMobile()) {
+                    $method = 'wap';
+                }
+            }
+            //创建支付对象
+            $pay = Pay::alipay($config);
+            $params = [
+                'out_trade_no' => $orderid,//你的订单号
+                'total_amount' => $amount,//单位元
+                'subject'      => $title,
+            ];
+
+            switch ($method) {
+                case 'web':
+                    //电脑支付
+                    $result = $pay->web($params);
+                    break;
+                case 'wap':
+                    //手机网页支付
+                    $result = $pay->wap($params);
+                    break;
+                case 'app':
+                    //APP支付
+                    $result = $pay->app($params);
+                    break;
+                case 'scan':
+                    //扫码支付
+                    $result = $pay->scan($params);
+                    break;
+                case 'pos':
+                    //刷卡支付必须要有auth_code
+                    $params['auth_code'] = $auth_code;
+                    $result = $pay->pos($params);
+                    break;
+                default:
+            }
+        } else {
+            //如果是PC支付,判断当前环境,进行跳转
+            if ($method == 'web') {
+                //如果是移动端,但不是微信环境
+                if ($request->isMobile() && !$isWechat) {
+                    $method = 'wap';
+                } else {
+                    Session::set("wechatorderdata", $params);
+                    $url = addon_url('epay/api/wechat', [], true, true);
+                    return RedirectResponse::create($url);
+                }
+            }
+
+            //创建支付对象
+            $pay = Pay::wechat($config);
+            $params = [
+                'out_trade_no' => $orderid,//你的订单号
+                'body'         => $title,
+                'total_fee'    => $amount * 100, //单位分
+            ];
+            switch ($method) {
+                //case 'web':
+                //    //电脑支付,跳转到自定义展示页面(FastAdmin独有)
+                //    $result = $pay->web($params);
+                //    break;
+                case 'mp':
+                    //公众号支付
+                    //公众号支付必须有openid
+                    $params['openid'] = $openid;
+                    $result = $pay->mp($params);
+                    break;
+                case 'wap':
+                    //手机网页支付,跳转
+                    $params['spbill_create_ip'] = $request->ip(0, false);
+                    $result = $pay->wap($params);
+                    break;
+                case 'app':
+                    //APP支付,直接返回字符串
+                    $result = $pay->app($params);
+                    break;
+                case 'scan':
+                    //扫码支付,直接返回字符串
+                    $result = $pay->scan($params);
+                    break;
+                case 'pos':
+                    //刷卡支付,直接返回字符串
+                    //刷卡支付必须要有auth_code
+                    $params['auth_code'] = $auth_code;
+                    $result = $pay->pos($params);
+                    break;
+                case 'miniapp':
+                    //小程序支付,直接返回字符串
+                    //小程序支付必须要有openid
+                    $params['openid'] = $openid;
+                    $result = $pay->miniapp($params);
+                    break;
+                default:
+            }
+        }
+
+        //使用重写的Response类、RedirectResponse、Collection类
+        if ($result instanceof \Symfony\Component\HttpFoundation\RedirectResponse) {
+            $result = new RedirectResponse($result->getTargetUrl());
+        } elseif ($result instanceof \Symfony\Component\HttpFoundation\Response) {
+            $result = new Response($result->getContent());
+        } elseif ($result instanceof \Yansongda\Supports\Collection) {
+            $result = Collection::make($result->all());
+        }
+
+        return $result;
+    }
+
+    /**
+     * 验证回调是否成功
+     * @param string $type   支付类型
+     * @param array  $config 配置信息
+     * @return bool|\Yansongda\Pay\Gateways\Alipay|\Yansongda\Pay\Gateways\Wechat
+     */
+    public static function checkNotify($type, $config = [])
+    {
+        $type = strtolower($type);
+        if (!in_array($type, ['wechat', 'alipay'])) {
+            return false;
+        }
+        try {
+            $config = self::getConfig($type);
+            $pay = $type == 'wechat' ? Pay::wechat($config) : Pay::alipay($config);
+            $data = $pay->verify();
+
+            if ($type == 'alipay') {
+                if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
+                    return $pay;
+                }
+            } else {
+                return $pay;
+            }
+        } catch (Exception $e) {
+            return false;
+        }
+
+        return false;
+    }
+
+    /**
+     * 验证返回是否成功,请勿用于判断是否支付成功的逻辑验证
+     * 已弃用
+     *
+     * @param string $type   支付类型
+     * @param array  $config 配置信息
+     * @return bool
+     * @deprecated  已弃用,请勿用于逻辑验证
+     */
+    public static function checkReturn($type, $config = [])
+    {
+        //由于PC及移动端无法获取请求的参数信息,取消return验证,均返回true
+        return true;
+    }
+
+    /**
+     * 获取配置
+     * @param string $type 支付类型
+     * @return array|mixed
+     */
+    public static function getConfig($type = 'wechat')
+    {
+        $config = get_addon_config('epay');
+        $config = isset($config[$type]) ? $config[$type] : $config['wechat'];
+        if ($config['log']) {
+            $config['log'] = [
+                'file'  => LOG_PATH . 'epaylogs' . DS . $type . '-' . date("Y-m-d") . '.log',
+                'level' => 'debug'
+            ];
+        }
+        if (isset($config['cert_client']) && substr($config['cert_client'], 0, 8) == '/addons/') {
+            $config['cert_client'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_client'], 1));
+        }
+        if (isset($config['cert_key']) && substr($config['cert_key'], 0, 8) == '/addons/') {
+            $config['cert_key'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_key'], 1));
+        }
+        if (isset($config['app_cert_public_key']) && substr($config['app_cert_public_key'], 0, 8) == '/addons/') {
+            $config['app_cert_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['app_cert_public_key'], 1));
+        }
+        if (isset($config['alipay_root_cert']) && substr($config['alipay_root_cert'], 0, 8) == '/addons/') {
+            $config['alipay_root_cert'] = ROOT_PATH . str_replace('/', DS, substr($config['alipay_root_cert'], 1));
+        }
+        if (isset($config['ali_public_key']) && (Str::endsWith($config['ali_public_key'], '.crt') || Str::endsWith($config['ali_public_key'], '.pem'))) {
+            $config['ali_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['ali_public_key'], 1));
+        }
+        // 可选
+        $config['http'] = [
+            'timeout'         => 10,
+            'connect_timeout' => 10,
+            // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
+        ];
+
+        $config['notify_url'] = empty($config['notify_url']) ? addon_url('epay/api/notifyx', [], false) . '/type/' . $type : $config['notify_url'];
+        $config['notify_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['notify_url']) ? request()->root(true) . $config['notify_url'] : $config['notify_url'];
+        $config['return_url'] = empty($config['return_url']) ? addon_url('epay/api/returnx', [], false) . '/type/' . $type : $config['return_url'];
+        $config['return_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['return_url']) ? request()->root(true) . $config['return_url'] : $config['return_url'];
+        return $config;
+    }
+
+    public static function getApp($type,$notify_url){
+        static $app=[];
+        $config=array_merge(self::getConfig($type),compact('notify_url'));
+        if(!isset($app[$type])){
+            $app[$type]=$type=='wechat'?Pay::wechat($config):Pay::alipay($config);
+        }
+        return $app[$type];
+    }
+
+    /**
+     * 获取微信Openid
+     *
+     * @return mixed|string
+     */
+    public static function getOpenid()
+    {
+        $config = self::getConfig('wechat');
+        $openid = '';
+        $auth = Auth::instance();
+        if ($auth->isLogin()) {
+            $third = get_addon_info('third');
+            if ($third && $third['state']) {
+                $thirdInfo = Third::where('user_id', $auth->id)->where('platform', 'wechat')->where('apptype', 'mp')->find();
+                $openid = $thirdInfo ? $thirdInfo['openid'] : '';
+            }
+        }
+        if (!$openid) {
+            $openid = Session::get("openid");
+
+            //如果未传openid,则去读取openid
+            if (!$openid) {
+                $wechat = new Wechat($config['app_id'], $config['app_secret']);
+                $openid = $wechat->getOpenid();
+            }
+        }
+        return $openid;
+    }
+
+    public static function notifyUrl($type,$order_no){
+        return request()->domain()."/index/payment/notify/type/{$type}/order_no/$order_no";
+    }
+    public static function notifyUrlJd($order_no){
+        return request()->domain()."/index/payment/notify_jd/order_no/$order_no";
+    }
+    public static function notifyUrlKs($order_no){
+        return request()->domain()."/index/payment/notify_ks/order_no/$order_no";
+    }
+    public static function refundUrl($type,$order_no){
+        return request()->domain()."/index/payment/refund_url/type/{$type}/order/$order_no";
+    }
+    public static function getWechatOpUser(){
+        return self::getConfig('wechat')['mch_id']??'';
+    }
+}

+ 110 - 0
addons/epay/library/Wechat.php

@@ -0,0 +1,110 @@
+<?php
+
+namespace addons\epay\library;
+
+use fast\Http;
+use think\Cache;
+use think\Session;
+
+/**
+ * 微信授权
+ *
+ */
+class Wechat
+{
+    private $app_id = '';
+    private $app_secret = '';
+    private $scope = 'snsapi_userinfo';
+
+    public function __construct($app_id, $app_secret)
+    {
+        $this->app_id = $app_id;
+        $this->app_secret = $app_secret;
+    }
+
+    /**
+     * 获取微信授权链接
+     *
+     * @return string
+     */
+    public function getAuthorizeUrl()
+    {
+        $redirect_uri = addon_url('epay/api/wechat', [], true, true);
+        $redirect_uri = urlencode($redirect_uri);
+        $state = \fast\Random::alnum();
+        Session::set('state', $state);
+        return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->app_id}&redirect_uri={$redirect_uri}&response_type=code&scope={$this->scope}&state={$state}#wechat_redirect";
+    }
+
+    /**
+     * 获取微信openid
+     *
+     * @return mixed|string
+     */
+    public function getOpenid()
+    {
+        $openid = Session::get('openid');
+        if (!$openid) {
+            if (!isset($_GET['code'])) {
+                $url = $this->getAuthorizeUrl();
+
+                Header("Location: $url");
+                exit();
+            } else {
+                $state = Session::get('state');
+                if ($state == $_GET['state']) {
+                    $code = $_GET['code'];
+                    $token = $this->getAccessToken($code);
+                    if (!isset($token['openid']) && isset($token['errmsg'])) {
+                        exception($token['errmsg']);
+                    }
+                    $openid = isset($token['openid']) ? $token['openid'] : '';
+                    if ($openid) {
+                        Session::set("openid", $openid);
+                    }
+                }
+            }
+        }
+        return $openid;
+    }
+
+    /**
+     * 获取授权token网页授权
+     *
+     * @param string $code
+     * @return mixed|string
+     */
+    public function getAccessToken($code = '')
+    {
+        $params = [
+            'appid'      => $this->app_id,
+            'secret'     => $this->app_secret,
+            'code'       => $code,
+            'grant_type' => 'authorization_code'
+        ];
+        $ret = Http::sendRequest('https://api.weixin.qq.com/sns/oauth2/access_token', $params, 'GET');
+        if ($ret['ret']) {
+            $ar = json_decode($ret['msg'], true);
+            return $ar;
+        }
+        return [];
+    }
+
+    public function getJsticket($code = '')
+    {
+        $jsticket = Session::get('jsticket');
+        if (!$jsticket) {
+            $token = $this->getAccessToken($code);
+            $params = [
+                'access_token' => 'token',
+                'type'         => 'jsapi',
+            ];
+            $ret = Http::sendRequest('https://api.weixin.qq.com/cgi-bin/ticket/getticket', $params, 'GET');
+            if ($ret['ret']) {
+                $ar = json_decode($ret['msg'], true);
+                return $ar;
+            }
+        }
+        return $jsticket;
+    }
+}

+ 83 - 0
addons/epay/library/Yansongda/Pay/Contracts/GatewayApplicationInterface.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace Yansongda\Pay\Contracts;
+
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Supports\Collection;
+
+interface GatewayApplicationInterface
+{
+    /**
+     * To pay.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string $gateway
+     * @param array  $params
+     *
+     * @return Collection|Response
+     */
+    public function pay($gateway, $params);
+
+    /**
+     * Query an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @return Collection
+     */
+    public function find($order, string $type);
+
+    /**
+     * Refund an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Collection
+     */
+    public function refund(array $order);
+
+    /**
+     * Cancel an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @return Collection
+     */
+    public function cancel($order);
+
+    /**
+     * Close an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @return Collection
+     */
+    public function close($order);
+
+    /**
+     * Verify a request.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array|null $content
+     *
+     * @return Collection
+     */
+    public function verify($content, bool $refund);
+
+    /**
+     * Echo success to server.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Response
+     */
+    public function success();
+}

+ 20 - 0
addons/epay/library/Yansongda/Pay/Contracts/GatewayInterface.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Yansongda\Pay\Contracts;
+
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Supports\Collection;
+
+interface GatewayInterface
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @return Collection|Response
+     */
+    public function pay($endpoint, array $payload);
+}

+ 98 - 0
addons/epay/library/Yansongda/Pay/Events.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace Yansongda\Pay;
+
+use Exception;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Contracts\EventDispatcher\Event;
+
+/**
+ * @author yansongda <me@yansongda.cn>
+ *
+ * @method static Event dispatch(Event $event)                                Dispatches an event to all registered listeners
+ * @method static array getListeners($eventName = null)                       Gets the listeners of a specific event or all listeners sorted by descending priority.
+ * @method static int|void getListenerPriority($eventName, $listener)         Gets the listener priority for a specific event.
+ * @method static bool hasListeners($eventName = null)                        Checks whether an event has any registered listeners.
+ * @method static void addListener($eventName, $listener, $priority = 0)      Adds an event listener that listens on the specified events.
+ * @method static removeListener($eventName, $listener)                       Removes an event listener from the specified events.
+ * @method static void addSubscriber(EventSubscriberInterface $subscriber)    Adds an event subscriber.
+ * @method static void removeSubscriber(EventSubscriberInterface $subscriber)
+ */
+class Events
+{
+    /**
+     * dispatcher.
+     *
+     * @var EventDispatcher
+     */
+    protected static $dispatcher;
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws Exception
+     *
+     * @return mixed
+     */
+    public static function __callStatic($method, $args)
+    {
+        return call_user_func_array([self::getDispatcher(), $method], $args);
+    }
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws Exception
+     *
+     * @return mixed
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([self::getDispatcher(), $method], $args);
+    }
+
+    /**
+     * setDispatcher.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function setDispatcher(EventDispatcher $dispatcher)
+    {
+        self::$dispatcher = $dispatcher;
+    }
+
+    /**
+     * getDispatcher.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function getDispatcher(): EventDispatcher
+    {
+        if (self::$dispatcher) {
+            return self::$dispatcher;
+        }
+
+        return self::$dispatcher = self::createDispatcher();
+    }
+
+    /**
+     * createDispatcher.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function createDispatcher(): EventDispatcher
+    {
+        return new EventDispatcher();
+    }
+}

+ 31 - 0
addons/epay/library/Yansongda/Pay/Events/ApiRequested.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class ApiRequested extends Event
+{
+    /**
+     * Endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * Result.
+     *
+     * @var array
+     */
+    public $result;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $result)
+    {
+        $this->endpoint = $endpoint;
+        $this->result = $result;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 31 - 0
addons/epay/library/Yansongda/Pay/Events/ApiRequesting.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class ApiRequesting extends Event
+{
+    /**
+     * Endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * Payload.
+     *
+     * @var array
+     */
+    public $payload;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $payload)
+    {
+        $this->endpoint = $endpoint;
+        $this->payload = $payload;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 40 - 0
addons/epay/library/Yansongda/Pay/Events/Event.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+use Symfony\Contracts\EventDispatcher\Event as SymfonyEvent;
+
+class Event extends SymfonyEvent
+{
+    /**
+     * Driver.
+     *
+     * @var string
+     */
+    public $driver;
+
+    /**
+     * Method.
+     *
+     * @var string
+     */
+    public $gateway;
+
+    /**
+     * Extra attributes.
+     *
+     * @var mixed
+     */
+    public $attributes;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway)
+    {
+        $this->driver = $driver;
+        $this->gateway = $gateway;
+    }
+}

+ 33 - 0
addons/epay/library/Yansongda/Pay/Events/MethodCalled.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class MethodCalled extends Event
+{
+    /**
+     * endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * payload.
+     *
+     * @var array
+     */
+    public $payload;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $payload = [])
+    {
+        $this->endpoint = $endpoint;
+        $this->payload = $payload;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 31 - 0
addons/epay/library/Yansongda/Pay/Events/PayStarted.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class PayStarted extends Event
+{
+    /**
+     * Endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * Payload.
+     *
+     * @var array
+     */
+    public $payload;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $payload)
+    {
+        $this->endpoint = $endpoint;
+        $this->payload = $payload;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 23 - 0
addons/epay/library/Yansongda/Pay/Events/PayStarting.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class PayStarting extends Event
+{
+    /**
+     * Params.
+     *
+     * @var array
+     */
+    public $params;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, array $params)
+    {
+        $this->params = $params;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 25 - 0
addons/epay/library/Yansongda/Pay/Events/RequestReceived.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class RequestReceived extends Event
+{
+    /**
+     * Received data.
+     *
+     * @var array
+     */
+    public $data;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway, array $data)
+    {
+        $this->data = $data;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 25 - 0
addons/epay/library/Yansongda/Pay/Events/SignFailed.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class SignFailed extends Event
+{
+    /**
+     * Received data.
+     *
+     * @var array
+     */
+    public $data;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway, array $data)
+    {
+        $this->data = $data;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 19 - 0
addons/epay/library/Yansongda/Pay/Exceptions/BusinessException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class BusinessException extends GatewayException
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('ERROR_BUSINESS: '.$message, $raw, self::ERROR_BUSINESS);
+    }
+}

+ 44 - 0
addons/epay/library/Yansongda/Pay/Exceptions/Exception.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class Exception extends \Exception
+{
+    const UNKNOWN_ERROR = 9999;
+
+    const INVALID_GATEWAY = 1;
+
+    const INVALID_CONFIG = 2;
+
+    const INVALID_ARGUMENT = 3;
+
+    const ERROR_GATEWAY = 4;
+
+    const INVALID_SIGN = 5;
+
+    const ERROR_BUSINESS = 6;
+
+    /**
+     * Raw error info.
+     *
+     * @var array
+     */
+    public $raw;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     * @param int|string   $code
+     */
+    public function __construct($message = '', $raw = [], $code = self::UNKNOWN_ERROR)
+    {
+        $message = '' === $message ? 'Unknown Error' : $message;
+        $this->raw = is_array($raw) ? $raw : [$raw];
+
+        parent::__construct($message, intval($code));
+    }
+}

+ 20 - 0
addons/epay/library/Yansongda/Pay/Exceptions/GatewayException.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class GatewayException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     * @param int          $code
+     */
+    public function __construct($message, $raw = [], $code = self::ERROR_GATEWAY)
+    {
+        parent::__construct('ERROR_GATEWAY: '.$message, $raw, $code);
+    }
+}

+ 19 - 0
addons/epay/library/Yansongda/Pay/Exceptions/InvalidArgumentException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidArgumentException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_ARGUMENT: '.$message, $raw, self::INVALID_ARGUMENT);
+    }
+}

+ 19 - 0
addons/epay/library/Yansongda/Pay/Exceptions/InvalidConfigException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidConfigException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_CONFIG: '.$message, $raw, self::INVALID_CONFIG);
+    }
+}

+ 19 - 0
addons/epay/library/Yansongda/Pay/Exceptions/InvalidGatewayException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidGatewayException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_GATEWAY: '.$message, $raw, self::INVALID_GATEWAY);
+    }
+}

+ 19 - 0
addons/epay/library/Yansongda/Pay/Exceptions/InvalidSignException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidSignException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_SIGN: '.$message, $raw, self::INVALID_SIGN);
+    }
+}

+ 422 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay.php

@@ -0,0 +1,422 @@
+<?php
+
+namespace Yansongda\Pay\Gateways;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Contracts\GatewayApplicationInterface;
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidGatewayException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay\Support;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+
+/**
+ * @method Response   app(array $config)      APP 支付
+ * @method Collection pos(array $config)      刷卡支付
+ * @method Collection scan(array $config)     扫码支付
+ * @method Collection transfer(array $config) 帐户转账
+ * @method Response   wap(array $config)      手机网站支付
+ * @method Response   web(array $config)      电脑支付
+ * @method Collection mini(array $config)     小程序支付
+ */
+class Alipay implements GatewayApplicationInterface
+{
+    /**
+     * Const mode_normal.
+     */
+    const MODE_NORMAL = 'normal';
+
+    /**
+     * Const mode_dev.
+     */
+    const MODE_DEV = 'dev';
+
+    /**
+     * Const mode_service.
+     */
+    const MODE_SERVICE = 'service';
+
+    /**
+     * Const url.
+     */
+    const URL = [
+        self::MODE_NORMAL => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+        self::MODE_DEV => 'https://openapi.alipaydev.com/gateway.do?charset=utf-8',
+    ];
+
+    /**
+     * Alipay payload.
+     *
+     * @var array
+     */
+    protected $payload;
+
+    /**
+     * Alipay gateway.
+     *
+     * @var string
+     */
+    protected $gateway;
+
+    /**
+     * extends.
+     *
+     * @var array
+     */
+    protected $extends;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \Exception
+     */
+    public function __construct(Config $config)
+    {
+        $this->gateway = Support::create($config)->getBaseUri();
+        $this->payload = [
+            'app_id' => $config->get('app_id'),
+            'method' => '',
+            'format' => 'JSON',
+            'charset' => 'utf-8',
+            'sign_type' => 'RSA2',
+            'version' => '1.0',
+            'return_url' => $config->get('return_url'),
+            'notify_url' => $config->get('notify_url'),
+            'timestamp' => date('Y-m-d H:i:s'),
+            'sign' => '',
+            'biz_content' => '',
+            'app_auth_token' => $config->get('app_auth_token'),
+        ];
+
+        if ($config->get('app_cert_public_key') && $config->get('alipay_root_cert')) {
+            $this->payload['app_cert_sn'] = Support::getCertSN($config->get('app_cert_public_key'));
+            $this->payload['alipay_root_cert_sn'] = Support::getRootCertSN($config->get('alipay_root_cert'));
+        }
+    }
+
+    /**
+     * Magic pay.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $params
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidGatewayException
+     * @throws InvalidSignException
+     *
+     * @return Response|Collection
+     */
+    public function __call($method, $params)
+    {
+        if (isset($this->extends[$method])) {
+            return $this->makeExtend($method, ...$params);
+        }
+
+        return $this->pay($method, ...$params);
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $gateway
+     * @param array  $params
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    public function pay($gateway, $params = [])
+    {
+        Events::dispatch(new Events\PayStarting('Alipay', $gateway, $params));
+
+        $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
+        $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
+
+        unset($params['return_url'], $params['notify_url']);
+
+        $this->payload['biz_content'] = json_encode($params);
+
+        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
+
+        if (class_exists($gateway)) {
+            return $this->makePay($gateway);
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
+    }
+
+    /**
+     * Verify sign.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array|null $data
+     *
+     * @throws InvalidSignException
+     * @throws InvalidConfigException
+     */
+    public function verify($data = null, bool $refund = false): Collection
+    {
+        if (is_null($data)) {
+            $request = Request::createFromGlobals();
+
+            $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
+        }
+
+        if (isset($data['fund_bill_list'])) {
+            $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
+        }
+
+        Events::dispatch(new Events\RequestReceived('Alipay', '', $data));
+
+        if (Support::verifySign($data)) {
+            return new Collection($data);
+        }
+
+        Events::dispatch(new Events\SignFailed('Alipay', '', $data));
+
+        throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
+    }
+
+    /**
+     * Query an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function find($order, string $type = 'wap'): Collection
+    {
+        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
+
+        if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
+            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
+        }
+
+        $config = call_user_func([new $gateway(), 'find'], $order);
+
+        $this->payload['method'] = $config['method'];
+        $this->payload['biz_content'] = $config['biz_content'];
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Refund an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function refund(array $order): Collection
+    {
+        $this->payload['method'] = 'alipay.trade.refund';
+        $this->payload['biz_content'] = json_encode($order);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Cancel an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array|string $order
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function cancel($order): Collection
+    {
+        $this->payload['method'] = 'alipay.trade.cancel';
+        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Close an order.
+     *
+     * @param string|array $order
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function close($order): Collection
+    {
+        $this->payload['method'] = 'alipay.trade.close';
+        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Download bill.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $bill
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function download($bill): string
+    {
+        $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
+        $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
+
+        $result = Support::requestApi($this->payload);
+
+        return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
+    }
+
+    /**
+     * Reply success to alipay.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function success(): Response
+    {
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Success', $this->gateway));
+
+        return new Response('success');
+    }
+
+    /**
+     * extend.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function extend(string $method, callable $function, bool $now = true): ?Collection
+    {
+        if (!$now && !method_exists($this, $method)) {
+            $this->extends[$method] = $function;
+
+            return null;
+        }
+
+        $customize = $function($this->payload);
+
+        if (!is_array($customize) && !($customize instanceof Collection)) {
+            throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
+        }
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'extend', $this->gateway, $customize));
+
+        if (is_array($customize)) {
+            $this->payload = $customize;
+            $this->payload['sign'] = Support::generateSign($this->payload);
+
+            return Support::requestApi($this->payload);
+        }
+
+        return $customize;
+    }
+
+    /**
+     * Make pay gateway.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    protected function makePay(string $gateway)
+    {
+        $app = new $gateway();
+
+        if ($app instanceof GatewayInterface) {
+            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
+                return '' !== $value && !is_null($value);
+            }));
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
+    }
+
+    /**
+     * makeExtend.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    protected function makeExtend(string $method, array ...$params): Collection
+    {
+        $params = count($params) >= 1 ? $params[0] : $params;
+
+        $function = $this->extends[$method];
+
+        $customize = $function($this->payload, $params);
+
+        if (!is_array($customize) && !($customize instanceof Collection)) {
+            throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
+        }
+
+        Events::dispatch(new Events\MethodCalled(
+            'Alipay',
+            'extend - '.$method,
+            $this->gateway,
+            is_array($customize) ? $customize : $customize->toArray()
+        ));
+
+        if (is_array($customize)) {
+            $this->payload = $customize;
+            $this->payload['sign'] = Support::generateSign($this->payload);
+
+            return Support::requestApi($this->payload);
+        }
+
+        return $customize;
+    }
+}

+ 38 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/AppGateway.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Gateways\Alipay;
+
+class AppGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidConfigException
+     * @throws InvalidArgumentException
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $payload['method'] = 'alipay.trade.app.pay';
+
+        $biz_array = json_decode($payload['biz_content'], true);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => 'QUICK_MSECURITY_PAY']));
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'App', $endpoint, $payload));
+
+        return new Response(http_build_query($payload));
+    }
+}

+ 40 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/Gateway.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Supports\Collection;
+
+abstract class Gateway implements GatewayInterface
+{
+    /**
+     * Mode.
+     *
+     * @var string
+     */
+    protected $mode;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct()
+    {
+        $this->mode = Support::getInstance()->mode;
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @return Collection
+     */
+    abstract public function pay($endpoint, array $payload);
+}

+ 46 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/MiniGateway.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Supports\Collection;
+
+class MiniGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author xiaozan <i@xiaozan.me>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     *
+     * @see https://docs.alipay.com/mini/introduce/pay
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $biz_array = json_decode($payload['biz_content'], true);
+        if (empty($biz_array['buyer_id'])) {
+            throw new InvalidArgumentException('buyer_id required');
+        }
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode($biz_array);
+        $payload['method'] = 'alipay.trade.create';
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Mini', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+}

+ 47 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/PosGateway.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Supports\Collection;
+
+class PosGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidArgumentException
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['method'] = 'alipay.trade.pay';
+        $biz_array = json_decode($payload['biz_content'], true);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode(array_merge(
+            $biz_array,
+            [
+                'product_code' => 'FACE_TO_FACE_PAYMENT',
+                'scene' => 'bar_code',
+            ]
+        ));
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Pos', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+}

+ 21 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/RefundGateway.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+class RefundGateway
+{
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'method' => 'alipay.trade.fastpay.refund.query',
+            'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]),
+        ];
+    }
+}

+ 41 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/ScanGateway.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Supports\Collection;
+
+class ScanGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['method'] = 'alipay.trade.precreate';
+        $biz_array = json_decode($payload['biz_content'], true);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => '']));
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Scan', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+}

+ 452 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/Support.php

@@ -0,0 +1,452 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Exception;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Pay\Log;
+use Yansongda\Supports\Arr;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+use Yansongda\Supports\Traits\HasHttpRequest;
+
+/**
+ * @author yansongda <me@yansongda.cn>
+ *
+ * @property string app_id alipay app_id
+ * @property string ali_public_key
+ * @property string private_key
+ * @property array http http options
+ * @property string mode current mode
+ * @property array log log options
+ * @property string pid ali pid
+ */
+class Support
+{
+    use HasHttpRequest;
+
+    /**
+     * Alipay gateway.
+     *
+     * @var string
+     */
+    protected $baseUri;
+
+    /**
+     * Config.
+     *
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * Instance.
+     *
+     * @var Support
+     */
+    private static $instance;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    private function __construct(Config $config)
+    {
+        $this->baseUri = Alipay::URL[$config->get('mode', Alipay::MODE_NORMAL)];
+        $this->config = $config;
+
+        $this->setHttpOptions();
+    }
+
+    /**
+     * __get.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $key
+     *
+     * @return mixed|Config|null
+     */
+    public function __get($key)
+    {
+        return $this->getConfig($key);
+    }
+
+    /**
+     * create.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Support
+     */
+    public static function create(Config $config)
+    {
+        if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
+            self::$instance = new self($config);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * getInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return Support
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$instance)) {
+            throw new InvalidArgumentException('You Should [Create] First Before Using');
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * clear.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function clear()
+    {
+        self::$instance = null;
+    }
+
+    /**
+     * Get Alipay API result.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public static function requestApi(array $data): Collection
+    {
+        Events::dispatch(new Events\ApiRequesting('Alipay', '', self::$instance->getBaseUri(), $data));
+
+        $data = array_filter($data, function ($value) {
+            return ('' == $value || is_null($value)) ? false : true;
+        });
+
+        $result = json_decode(self::$instance->post('', $data), true);
+
+        Events::dispatch(new Events\ApiRequested('Alipay', '', self::$instance->getBaseUri(), $result));
+
+        return self::processingApiResult($data, $result);
+    }
+
+    /**
+     * Generate sign.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidConfigException
+     */
+    public static function generateSign(array $params): string
+    {
+        $privateKey = self::$instance->private_key;
+
+        if (is_null($privateKey)) {
+            throw new InvalidConfigException('Missing Alipay Config -- [private_key]');
+        }
+
+        if (Str::endsWith($privateKey, '.pem')) {
+            $privateKey = openssl_pkey_get_private(
+                Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://'.$privateKey
+            );
+        } else {
+            $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n".
+                wordwrap($privateKey, 64, "\n", true).
+                "\n-----END RSA PRIVATE KEY-----";
+        }
+
+        openssl_sign(self::getSignContent($params), $sign, $privateKey, OPENSSL_ALGO_SHA256);
+
+        $sign = base64_encode($sign);
+
+        Log::debug('Alipay Generate Sign', [$params, $sign]);
+
+        if (is_resource($privateKey)) {
+            openssl_free_key($privateKey);
+        }
+
+        return $sign;
+    }
+
+    /**
+     * Verify sign.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param bool        $sync
+     * @param string|null $sign
+     *
+     * @throws InvalidConfigException
+     */
+    public static function verifySign(array $data, $sync = false, $sign = null): bool
+    {
+        $publicKey = self::$instance->ali_public_key;
+
+        if (is_null($publicKey)) {
+            throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]');
+        }
+
+        if (Str::endsWith($publicKey, '.crt')) {
+            $publicKey = file_get_contents($publicKey);
+        } elseif (Str::endsWith($publicKey, '.pem')) {
+            $publicKey = openssl_pkey_get_public(
+                Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey
+            );
+        } else {
+            $publicKey = "-----BEGIN PUBLIC KEY-----\n".
+                wordwrap($publicKey, 64, "\n", true).
+                "\n-----END PUBLIC KEY-----";
+        }
+
+        $sign = $sign ?? $data['sign'];
+
+        $toVerify = $sync ? json_encode($data, JSON_UNESCAPED_UNICODE) : self::getSignContent($data, true);
+
+        $isVerify = 1 === openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
+
+        if (is_resource($publicKey)) {
+            openssl_free_key($publicKey);
+        }
+
+        return $isVerify;
+    }
+
+    /**
+     * Get signContent that is to be signed.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param bool $verify
+     */
+    public static function getSignContent(array $data, $verify = false): string
+    {
+        ksort($data);
+
+        $stringToBeSigned = '';
+        foreach ($data as $k => $v) {
+            if ($verify && 'sign' != $k && 'sign_type' != $k) {
+                $stringToBeSigned .= $k.'='.$v.'&';
+            }
+            if (!$verify && '' !== $v && !is_null($v) && 'sign' != $k && '@' != substr($v, 0, 1)) {
+                $stringToBeSigned .= $k.'='.$v.'&';
+            }
+        }
+
+        Log::debug('Alipay Generate Sign Content Before Trim', [$data, $stringToBeSigned]);
+
+        return trim($stringToBeSigned, '&');
+    }
+
+    /**
+     * Convert encoding.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string|array $data
+     * @param string       $to
+     * @param string       $from
+     */
+    public static function encoding($data, $to = 'utf-8', $from = 'gb2312'): array
+    {
+        return Arr::encoding((array) $data, $to, $from);
+    }
+
+    /**
+     * Get service config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $key
+     * @param mixed|null  $default
+     *
+     * @return mixed|null
+     */
+    public function getConfig($key = null, $default = null)
+    {
+        if (is_null($key)) {
+            return $this->config->all();
+        }
+
+        if ($this->config->has($key)) {
+            return $this->config[$key];
+        }
+
+        return $default;
+    }
+
+    /**
+     * Get Base Uri.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    public function getBaseUri()
+    {
+        return $this->baseUri;
+    }
+
+    /**
+     * 生成应用证书SN.
+     *
+     * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+     *
+     * @param $certPath
+     *
+     * @throws /Exception
+     */
+    public static function getCertSN($certPath): string
+    {
+        if (!is_file($certPath)) {
+            throw new Exception('unknown certPath -- [getCertSN]');
+        }
+        $x509data = file_get_contents($certPath);
+        if (false === $x509data) {
+            throw new Exception('Alipay CertSN Error -- [getCertSN]');
+        }
+        openssl_x509_read($x509data);
+        $certdata = openssl_x509_parse($x509data);
+        if (empty($certdata)) {
+            throw new Exception('Alipay openssl_x509_parse Error -- [getCertSN]');
+        }
+        $issuer_arr = [];
+        foreach ($certdata['issuer'] as $key => $val) {
+            $issuer_arr[] = $key.'='.$val;
+        }
+        $issuer = implode(',', array_reverse($issuer_arr));
+        Log::debug('getCertSN:', [$certPath, $issuer, $certdata['serialNumber']]);
+
+        return md5($issuer.$certdata['serialNumber']);
+    }
+
+    /**
+     * 生成支付宝根证书SN.
+     *
+     * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+     *
+     * @param $certPath
+     *
+     * @return string
+     *
+     * @throws /Exception
+     */
+    public static function getRootCertSN($certPath)
+    {
+        if (!is_file($certPath)) {
+            throw new Exception('unknown certPath -- [getRootCertSN]');
+        }
+        $x509data = file_get_contents($certPath);
+        if (false === $x509data) {
+            throw new Exception('Alipay CertSN Error -- [getRootCertSN]');
+        }
+        $kCertificateEnd = '-----END CERTIFICATE-----';
+        $certStrList = explode($kCertificateEnd, $x509data);
+        $md5_arr = [];
+        foreach ($certStrList as $one) {
+            if (!empty(trim($one))) {
+                $_x509data = $one.$kCertificateEnd;
+                openssl_x509_read($_x509data);
+                $_certdata = openssl_x509_parse($_x509data);
+                if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) {
+                    $issuer_arr = [];
+                    foreach ($_certdata['issuer'] as $key => $val) {
+                        $issuer_arr[] = $key.'='.$val;
+                    }
+                    $_issuer = implode(',', array_reverse($issuer_arr));
+                    if (0 === strpos($_certdata['serialNumber'], '0x')) {
+                        $serialNumber = self::bchexdec($_certdata['serialNumber']);
+                    } else {
+                        $serialNumber = $_certdata['serialNumber'];
+                    }
+                    $md5_arr[] = md5($_issuer.$serialNumber);
+                    Log::debug('getRootCertSN Sub:', [$certPath, $_issuer, $serialNumber]);
+                }
+            }
+        }
+
+        return implode('_', $md5_arr);
+    }
+
+    /**
+     * processingApiResult.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $data
+     * @param $result
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    protected static function processingApiResult($data, $result): Collection
+    {
+        $method = str_replace('.', '_', $data['method']).'_response';
+
+        if (!isset($result['sign']) || '10000' != $result[$method]['code']) {
+            throw new GatewayException('Get Alipay API Error:'.$result[$method]['msg'].(isset($result[$method]['sub_code']) ? (' - '.$result[$method]['sub_code']) : ''), $result);
+        }
+
+        if (self::verifySign($result[$method], true, $result['sign'])) {
+            return new Collection($result[$method]);
+        }
+
+        Events::dispatch(new Events\SignFailed('Alipay', '', $result));
+
+        throw new InvalidSignException('Alipay Sign Verify FAILED', $result);
+    }
+
+    /**
+     * Set Http options.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function setHttpOptions(): self
+    {
+        if ($this->config->has('http') && is_array($this->config->get('http'))) {
+            $this->config->forget('http.base_uri');
+            $this->httpOptions = $this->config->get('http');
+        }
+
+        return $this;
+    }
+
+    /**
+     * 0x转高精度数字.
+     *
+     * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+     *
+     * @param $hex
+     *
+     * @return int|string
+     */
+    private static function bchexdec($hex)
+    {
+        $dec = 0;
+        $len = strlen($hex);
+        for ($i = 1; $i <= $len; ++$i) {
+            if (ctype_xdigit($hex[$i - 1])) {
+                $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
+            }
+        }
+
+        return str_replace('.00', '', $dec);
+    }
+}

+ 49 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/TransferGateway.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class TransferGateway implements GatewayInterface
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['method'] = 'alipay.fund.trans.uni.transfer';
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Transfer', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'method' => 'alipay.fund.trans.order.query',
+            'biz_content' => json_encode(is_array($order) ? $order : ['out_biz_no' => $order]),
+        ];
+    }
+}

+ 26 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/WapGateway.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+class WapGateway extends WebGateway
+{
+    /**
+     * Get method config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getMethod(): string
+    {
+        return 'alipay.trade.wap.pay';
+    }
+
+    /**
+     * Get productCode config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getProductCode(): string
+    {
+        return 'QUICK_WAP_WAY';
+    }
+}

+ 104 - 0
addons/epay/library/Yansongda/Pay/Gateways/Alipay/WebGateway.php

@@ -0,0 +1,104 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Gateways\Alipay;
+
+class WebGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidConfigException
+     * @throws InvalidArgumentException
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $biz_array = json_decode($payload['biz_content'], true);
+        $biz_array['product_code'] = $this->getProductCode();
+
+        $method = $biz_array['http_method'] ?? 'POST';
+
+        unset($biz_array['http_method']);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['method'] = $this->getMethod();
+        $payload['biz_content'] = json_encode($biz_array);
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Web/Wap', $endpoint, $payload));
+
+        return $this->buildPayHtml($endpoint, $payload, $method);
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'method' => 'alipay.trade.query',
+            'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]),
+        ];
+    }
+
+    /**
+     * Build Html response.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     * @param array  $payload
+     * @param string $method
+     */
+    protected function buildPayHtml($endpoint, $payload, $method = 'POST'): Response
+    {
+        if ('GET' === strtoupper($method)) {
+            return new RedirectResponse($endpoint.'&'.http_build_query($payload));
+        }
+
+        $sHtml = "<form id='alipay_submit' name='alipay_submit' action='".$endpoint."' method='".$method."'>";
+        foreach ($payload as $key => $val) {
+            $val = str_replace("'", '&apos;', $val);
+            $sHtml .= "<input type='hidden' name='".$key."' value='".$val."'/>";
+        }
+        $sHtml .= "<input type='submit' value='ok' style='display:none;'></form>";
+        $sHtml .= "<script>document.forms['alipay_submit'].submit();</script>";
+
+        return new Response($sHtml);
+    }
+
+    /**
+     * Get method config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getMethod(): string
+    {
+        return 'alipay.trade.page.pay';
+    }
+
+    /**
+     * Get productCode config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getProductCode(): string
+    {
+        return 'FAST_INSTANT_TRADE_PAY';
+    }
+}

+ 366 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat.php

@@ -0,0 +1,366 @@
+<?php
+
+namespace Yansongda\Pay\Gateways;
+
+use Exception;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Contracts\GatewayApplicationInterface;
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidGatewayException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat\Support;
+use Yansongda\Pay\Log;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+
+/**
+ * @method Response         app(array $config)          APP 支付
+ * @method Collection       groupRedpack(array $config) 分裂红包
+ * @method Collection       miniapp(array $config)      小程序支付
+ * @method Collection       mp(array $config)           公众号支付
+ * @method Collection       pos(array $config)          刷卡支付
+ * @method Collection       redpack(array $config)      普通红包
+ * @method Collection       scan(array $config)         扫码支付
+ * @method Collection       transfer(array $config)     企业付款
+ * @method RedirectResponse web(array $config)          Web 扫码支付
+ * @method RedirectResponse wap(array $config)          H5 支付
+ */
+class Wechat implements GatewayApplicationInterface
+{
+    /**
+     * 普通模式.
+     */
+    const MODE_NORMAL = 'normal';
+
+    /**
+     * 沙箱模式.
+     */
+    const MODE_DEV = 'dev';
+
+    /**
+     * 香港钱包 API.
+     */
+    const MODE_HK = 'hk';
+
+    /**
+     * 境外 API.
+     */
+    const MODE_US = 'us';
+
+    /**
+     * 服务商模式.
+     */
+    const MODE_SERVICE = 'service';
+
+    /**
+     * Const url.
+     */
+    const URL = [
+        self::MODE_NORMAL => 'https://api.mch.weixin.qq.com/',
+        self::MODE_DEV => 'https://api.mch.weixin.qq.com/sandboxnew/',
+        self::MODE_HK => 'https://apihk.mch.weixin.qq.com/',
+        self::MODE_SERVICE => 'https://api.mch.weixin.qq.com/',
+        self::MODE_US => 'https://apius.mch.weixin.qq.com/',
+    ];
+
+    /**
+     * Wechat payload.
+     *
+     * @var array
+     */
+    protected $payload;
+
+    /**
+     * Wechat gateway.
+     *
+     * @var string
+     */
+    protected $gateway;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function __construct(Config $config)
+    {
+        $this->gateway = Support::create($config)->getBaseUri();
+        $this->payload = [
+            'appid' => $config->get('app_id', ''),
+            'mch_id' => $config->get('mch_id', ''),
+            'nonce_str' => Str::random(),
+            'notify_url' => $config->get('notify_url', ''),
+            'sign' => '',
+            'trade_type' => '',
+            'spbill_create_ip' => Request::createFromGlobals()->getClientIp(),
+        ];
+
+        if ($config->get('mode', self::MODE_NORMAL) === static::MODE_SERVICE) {
+            $this->payload = array_merge($this->payload, [
+                'sub_mch_id' => $config->get('sub_mch_id'),
+                'sub_appid' => $config->get('sub_app_id', ''),
+            ]);
+        }
+    }
+
+    /**
+     * Magic pay.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param string $params
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    public function __call($method, $params)
+    {
+        return self::pay($method, ...$params);
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $gateway
+     * @param array  $params
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    public function pay($gateway, $params = [])
+    {
+        Events::dispatch(new Events\PayStarting('Wechat', $gateway, $params));
+
+        $this->payload = array_merge($this->payload, $params);
+
+        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
+
+        if (class_exists($gateway)) {
+            return $this->makePay($gateway);
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Not Exists");
+    }
+
+    /**
+     * Verify data.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $content
+     *
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function verify($content = null, bool $refund = false): Collection
+    {
+        $content = $content ?? Request::createFromGlobals()->getContent();
+
+        Events::dispatch(new Events\RequestReceived('Wechat', '', [$content]));
+
+        $data = Support::fromXml($content);
+        if ($refund) {
+            $decrypt_data = Support::decryptRefundContents($data['req_info']);
+            $data = array_merge(Support::fromXml($decrypt_data), $data);
+        }
+
+        Log::debug('Resolved The Received Wechat Request Data', $data);
+
+        if ($refund || Support::generateSign($data) === $data['sign']) {
+            return new Collection($data);
+        }
+
+        Events::dispatch(new Events\SignFailed('Wechat', '', $data));
+
+        throw new InvalidSignException('Wechat Sign Verify FAILED', $data);
+    }
+
+    /**
+     * Query an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function find($order, string $type = 'wap'): Collection
+    {
+        if ('wap' != $type) {
+            unset($this->payload['spbill_create_ip']);
+        }
+
+        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
+
+        if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
+            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
+        }
+
+        $config = call_user_func([new $gateway(), 'find'], $order);
+
+        $this->payload = Support::filterPayload($this->payload, $config['order']);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Find', $this->gateway, $this->payload));
+
+        return Support::requestApi(
+            $config['endpoint'],
+            $this->payload,
+            $config['cert']
+        );
+    }
+
+    /**
+     * Refund an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function refund(array $order): Collection
+    {
+        $this->payload = Support::filterPayload($this->payload, $order, true);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Refund', $this->gateway, $this->payload));
+
+        return Support::requestApi(
+            'secapi/pay/refund',
+            $this->payload,
+            true
+        );
+    }
+
+    /**
+     * Cancel an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function cancel($order): Collection
+    {
+        unset($this->payload['spbill_create_ip']);
+
+        $this->payload = Support::filterPayload($this->payload, $order);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Cancel', $this->gateway, $this->payload));
+
+        return Support::requestApi(
+            'secapi/pay/reverse',
+            $this->payload,
+            true
+        );
+    }
+
+    /**
+     * Close an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function close($order): Collection
+    {
+        unset($this->payload['spbill_create_ip']);
+
+        $this->payload = Support::filterPayload($this->payload, $order);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Close', $this->gateway, $this->payload));
+
+        return Support::requestApi('pay/closeorder', $this->payload);
+    }
+
+    /**
+     * Echo success to server.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function success(): Response
+    {
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Success', $this->gateway));
+
+        return new Response(
+            Support::toXml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']),
+            200,
+            ['Content-Type' => 'application/xml']
+        );
+    }
+
+    /**
+     * Download the bill.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     */
+    public function download(array $params): string
+    {
+        unset($this->payload['spbill_create_ip']);
+
+        $this->payload = Support::filterPayload($this->payload, $params, true);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Download', $this->gateway, $this->payload));
+
+        $result = Support::getInstance()->post(
+            'pay/downloadbill',
+            Support::getInstance()->toXml($this->payload)
+        );
+
+        if (is_array($result)) {
+            throw new GatewayException('Get Wechat API Error: '.$result['return_msg'], $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Make pay gateway.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $gateway
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    protected function makePay($gateway)
+    {
+        $app = new $gateway();
+
+        if ($app instanceof GatewayInterface) {
+            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
+                return '' !== $value && !is_null($value);
+            }));
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
+    }
+}

+ 62 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/AppGateway.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Exception;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Str;
+
+class AppGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     * @throws Exception
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $payload['appid'] = Support::getInstance()->appid;
+        $payload['trade_type'] = $this->getTradeType();
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['sub_appid'] = Support::getInstance()->sub_appid;
+        }
+
+        $pay_request = [
+            'appid' => Wechat::MODE_SERVICE === $this->mode ? $payload['sub_appid'] : $payload['appid'],
+            'partnerid' => Wechat::MODE_SERVICE === $this->mode ? $payload['sub_mch_id'] : $payload['mch_id'],
+            'prepayid' => $this->preOrder($payload)->get('prepay_id'),
+            'timestamp' => strval(time()),
+            'noncestr' => Str::random(),
+            'package' => 'Sign=WXPay',
+        ];
+        $pay_request['sign'] = Support::generateSign($pay_request);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'App', $endpoint, $pay_request));
+
+        return new JsonResponse($pay_request);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'APP';
+    }
+}

+ 88 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/Gateway.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+abstract class Gateway implements GatewayInterface
+{
+    /**
+     * Mode.
+     *
+     * @var string
+     */
+    protected $mode;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct()
+    {
+        $this->mode = Support::getInstance()->mode;
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @return Collection
+     */
+    abstract public function pay($endpoint, array $payload);
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     */
+    public function find($order): array
+    {
+        return [
+            'endpoint' => 'pay/orderquery',
+            'order' => is_array($order) ? $order : ['out_trade_no' => $order],
+            'cert' => false,
+        ];
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    abstract protected function getTradeType();
+
+    /**
+     * Schedule an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $payload
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    protected function preOrder($payload): Collection
+    {
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'PreOrder', '', $payload));
+
+        return Support::requestApi('pay/unifiedorder', $payload);
+    }
+}

+ 57 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/GroupRedpackGateway.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class GroupRedpackGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['wxappid'] = $payload['appid'];
+        $payload['amt_type'] = 'ALL_RAND';
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['msgappid'] = $payload['appid'];
+        }
+
+        unset($payload['appid'], $payload['trade_type'],
+              $payload['notify_url'], $payload['spbill_create_ip']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Group Redpack', $endpoint, $payload));
+
+        return Support::requestApi(
+            'mmpaymkttransfers/sendgroupredpack',
+            $payload,
+            true
+        );
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return '';
+    }
+}

+ 35 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/MiniappGateway.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class MiniappGateway extends MpGateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['appid'] = Support::getInstance()->miniapp_id;
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['sub_appid'] = Support::getInstance()->sub_miniapp_id;
+            $this->payRequestUseSubAppId = true;
+        }
+
+        return parent::pay($endpoint, $payload);
+    }
+}

+ 59 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/MpGateway.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Exception;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Str;
+
+class MpGateway extends Gateway
+{
+    /**
+     * @var bool
+     */
+    protected $payRequestUseSubAppId = false;
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     * @throws Exception
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['trade_type'] = $this->getTradeType();
+
+        $pay_request = [
+            'appId' => !$this->payRequestUseSubAppId ? $payload['appid'] : $payload['sub_appid'],
+            'timeStamp' => strval(time()),
+            'nonceStr' => Str::random(),
+            'package' => 'prepay_id='.$this->preOrder($payload)->get('prepay_id'),
+            'signType' => 'MD5',
+        ];
+        $pay_request['paySign'] = Support::generateSign($pay_request);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'JSAPI', $endpoint, $pay_request));
+
+        return new Collection($pay_request);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'JSAPI';
+    }
+}

+ 44 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/PosGateway.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class PosGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        unset($payload['trade_type'], $payload['notify_url']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Pos', $endpoint, $payload));
+
+        return Support::requestApi('pay/micropay', $payload);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'MICROPAY';
+    }
+}

+ 61 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/RedpackGateway.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class RedpackGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['wxappid'] = $payload['appid'];
+
+        if ('cli' !== php_sapi_name()) {
+            $payload['client_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        }
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['msgappid'] = $payload['appid'];
+        }
+
+        unset($payload['appid'], $payload['trade_type'],
+              $payload['notify_url'], $payload['spbill_create_ip']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Redpack', $endpoint, $payload));
+
+        return Support::requestApi(
+            'mmpaymkttransfers/sendredpack',
+            $payload,
+            true
+        );
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return '';
+    }
+}

+ 50 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/RefundGateway.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+
+class RefundGateway extends Gateway
+{
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'endpoint' => 'pay/refundquery',
+            'order' => is_array($order) ? $order : ['out_trade_no' => $order],
+            'cert' => false,
+        ];
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidArgumentException
+     */
+    public function pay($endpoint, array $payload)
+    {
+        throw new InvalidArgumentException('Not Support Refund In Pay');
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    protected function getTradeType()
+    {
+        throw new InvalidArgumentException('Not Support Refund In Pay');
+    }
+}

+ 44 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/ScanGateway.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class ScanGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        $payload['trade_type'] = $this->getTradeType();
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Scan', $endpoint, $payload));
+
+        return $this->preOrder($payload);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'NATIVE';
+    }
+}

+ 449 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/Support.php

@@ -0,0 +1,449 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Exception;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\BusinessException;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Pay\Log;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+use Yansongda\Supports\Traits\HasHttpRequest;
+
+/**
+ * @author yansongda <me@yansongda.cn>
+ *
+ * @property string appid
+ * @property string app_id
+ * @property string miniapp_id
+ * @property string sub_appid
+ * @property string sub_app_id
+ * @property string sub_miniapp_id
+ * @property string mch_id
+ * @property string sub_mch_id
+ * @property string key
+ * @property string return_url
+ * @property string cert_client
+ * @property string cert_key
+ * @property array log
+ * @property array http
+ * @property string mode
+ */
+class Support
+{
+    use HasHttpRequest;
+
+    /**
+     * Wechat gateway.
+     *
+     * @var string
+     */
+    protected $baseUri;
+
+    /**
+     * Config.
+     *
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * Instance.
+     *
+     * @var Support
+     */
+    private static $instance;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    private function __construct(Config $config)
+    {
+        $this->baseUri = Wechat::URL[$config->get('mode', Wechat::MODE_NORMAL)];
+        $this->config = $config;
+
+        $this->setHttpOptions();
+    }
+
+    /**
+     * __get.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $key
+     *
+     * @return mixed|Config|null
+     */
+    public function __get($key)
+    {
+        return $this->getConfig($key);
+    }
+
+    /**
+     * create.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     *
+     * @return Support
+     */
+    public static function create(Config $config)
+    {
+        if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
+            self::$instance = new self($config);
+
+            self::setDevKey();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * getInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return Support
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$instance)) {
+            throw new InvalidArgumentException('You Should [Create] First Before Using');
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * clear.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function clear()
+    {
+        self::$instance = null;
+    }
+
+    /**
+     * Request wechat api.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     * @param array  $data
+     * @param bool   $cert
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public static function requestApi($endpoint, $data, $cert = false): Collection
+    {
+        Events::dispatch(new Events\ApiRequesting('Wechat', '', self::$instance->getBaseUri().$endpoint, $data));
+
+        $result = self::$instance->post(
+            $endpoint,
+            self::toXml($data),
+            $cert ? [
+                'cert' => self::$instance->cert_client,
+                'ssl_key' => self::$instance->cert_key,
+            ] : []
+        );
+        $result = is_array($result) ? $result : self::fromXml($result);
+
+        Events::dispatch(new Events\ApiRequested('Wechat', '', self::$instance->getBaseUri().$endpoint, $result));
+
+        return self::processingApiResult($endpoint, $result);
+    }
+
+    /**
+     * Filter payload.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array        $payload
+     * @param array|string $params
+     * @param bool         $preserve_notify_url
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function filterPayload($payload, $params, $preserve_notify_url = false): array
+    {
+        $type = self::getTypeName($params['type'] ?? '');
+
+        $payload = array_merge(
+            $payload,
+            is_array($params) ? $params : ['out_trade_no' => $params]
+        );
+        $payload['appid'] = self::$instance->getConfig($type, '');
+
+        if (Wechat::MODE_SERVICE === self::$instance->getConfig('mode', Wechat::MODE_NORMAL)) {
+            $payload['sub_appid'] = self::$instance->getConfig('sub_'.$type, '');
+        }
+
+        unset($payload['trade_type'], $payload['type']);
+        if (!$preserve_notify_url) {
+            unset($payload['notify_url']);
+        }
+
+        $payload['sign'] = self::generateSign($payload);
+
+        return $payload;
+    }
+
+    /**
+     * Generate wechat sign.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $data
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function generateSign($data): string
+    {
+        $key = self::$instance->key;
+
+        if (is_null($key)) {
+            throw new InvalidArgumentException('Missing Wechat Config -- [key]');
+        }
+
+        ksort($data);
+
+        $string = md5(self::getSignContent($data).'&key='.$key);
+
+        Log::debug('Wechat Generate Sign Before UPPER', [$data, $string]);
+
+        return strtoupper($string);
+    }
+
+    /**
+     * Generate sign content.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $data
+     */
+    public static function getSignContent($data): string
+    {
+        $buff = '';
+
+        foreach ($data as $k => $v) {
+            $buff .= ('sign' != $k && '' != $v && !is_array($v)) ? $k.'='.$v.'&' : '';
+        }
+
+        Log::debug('Wechat Generate Sign Content Before Trim', [$data, $buff]);
+
+        return trim($buff, '&');
+    }
+
+    /**
+     * Decrypt refund contents.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $contents
+     */
+    public static function decryptRefundContents($contents): string
+    {
+        return openssl_decrypt(
+            base64_decode($contents),
+            'AES-256-ECB',
+            md5(self::$instance->key),
+            OPENSSL_RAW_DATA
+        );
+    }
+
+    /**
+     * Convert array to xml.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $data
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function toXml($data): string
+    {
+        if (!is_array($data) || count($data) <= 0) {
+            throw new InvalidArgumentException('Convert To Xml Error! Invalid Array!');
+        }
+
+        $xml = '<xml>';
+        foreach ($data as $key => $val) {
+            $xml .= is_numeric($val) ? '<'.$key.'>'.$val.'</'.$key.'>' :
+                                       '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
+        }
+        $xml .= '</xml>';
+
+        return $xml;
+    }
+
+    /**
+     * Convert xml to array.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $xml
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function fromXml($xml): array
+    {
+        if (!$xml) {
+            throw new InvalidArgumentException('Convert To Array Error! Invalid Xml!');
+        }
+
+        libxml_disable_entity_loader(true);
+
+        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
+    }
+
+    /**
+     * Get service config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $key
+     * @param mixed|null  $default
+     *
+     * @return mixed|null
+     */
+    public function getConfig($key = null, $default = null)
+    {
+        if (is_null($key)) {
+            return $this->config->all();
+        }
+
+        if ($this->config->has($key)) {
+            return $this->config[$key];
+        }
+
+        return $default;
+    }
+
+    /**
+     * Get app id according to param type.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $type
+     */
+    public static function getTypeName($type = ''): string
+    {
+        switch ($type) {
+            case '':
+                $type = 'app_id';
+                break;
+            case 'app':
+                $type = 'appid';
+                break;
+            default:
+                $type = $type.'_id';
+        }
+
+        return $type;
+    }
+
+    /**
+     * Get Base Uri.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    public function getBaseUri()
+    {
+        return $this->baseUri;
+    }
+
+    /**
+     * processingApiResult.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     *
+     * @return Collection
+     */
+    protected static function processingApiResult($endpoint, array $result)
+    {
+        if (!isset($result['return_code']) || 'SUCCESS' != $result['return_code']) {
+            throw new GatewayException('Get Wechat API Error:'.($result['return_msg'] ?? $result['retmsg'] ?? ''), $result);
+        }
+
+        if (isset($result['result_code']) && 'SUCCESS' != $result['result_code']) {
+            throw new BusinessException('Wechat Business Error: '.$result['err_code'].' - '.$result['err_code_des'], $result);
+        }
+
+        if ('pay/getsignkey' === $endpoint ||
+            false !== strpos($endpoint, 'mmpaymkttransfers') ||
+            self::generateSign($result) === $result['sign']) {
+            return new Collection($result);
+        }
+
+        Events::dispatch(new Events\SignFailed('Wechat', '', $result));
+
+        throw new InvalidSignException('Wechat Sign Verify FAILED', $result);
+    }
+
+    /**
+     * setDevKey.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     * @throws Exception
+     *
+     * @return Support
+     */
+    private static function setDevKey()
+    {
+        if (Wechat::MODE_DEV == self::$instance->mode) {
+            $data = [
+                'mch_id' => self::$instance->mch_id,
+                'nonce_str' => Str::random(),
+            ];
+            $data['sign'] = self::generateSign($data);
+
+            $result = self::requestApi('pay/getsignkey', $data);
+
+            self::$instance->config->set('key', $result['sandbox_signkey']);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Set Http options.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    private function setHttpOptions(): self
+    {
+        if ($this->config->has('http') && is_array($this->config->get('http'))) {
+            $this->config->forget('http.base_uri');
+            $this->httpOptions = $this->config->get('http');
+        }
+
+        return $this;
+    }
+}

+ 80 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/TransferGateway.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class TransferGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            unset($payload['sub_mch_id'], $payload['sub_appid']);
+        }
+
+        $type = Support::getTypeName($payload['type'] ?? '');
+
+        $payload['mch_appid'] = Support::getInstance()->getConfig($type, '');
+        $payload['mchid'] = $payload['mch_id'];
+
+        if ('cli' !== php_sapi_name() && !isset($payload['spbill_create_ip'])) {
+            $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        }
+
+        unset($payload['appid'], $payload['mch_id'], $payload['trade_type'],
+            $payload['notify_url'], $payload['type']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Transfer', $endpoint, $payload));
+
+        return Support::requestApi(
+            'mmpaymkttransfers/promotion/transfers',
+            $payload,
+            true
+        );
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'endpoint' => 'mmpaymkttransfers/gettransferinfo',
+            'order' => is_array($order) ? $order : ['partner_trade_no' => $order],
+            'cert' => true,
+        ];
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return '';
+    }
+}

+ 47 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/WapGateway.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+
+class WapGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): RedirectResponse
+    {
+        $payload['trade_type'] = $this->getTradeType();
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Wap', $endpoint, $payload));
+
+        $mweb_url = $this->preOrder($payload)->get('mweb_url');
+
+        $url = is_null(Support::getInstance()->return_url) ? $mweb_url : $mweb_url.
+                        '&redirect_url='.urlencode(Support::getInstance()->return_url);
+
+        return new RedirectResponse($url);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'MWEB';
+    }
+}

+ 86 - 0
addons/epay/library/Yansongda/Pay/Gateways/Wechat/WebGateway.php

@@ -0,0 +1,86 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class WebGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @param string $endpoint
+     * @param array  $payload
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        $payload['trade_type'] = $this->getTradeType();
+
+        $code_url = $this->preOrder($payload)['code_url'];
+        $params = [
+            'body'         => $payload['body'],
+            'code_url'     => $code_url,
+            'out_trade_no' => $payload['out_trade_no'],
+            'return_url'   => Support::getInstance()->return_url,
+            'total_fee'    => $payload['total_fee'],
+        ];
+
+        $params['sign'] = md5(implode('', $params) . Support::getInstance()->app_id);
+        $endpoint = addon_url("epay/api/wechat");
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Web/Wap', $endpoint, $payload));
+
+        return $this->buildPayHtml($endpoint, $params);
+    }
+
+    /**
+     * Build Html response.
+     *
+     * @param string $endpoint
+     * @param array  $payload
+     * @param string $method
+     *
+     * @return Response
+     * @author yansongda <me@yansongda.cn>
+     *
+     */
+    protected function buildPayHtml($endpoint, $payload, $method = 'POST'): Response
+    {
+        if (strtoupper($method) === 'GET') {
+            return RedirectResponse::create($endpoint . '?' . http_build_query($payload));
+        }
+
+        $sHtml = "<form id='wechat_submit' name='wechat_submit' action='" . $endpoint . "' method='" . $method . "'>";
+        foreach ($payload as $key => $val) {
+            $val = str_replace("'", '&apos;', $val);
+            $sHtml .= "<input type='hidden' name='" . $key . "' value='" . $val . "'/>";
+        }
+        $sHtml .= "<input type='submit' value='ok' style='display:none;'></form>";
+        $sHtml .= "<script>document.forms['wechat_submit'].submit();</script>";
+
+        return Response::create($sHtml);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @return string
+     * @author yansongda <me@yansongda.cn>
+     *
+     */
+    protected function getTradeType(): string
+    {
+        return 'NATIVE';
+    }
+}

+ 20 - 0
addons/epay/library/Yansongda/Pay/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yansongda <me@yansongda.cn>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 114 - 0
addons/epay/library/Yansongda/Pay/Listeners/KernelLogSubscriber.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace Yansongda\Pay\Listeners;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Log;
+
+class KernelLogSubscriber implements EventSubscriberInterface
+{
+    /**
+     * Returns an array of event names this subscriber wants to listen to.
+     *
+     * The array keys are event names and the value can be:
+     *
+     *  * The method name to call (priority defaults to 0)
+     *  * An array composed of the method name to call and the priority
+     *  * An array of arrays composed of the method names to call and respective
+     *    priorities, or 0 if unset
+     *
+     * For instance:
+     *
+     *  * array('eventName' => 'methodName')
+     *  * array('eventName' => array('methodName', $priority))
+     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
+     *
+     * @return array The event names to listen to
+     */
+    public static function getSubscribedEvents()
+    {
+        return [
+            Events\PayStarting::class => ['writePayStartingLog', 256],
+            Events\PayStarted::class => ['writePayStartedLog', 256],
+            Events\ApiRequesting::class => ['writeApiRequestingLog', 256],
+            Events\ApiRequested::class => ['writeApiRequestedLog', 256],
+            Events\SignFailed::class => ['writeSignFailedLog', 256],
+            Events\RequestReceived::class => ['writeRequestReceivedLog', 256],
+            Events\MethodCalled::class => ['writeMethodCalledLog', 256],
+        ];
+    }
+
+    /**
+     * writePayStartingLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writePayStartingLog(Events\PayStarting $event)
+    {
+        Log::debug("Starting To {$event->driver}", [$event->gateway, $event->params]);
+    }
+
+    /**
+     * writePayStartedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writePayStartedLog(Events\PayStarted $event)
+    {
+        Log::info(
+            "{$event->driver} {$event->gateway} Has Started",
+            [$event->endpoint, $event->payload]
+        );
+    }
+
+    /**
+     * writeApiRequestingLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeApiRequestingLog(Events\ApiRequesting $event)
+    {
+        Log::debug("Requesting To {$event->driver} Api", [$event->endpoint, $event->payload]);
+    }
+
+    /**
+     * writeApiRequestedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeApiRequestedLog(Events\ApiRequested $event)
+    {
+        Log::debug("Result Of {$event->driver} Api", $event->result);
+    }
+
+    /**
+     * writeSignFailedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeSignFailedLog(Events\SignFailed $event)
+    {
+        Log::warning("{$event->driver} Sign Verify FAILED", $event->data);
+    }
+
+    /**
+     * writeRequestReceivedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeRequestReceivedLog(Events\RequestReceived $event)
+    {
+        Log::info("Received {$event->driver} Request", $event->data);
+    }
+
+    /**
+     * writeMethodCalledLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeMethodCalledLog(Events\MethodCalled $event)
+    {
+        Log::info("{$event->driver} {$event->gateway} Method Has Called", [$event->endpoint, $event->payload]);
+    }
+}

+ 49 - 0
addons/epay/library/Yansongda/Pay/Log.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Yansongda\Pay;
+
+use Yansongda\Supports\Log as BaseLog;
+
+/**
+ * @method static void emergency($message, array $context = array())
+ * @method static void alert($message, array $context = array())
+ * @method static void critical($message, array $context = array())
+ * @method static void error($message, array $context = array())
+ * @method static void warning($message, array $context = array())
+ * @method static void notice($message, array $context = array())
+ * @method static void info($message, array $context = array())
+ * @method static void debug($message, array $context = array())
+ * @method static void log($message, array $context = array())
+ */
+class Log
+{
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @return mixed
+     */
+    public static function __callStatic($method, $args)
+    {
+        return forward_static_call_array([BaseLog::class, $method], $args);
+    }
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @return mixed
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([BaseLog::class, $method], $args);
+    }
+}

+ 131 - 0
addons/epay/library/Yansongda/Pay/Pay.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace Yansongda\Pay;
+
+use Exception;
+use Yansongda\Pay\Contracts\GatewayApplicationInterface;
+use Yansongda\Pay\Exceptions\InvalidGatewayException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Pay\Listeners\KernelLogSubscriber;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Log;
+use Yansongda\Supports\Logger;
+use Yansongda\Supports\Str;
+
+/**
+ * @method static Alipay alipay(array $config) 支付宝
+ * @method static Wechat wechat(array $config) 微信
+ */
+class Pay
+{
+    /**
+     * Config.
+     *
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function __construct(array $config)
+    {
+        $this->config = new Config($config);
+
+        $this->registerLogService();
+        $this->registerEventService();
+    }
+
+    /**
+     * Magic static call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $params
+     *
+     * @throws InvalidGatewayException
+     * @throws Exception
+     */
+    public static function __callStatic($method, $params): GatewayApplicationInterface
+    {
+        $app = new self(...$params);
+
+        return $app->create($method);
+    }
+
+    /**
+     * Create a instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     *
+     * @throws InvalidGatewayException
+     */
+    protected function create($method): GatewayApplicationInterface
+    {
+        $gateway = __NAMESPACE__.'\\Gateways\\'.Str::studly($method);
+
+        if (class_exists($gateway)) {
+            return self::make($gateway);
+        }
+
+        throw new InvalidGatewayException("Gateway [{$method}] Not Exists");
+    }
+
+    /**
+     * Make a gateway.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string $gateway
+     *
+     * @throws InvalidGatewayException
+     */
+    protected function make($gateway): GatewayApplicationInterface
+    {
+        $app = new $gateway($this->config);
+
+        if ($app instanceof GatewayApplicationInterface) {
+            return $app;
+        }
+
+        throw new InvalidGatewayException("Gateway [{$gateway}] Must Be An Instance Of GatewayApplicationInterface");
+    }
+
+    /**
+     * Register log service.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    protected function registerLogService()
+    {
+        $config = $this->config->get('log');
+        $config['identify'] = 'yansongda.pay';
+
+        $logger = new Logger();
+        $logger->setConfig($config);
+
+        Log::setInstance($logger);
+    }
+
+    /**
+     * Register event service.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function registerEventService()
+    {
+        Events::setDispatcher(Events::createDispatcher());
+
+        Events::addSubscriber(new KernelLogSubscriber());
+    }
+}

+ 605 - 0
addons/epay/library/Yansongda/Supports/Arr.php

@@ -0,0 +1,605 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use ArrayAccess;
+
+/**
+ * Array helper from Illuminate\Support\Arr.
+ */
+class Arr
+{
+    /**
+     * Determine whether the given value is array accessible.
+     *
+     * @param mixed $value
+     */
+    public static function accessible($value): bool
+    {
+        return is_array($value) || $value instanceof ArrayAccess;
+    }
+
+    /**
+     * Add an element to an array using "dot" notation if it doesn't exist.
+     *
+     * @param mixed $value
+     */
+    public static function add(array $array, string $key, $value): array
+    {
+        if (is_null(static::get($array, $key))) {
+            static::set($array, $key, $value);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Build a new array using a callback.
+     */
+    public static function build(array $array, callable $callback): array
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            [$innerKey, $innerValue] = call_user_func($callback, $key, $value);
+            $results[$innerKey] = $innerValue;
+        }
+
+        return $results;
+    }
+
+    /**
+     * Divide an array into two arrays. One with keys and the other with values.
+     */
+    public static function divide(array $array): array
+    {
+        return [
+                array_keys($array),
+                array_values($array),
+               ];
+    }
+
+    /**
+     * Flatten a multi-dimensional associative array with dots.
+     */
+    public static function dot(array $array, string $prepend = ''): array
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            if (is_array($value)) {
+                $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+            } else {
+                $results[$prepend.$key] = $value;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get all of the given array except for a specified array of items.
+     *
+     * @param array|string $keys
+     */
+    public static function except(array $array, $keys): array
+    {
+        return array_diff_key($array, array_flip((array) $keys));
+    }
+
+    /**
+     * access array.
+     *
+     * if not array access, return original.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $data
+     *
+     * @return mixed
+     */
+    public static function access($data)
+    {
+        if (!self::accessible($data) &&
+            !(is_object($data) && method_exists($data, 'toArray'))) {
+            return $data;
+        }
+
+        return is_object($data) ? $data->toArray() : $data;
+    }
+
+    /**
+     * Determine if the given key exists in the provided array.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|int         $key
+     *
+     * @return bool
+     */
+    public static function exists($array, $key)
+    {
+        $array = self::access($array);
+
+        if ($array instanceof ArrayAccess) {
+            return $array->offsetExists($key);
+        }
+
+        return array_key_exists($key, $array);
+    }
+
+    /**
+     * Check if an item or items exist in an array using "dot" notation.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|array       $keys
+     *
+     * @return bool
+     */
+    public static function has($array, $keys)
+    {
+        $array = self::access($array);
+
+        $keys = (array) $keys;
+
+        if (!$array || $keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            $subKeyArray = $array;
+
+            if (static::exists($array, $key)) {
+                continue;
+            }
+
+            foreach (explode('.', $key) as $segment) {
+                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+                    $subKeyArray = $subKeyArray[$segment];
+                } else {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if any of the keys exist in an array using "dot" notation.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|array       $keys
+     *
+     * @return bool
+     */
+    public static function hasAny($array, $keys)
+    {
+        $array = self::access($array);
+
+        if (is_null($keys)) {
+            return false;
+        }
+
+        $keys = (array) $keys;
+
+        if (!$array) {
+            return false;
+        }
+
+        if ($keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            if (static::has($array, $key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Fetch a flattened array of a nested array element.
+     */
+    public static function fetch(array $array, string $key): array
+    {
+        $results = [];
+
+        foreach (explode('.', $key) as $segment) {
+            $results = [];
+            foreach ($array as $value) {
+                $value = (array) $value;
+                $results[] = $value[$segment];
+            }
+            $array = array_values($results);
+        }
+
+        return array_values($results);
+    }
+
+    /**
+     * Return the first element in an array passing a given truth test.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function first(array $array, callable $callback, $default = null)
+    {
+        foreach ($array as $key => $value) {
+            if (call_user_func($callback, $key, $value)) {
+                return $value;
+            }
+        }
+
+        return $default;
+    }
+
+    /**
+     * Return the last element in an array passing a given truth test.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function last(array $array, callable $callback, $default = null)
+    {
+        return static::first(array_reverse($array), $callback, $default);
+    }
+
+    /**
+     * Flatten a multi-dimensional array into a single level.
+     */
+    public static function flatten(array $array): array
+    {
+        $return = [];
+        array_walk_recursive(
+            $array,
+            function ($x) use (&$return) {
+                $return[] = $x;
+            }
+        );
+
+        return $return;
+    }
+
+    /**
+     * Remove one or many array items from a given array using "dot" notation.
+     *
+     * @param array        $array
+     * @param array|string $keys
+     */
+    public static function forget(&$array, $keys)
+    {
+        $original = &$array;
+
+        $keys = (array) $keys;
+
+        if (0 === count($keys)) {
+            return;
+        }
+
+        foreach ($keys as $key) {
+            // if the exact key exists in the top-level, remove it
+            if (static::exists($array, $key)) {
+                unset($array[$key]);
+
+                continue;
+            }
+
+            $parts = explode('.', $key);
+
+            // clean up before each pass
+            $array = &$original;
+
+            while (count($parts) > 1) {
+                $part = array_shift($parts);
+
+                if (isset($array[$part]) && is_array($array[$part])) {
+                    $array = &$array[$part];
+                } else {
+                    continue 2;
+                }
+            }
+
+            unset($array[array_shift($parts)]);
+        }
+    }
+
+    /**
+     * Get an item from an array using "dot" notation.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function get(array $array, string $key, $default = null)
+    {
+        if (is_null($key)) {
+            return $array;
+        }
+
+        if (isset($array[$key])) {
+            return $array[$key];
+        }
+
+        foreach (explode('.', $key) as $segment) {
+            if (!is_array($array) || !array_key_exists($segment, $array)) {
+                return $default;
+            }
+            $array = $array[$segment];
+        }
+
+        return $array;
+    }
+
+    /**
+     * Get a subset of the items from the given array.
+     *
+     * @param array|string $keys
+     */
+    public static function only(array $array, $keys): array
+    {
+        return array_intersect_key($array, array_flip((array) $keys));
+    }
+
+    /**
+     * Pluck an array of values from an array.
+     *
+     * @param string $key
+     */
+    public static function pluck(array $array, string $value, string $key = null): array
+    {
+        $results = [];
+
+        foreach ($array as $item) {
+            $itemValue = is_object($item) ? $item->{$value} : $item[$value];
+            // If the key is "null", we will just append the value to the array and keep
+            // looping. Otherwise we will key the array using the value of the key we
+            // received from the developer. Then we'll return the final array form.
+            if (is_null($key)) {
+                $results[] = $itemValue;
+            } else {
+                $itemKey = is_object($item) ? $item->{$key} : $item[$key];
+                $results[$itemKey] = $itemValue;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Push an item onto the beginning of an array.
+     *
+     * @param mixed $value
+     * @param mixed $key
+     *
+     * @return array
+     */
+    public static function prepend(array $array, $value, $key = null)
+    {
+        if (is_null($key)) {
+            array_unshift($array, $value);
+        } else {
+            $array = [$key => $value] + $array;
+        }
+
+        return $array;
+    }
+
+    /**
+     * Get a value from the array, and remove it.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function pull(array &$array, string $key, $default = null)
+    {
+        $value = static::get($array, $key, $default);
+
+        static::forget($array, $key);
+
+        return $value;
+    }
+
+    /**
+     * Get one or a specified number of random values from an array.
+     *
+     * @param array    $array
+     * @param int|null $number
+     *
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function random(array $array, $number = null)
+    {
+        $requested = is_null($number) ? 1 : $number;
+
+        $count = count($array);
+
+        $number = $requested > $count ? $count : $requested;
+
+        if (is_null($number)) {
+            return $array[array_rand($array)];
+        }
+
+        if (0 === (int) $number) {
+            return [];
+        }
+
+        $keys = array_rand($array, $number);
+
+        $results = [];
+
+        foreach ((array) $keys as $key) {
+            $results[] = $array[$key];
+        }
+
+        return $results;
+    }
+
+    /**
+     * Set an array item to a given value using "dot" notation.
+     *
+     * If no key is given to the method, the entire array will be replaced.
+     *
+     * @param mixed $value
+     */
+    public static function set(array &$array, string $key, $value): array
+    {
+        if (is_null($key)) {
+            return $array = $value;
+        }
+
+        $keys = explode('.', $key);
+
+        while (count($keys) > 1) {
+            $key = array_shift($keys);
+            // If the key doesn't exist at this depth, we will just create an empty array
+            // to hold the next value, allowing us to create the arrays to hold final
+            // values at the correct depth. Then we'll keep digging into the array.
+            if (!isset($array[$key]) || !is_array($array[$key])) {
+                $array[$key] = [];
+            }
+            $array = &$array[$key];
+        }
+        $array[array_shift($keys)] = $value;
+
+        return $array;
+    }
+
+    /**
+     * Sort the array using the given Closure.
+     */
+    public static function sort(array $array, callable $callback): array
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            $results[$key] = $callback($value);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Shuffle the given array and return the result.
+     *
+     * @param array    $array
+     * @param int|null $seed
+     *
+     * @return array
+     */
+    public static function shuffle(array $array, $seed = null): array
+    {
+        if (is_null($seed)) {
+            shuffle($array);
+        } else {
+            mt_srand($seed);
+            shuffle($array);
+            mt_srand();
+        }
+
+        return $array;
+    }
+
+    /**
+     * Convert the array into a query string.
+     */
+    public static function query(array $array): string
+    {
+        return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
+    }
+
+    /**
+     * Filter the array using the given callback.
+     */
+    public static function where(array $array, ?callable $callback = null): array
+    {
+        return array_filter($array, $callback ?? function ($value) use ($callback) {
+            if (static::accessible($value)) {
+                $value = static::where($value, $callback);
+            }
+
+            if (is_array($value) && 0 === count($value)) {
+                $value = null;
+            }
+
+            return '' !== $value && !is_null($value);
+        }, ARRAY_FILTER_USE_BOTH);
+    }
+
+    /**
+     * Convert encoding.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $from_encoding
+     */
+    public static function encoding(array $array, string $to_encoding, $from_encoding = 'gb2312'): array
+    {
+        $encoded = [];
+
+        foreach ($array as $key => $value) {
+            $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) :
+                                                mb_convert_encoding($value, $to_encoding, $from_encoding);
+        }
+
+        return $encoded;
+    }
+
+    /**
+     * camelCaseKey.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $data
+     *
+     * @return mixed
+     */
+    public static function camelCaseKey($data)
+    {
+        if (!self::accessible($data) &&
+            !(is_object($data) && method_exists($data, 'toArray'))) {
+            return $data;
+        }
+
+        $result = [];
+        $data = self::access($data);
+
+        foreach ($data as $key => $value) {
+            $result[is_string($key) ? Str::camel($key) : $key] = self::camelCaseKey($value);
+        }
+
+        return $result;
+    }
+
+    /**
+     * snakeCaseKey.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $data
+     *
+     * @return mixed
+     */
+    public static function snakeCaseKey($data)
+    {
+        if (!self::accessible($data) &&
+            !(is_object($data) && method_exists($data, 'toArray'))) {
+            return $data;
+        }
+
+        $data = self::access($data);
+        $result = [];
+
+        foreach ($data as $key => $value) {
+            $result[is_string($key) ? Str::snake($key) : $key] = self::snakeCaseKey($value);
+        }
+
+        return $result;
+    }
+}

+ 363 - 0
addons/epay/library/Yansongda/Supports/Collection.php

@@ -0,0 +1,363 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use ArrayAccess;
+use ArrayIterator;
+use Countable;
+use IteratorAggregate;
+use JsonSerializable;
+use Serializable;
+
+class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Serializable
+{
+    /**
+     * The collection data.
+     *
+     * @var array
+     */
+    protected $items = [];
+
+    /**
+     * set data.
+     *
+     * @param mixed $items
+     */
+    public function __construct(array $items = [])
+    {
+        foreach ($items as $key => $value) {
+            $this->set($key, $value);
+        }
+    }
+
+    /**
+     * To string.
+     */
+    public function __toString(): string
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * Get a data by key.
+     *
+     * @return mixed
+     */
+    public function __get(string $key)
+    {
+        return $this->get($key);
+    }
+
+    /**
+     * Assigns a value to the specified data.
+     *
+     * @param mixed $value
+     */
+    public function __set(string $key, $value)
+    {
+        $this->set($key, $value);
+    }
+
+    /**
+     * Whether or not an data exists by key.
+     */
+    public function __isset(string $key): bool
+    {
+        return $this->has($key);
+    }
+
+    /**
+     * Unsets an data by key.
+     */
+    public function __unset(string $key)
+    {
+        $this->forget($key);
+    }
+
+    /**
+     * Return all items.
+     */
+    public function all(): array
+    {
+        return $this->items;
+    }
+
+    /**
+     * Return specific items.
+     */
+    public function only(array $keys): array
+    {
+        $return = [];
+
+        foreach ($keys as $key) {
+            $value = $this->get($key);
+
+            if (!is_null($value)) {
+                $return[$key] = $value;
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param mixed $keys
+     *
+     * @return static
+     */
+    public function except($keys)
+    {
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        return new static(Arr::except($this->items, $keys));
+    }
+
+    /**
+     * Merge data.
+     *
+     * @param Collection|array $items
+     */
+    public function merge($items): array
+    {
+        foreach ($items as $key => $value) {
+            $this->set($key, $value);
+        }
+
+        return $this->all();
+    }
+
+    /**
+     * To determine Whether the specified element exists.
+     */
+    public function has(string $key): bool
+    {
+        return !is_null(Arr::get($this->items, $key));
+    }
+
+    /**
+     * Retrieve the first item.
+     *
+     * @return mixed
+     */
+    public function first()
+    {
+        return reset($this->items);
+    }
+
+    /**
+     * Retrieve the last item.
+     *
+     * @return mixed
+     */
+    public function last()
+    {
+        $end = end($this->items);
+
+        reset($this->items);
+
+        return $end;
+    }
+
+    /**
+     * add the item value.
+     *
+     * @param mixed $value
+     */
+    public function add(string $key, $value)
+    {
+        Arr::set($this->items, $key, $value);
+    }
+
+    /**
+     * Set the item value.
+     *
+     * @param mixed $value
+     */
+    public function set(string $key, $value)
+    {
+        Arr::set($this->items, $key, $value);
+    }
+
+    /**
+     * Retrieve item from Collection.
+     *
+     * @param string $key
+     * @param mixed  $default
+     *
+     * @return mixed
+     */
+    public function get(?string $key = null, $default = null)
+    {
+        return Arr::get($this->items, $key, $default);
+    }
+
+    /**
+     * Remove item form Collection.
+     */
+    public function forget(string $key)
+    {
+        Arr::forget($this->items, $key);
+    }
+
+    /**
+     * Build to array.
+     */
+    public function toArray(): array
+    {
+        return $this->all();
+    }
+
+    /**
+     * Build to json.
+     */
+    public function toJson(int $option = JSON_UNESCAPED_UNICODE): string
+    {
+        return json_encode($this->all(), $option);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON.
+     *
+     * @see http://php.net/manual/en/jsonserializable.jsonserialize.php
+     *
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     *               which is a value of any type other than a resource
+     */
+    public function jsonSerialize()
+    {
+        return $this->items;
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * String representation of object.
+     *
+     * @see http://php.net/manual/en/serializable.serialize.php
+     *
+     * @return string the string representation of the object or null
+     */
+    public function serialize()
+    {
+        return serialize($this->items);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Retrieve an external iterator.
+     *
+     * @see http://php.net/manual/en/iteratoraggregate.getiterator.php
+     *
+     * @return ArrayIterator An instance of an object implementing <b>Iterator</b> or
+     *                       <b>ArrayIterator</b>
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Count elements of an object.
+     *
+     * @see http://php.net/manual/en/countable.count.php
+     *
+     * @return int The custom count as an integer.
+     *             </p>
+     *             <p>
+     *             The return value is cast to an integer
+     */
+    public function count()
+    {
+        return count($this->items);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Constructs the object.
+     *
+     * @see  http://php.net/manual/en/serializable.unserialize.php
+     *
+     * @param string $serialized <p>
+     *                           The string representation of the object.
+     *                           </p>
+     *
+     * @return mixed|void
+     */
+    public function unserialize($serialized)
+    {
+        return $this->items = unserialize($serialized);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Whether a offset exists.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetexists.php
+     *
+     * @param mixed $offset <p>
+     *                      An offset to check for.
+     *                      </p>
+     *
+     * @return bool true on success or false on failure.
+     *              The return value will be casted to boolean if non-boolean was returned
+     */
+    public function offsetExists($offset)
+    {
+        return $this->has($offset);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Offset to unset.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetunset.php
+     *
+     * @param mixed $offset <p>
+     *                      The offset to unset.
+     *                      </p>
+     */
+    public function offsetUnset($offset)
+    {
+        if ($this->offsetExists($offset)) {
+            $this->forget($offset);
+        }
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Offset to retrieve.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetget.php
+     *
+     * @param mixed $offset <p>
+     *                      The offset to retrieve.
+     *                      </p>
+     *
+     * @return mixed Can return all value types
+     */
+    public function offsetGet($offset)
+    {
+        return $this->offsetExists($offset) ? $this->get($offset) : null;
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Offset to set.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetset.php
+     *
+     * @param mixed $offset <p>
+     *                      The offset to assign the value to.
+     *                      </p>
+     * @param mixed $value  <p>
+     *                      The value to set.
+     *                      </p>
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->set($offset, $value);
+    }
+}

+ 7 - 0
addons/epay/library/Yansongda/Supports/Config.php

@@ -0,0 +1,7 @@
+<?php
+
+namespace Yansongda\Supports;
+
+class Config extends Collection
+{
+}

+ 20 - 0
addons/epay/library/Yansongda/Supports/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yansongda <me@yansongda.cn>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 91 - 0
addons/epay/library/Yansongda/Supports/Log.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace Yansongda\Supports;
+
+/**
+ * @method static void emergency($message, array $context = array())
+ * @method static void alert($message, array $context = array())
+ * @method static void critical($message, array $context = array())
+ * @method static void error($message, array $context = array())
+ * @method static void warning($message, array $context = array())
+ * @method static void notice($message, array $context = array())
+ * @method static void info($message, array $context = array())
+ * @method static void debug($message, array $context = array())
+ * @method static void log($message, array $context = array())
+ */
+class Log extends Logger
+{
+    /**
+     * instance.
+     *
+     * @var \Psr\Log\LoggerInterface
+     */
+    private static $instance;
+
+    /**
+     * Bootstrap.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * __call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws \Exception
+     */
+    public function __call($method, $args): void
+    {
+        call_user_func_array([self::getInstance(), $method], $args);
+    }
+
+    /**
+     * __callStatic.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws \Exception
+     */
+    public static function __callStatic($method, $args): void
+    {
+        forward_static_call_array([self::getInstance(), $method], $args);
+    }
+
+    /**
+     * getInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return \Yansongda\Supports\Logger
+     */
+    public static function getInstance(): Logger
+    {
+        if (is_null(self::$instance)) {
+            self::$instance = new Logger();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * setInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param \Yansongda\Supports\Logger $logger
+     *
+     * @throws \Exception
+     */
+    public static function setInstance(Logger $logger): void
+    {
+        self::$instance = $logger;
+    }
+}

+ 240 - 0
addons/epay/library/Yansongda/Supports/Logger.php

@@ -0,0 +1,240 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use Exception;
+use Monolog\Formatter\FormatterInterface;
+use Monolog\Formatter\LineFormatter;
+use Monolog\Handler\AbstractHandler;
+use Monolog\Handler\RotatingFileHandler;
+use Monolog\Handler\StreamHandler;
+use Monolog\Logger as BaseLogger;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @method void emergency($message, array $context = array())
+ * @method void alert($message, array $context = array())
+ * @method void critical($message, array $context = array())
+ * @method void error($message, array $context = array())
+ * @method void warning($message, array $context = array())
+ * @method void notice($message, array $context = array())
+ * @method void info($message, array $context = array())
+ * @method void debug($message, array $context = array())
+ * @method void log($message, array $context = array())
+ */
+class Logger
+{
+    /**
+     * Logger instance.
+     *
+     * @var LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * formatter.
+     *
+     * @var \Monolog\Formatter\FormatterInterface
+     */
+    protected $formatter;
+
+    /**
+     * handler.
+     *
+     * @var AbstractHandler
+     */
+    protected $handler;
+
+    /**
+     * config.
+     *
+     * @var array
+     */
+    protected $config = [
+        'file' => null,
+        'identify' => 'yansongda.supports',
+        'level' => BaseLogger::DEBUG,
+        'type' => 'daily',
+        'max_files' => 30,
+    ];
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws Exception
+     */
+    public function __call($method, $args): void
+    {
+        call_user_func_array([$this->getLogger(), $method], $args);
+    }
+
+    /**
+     * Set logger.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function setLogger(LoggerInterface $logger): Logger
+    {
+        $this->logger = $logger;
+
+        return $this;
+    }
+
+    /**
+     * Return the logger instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function getLogger(): LoggerInterface
+    {
+        if (is_null($this->logger)) {
+            $this->logger = $this->createLogger();
+        }
+
+        return $this->logger;
+    }
+
+    /**
+     * Make a default log instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function createLogger(): BaseLogger
+    {
+        $handler = $this->getHandler();
+
+        $handler->setFormatter($this->getFormatter());
+
+        $logger = new BaseLogger($this->config['identify']);
+
+        $logger->pushHandler($handler);
+
+        return $logger;
+    }
+
+    /**
+     * setFormatter.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setFormatter(FormatterInterface $formatter): self
+    {
+        $this->formatter = $formatter;
+
+        return $this;
+    }
+
+    /**
+     * getFormatter.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getFormatter(): FormatterInterface
+    {
+        if (is_null($this->formatter)) {
+            $this->formatter = $this->createFormatter();
+        }
+
+        return $this->formatter;
+    }
+
+    /**
+     * createFormatter.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function createFormatter(): LineFormatter
+    {
+        return new LineFormatter(
+            "%datetime% > %channel%.%level_name% > %message% %context% %extra%\n\n",
+            null,
+            false,
+            true
+        );
+    }
+
+    /**
+     * setHandler.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setHandler(AbstractHandler $handler): self
+    {
+        $this->handler = $handler;
+
+        return $this;
+    }
+
+    /**
+     * getHandler.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \Exception
+     */
+    public function getHandler(): AbstractHandler
+    {
+        if (is_null($this->handler)) {
+            $this->handler = $this->createHandler();
+        }
+
+        return $this->handler;
+    }
+
+    /**
+     * createHandler.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \Exception
+     *
+     * @return \Monolog\Handler\RotatingFileHandler|\Monolog\Handler\StreamHandler
+     */
+    public function createHandler(): AbstractHandler
+    {
+        $file = $this->config['file'] ?? sys_get_temp_dir().'/logs/'.$this->config['identify'].'.log';
+
+        if ('single' === $this->config['type']) {
+            return new StreamHandler($file, $this->config['level']);
+        }
+
+        return new RotatingFileHandler($file, $this->config['max_files'], $this->config['level']);
+    }
+
+    /**
+     * setConfig.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setConfig(array $config): self
+    {
+        $this->config = array_merge($this->config, $config);
+
+        return $this;
+    }
+
+    /**
+     * getConfig.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getConfig(): array
+    {
+        return $this->config;
+    }
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff