Przeglądaj źródła

【程序目录】优化程序

吴昊天 3 lat temu
rodzic
commit
75174a99df
42 zmienionych plików z 1395 dodań i 121 usunięć
  1. 1 1
      crmeb/app/adminapi/controller/Common.php
  2. 1 1
      crmeb/app/adminapi/controller/v1/marketing/integral/StoreIntegralOrder.php
  3. 1 1
      crmeb/app/adminapi/controller/v1/order/StoreOrder.php
  4. 28 0
      crmeb/app/adminapi/controller/v1/product/StoreProduct.php
  5. 14 7
      crmeb/app/adminapi/controller/v1/setting/LangCountry.php
  6. 18 2
      crmeb/app/adminapi/controller/v1/setting/SystemAdmin.php
  7. 14 5
      crmeb/app/adminapi/controller/v1/setting/SystemConfig.php
  8. 5 0
      crmeb/app/adminapi/route/product.php
  9. 8 4
      crmeb/app/adminapi/route/setting.php
  10. 5 1
      crmeb/app/adminapi/route/system.php
  11. 7 2
      crmeb/app/api/controller/v1/PayController.php
  12. 1 1
      crmeb/app/api/controller/v1/order/StoreIntegralOrderController.php
  13. 2 2
      crmeb/app/api/controller/v1/order/StoreOrderController.php
  14. 5 0
      crmeb/app/api/controller/v1/wechat/WechatController.php
  15. 1 0
      crmeb/app/api/route/v1.php
  16. 2 2
      crmeb/app/dao/product/product/StoreProductDao.php
  17. 6 1
      crmeb/app/services/order/StoreOrderRefundServices.php
  18. 1 0
      crmeb/app/services/order/StoreOrderServices.php
  19. 5 1
      crmeb/app/services/pay/PayServices.php
  20. 95 0
      crmeb/app/services/product/product/StoreProductServices.php
  21. 2 2
      crmeb/app/services/shipping/ExpressServices.php
  22. 23 3
      crmeb/app/services/system/admin/SystemAdminServices.php
  23. 13 1
      crmeb/app/services/system/config/SystemConfigServices.php
  24. 2 2
      crmeb/app/services/system/lang/LangCodeServices.php
  25. 27 18
      crmeb/app/services/system/lang/LangCountryServices.php
  26. 16 0
      crmeb/app/services/wechat/WechatServices.php
  27. 7 7
      crmeb/crmeb/services/app/WechatService.php
  28. 8 1
      crmeb/crmeb/services/easywechat/Application.php
  29. 17 17
      crmeb/crmeb/services/easywechat/miniPayment/WeChatClient.php
  30. 280 0
      crmeb/crmeb/services/easywechat/v3pay/BaseClient.php
  31. 68 0
      crmeb/crmeb/services/easywechat/v3pay/Certficates.php
  32. 400 0
      crmeb/crmeb/services/easywechat/v3pay/PayClient.php
  33. 36 0
      crmeb/crmeb/services/easywechat/v3pay/ServiceProvider.php
  34. 4 3
      crmeb/crmeb/services/express/storage/Express.php
  35. 2 0
      crmeb/crmeb/services/pay/Pay.php
  36. 1 1
      crmeb/crmeb/services/pay/PayInterface.php
  37. 216 0
      crmeb/crmeb/services/pay/storage/V3WechatPay.php
  38. 5 7
      crmeb/crmeb/services/pay/storage/WechatPay.php
  39. 40 16
      crmeb/public/install/crmeb.sql
  40. 6 6
      crmeb/vendor/composer/installed.php
  41. 1 5
      crmeb/vendor/overtrue/wechat/src/Foundation/Application.php
  42. 1 1
      crmeb/vendor/services.php

+ 1 - 1
crmeb/app/adminapi/controller/Common.php

@@ -111,7 +111,7 @@ class Common extends AuthController
                 } catch (\Throwable $e) {
                 } catch (\Throwable $e) {
                     return app('json')->fail(400330);
                     return app('json')->fail(400330);
                 }
                 }
-                return app('json')->success(['status' => 1, 'copyright' => $this->copyright(), 'authCode' => $authCode, 'day' => 0, 'force_reminder' => $upgradeStatus['force_reminder'] ?? 0]);
+                return app('json')->success(['status' => 1, 'copyright' => $res['data']['copyright'], 'authCode' => $authCode, 'day' => 0, 'force_reminder' => $upgradeStatus['force_reminder'] ?? 0]);
             default:
             default:
                 return app('json')->success(['status' => -9, 'force_reminder' => $upgradeStatus['force_reminder'] ?? 0]);
                 return app('json')->success(['status' => -9, 'force_reminder' => $upgradeStatus['force_reminder'] ?? 0]);
         }
         }

+ 1 - 1
crmeb/app/adminapi/controller/v1/marketing/integral/StoreIntegralOrder.php

@@ -216,7 +216,7 @@ class StoreIntegralOrder extends AuthController
 
 
         $data['delivery_name'] = $orderInfo['delivery_name'];
         $data['delivery_name'] = $orderInfo['delivery_name'];
         $data['delivery_id'] = $orderInfo['delivery_id'];
         $data['delivery_id'] = $orderInfo['delivery_id'];
-        $data['result'] = $services->query($cacheName, $orderInfo['delivery_id'], $orderInfo['delivery_code'] ?? null);
+        $data['result'] = $services->query($cacheName, $orderInfo['delivery_id'], $orderInfo['delivery_code'] ?? null, $orderInfo['user_phone']);
         return app('json')->success($data);
         return app('json')->success($data);
     }
     }
 
 

+ 1 - 1
crmeb/app/adminapi/controller/v1/order/StoreOrder.php

@@ -510,7 +510,7 @@ class StoreOrder extends AuthController
 
 
         $data['delivery_name'] = $orderInfo['delivery_name'];
         $data['delivery_name'] = $orderInfo['delivery_name'];
         $data['delivery_id'] = $orderInfo['delivery_id'];
         $data['delivery_id'] = $orderInfo['delivery_id'];
-        $data['result'] = $services->query($cacheName, $orderInfo['delivery_id'], $orderInfo['delivery_code'] ?? null);
+        $data['result'] = $services->query($cacheName, $orderInfo['delivery_id'], $orderInfo['delivery_code'] ?? null, $orderInfo['user_phone']);
         return app('json')->success($data);
         return app('json')->success($data);
     }
     }
 
 

+ 28 - 0
crmeb/app/adminapi/controller/v1/product/StoreProduct.php

@@ -359,6 +359,11 @@ class StoreProduct extends AuthController
         return app('json')->success(100002);
         return app('json')->success(100002);
     }
     }
 
 
+    /**
+     * 导入卡密
+     * @return mixed
+     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
+     */
     public function import_card()
     public function import_card()
     {
     {
         $data = $this->request->getMore([
         $data = $this->request->getMore([
@@ -371,4 +376,27 @@ class StoreProduct extends AuthController
         $cardData = $readExcelService->readExcel($file);
         $cardData = $readExcelService->readExcel($file);
         return app('json')->success($cardData);
         return app('json')->success($cardData);
     }
     }
+
+    /**
+     * 商品批量设置
+     * @return mixed
+     */
+    public function batchSetting()
+    {
+        $data = $this->request->postMore([
+            ['ids', []],
+            ['cate_id', []],
+            ['logistics', []],
+            ['freight', 2],
+            ['postage', 0],
+            ['temp_id', 1],
+            ['give_integral', 0],
+            ['coupon_ids', []],
+            ['label_id', []],
+            ['recommend', []],
+            ['type', 0]
+        ]);
+        $this->service->batchSetting($data);
+        return app('json')->success(100014);
+    }
 }
 }

+ 14 - 7
crmeb/app/adminapi/controller/v1/setting/LangCountry.php

@@ -50,22 +50,29 @@ class LangCountry extends AuthController
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      * @throws \think\db\exception\ModelNotFoundException
      */
      */
-    public function langCountryTypeForm($id)
+    public function langCountryForm($id)
     {
     {
-        return app('json')->success($this->services->LangCountryTypeForm($id));
+        return app('json')->success($this->services->langCountryForm($id));
     }
     }
 
 
     /**
     /**
-     * 国家语言修改
+     * 地区语言修改
      * @param $id
      * @param $id
      * @return mixed
      * @return mixed
      */
      */
     public function langCountrySave($id)
     public function langCountrySave($id)
     {
     {
-        [$typeId] = $this->request->postMore([
-            ['type', 0],
-        ], true);
-        $this->services->langCountrySave($id, $typeId);
+        $data = $this->request->postMore([
+            ['name', ''],
+            ['code', ''],
+        ]);
+        $this->services->langCountrySave($id, $data);
         return app('json')->success(100000);
         return app('json')->success(100000);
     }
     }
+
+    public function langCountryDel($id)
+    {
+        $this->services->langCountryDel($id);
+        return app('json')->success(100002);
+    }
 }
 }

+ 18 - 2
crmeb/app/adminapi/controller/v1/setting/SystemAdmin.php

@@ -168,8 +168,6 @@ class SystemAdmin extends AuthController
             ['pwd', ''],
             ['pwd', ''],
             ['new_pwd', ''],
             ['new_pwd', ''],
             ['conf_pwd', ''],
             ['conf_pwd', ''],
-            ['file_pwd', ''],
-            ['conf_file_pwd', ''],
         ]);
         ]);
         if(!preg_match('/^(?![^a-zA-Z]+$)(?!\D+$).{6,}$/',$data['new_pwd'])){
         if(!preg_match('/^(?![^a-zA-Z]+$)(?!\D+$).{6,}$/',$data['new_pwd'])){
             return app('json')->fail(400183);
             return app('json')->fail(400183);
@@ -179,6 +177,24 @@ class SystemAdmin extends AuthController
         else
         else
             return app('json')->fail(100007);
             return app('json')->fail(100007);
     }
     }
+    /**
+     * 修改当前登陆admin的文件管理密码
+     * @return mixed
+     */
+    public function set_file_password()
+    {
+        $data = $this->request->postMore([
+            ['file_pwd', ''],
+            ['conf_file_pwd', ''],
+        ]);
+        if(!preg_match('/^(?![^a-zA-Z]+$)(?!\D+$).{6,}$/',$data['file_pwd'])){
+            return app('json')->fail(400183);
+        }
+        if ($this->services->setFilePassword($this->adminId, $data))
+            return app('json')->success(100001);
+        else
+            return app('json')->fail(100007);
+    }
 
 
     /**
     /**
      * 退出登陆
      * 退出登陆

+ 14 - 5
crmeb/app/adminapi/controller/v1/setting/SystemConfig.php

@@ -12,6 +12,7 @@ namespace app\adminapi\controller\v1\setting;
 
 
 use app\adminapi\controller\AuthController;
 use app\adminapi\controller\AuthController;
 use app\Request;
 use app\Request;
+use app\services\order\StoreOrderServices;
 use app\services\system\config\SystemConfigServices;
 use app\services\system\config\SystemConfigServices;
 use app\services\system\config\SystemConfigTabServices;
 use app\services\system\config\SystemConfigTabServices;
 use think\facade\App;
 use think\facade\App;
@@ -301,13 +302,21 @@ class SystemConfig extends AuthController
         if (isset($post['day_brokerage_price_upper']) && $post['day_brokerage_price_upper'] < -1) {
         if (isset($post['day_brokerage_price_upper']) && $post['day_brokerage_price_upper'] < -1) {
             return app('json')->fail(400757);
             return app('json')->fail(400757);
         }
         }
-        if( isset($post['pay_new_weixin_open']) && (bool)$post['pay_new_weixin_open'])
-        {
-            if(empty($post['pay_new_weixin_mchid']))
-            {
-                return  app('json')->fail(400763);
+        if (isset($post['pay_new_weixin_open']) && (bool)$post['pay_new_weixin_open']) {
+            if (empty($post['pay_new_weixin_mchid'])) {
+                return app('json')->fail(400763);
             }
             }
         }
         }
+
+        //支付接口类型选择,如果有订单就不能再进行切换
+        if (isset($post['pay_wechat_type'])) {
+            /** @var StoreOrderServices $orderServices */
+            $orderServices = app()->make(StoreOrderServices::class);
+            if ($post['pay_wechat_type'] != -1 && $orderServices->count()) {
+                return app('json')->fail('支付接口类型已经选择,不能再次进行切换,切换后会导致无法退款等问题。');
+            }
+        }
+
         if (isset($post['weixin_ckeck_file'])) {
         if (isset($post['weixin_ckeck_file'])) {
             $from = public_path() . $post['weixin_ckeck_file'];
             $from = public_path() . $post['weixin_ckeck_file'];
             $to = public_path() . array_reverse(explode('/', $post['weixin_ckeck_file']))[0];
             $to = public_path() . array_reverse(explode('/', $post['weixin_ckeck_file']))[0];

+ 5 - 0
crmeb/app/adminapi/route/product.php

@@ -93,6 +93,11 @@ Route::group('product', function () {
     Route::get('product/check_activity/:id', 'v1.product.StoreProduct/check_activity')->option(['real_name' => '检测是商品否有活动开启']);
     Route::get('product/check_activity/:id', 'v1.product.StoreProduct/check_activity')->option(['real_name' => '检测是商品否有活动开启']);
     //导入虚拟商品卡密
     //导入虚拟商品卡密
     Route::get('product/import_card', 'v1.product.StoreProduct/import_card')->option(['real_name' => '导入虚拟商品卡密']);
     Route::get('product/import_card', 'v1.product.StoreProduct/import_card')->option(['real_name' => '导入虚拟商品卡密']);
+
+    /** 商品批量操作 */
+    Route::post('batch/setting', 'v1.product.StoreProduct/batchSetting')->option(['real_name' => '商品批量设置']);
+
+
 })->middleware([
 })->middleware([
     \app\http\middleware\AllowOriginMiddleware::class,
     \app\http\middleware\AllowOriginMiddleware::class,
     \app\adminapi\middleware\AdminAuthTokenMiddleware::class,
     \app\adminapi\middleware\AdminAuthTokenMiddleware::class,

+ 8 - 4
crmeb/app/adminapi/route/setting.php

@@ -25,6 +25,8 @@ Route::group('setting', function () {
     Route::get('info', 'v1.setting.SystemAdmin/info')->name('SystemAdminInfo')->option(['real_name' => '获取当前管理员信息']);
     Route::get('info', 'v1.setting.SystemAdmin/info')->name('SystemAdminInfo')->option(['real_name' => '获取当前管理员信息']);
     //修改当前管理员信息
     //修改当前管理员信息
     Route::put('update_admin', 'v1.setting.SystemAdmin/update_admin')->name('SystemAdminUpdateAdmin')->option(['real_name' => '修改当前管理员信息']);
     Route::put('update_admin', 'v1.setting.SystemAdmin/update_admin')->name('SystemAdminUpdateAdmin')->option(['real_name' => '修改当前管理员信息']);
+    //设置文件管理密码
+    Route::put('set_file_password', 'v1.setting.SystemAdmin/set_file_password')->name('SystemAdminSetFilePassword')->option(['real_name' => '设置当前文件管理密码']);
     //权限菜单资源路由
     //权限菜单资源路由
     Route::resource('menus', 'v1.setting.SystemMenus')->option(['real_name' => '权限菜单']);
     Route::resource('menus', 'v1.setting.SystemMenus')->option(['real_name' => '权限菜单']);
     //未添加的权限规则列表
     //未添加的权限规则列表
@@ -169,10 +171,12 @@ Route::group('setting', function () {
     /** 多语言 */
     /** 多语言 */
     //语言国家列表
     //语言国家列表
     Route::get('lang_country/list', 'v1.setting.LangCountry/langCountryList')->option(['real_name' => '语言国家列表']);
     Route::get('lang_country/list', 'v1.setting.LangCountry/langCountryList')->option(['real_name' => '语言国家列表']);
-    //设置国家语言类型表单
-    Route::get('lang_country/type_form/:id', 'v1.setting.LangCountry/langCountryTypeForm')->option(['real_name' => '设置国家语言类型表单']);
-    //语言类型列表
-    Route::post('lang_country/save/:id', 'v1.setting.LangCountry/langCountrySave')->option(['real_name' => '语言国家修改']);
+    //添加语言地区表单
+    Route::get('lang_country/form/:id', 'v1.setting.LangCountry/langCountryForm')->option(['real_name' => '添加语言地区表单']);
+    //保存语言地区
+    Route::post('lang_country/save/:id', 'v1.setting.LangCountry/langCountrySave')->option(['real_name' => '保存语言地区']);
+    //删除语言地区
+    Route::delete('lang_country/del/:id', 'v1.setting.LangCountry/langCountryDel')->option(['real_name' => '删除语言地区']);
     //语言类型列表
     //语言类型列表
     Route::get('lang_type/list', 'v1.setting.LangType/langTypeList')->option(['real_name' => '语言类型列表']);
     Route::get('lang_type/list', 'v1.setting.LangType/langTypeList')->option(['real_name' => '语言类型列表']);
     //新增修改语言类型表单
     //新增修改语言类型表单

+ 5 - 1
crmeb/app/adminapi/route/system.php

@@ -94,7 +94,7 @@ Route::group('system', function () {
     Route::get('upgrade_log/list', 'UpgradeController/upgradeLogList')->option(['real_name' => '升级记录']);
     Route::get('upgrade_log/list', 'UpgradeController/upgradeLogList')->option(['real_name' => '升级记录']);
     //导出备份项目
     //导出备份项目
     Route::get('upgrade_export/:id/:type', 'UpgradeController/export')->option(['real_name' => '导出备份']);
     Route::get('upgrade_export/:id/:type', 'UpgradeController/export')->option(['real_name' => '导出备份']);
-
+    //文件管理登录
     Route::post('file/login', 'v1.system.SystemFile/login')->option(['real_name' => '文件管理登录']);
     Route::post('file/login', 'v1.system.SystemFile/login')->option(['real_name' => '文件管理登录']);
 })->middleware([
 })->middleware([
     \app\http\middleware\AllowOriginMiddleware::class,
     \app\http\middleware\AllowOriginMiddleware::class,
@@ -111,9 +111,13 @@ Route::group('system', function () {
     Route::get('file/openfile', 'v1.system.SystemFile/openfile')->option(['real_name' => '读取文件']);
     Route::get('file/openfile', 'v1.system.SystemFile/openfile')->option(['real_name' => '读取文件']);
     //保存文件
     //保存文件
     Route::post('file/savefile', 'v1.system.SystemFile/savefile')->option(['real_name' => '保存文件']);
     Route::post('file/savefile', 'v1.system.SystemFile/savefile')->option(['real_name' => '保存文件']);
+    //创建文件夹
     Route::get('file/createFolder', 'v1.system.SystemFile/createFolder')->option(['real_name' => '创建文件夹']);
     Route::get('file/createFolder', 'v1.system.SystemFile/createFolder')->option(['real_name' => '创建文件夹']);
+    //创建文件
     Route::get('file/createFile', 'v1.system.SystemFile/createFile')->option(['real_name' => '创建文件']);
     Route::get('file/createFile', 'v1.system.SystemFile/createFile')->option(['real_name' => '创建文件']);
+    //删除文件夹或者文件
     Route::get('file/delFolder', 'v1.system.SystemFile/delFolder')->option(['real_name' => '删除文件夹']);
     Route::get('file/delFolder', 'v1.system.SystemFile/delFolder')->option(['real_name' => '删除文件夹']);
+    //重命名文件
     Route::get('file/rename', 'v1.system.SystemFile/rename')->option(['real_name' => '重命名文件夹']);
     Route::get('file/rename', 'v1.system.SystemFile/rename')->option(['real_name' => '重命名文件夹']);
 })->middleware([
 })->middleware([
     \app\http\middleware\AllowOriginMiddleware::class,
     \app\http\middleware\AllowOriginMiddleware::class,

+ 7 - 2
crmeb/app/api/controller/v1/PayController.php

@@ -38,8 +38,13 @@ class PayController
                 return $pay->handleNotify();
                 return $pay->handleNotify();
             case 'wechat':
             case 'wechat':
             case 'routine':
             case 'routine':
-                /** @var Pay $pay */
-                $pay = app()->make(Pay::class);
+                if (sys_config('pay_wechat_type')) {
+                    /** @var Pay $pay */
+                    $pay = app()->make(Pay::class, ['v3_wechat_pay']);
+                } else {
+                    /** @var Pay $pay */
+                    $pay = app()->make(Pay::class);
+                }
                 return $pay->handleNotify()->getContent();
                 return $pay->handleNotify()->getContent();
         }
         }
     }
     }

+ 1 - 1
crmeb/app/api/controller/v1/order/StoreIntegralOrderController.php

@@ -142,7 +142,7 @@ class StoreIntegralOrderController
         return app('json')->success([
         return app('json')->success([
             'order' => $order,
             'order' => $order,
             'express' => [
             'express' => [
-                'result' => ['list' => $expressServices->query($cacheName, $order['delivery_id'], $order['delivery_code'])
+                'result' => ['list' => $expressServices->query($cacheName, $order['delivery_id'], $order['delivery_code'], $order['user_phone'])
                 ]
                 ]
             ]
             ]
         ]);
         ]);

+ 2 - 2
crmeb/app/api/controller/v1/order/StoreOrderController.php

@@ -409,7 +409,7 @@ class StoreOrderController
                 case 'routine':
                 case 'routine':
                     if ($type == 1 || in_array($order['is_channel'], [0, 2, 3, 4])) {
                     if ($type == 1 || in_array($order['is_channel'], [0, 2, 3, 4])) {
                         $order['order_id'] = app()->make(StoreOrderCreateServices::class)->getNewOrderId('cp');
                         $order['order_id'] = app()->make(StoreOrderCreateServices::class)->getNewOrderId('cp');
-                        $this->services->update($order['id'],['order_id'=>$order['order_id']],'id');
+                        $this->services->update($order['id'], ['order_id' => $order['order_id']], 'id');
                     }
                     }
                     break;
                     break;
                 case 'app':
                 case 'app':
@@ -690,7 +690,7 @@ class StoreOrderController
         return app('json')->success([
         return app('json')->success([
             'order' => $orderInfo,
             'order' => $orderInfo,
             'express' => [
             'express' => [
-                'result' => ['list' => $expressServices->query($cacheName, $orderInfo['delivery_id'], $orderInfo['delivery_code'])
+                'result' => ['list' => $expressServices->query($cacheName, $orderInfo['delivery_id'], $orderInfo['delivery_code'], $order['user_phone'])
                 ]
                 ]
             ]
             ]
         ]);
         ]);

+ 5 - 0
crmeb/app/api/controller/v1/wechat/WechatController.php

@@ -51,6 +51,11 @@ class WechatController
         return $this->services->notify();
         return $this->services->notify();
     }
     }
 
 
+    public function v3notify()
+    {
+        return $this->services->v3notify();
+    }
+
     /**
     /**
      * 公众号权限配置信息获取
      * 公众号权限配置信息获取
      * @param Request $request
      * @param Request $request

+ 1 - 0
crmeb/app/api/route/v1.php

@@ -15,6 +15,7 @@ use think\Response;
 
 
 Route::any('wechat/serve', 'v1.wechat.WechatController/serve');//公众号服务
 Route::any('wechat/serve', 'v1.wechat.WechatController/serve');//公众号服务
 Route::any('wechat/notify', 'v1.wechat.WechatController/notify');//公众号支付回调
 Route::any('wechat/notify', 'v1.wechat.WechatController/notify');//公众号支付回调
+Route::any('wechat/v3notify', 'v1.wechat.WechatController/v3notify');//公众号支付回调
 Route::any('routine/notify', 'v1.wechat.AuthController/notify');//小程序支付回调
 Route::any('routine/notify', 'v1.wechat.AuthController/notify');//小程序支付回调
 Route::any('pay/notify/:type', 'v1.PayController/notify');//支付回调
 Route::any('pay/notify/:type', 'v1.PayController/notify');//支付回调
 Route::get('get_script', 'v1.PublicController/getScript');//获取统计代码
 Route::get('get_script', 'v1.PublicController/getScript');//获取统计代码

+ 2 - 2
crmeb/app/dao/product/product/StoreProductDao.php

@@ -160,7 +160,7 @@ class StoreProductDao extends BaseDao
             }
             }
         })->when(!$page && $limit, function ($query) use ($limit) {
         })->when(!$page && $limit, function ($query) use ($limit) {
             $query->limit($limit);
             $query->limit($limit);
-        })->field($field)->select()->toArray();
+        })->order('sort desc')->field($field)->select()->toArray();
     }
     }
 
 
     /**商品列表
     /**商品列表
@@ -225,7 +225,7 @@ class StoreProductDao extends BaseDao
             ->when($limit, function ($query) use ($limit) {
             ->when($limit, function ($query) use ($limit) {
                 $query->limit($limit);
                 $query->limit($limit);
             })
             })
-            ->order((in_array($field, ['is_hot','is_best']) ? 'sales DESC' : 'sort DESC') . ', id DESC')->select()->toArray();
+            ->order(in_array($field, ['is_hot', 'is_best']) ? 'sales DESC,sort DESC, id desc' : 'sort DESC, id desc')->select()->toArray();
 
 
     }
     }
 
 

+ 6 - 1
crmeb/app/services/order/StoreOrderRefundServices.php

@@ -188,8 +188,13 @@ class StoreOrderRefundServices extends BaseServices
                             $no = $refundOrder['trade_no'];
                             $no = $refundOrder['trade_no'];
                             $refundData['type'] = 'trade_no';
                             $refundData['type'] = 'trade_no';
                         }
                         }
+                        if (sys_config('pay_wechat_type')) {
+                            $drivers = 'v3_wechat_pay';
+                        } else {
+                            $drivers = 'wechat_pay';
+                        }
                         /** @var Pay $pay */
                         /** @var Pay $pay */
-                        $pay = app()->make(Pay::class);
+                        $pay = app()->make(Pay::class, [$drivers]);
                         if ($refundOrder['is_channel'] == 1) {
                         if ($refundOrder['is_channel'] == 1) {
                             $refundData['trade_no'] = $refundOrder['trade_no'];
                             $refundData['trade_no'] = $refundOrder['trade_no'];
                             $refundData['pay_new_weixin_open'] = sys_config('pay_new_weixin_open');
                             $refundData['pay_new_weixin_open'] = sys_config('pay_new_weixin_open');

+ 1 - 0
crmeb/app/services/order/StoreOrderServices.php

@@ -1581,6 +1581,7 @@ HTML;
         /** @var UserBillServices $userBillServices */
         /** @var UserBillServices $userBillServices */
         $userBillServices = app()->make(UserBillServices::class);
         $userBillServices = app()->make(UserBillServices::class);
         $data['usable_integral'] = bcsub((string)$user['integral'], (string)$userBillServices->getBillSum(['uid' => $user['uid'], 'is_frozen' => 1]), 0);
         $data['usable_integral'] = bcsub((string)$user['integral'], (string)$userBillServices->getBillSum(['uid' => $user['uid'], 'is_frozen' => 1]), 0);
+        $data['integral_open'] = sys_config('integral_ratio', 0) > 0;
         return $data;
         return $data;
     }
     }
 
 

+ 5 - 1
crmeb/app/services/pay/PayServices.php

@@ -76,6 +76,10 @@ class PayServices
             //这些全都是微信支付
             //这些全都是微信支付
             if (in_array($payType, ['routine', 'weixinh5', 'weixin', 'pc', 'store'])) {
             if (in_array($payType, ['routine', 'weixinh5', 'weixin', 'pc', 'store'])) {
                 $payType = 'wechat_pay';
                 $payType = 'wechat_pay';
+                //判断是否使用v3
+                if (sys_config('pay_wechat_type') == 1) {
+                    $payType = 'v3_wechat_pay';
+                }
             }
             }
 
 
             if ($payType == 'alipay') {
             if ($payType == 'alipay') {
@@ -85,7 +89,7 @@ class PayServices
             /** @var Pay $pay */
             /** @var Pay $pay */
             $pay = app()->make(Pay::class, [$payType]);
             $pay = app()->make(Pay::class, [$payType]);
 
 
-            return $pay->create($orderId, $price, $successAction, $body, '', ['openid' => $openid, 'isCode' => $isCode,'pay_new_weixin_open' => (bool)sys_config('pay_new_weixin_open')]);
+            return $pay->create($orderId, $price, $successAction, $body, '', ['openid' => $openid, 'isCode' => $isCode, 'pay_new_weixin_open' => (bool)sys_config('pay_new_weixin_open')]);
 
 
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             throw new ApiException($e->getMessage());
             throw new ApiException($e->getMessage());

+ 95 - 0
crmeb/app/services/product/product/StoreProductServices.php

@@ -1954,4 +1954,99 @@ class StoreProductServices extends BaseServices
 
 
         }
         }
     }
     }
+
+    /**
+     * 商品批量设置
+     * @param $data
+     * @return bool
+     * @throws \Exception
+     */
+    public function batchSetting($data)
+    {
+        $ids = $data['ids'];
+        $batchData = [];
+        if (!count($ids)) throw new AdminException(400337);
+        switch ($data['type']) {
+            case 1: // 修改分类
+                $cate_id = $data['cate_id'];
+                if (!count($cate_id)) throw new AdminException(410107);
+                /** @var StoreCategoryServices $storeCategoryServices */
+                $storeCategoryServices = app()->make(StoreCategoryServices::class);
+                $cateGory = $storeCategoryServices->getColumn([['id', 'IN', $cate_id]], 'id,pid', 'id');
+                if (!$cateGory) throw new AdminException(410096);
+                $time = time();
+                $cateData = [];
+                foreach ($cate_id as $cid) {
+                    if ($cid && isset($cateGory[$cid]['pid'])) {
+                        foreach ($ids as $product_id) {
+                            $cateData[$product_id][] = ['product_id' => $product_id, 'cate_id' => $cid, 'cate_pid' => $cateGory[$cid]['pid'], 'status' => 1, 'add_time' => $time];
+                        }
+                    }
+                }
+                /** @var StoreProductCateServices $storeProductCateServices */
+                $storeProductCateServices = app()->make(StoreProductCateServices::class);
+                foreach ($ids as $product_id) {
+                    $storeProductCateServices->change($product_id, $cateData[$product_id]);
+                    $this->dao->update($product_id, ['cate_id' => implode(',', $cate_id)]);
+                }
+                break;
+            case 2:
+                foreach ($ids as $product_id) {
+                    $batchData[] = [
+                        'id' => $product_id,
+                        'logistics' => implode(',', $data['logistics']),
+                        'freight' => $data['freight'],
+                        'postage' => $data['freight'] == 2 ? $data['postage'] : 0,
+                        'temp_id' => $data['freight'] == 3 ? $data['temp_id'] : 0
+                    ];
+                }
+                if (count($batchData)) $this->dao->saveAll($batchData);
+                break;
+            case 3:
+                foreach ($ids as $product_id) {
+                    $batchData[] = [
+                        'id' => $product_id,
+                        'give_integral' => $data['give_integral']
+                    ];
+                }
+                if (count($batchData)) $this->dao->saveAll($batchData);
+                break;
+            case 4:
+                /** @var StoreProductCouponServices $storeProductCouponServices */
+                $storeProductCouponServices = app()->make(StoreProductCouponServices::class);
+                foreach ($ids as $product_id) {
+                    if (!empty($data['coupon_ids'])) {
+                        $storeProductCouponServices->setCoupon($product_id, $data['coupon_ids']);
+                    } else {
+                        $storeProductCouponServices->delete(['product_id' => $product_id]);
+                    }
+                }
+                break;
+            case 5:
+                foreach ($ids as $product_id) {
+                    $batchData[] = [
+                        'id' => $product_id,
+                        'label_id' => implode(',', $data['label_id'])
+                    ];
+                }
+                if (count($batchData)) $this->dao->saveAll($batchData);
+                break;
+            case 6:
+                foreach ($ids as $product_id) {
+                    $batchData[] = [
+                        'id' => $product_id,
+                        'is_hot' => in_array('is_hot',$data['recommend']) ? 1 : 0,
+                        'is_benefit' => in_array('is_benefit',$data['recommend']) ? 1 : 0,
+                        'is_new' => in_array('is_new',$data['recommend']) ? 1 : 0,
+                        'is_good' => in_array('is_good',$data['recommend']) ? 1 : 0,
+                        'is_best' => in_array('is_best',$data['recommend']) ? 1 : 0
+                    ];
+                }
+                if (count($batchData)) $this->dao->saveAll($batchData);
+                break;
+            default:
+                return true;
+        }
+        return true;
+    }
 }
 }

+ 2 - 2
crmeb/app/services/shipping/ExpressServices.php

@@ -190,7 +190,7 @@ class ExpressServices extends BaseServices
      * @param string|null $com
      * @param string|null $com
      * @return array
      * @return array
      */
      */
-    public function query(string $cacheName, string $expressNum, string $com = null)
+    public function query(string $cacheName, string $expressNum, string $com = null, $phone = '')
     {
     {
         $resultData = CacheService::get($cacheName, null);
         $resultData = CacheService::get($cacheName, null);
         if ($resultData === null || !is_array($resultData)) {
         if ($resultData === null || !is_array($resultData)) {
@@ -200,7 +200,7 @@ class ExpressServices extends BaseServices
                 case 1:
                 case 1:
                     /** @var ServeServices $services */
                     /** @var ServeServices $services */
                     $services = app()->make(ServeServices::class);
                     $services = app()->make(ServeServices::class);
-                    $result = $services->express()->query($expressNum, $com);
+                    $result = $services->express()->query($expressNum, $com, $phone);
                     if (isset($result['ischeck']) && $result['ischeck'] == 1) {
                     if (isset($result['ischeck']) && $result['ischeck'] == 1) {
                         $cacheTime = 0;
                         $cacheTime = 0;
                     } else {
                     } else {

+ 23 - 3
crmeb/app/services/system/admin/SystemAdminServices.php

@@ -138,6 +138,7 @@ class SystemAdminServices extends BaseServices
                 'id' => $adminInfo->getData('id'),
                 'id' => $adminInfo->getData('id'),
                 'account' => $adminInfo->getData('account'),
                 'account' => $adminInfo->getData('account'),
                 'head_pic' => $adminInfo->getData('head_pic'),
                 'head_pic' => $adminInfo->getData('head_pic'),
+                'level' => $adminInfo->getData('level'),
             ],
             ],
             'logo' => sys_config('site_logo'),
             'logo' => sys_config('site_logo'),
             'logo_square' => sys_config('site_logo_square'),
             'logo_square' => sys_config('site_logo_square'),
@@ -358,6 +359,28 @@ class SystemAdminServices extends BaseServices
                 throw new AdminException(400264);
                 throw new AdminException(400264);
             $adminInfo->pwd = $this->passwordHash($data['new_pwd']);
             $adminInfo->pwd = $this->passwordHash($data['new_pwd']);
         }
         }
+
+        $adminInfo->real_name = $data['real_name'];
+        $adminInfo->head_pic = $data['head_pic'];
+        if ($adminInfo->save())
+            return true;
+        else
+            return false;
+    }
+    /**
+     * 设置当前管理员文件管理密码
+     * @param int $id
+     * @param array $data
+     * @return bool
+     */
+    public function setFilePassword(int $id, array $data)
+    {
+        $adminInfo = $this->dao->get($id);
+        if (!$adminInfo)
+            throw new AdminException(400451);
+        if ($adminInfo->is_del) {
+            throw new AdminException(400452);
+        }
         if ($data['file_pwd']) {
         if ($data['file_pwd']) {
             if ($adminInfo->level != 0) throw new AdminException(400611);
             if ($adminInfo->level != 0) throw new AdminException(400611);
             if (!$data['conf_file_pwd'])
             if (!$data['conf_file_pwd'])
@@ -366,9 +389,6 @@ class SystemAdminServices extends BaseServices
                 throw new AdminException(400264);
                 throw new AdminException(400264);
             $adminInfo->file_pwd = $this->passwordHash($data['file_pwd']);
             $adminInfo->file_pwd = $this->passwordHash($data['file_pwd']);
         }
         }
-
-        $adminInfo->real_name = $data['real_name'];
-        $adminInfo->head_pic = $data['head_pic'];
         if ($adminInfo->save())
         if ($adminInfo->save())
             return true;
             return true;
         else
         else

+ 13 - 1
crmeb/app/services/system/config/SystemConfigServices.php

@@ -139,7 +139,19 @@ class SystemConfigServices extends BaseServices
         'pay_weixin_open' => [
         'pay_weixin_open' => [
             'son_type' => [
             'son_type' => [
                 'pay_weixin_mchid' => '',
                 'pay_weixin_mchid' => '',
-                'pay_weixin_key' => '',
+                'pay_wechat_type' => [
+                    'son_type' => [
+                        'pay_weixin_key' => '',
+                    ],
+                    'show_value' => 0
+                ],
+                'pay_wechat_type@' => [
+                    'son_type' => [
+                        'pay_weixin_serial_no' => '',
+                        'pay_weixin_key_v3' => ''
+                    ],
+                    'show_value' => 1
+                ],
                 'pay_weixin_client_cert' => '',
                 'pay_weixin_client_cert' => '',
                 'pay_weixin_client_key' => '',
                 'pay_weixin_client_key' => '',
                 'paydir' => '',
                 'paydir' => '',

+ 2 - 2
crmeb/app/services/system/lang/LangCodeServices.php

@@ -34,8 +34,8 @@ class LangCodeServices extends BaseServices
         $typeList = $langTypeServices->getColumn([['status', '=', 1], ['is_del', '=', 0]], 'language_name,file_name,id', 'id');
         $typeList = $langTypeServices->getColumn([['status', '=', 1], ['is_del', '=', 0]], 'language_name,file_name,id', 'id');
         $langType = [
         $langType = [
             'isAdmin' => [
             'isAdmin' => [
-                ['title' => '用户端', 'value' => 0],
-                ['title' => '管理端', 'value' => 1]
+                ['title' => '用户端页面', 'value' => 0],
+                ['title' => '后端接口', 'value' => 1]
             ]
             ]
         ];
         ];
         foreach ($typeList as $value) {
         foreach ($typeList as $value) {

+ 27 - 18
crmeb/app/services/system/lang/LangCountryServices.php

@@ -19,7 +19,7 @@ class LangCountryServices extends BaseServices
     }
     }
 
 
     /**
     /**
-     * 国家语言列表
+     * 地区语言列表
      * @param array $where
      * @param array $where
      * @return array
      * @return array
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\DataNotFoundException
@@ -35,7 +35,7 @@ class LangCountryServices extends BaseServices
     }
     }
 
 
     /**
     /**
-     * 设置国家语言类型表单
+     * 添加语言地区表单
      * @param $id
      * @param $id
      * @return array
      * @return array
      * @throws \FormBuilder\Exception\FormBuilderException
      * @throws \FormBuilder\Exception\FormBuilderException
@@ -43,32 +43,41 @@ class LangCountryServices extends BaseServices
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      * @throws \think\db\exception\ModelNotFoundException
      */
      */
-    public function LangCountryTypeForm($id)
+    public function langCountryForm($id)
     {
     {
-        if (!$id) throw new AdminException(100100);
-        $info = $this->dao->get($id);
-        if (!$info) throw new AdminException(100026);
-        /** @var LangTypeServices $typeServices */
-        $typeServices = app()->make(LangTypeServices::class);
-        $typeList = $typeServices->getColumn(['status' => 1, 'is_del' => 0], 'language_name,file_name', 'id');
-        $options[] = ['value' => 0, 'label' => '未定义'];
-        foreach ($typeList as $item) {
-            $options[] = ['value' => $item['id'], 'label' => $item['language_name'] . '(' . $item['file_name'] . ')'];
-        }
-        $field[] = Form::select('type', '选择语言类型', $info->type_id)->setOptions($options)->filterable(1);
-        return create_form('设置语言类型', $field, Url::buildUrl('/setting/lang_country/save/' . $id), 'POST');
+        if ($id) $info = $this->dao->get($id);
+        $field = [];
+        $field[] = Form::input('name', '所属地区', $info['name'] ?? '')->required('请填写所属地区');
+        $field[] = Form::input('code', '语言码', $info['code'] ?? '')->required('请填写语言码');
+        return create_form($id ? '修改语言地区' : '新增语言地区', $field, Url::buildUrl('/setting/lang_country/save/' . $id), 'POST');
     }
     }
 
 
     /**
     /**
-     * 国家语言修改
+     * 保存语言地区
      * @param $id
      * @param $id
      * @param $typeId
      * @param $typeId
      * @return bool
      * @return bool
      */
      */
-    public function LangCountrySave($id, $typeId)
+    public function LangCountrySave($id, $data)
     {
     {
-        $res = $this->dao->update(['id' => $id], ['type_id' => $typeId]);
+        if ($id) {
+            $res = $this->dao->update(['id' => $id], $data);
+        } else {
+            $res = $this->dao->save($data);
+        }
         if (!$res) throw new AdminException(100007);
         if (!$res) throw new AdminException(100007);
         return true;
         return true;
     }
     }
+
+    /**
+     * 删除语言地区
+     * @param $id
+     * @return bool
+     */
+    public function langCountryDel($id)
+    {
+        $res = $this->dao->delete($id);
+        if (!$res) throw new AdminException(100008);
+        return true;
+    }
 }
 }

+ 16 - 0
crmeb/app/services/wechat/WechatServices.php

@@ -21,6 +21,7 @@ use crmeb\services\CacheService;
 use crmeb\services\CacheService as Cache;
 use crmeb\services\CacheService as Cache;
 use crmeb\services\app\WechatService as WechatAuthService;
 use crmeb\services\app\WechatService as WechatAuthService;
 use crmeb\services\oauth\OAuth;
 use crmeb\services\oauth\OAuth;
+use crmeb\services\pay\Pay;
 use crmeb\utils\Canvas;
 use crmeb\utils\Canvas;
 
 
 /**
 /**
@@ -60,6 +61,21 @@ class WechatServices extends BaseServices
         return WechatAuthService::handleNotify()->getContent();
         return WechatAuthService::handleNotify()->getContent();
     }
     }
 
 
+    /**
+     * v3支付回调
+     * @return string
+     * @throws \EasyWeChat\Core\Exceptions\FaultException
+     * @author 等风来
+     * @email 136327134@qq.com
+     * @date 2022/9/22
+     */
+    public function v3notify()
+    {
+        /** @var Pay $pay */
+        $pay = app()->make(Pay::class, ['v3_wechat_pay']);
+        return $pay->handleNotify()->getContent();
+    }
+
     /**
     /**
      * 公众号权限配置信息获取
      * 公众号权限配置信息获取
      * @param $url
      * @param $url

+ 7 - 7
crmeb/crmeb/services/app/WechatService.php

@@ -146,9 +146,9 @@ class WechatService
                             $response = $messageService->wechatEventView($message);
                             $response = $messageService->wechatEventView($message);
                             break;
                             break;
                         case 'funds_order_pay':
                         case 'funds_order_pay':
-                            $prefix = substr($message['order_info']['trade_no'],0,2);
+                            $prefix = substr($message['order_info']['trade_no'], 0, 2);
                             //处理一下参数
                             //处理一下参数
-                            switch ($prefix){
+                            switch ($prefix) {
                                 case 'cp':
                                 case 'cp':
                                     $data['attach'] = 'Product';
                                     $data['attach'] = 'Product';
                                     break;
                                     break;
@@ -162,14 +162,12 @@ class WechatService
                             $data['out_trade_no'] = $message['order_info']['trade_no'];
                             $data['out_trade_no'] = $message['order_info']['trade_no'];
                             $data['transaction_id'] = $message['order_info']['transaction_id'];
                             $data['transaction_id'] = $message['order_info']['transaction_id'];
                             $data['opneid'] = $message['FromUserName'];
                             $data['opneid'] = $message['FromUserName'];
-                            if(Event::until('pay.notify', [$data]))
-                            {
+                            if (Event::until('pay.notify', [$data])) {
                                 $response = 'success';
                                 $response = 'success';
-                            }else
-                            {
+                            } else {
                                 $response = 'faild';
                                 $response = 'faild';
                             }
                             }
-                            Log::error(['data'=>$data,'res'=>$response,'message'=>$message]);
+                            Log::error(['data' => $data, 'res' => $response, 'message' => $message]);
                             break;
                             break;
                     }
                     }
                     break;
                     break;
@@ -446,6 +444,7 @@ class WechatService
             }
             }
         }
         }
     }
     }
+
     /**
     /**
      * 获得下单ID 新小程序支付
      * 获得下单ID 新小程序支付
      * @param $openid
      * @param $openid
@@ -500,6 +499,7 @@ class WechatService
         $paymentPrepare = self::paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
         $paymentPrepare = self::paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
         return self::paymentService()->configForJSSDKPayment($paymentPrepare->prepay_id);
         return self::paymentService()->configForJSSDKPayment($paymentPrepare->prepay_id);
     }
     }
+
     /**
     /**
      * 获得jsSdk支付参数  新小程序支付
      * 获得jsSdk支付参数  新小程序支付
      * @param $openid
      * @param $openid

+ 8 - 1
crmeb/crmeb/services/easywechat/Application.php

@@ -14,7 +14,10 @@ namespace crmeb\services\easywechat;
 
 
 use crmeb\services\easywechat\miniPayment\ServiceProvider;
 use crmeb\services\easywechat\miniPayment\ServiceProvider;
 use crmeb\services\easywechat\oauth2\wechat\WechatOauth2Provider;
 use crmeb\services\easywechat\oauth2\wechat\WechatOauth2Provider;
+use crmeb\services\easywechat\subscribe\ProgramProvider;
+use crmeb\services\easywechat\v3pay\PayClient;
 use crmeb\services\easywechat\wechatlive\ProgramProvider as LiveProgramProvider;
 use crmeb\services\easywechat\wechatlive\ProgramProvider as LiveProgramProvider;
+use crmeb\services\easywechat\v3pay\ServiceProvider as V3PayServiceProvider;
 
 
 
 
 /**
 /**
@@ -22,6 +25,7 @@ use crmeb\services\easywechat\wechatlive\ProgramProvider as LiveProgramProvider;
  * @package crmeb\services\easywechat
  * @package crmeb\services\easywechat
  * @property LiveProgramProvider $wechat_live
  * @property LiveProgramProvider $wechat_live
  * @property WechatOauth2Provider $oauth2
  * @property WechatOauth2Provider $oauth2
+ * @property PayClient $v3pay
  */
  */
 class Application extends \EasyWeChat\Foundation\Application
 class Application extends \EasyWeChat\Foundation\Application
 {
 {
@@ -32,7 +36,10 @@ class Application extends \EasyWeChat\Foundation\Application
     protected $providersNew = [
     protected $providersNew = [
         LiveProgramProvider::class,
         LiveProgramProvider::class,
         WechatOauth2Provider::class,
         WechatOauth2Provider::class,
-        ServiceProvider::class
+        ServiceProvider::class,
+        ProgramProvider::class,
+        V3PayServiceProvider::class,
+        \crmeb\services\easywechat\Open3rd\ProgramProvider::class,
     ];
     ];
 
 
     /**
     /**

+ 17 - 17
crmeb/crmeb/services/easywechat/miniPayment/WeChatClient.php

@@ -49,7 +49,7 @@ class WeChatClient extends AbstractAPI
 
 
     /**
     /**
      * 支付
      * 支付
-     * @param array $params[
+     * @param array $params [
      *                      'openid'=>'支付者的openid',
      *                      'openid'=>'支付者的openid',
      *                      'out_trade_no'=>'商家合单支付总交易单号',
      *                      'out_trade_no'=>'商家合单支付总交易单号',
      *                      'total_fee'=>'支付金额',
      *                      'total_fee'=>'支付金额',
@@ -64,15 +64,15 @@ class WeChatClient extends AbstractAPI
     public function createorder($order)
     public function createorder($order)
     {
     {
         $params = [
         $params = [
-            'openid'=>$order['openid'],    // 支付者的openid
-            'combine_trade_no'=>$order['out_trade_no'],  // 商家合单支付总交易单号
-            'expire_time'=>time()+$this->expire_time,
-            'sub_orders'=>[
+            'openid' => $order['openid'],    // 支付者的openid
+            'combine_trade_no' => $order['out_trade_no'],  // 商家合单支付总交易单号
+            'expire_time' => time() + $this->expire_time,
+            'sub_orders' => [
                 [
                 [
-                    'mchid'=>$this->merchant->merchant_id,
-                    'amount'=>(int)$order['total_fee'],
-                    'trade_no'=>$order['out_trade_no'],
-                    'description'=>$order['body']
+                    'mchid' => $this->merchant->merchant_id,
+                    'amount' => (int)$order['total_fee'],
+                    'trade_no' => $order['out_trade_no'],
+                    'description' => $order['body']
                 ]
                 ]
             ]
             ]
         ];
         ];
@@ -81,7 +81,7 @@ class WeChatClient extends AbstractAPI
 
 
     /**
     /**
      * 退款
      * 退款
-     * @param array $params[
+     * @param array $params [
      *                      'openid'=>'退款者的openid',
      *                      'openid'=>'退款者的openid',
      *                      'trade_no'=>'商家交易单号',
      *                      'trade_no'=>'商家交易单号',
      *                      'transaction_id'=>'支付单号',
      *                      'transaction_id'=>'支付单号',
@@ -95,13 +95,13 @@ class WeChatClient extends AbstractAPI
     public function refundorder(array $order)
     public function refundorder(array $order)
     {
     {
         $params = [
         $params = [
-            'openid'=>$order['openid'],
-            'mchid'=>$this->merchant->merchant_id,
-            'trade_no'=>$order['trade_no'],
-            'transaction_id'=>$order['transaction_id'],
-            'refund_no'=>$order['refund_no'],
-            'total_amount'=>$order['total_amount'],
-            'refund_amount'=>$order['refund_amount'],
+            'openid' => $order['openid'],
+            'mchid' => $this->merchant->merchant_id,
+            'trade_no' => $order['trade_no'],
+            'transaction_id' => $order['transaction_id'],
+            'refund_no' => $order['refund_no'],
+            'total_amount' => $order['total_amount'],
+            'refund_amount' => $order['refund_amount'],
         ];
         ];
         return $this->parseJSON('post', [self::API_SET_REFUND_ORDER, json_encode($params)]);
         return $this->parseJSON('post', [self::API_SET_REFUND_ORDER, json_encode($params)]);
     }
     }

+ 280 - 0
crmeb/crmeb/services/easywechat/v3pay/BaseClient.php

@@ -0,0 +1,280 @@
+<?php
+/**
+ *  +----------------------------------------------------------------------
+ *  | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+ *  +----------------------------------------------------------------------
+ *  | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
+ *  +----------------------------------------------------------------------
+ *  | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+ *  +----------------------------------------------------------------------
+ *  | Author: CRMEB Team <admin@crmeb.com>
+ *  +----------------------------------------------------------------------
+ */
+
+namespace crmeb\services\easywechat\v3pay;
+
+
+use crmeb\exceptions\PayException;
+use crmeb\services\easywechat\Application;
+use EasyWeChat\Core\AbstractAPI;
+use EasyWeChat\Core\AccessToken;
+use EasyWeChat\Core\Exceptions\HttpException;
+use EasyWeChat\Core\Exceptions\InvalidConfigException;
+use EasyWeChat\Core\Http;
+use EasyWeChat\Encryption\EncryptionException;
+use think\exception\InvalidArgumentException;
+
+
+class BaseClient extends AbstractAPI
+{
+
+    use Certficates;
+
+    /**
+     * @var Application
+     */
+    protected $app;
+
+    const BASE_URL = 'https://api.mch.weixin.qq.com/';
+
+    const KEY_LENGTH_BYTE = 32;
+
+    const AUTH_TAG_LENGTH_BYTE = 16;
+
+    /**
+     * BaseClient constructor.
+     * @param AccessToken $accessToken
+     * @param $app
+     */
+    public function __construct(AccessToken $accessToken, $app)
+    {
+        parent::__construct($accessToken);
+        $this->app = $app;
+    }
+
+    /**
+     * request.
+     *
+     * @param string $endpoint
+     * @param string $method
+     * @param array $options
+     * @param bool $returnResponse
+     */
+    public function request(string $endpoint, string $method = 'POST', array $options = [], $serial = true)
+    {
+        $body = $options['body'] ?? '';
+
+        if (isset($options['json'])) {
+            $body = json_encode($options['json']);
+            $options['body'] = $body;
+            unset($options['json']);
+        }
+
+        $headers = [
+            'Content-Type' => 'application/json',
+            'User-Agent' => 'curl',
+            'Accept' => 'application/json',
+            'Authorization' => $this->getAuthorization($endpoint, $method, $body),
+        ];
+
+        $options['headers'] = array_merge($headers, ($options['headers'] ?? []));
+
+        if ($serial) {
+            $options['headers']['Wechatpay-Serial'] = $this->getCertficatescAttr('serial_no');
+        }
+
+        return $this->_doRequestCurl($method, self::BASE_URL . $endpoint, $options);
+    }
+
+    /**
+     * @param $method
+     * @param $location
+     * @param array $options
+     * @return mixed
+     */
+    private function _doRequestCurl($method, $location, $options = [])
+    {
+        $curl = curl_init();
+        // POST数据设置
+        if (strtolower($method) === 'post') {
+            curl_setopt($curl, CURLOPT_POST, true);
+            curl_setopt($curl, CURLOPT_POSTFIELDS, $options['data'] ?? $options['body'] ?? '');
+        }
+        // CURL头信息设置
+        if (!empty($options['headers'])) {
+            $headers = [];
+            foreach ($options['headers'] as $k => $v) {
+                $headers[] = "$k: $v";
+            }
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+        }
+        curl_setopt($curl, CURLOPT_URL, $location);
+        curl_setopt($curl, CURLOPT_HEADER, true);
+        curl_setopt($curl, CURLOPT_TIMEOUT, 60);
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+        $content = curl_exec($curl);
+        $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
+        curl_close($curl);
+        return json_decode(substr($content, $headerSize), true);
+    }
+
+    /**
+     * To id card, mobile phone number and other fields sensitive information encryption.
+     *
+     * @param string $string
+     *
+     * @return string
+     */
+    protected function encryptSensitiveInformation(string $string)
+    {
+        $certificates = $this->app->certficates->get()['certificates'];
+        if (null === $certificates) {
+            throw new InvalidConfigException('config certificate connot be empty.');
+        }
+        $encrypted = '';
+        if (openssl_public_encrypt($string, $encrypted, $certificates, OPENSSL_PKCS1_OAEP_PADDING)) {
+            //base64编码
+            $sign = base64_encode($encrypted);
+        } else {
+            throw new EncryptionException('Encryption of sensitive information failed');
+        }
+        return $sign;
+    }
+
+
+    /**
+     * @param string $url
+     * @param string $method
+     * @param string $body
+     * @return string
+     */
+    protected function getAuthorization(string $url, string $method, string $body)
+    {
+        $nonceStr = uniqid();
+        $timestamp = time();
+        $message = $method . "\n" .
+            '/' . $url . "\n" .
+            $timestamp . "\n" .
+            $nonceStr . "\n" .
+            $body . "\n";
+        openssl_sign($message, $raw_sign, $this->getPrivateKey(), 'sha256WithRSAEncryption');
+        $sign = base64_encode($raw_sign);
+        $schema = 'WECHATPAY2-SHA256-RSA2048 ';
+        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
+            $this->app['config']['v3_payment']['mchid'], $nonceStr, $timestamp, $this->app['config']['v3_payment']['serial_no'], $sign);
+        return $schema . $token;
+    }
+
+    /**
+     * 获取商户私钥
+     * @return bool|resource
+     */
+    protected function getPrivateKey()
+    {
+        $key_path = $this->app['config']['v3_payment']['key_path'];
+        if (!file_exists($key_path)) {
+            throw new \InvalidArgumentException(
+                "SSL certificate not found: {$key_path}"
+            );
+        }
+        return openssl_pkey_get_private(file_get_contents($key_path));
+    }
+
+    /**
+     * 获取商户公钥
+     * @return bool|resource
+     */
+    protected function getPublicKey()
+    {
+        $key_path = $this->app['config']['v3_payment']['cert_path'];
+        if (!file_exists($key_path)) {
+            throw new \InvalidArgumentException(
+                "SSL certificate not found: {$key_path}"
+            );
+        }
+        return openssl_pkey_get_public(file_get_contents($key_path));
+    }
+
+    /**
+     * 替换url
+     * @param string $url
+     * @param $search
+     * @param $replace
+     * @return array|string|string[]
+     */
+    public function getApiUrl(string $url, $search, $replace)
+    {
+        $newSearch = [];
+        foreach ($search as $key) {
+            $newSearch[] = '{' . $key . '}';
+        }
+        return str_replace($newSearch, $replace, $url);
+    }
+
+    /**
+     * @param int $padding
+     */
+    private static function paddingModeLimitedCheck(int $padding): void
+    {
+        if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
+            throw new PayException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
+        }
+    }
+
+    /**
+     * 加密数据
+     * @param string $plaintext
+     * @param int $padding
+     * @return string
+     */
+    public function encryptor(string $plaintext, int $padding = OPENSSL_PKCS1_OAEP_PADDING)
+    {
+        self::paddingModeLimitedCheck($padding);
+
+        if (!openssl_public_encrypt($plaintext, $encrypted, $this->getPublicKey(), $padding)) {
+            throw new PayException('Encrypting the input $plaintext failed, please checking your $publicKey whether or nor correct.');
+        }
+
+        return base64_encode($encrypted);
+    }
+
+    /**
+     * decrypt ciphertext.
+     *
+     * @param array $encryptCertificate
+     *
+     * @return string
+     */
+    public function decrypt(array $encryptCertificate)
+    {
+        $ciphertext = base64_decode($encryptCertificate['ciphertext'], true);
+        $associatedData = $encryptCertificate['associated_data'];
+        $nonceStr = $encryptCertificate['nonce'];
+        $aesKey = $this->app['config']['v3_payment']['key'];
+
+        try {
+            // ext-sodium (default installed on >= PHP 7.2)
+            if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
+                return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
+            }
+            // ext-libsodium (need install libsodium-php 1.x via pecl)
+            if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
+                return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
+            }
+            // openssl (PHP >= 7.1 support AEAD)
+            if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
+                $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
+                $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
+                return \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData);
+            }
+        } catch (\Exception $exception) {
+            throw new InvalidArgumentException($exception->getMessage(), $exception->getCode());
+        } catch (\SodiumException $exception) {
+            throw new InvalidArgumentException($exception->getMessage(), $exception->getCode());
+        }
+        throw new InvalidArgumentException('AEAD_AES_256_GCM 需要 PHP 7.1 以上或者安装 libsodium-php');
+    }
+}
+

+ 68 - 0
crmeb/crmeb/services/easywechat/v3pay/Certficates.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ *  +----------------------------------------------------------------------
+ *  | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+ *  +----------------------------------------------------------------------
+ *  | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
+ *  +----------------------------------------------------------------------
+ *  | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+ *  +----------------------------------------------------------------------
+ *  | Author: CRMEB Team <admin@crmeb.com>
+ *  +----------------------------------------------------------------------
+ */
+
+namespace crmeb\services\easywechat\v3pay;
+
+
+use crmeb\exceptions\PayException;
+use think\facade\Cache;
+
+/**
+ * Class Certficates
+ * @package crmeb\services\easywechat\v3pay
+ */
+trait Certficates
+{
+
+    /**
+     * @param string|null $key
+     * @return array|mixed|null
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function getCertficatescAttr(string $key = null)
+    {
+        $driver = Cache::store('file');
+        $cacheKey = '_wx_v3' . $this->app['config']['v3_payment']['serial_no'];
+        if ($driver->has($cacheKey)) {
+            $res = $driver->get($cacheKey);
+            if ($key && $res) {
+                return $res[$key] ?? null;
+            } else {
+                return $res;
+            }
+        }
+        $certficates = $this->getCertficates();
+        $driver->set($cacheKey, $certficates, 3600 * 24 * 30);
+        if ($key && $certficates) {
+            return $certficates[$key] ?? null;
+        }
+        return $certficates;
+    }
+
+    /**
+     * get certficates.
+     *
+     * @return array
+     */
+    public function getCertficates()
+    {
+        $response = $this->request('v3/certificates', 'GET', [], false);
+        if (isset($response['code'])) {
+            throw new PayException($response['message']);
+        }
+        $certificates = $response['data'][0];
+        $certificates['certificates'] = $this->decrypt($certificates['encrypt_certificate']);
+        unset($certificates['encrypt_certificate']);
+        return $certificates;
+    }
+}

+ 400 - 0
crmeb/crmeb/services/easywechat/v3pay/PayClient.php

@@ -0,0 +1,400 @@
+<?php
+/**
+ *  +----------------------------------------------------------------------
+ *  | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+ *  +----------------------------------------------------------------------
+ *  | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
+ *  +----------------------------------------------------------------------
+ *  | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+ *  +----------------------------------------------------------------------
+ *  | Author: CRMEB Team <admin@crmeb.com>
+ *  +----------------------------------------------------------------------
+ */
+
+namespace crmeb\services\easywechat\v3pay;
+
+
+use crmeb\exceptions\PayException;
+
+/**
+ * v3支付
+ * Class PayClient
+ * @package crmeb\services\easywechat\v3pay
+ */
+class PayClient extends BaseClient
+{
+    //app支付
+    const API_APP_APY_URL = 'v3/pay/transactions/app';
+    //二维码支付截图
+    const API_NATIVE_URL = 'v3/pay/transactions/native';
+    //h5支付接口
+    const API_H5_URL = 'v3/pay/transactions/h5';
+    //jsapi支付接口
+    const API_JSAPI_URL = 'v3/pay/transactions/jsapi';
+    //发起商家转账API
+    const API_BATCHES_URL = 'v3/transfer/batches';
+    //退款
+    const API_REFUND_URL = 'v3/refund/domestic/refunds';
+    //退款查询接口
+    const API_REFUND_QUERY_URL = 'v3/refund/domestic/refunds/{out_refund_no}';
+
+    /**
+     * 公众号jsapi支付下单
+     * @param string $outTradeNo
+     * @param string $total
+     * @param string $description
+     * @param string $attach
+     * @return mixed
+     */
+    public function jsapiPay(string $openid, string $outTradeNo, string $total, string $description, string $attach)
+    {
+        $appId = $this->app['config']['wechat']['appid'];
+        $res = $this->pay('jsapi', $appId, $outTradeNo, $total, $description, $attach, ['openid' => $openid]);
+        return $this->configForJSSDKPayment($appId, $res['prepay_id']);
+    }
+
+    /**
+     * 小程序支付
+     * @param string $outTradeNo
+     * @param string $total
+     * @param string $description
+     * @param string $attach
+     * @return array|false|string
+     */
+    public function miniprogPay(string $openid, string $outTradeNo, string $total, string $description, string $attach)
+    {
+        $appId = $this->app['config']['miniprog']['appid'];
+        $res = $this->pay('jsapi', $appId, $outTradeNo, $total, $description, $attach, ['openid' => $openid]);
+        return $this->configForJSSDKPayment($appId, $res['prepay_id']);
+    }
+
+    /**
+     * APP支付下单
+     * @param string $outTradeNo
+     * @param string $total
+     * @param string $description
+     * @param string $attach
+     * @return mixed
+     */
+    public function appPay(string $outTradeNo, string $total, string $description, string $attach)
+    {
+        $res = $this->pay('app', $this->app['config']['app']['appid'], $outTradeNo, $total, $description, $attach);
+        return $this->configForAppPayment($res['prepay_id']);
+    }
+
+    /**
+     * native支付下单
+     * @param string $outTradeNo
+     * @param string $total
+     * @param string $description
+     * @param string $attach
+     * @return mixed
+     */
+    public function nativePay(string $outTradeNo, string $total, string $description, string $attach)
+    {
+        return $this->pay('native', $this->app['config']['web']['appid'], $outTradeNo, $total, $description, $attach);
+    }
+
+    /**
+     * h5支付下单
+     * @param string $outTradeNo
+     * @param string $total
+     * @param string $description
+     * @param string $attach
+     * @return mixed
+     */
+    public function h5Pay(string $outTradeNo, string $total, string $description, string $attach)
+    {
+        return $this->pay('h5', $this->app['config']['wechat']['appid'], $outTradeNo, $total, $description, $attach);
+    }
+
+    /**
+     * 下单
+     * @param string $type
+     * @param string $appid
+     * @param string $outTradeNo
+     * @param string $total
+     * @param string $description
+     * @param string $attach
+     * @param array $payer
+     * @return mixed
+     */
+    public function pay(string $type, string $appid, string $outTradeNo, string $total, string $description, string $attach, array $payer = [])
+    {
+        $totalFee = (int)bcmul($total, '100');
+
+        $data = [
+            'appid' => $appid,
+            'mchid' => $this->app['config']['v3_payment']['mchid'],
+            'out_trade_no' => $outTradeNo,
+            'attach' => $attach,
+            'description' => $description,
+            'notify_url' => $this->app['config']['v3_payment']['notify_url'],
+            'amount' => [
+                'total' => $totalFee,
+                'currency' => 'CNY'
+            ],
+        ];
+
+        if ($payer) {
+            $data['payer'] = $payer;
+        }
+
+        $url = '';
+        switch ($type) {
+            case 'h5':
+                $url = self::API_H5_URL;
+                $data['scene_info'] = [
+                    'payer_client_ip' => request()->ip(),
+                    'h5_info' => [
+                        'type' => 'Wap'
+                    ]
+                ];
+                break;
+            case 'native':
+                $url = self::API_NATIVE_URL;
+                break;
+            case 'app':
+                $url = self::API_APP_APY_URL;
+                break;
+            case 'jsapi':
+                $url = self::API_JSAPI_URL;
+                break;
+        }
+
+        if (!$url) {
+            throw new PayException('缺少请求地址');
+        }
+
+        $res = $this->request($url, 'POST', ['json' => $data]);
+
+        if (!$res) {
+            throw new PayException('微信支付:下单失败');
+        }
+        if (isset($res['code']) && isset($res['message'])) {
+            throw new PayException($res['message']);
+        }
+
+        return $res;
+    }
+
+    /**
+     * 发起商家转账API
+     * @param string $outBatchNo
+     * @param string $amount
+     * @param string $batchName
+     * @param string $remark
+     * @param array $transferDetailList
+     * @return mixed
+     */
+    public function batches(string $outBatchNo, string $amount, string $batchName, string $remark, array $transferDetailList)
+    {
+        $totalFee = '0';
+
+        foreach ($transferDetailList as &$item) {
+            if ($item['transfer_amount'] >= 2000 && !empty($item['user_name'])) {
+                throw new PayException('明细金额大于等于2000时,收款人姓名必须填写');
+            }
+            $totalFee = bcadd($totalFee, $item['transfer_amount'], 2);
+            $item['transfer_amount'] = (int)bcmul($item['transfer_amount'], 100, 0);
+            if (isset($item['user_name'])) {
+                $item['user_name'] = $this->encryptor($item['user_name']);
+            }
+        }
+
+        if ($totalFee !== $amount) {
+            throw new PayException('转账明细金额总和和转账总金额不一致');
+        }
+
+        $amount = (int)bcmul($amount, 100, 0);
+
+        $data = [
+            'appid' => $this->app['config']['wechat']['appid'],
+            'out_batch_no' => $outBatchNo,
+            'batch_name' => $batchName,
+            'batch_remark' => $remark,
+            'total_amount' => $amount,
+            'total_num' => count($transferDetailList),
+            'transfer_detail_list' => $transferDetailList
+        ];
+
+        $res = $this->request(self::API_BATCHES_URL, 'POST', ['json' => $data]);
+
+        if (!$res) {
+            throw new PayException('微信支付:发起商家转账失败');
+        }
+
+        if (isset($res['code']) && isset($res['message'])) {
+            throw new PayException($res['message']);
+        }
+
+        return $res;
+
+    }
+
+    /**
+     * 退款
+     * @param string $outTradeNo
+     * @param array $options
+     * @return mixed
+     */
+    public function refund(string $outTradeNo, array $options = [])
+    {
+        if (!isset($options['pay_price'])) {
+            throw new PayException(400730);
+        }
+        $totalFee = floatval(bcmul($options['pay_price'], 100, 0));
+        $refundFee = isset($options['refund_price']) ? floatval(bcmul($options['refund_price'], 100, 0)) : null;
+        $refundReason = $options['desc'] ?? '';
+        $refundNo = $options['refund_id'] ?? $outTradeNo;
+        /*仅针对老资金流商户使用
+        REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款)
+        REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款
+        */
+        $refundAccount = $opt['refund_account'] ?? 'AVAILABLE';
+
+        $data = [
+            'transaction_id' => $outTradeNo,
+            'out_refund_no' => $refundNo,
+            'amount' => [
+                'refund' => (int)$refundFee,
+                'currency' => 'CNY',
+                'total' => (int)$totalFee
+            ],
+            'funds_account' => $refundAccount
+        ];
+
+        if ($refundReason) {
+            $data['reason'] = $refundReason;
+        }
+
+        $res = $this->request(self::API_REFUND_URL, 'POST', ['json' => $data]);
+
+        if (!$res) {
+            throw new PayException('微信支付:发起退款失败');
+        }
+
+        if (isset($res['code']) && isset($res['message'])) {
+            throw new PayException($res['message']);
+        }
+
+        return $res;
+    }
+
+    /**
+     * 查询退款
+     * @param string $outRefundNo
+     * @return mixed
+     */
+    public function queryRefund(string $outRefundNo)
+    {
+        $res = $this->request($this->getApiUrl(self::API_REFUND_QUERY_URL, ['out_refund_no'], [$outRefundNo]), 'GET');
+
+        if (!$res) {
+            throw new PayException(50001);
+        }
+
+        if (isset($res['code']) && isset($res['message'])) {
+            throw new PayException($res['message']);
+        }
+
+        return $res;
+    }
+
+    /**
+     * jsapi支付
+     * @param string $appid
+     * @param string $prepayId
+     * @param bool $json
+     * @return array|false|string
+     */
+    public function configForPayment(string $appid, string $prepayId, bool $json = true)
+    {
+        $params = [
+            'appId' => $appid,
+            'timeStamp' => strval(time()),
+            'nonceStr' => uniqid(),
+            'package' => "prepay_id=$prepayId",
+            'signType' => 'RSA',
+        ];
+        $message = $params['appId'] . "\n" .
+            $params['timeStamp'] . "\n" .
+            $params['nonceStr'] . "\n" .
+            $params['package'] . "\n";
+        openssl_sign($message, $raw_sign, $this->getPrivateKey(), 'sha256WithRSAEncryption');
+        $sign = base64_encode($raw_sign);
+
+        $params['paySign'] = $sign;
+
+        return $json ? json_encode($params) : $params;
+    }
+
+    /**
+     * Generate app payment parameters.
+     * @param string $prepayId
+     * @return array
+     */
+    public function configForAppPayment(string $prepayId): array
+    {
+        $params = [
+            'appid' => $this->app['config']['app']['appid'],
+            'partnerid' => $this->app['config']['v3_payment']['mchid'],
+            'prepayid' => $prepayId,
+            'noncestr' => uniqid(),
+            'timestamp' => time(),
+            'package' => 'Sign=WXPay',
+        ];
+        $message = $params['appid'] . "\n" .
+            $params['timestamp'] . "\n" .
+            $params['noncestr'] . "\n" .
+            $params['prepayid'] . "\n";
+        openssl_sign($message, $raw_sign, $this->getPrivateKey(), 'sha256WithRSAEncryption');
+        $sign = base64_encode($raw_sign);
+
+        $params['sign'] = $sign;
+
+        return $params;
+    }
+
+    /**
+     * 小程序支付
+     * @param string $appid
+     * @param string $prepayId
+     * @return array|false|string
+     */
+    public function configForJSSDKPayment(string $appid, string $prepayId)
+    {
+        $config = $this->configForPayment($appid, $prepayId, false);
+
+        $config['timestamp'] = $config['timeStamp'];
+        unset($config['timeStamp']);
+
+        return $config;
+    }
+
+    /**
+     * @param $callback
+     * @return \think\Response
+     */
+    public function handleNotify($callback)
+    {
+        $request = request();
+        $success = $request->post('event_type') === 'TRANSACTION.SUCCESS';
+        $data = $this->decrypt($request->post('resource', []));
+
+        $handleResult = call_user_func_array($callback, [json_decode($data), $success]);
+        if (is_bool($handleResult) && $handleResult) {
+            $response = [
+                'code' => 'SUCCESS',
+                'message' => 'OK',
+            ];
+        } else {
+            $response = [
+                'code' => 'FAIL',
+                'message' => $handleResult,
+            ];
+        }
+
+        return response($response, 200, [], 'json');
+    }
+}

+ 36 - 0
crmeb/crmeb/services/easywechat/v3pay/ServiceProvider.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ *  +----------------------------------------------------------------------
+ *  | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+ *  +----------------------------------------------------------------------
+ *  | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
+ *  +----------------------------------------------------------------------
+ *  | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+ *  +----------------------------------------------------------------------
+ *  | Author: CRMEB Team <admin@crmeb.com>
+ *  +----------------------------------------------------------------------
+ */
+
+namespace crmeb\services\easywechat\v3pay;
+
+
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+/**
+ * Class ServiceProvider
+ * @package crmeb\services\easywechat\v3pay
+ */
+class ServiceProvider implements ServiceProviderInterface
+{
+
+    /**
+     * @param Container $pimple
+     */
+    public function register(Container $pimple)
+    {
+        $pimple['v3pay'] = function ($pimple) {
+            return new PayClient($pimple['access_token'], $pimple);
+        };
+    }
+}

+ 4 - 3
crmeb/crmeb/services/express/storage/Express.php

@@ -110,11 +110,12 @@ class Express extends BaseExpress
      * @return 物流状态:status 0在途,1揽收,2疑难,3签收,4退签,5派件,6退回,7转单,10待清关,11清关中,12已清关,13清关异常,14收件人拒签
      * @return 物流状态:status 0在途,1揽收,2疑难,3签收,4退签,5派件,6退回,7转单,10待清关,11清关中,12已清关,13清关异常,14收件人拒签
      * @return 物流详情 content
      * @return 物流详情 content
      */
      */
-    public function query(string $num, string $com = '')
+    public function query(string $num, string $com = '', $phone = '')
     {
     {
         $param = [
         $param = [
             'com' => $com,
             'com' => $com,
-            'num' => $num
+            'num' => $num,
+            'phone' => $phone
         ];
         ];
         if ($com === null) {
         if ($com === null) {
             unset($param['com']);
             unset($param['com']);
@@ -156,7 +157,7 @@ class Express extends BaseExpress
         }
         }
         /** @var ExpressServices $expressServices */
         /** @var ExpressServices $expressServices */
         $expressServices = app()->make(ExpressServices::class);
         $expressServices = app()->make(ExpressServices::class);
-        $expressData = $expressServices->getOneByWhere(['code'=>$param['com']])->toArray();
+        $expressData = $expressServices->getOneByWhere(['code' => $param['com']])->toArray();
         if (isset($data['cargo'])) $param['cargo'] = $data['cargo'];
         if (isset($data['cargo'])) $param['cargo'] = $data['cargo'];
         if ($expressData['partner_id'] == 1) $param['partner_id'] = $expressData['account'];
         if ($expressData['partner_id'] == 1) $param['partner_id'] = $expressData['account'];
         if ($expressData['partner_key'] == 1) $param['partner_key'] = $expressData['key'];
         if ($expressData['partner_key'] == 1) $param['partner_key'] = $expressData['key'];

+ 2 - 0
crmeb/crmeb/services/pay/Pay.php

@@ -14,6 +14,7 @@ namespace crmeb\services\pay;
 
 
 use crmeb\basic\BaseManager;
 use crmeb\basic\BaseManager;
 use crmeb\services\pay\storage\AliPay;
 use crmeb\services\pay\storage\AliPay;
+use crmeb\services\pay\storage\V3WechatPay;
 use crmeb\services\pay\storage\WechatPay;
 use crmeb\services\pay\storage\WechatPay;
 use think\facade\Config;
 use think\facade\Config;
 
 
@@ -23,6 +24,7 @@ use think\facade\Config;
  * @package crmeb\services\pay
  * @package crmeb\services\pay
  * @mixin AliPay
  * @mixin AliPay
  * @mixin WechatPay
  * @mixin WechatPay
+ * @mixin V3WechatPay
  */
  */
 class Pay extends BaseManager
 class Pay extends BaseManager
 {
 {

+ 1 - 1
crmeb/crmeb/services/pay/PayInterface.php

@@ -72,6 +72,6 @@ interface PayInterface
      * 支付回调
      * 支付回调
      * @return mixed
      * @return mixed
      */
      */
-    public static function handleNotify();
+    public function handleNotify();
 
 
 }
 }

+ 216 - 0
crmeb/crmeb/services/pay/storage/V3WechatPay.php

@@ -0,0 +1,216 @@
+<?php
+/**
+ *  +----------------------------------------------------------------------
+ *  | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+ *  +----------------------------------------------------------------------
+ *  | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
+ *  +----------------------------------------------------------------------
+ *  | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+ *  +----------------------------------------------------------------------
+ *  | Author: CRMEB Team <admin@crmeb.com>
+ *  +----------------------------------------------------------------------
+ */
+
+namespace crmeb\services\pay\storage;
+
+use app\services\pay\PayNotifyServices;
+use app\services\wechat\WechatMessageServices;
+use crmeb\exceptions\PayException;
+use crmeb\services\app\MiniProgramService;
+use crmeb\services\easywechat\Application;
+use crmeb\services\pay\BasePay;
+use crmeb\services\pay\PayInterface;
+use crmeb\utils\Hook;
+use EasyWeChat\Payment\Order;
+use think\facade\Log;
+
+/**
+ * Class 微信支付v3
+ * @author 等风来
+ * @email 136327134@qq.com
+ * @date 2022/9/22
+ * @package crmeb\services\pay\storage
+ */
+class V3WechatPay extends BasePay implements PayInterface
+{
+
+    /**
+     * @var Application
+     */
+    protected $instance;
+
+    /**
+     * @param array $config
+     * @return mixed|void
+     * @author 等风来
+     * @email 136327134@qq.com
+     * @date 2022/9/22
+     */
+    protected function initialize(array $config = [])
+    {
+        $wechatAppid = sys_config('wechat_appid');
+        $config = [
+            'app' => [
+                'appid' => sys_config('wechat_app_appid'),
+            ],
+            'wechat' => [
+                'appid' => $wechatAppid,
+            ],
+            'miniprog' => [
+                'appid' => sys_config('routine_appId'),
+            ],
+            'web' => [
+                'appid' => sys_config('wechat_open_app_id', $wechatAppid),
+            ],
+            'v3_payment' => [
+                'mchid' => sys_config('pay_weixin_mchid'),
+                'key' => sys_config('pay_weixin_key_v3'),
+                'serial_no' => sys_config('pay_weixin_serial_no'),
+                'cert_path' => public_path() . $this->getPemPath(sys_config('pay_weixin_client_cert')),
+                'key_path' => public_path() . $this->getPemPath(sys_config('pay_weixin_client_key')),
+                'notify_url' => trim(sys_config('site_url')) . '/api/wechat/v3notify',
+            ]
+        ];
+        $this->instance = new Application($config);
+    }
+
+    /**
+     * 获取证书不带域名的路径
+     * @param string $path
+     * @return mixed|string
+     * @author 等风来
+     * @email 136327134@qq.com
+     * @date 2022/9/22
+     */
+    public function getPemPath(string $path)
+    {
+        if (strstr($path, 'http://') === false && strstr($path, 'https://') === false) {
+            return $path;
+        } else {
+            $res = parse_url($path);
+            return $res['path'] ?? '';
+        }
+    }
+
+    /**
+     * 创建订单返回支付参数
+     * @param string $orderId
+     * @param string $totalFee
+     * @param string $attach
+     * @param string $body
+     * @param string $detail
+     * @param array $options
+     * @return array|false|mixed|string
+     * @author 等风来
+     * @email 136327134@qq.com
+     * @date 2022/9/22
+     */
+    public function create(string $orderId, string $totalFee, string $attach, string $body, string $detail, array $options = [])
+    {
+        $this->authSetPayType();
+
+        switch ($this->payType) {
+            case Order::NATIVE:
+                $res = $this->instance->v3pay->nativePay($orderId, $totalFee, $body, $attach);
+                $res['invalid'] = time() + 60;
+                $res['logo'] = sys_config('wap_login_logo');
+                return $res;
+            case Order::APP:
+                return $this->instance->v3pay->appPay($orderId, $totalFee, $body, $attach);
+            case Order::JSAPI:
+                if (empty($options['openid'])) {
+                    throw new PayException('缺少openid');
+                }
+                if (request()->isRoutine()) {
+                    // 获取配置  判断是否为新支付
+                    if ($options['pay_new_weixin_open']) {
+                        return MiniProgramService::newJsPay($options['openid'], $orderId, $totalFee, $attach, $body, $detail, $options);
+                    }
+                    return $this->instance->v3pay->miniprogPay($options['openid'], $orderId, $totalFee, $body, $attach);
+                }
+                return $this->instance->v3pay->jsapiPay($options['openid'], $orderId, $totalFee, $body, $attach);
+            case 'h5':
+                return $this->instance->v3pay->h5Pay($orderId, $totalFee, $body, $attach);
+            default:
+                throw new PayException('微信支付:支付类型错误');
+        }
+    }
+
+    /**
+     * @param string $openid
+     * @param string $orderId
+     * @param string $amount
+     * @param array $options
+     * @return mixed
+     * @author 等风来
+     * @email 136327134@qq.com
+     * @date 2022/9/22
+     */
+    public function merchantPay(string $openid, string $orderId, string $amount, array $options = [])
+    {
+        $res = $this->instance->v3pay->batches(
+            $orderId,
+            $amount,
+            $options['batch_name'],
+            $options['batch_remark'],
+            [
+                [
+                    'out_detail_no' => $orderId,
+                    'transfer_amount' => $amount,
+                    'transfer_remark' => $options['batch_remark'],
+                    'openid' => $openid
+                ]
+            ]
+        );
+
+        return $res;
+    }
+
+    /**
+     * 发起退款
+     * @param string $outTradeNo
+     * @param array $options
+     * @return mixed
+     */
+    public function refund(string $outTradeNo, array $options = [])
+    {
+        return $this->instance->v3pay->refund($outTradeNo, $options);
+    }
+
+    /**
+     * 查询退款
+     * @param string $outTradeNo
+     * @param string|null $outRequestNo
+     * @param array $other
+     * @return mixed
+     */
+    public function queryRefund(string $outTradeNo, string $outRequestNo = null, array $other = [])
+    {
+        return $this->instance->v3pay->queryRefund($outTradeNo);
+    }
+
+    /**
+     * @return mixed|\think\Response
+     * @author 等风来
+     * @email 136327134@qq.com
+     * @date 2022/9/22
+     */
+    public function handleNotify()
+    {
+        return $this->instance->v3pay->handleNotify(function ($notify, $successful) {
+            Log::error('支付回调:' . json_encode($notify));
+            if ($successful && isset($notify->out_trade_no)) {
+                if (isset($notify->attach) && $notify->attach) {
+                    if (($count = strpos($notify->out_trade_no, '_')) !== false) {
+                        $notify->out_trade_no = substr($notify->out_trade_no, $count + 1);
+                    }
+                    return (new Hook(PayNotifyServices::class, 'wechat'))->listen($notify->attach, $notify->out_trade_no, $notify->transaction_id);
+                }
+                /** @var WechatMessageServices $wechatMessageService */
+                $wechatMessageService = app()->make(WechatMessageServices::class);
+                $wechatMessageService->setOnceMessage($notify, $notify->openid, 'payment_success', $notify->out_trade_no);
+                return false;
+            }
+        });
+    }
+}

+ 5 - 7
crmeb/crmeb/services/pay/storage/WechatPay.php

@@ -64,9 +64,8 @@ class WechatPay extends BasePay implements PayInterface
                 }
                 }
                 if (request()->isRoutine()) {
                 if (request()->isRoutine()) {
                     // 获取配置  判断是否为新支付
                     // 获取配置  判断是否为新支付
-                    if($options['pay_new_weixin_open'])
-                    {
-                        return MiniProgramService::newJsPay($options['openid'], $orderId, $totalFee, $attach, $body, $detail,$options);
+                    if ($options['pay_new_weixin_open']) {
+                        return MiniProgramService::newJsPay($options['openid'], $orderId, $totalFee, $attach, $body, $detail, $options);
                     }
                     }
                     return MiniProgramService::jsPay($options['openid'], $orderId, $totalFee, $attach, $body, $detail);
                     return MiniProgramService::jsPay($options['openid'], $orderId, $totalFee, $attach, $body, $detail);
                 }
                 }
@@ -114,10 +113,9 @@ class WechatPay extends BasePay implements PayInterface
         if (isset($opt['wechat'])) {
         if (isset($opt['wechat'])) {
             return WechatService::refund($outTradeNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount);
             return WechatService::refund($outTradeNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount);
         } else {
         } else {
-            if($opt['pay_new_weixin_open'])
-            {
+            if ($opt['pay_new_weixin_open']) {
                 return MiniProgramService::miniRefund($outTradeNo, $totalFee, $refundFee, $opt);
                 return MiniProgramService::miniRefund($outTradeNo, $totalFee, $refundFee, $opt);
-            }else{
+            } else {
                 return MiniProgramService::refund($outTradeNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount);
                 return MiniProgramService::refund($outTradeNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount);
             }
             }
         }
         }
@@ -140,7 +138,7 @@ class WechatPay extends BasePay implements PayInterface
      * @return mixed|\Symfony\Component\HttpFoundation\Response
      * @return mixed|\Symfony\Component\HttpFoundation\Response
      * @throws \EasyWeChat\Core\Exceptions\FaultException
      * @throws \EasyWeChat\Core\Exceptions\FaultException
      */
      */
-    public static function handleNotify()
+    public function handleNotify()
     {
     {
         return WechatService::handleNotify();
         return WechatService::handleNotify();
     }
     }

+ 40 - 16
crmeb/public/install/crmeb.sql

@@ -15057,7 +15057,7 @@ INSERT INTO `eb_lang_code` (`id`, `type_id`, `code`, `remarks`, `lang_explain`,
 (13530, 1, '账户充值', '账户充值', '账户充值', 0),
 (13530, 1, '账户充值', '账户充值', '账户充值', 0),
 (13531, 1, '佣金转入', '佣金转入', '佣金转入', 0),
 (13531, 1, '佣金转入', '佣金转入', '佣金转入', 0),
 (13532, 1, '周榜', '周榜', '周榜', 0),
 (13532, 1, '周榜', '周榜', '周榜', 0),
-(13533, 1, '月榜', '月榜', '榜', 0),
+(13533, 1, '月榜', '月榜', '榜', 0),
 (13534, 1, '周排行', '周排行', '周排行', 0),
 (13534, 1, '周排行', '周排行', '周排行', 0),
 (13535, 1, '月排行', '月排行', '月排行', 0),
 (13535, 1, '月排行', '月排行', '月排行', 0),
 (13536, 1, '语言切换', '语言切换', '语言切换', 0),
 (13536, 1, '语言切换', '语言切换', '语言切换', 0),
@@ -17418,7 +17418,7 @@ INSERT INTO `eb_lang_code` (`id`, `type_id`, `code`, `remarks`, `lang_explain`,
 (15888, 3, '账户充值', '账户充值', '賬戶充值', 0),
 (15888, 3, '账户充值', '账户充值', '賬戶充值', 0),
 (15889, 3, '佣金转入', '佣金转入', '佣金轉入', 0),
 (15889, 3, '佣金转入', '佣金转入', '佣金轉入', 0),
 (15890, 3, '周榜', '周榜', '周榜', 0),
 (15890, 3, '周榜', '周榜', '周榜', 0),
-(15891, 3, '月榜', '月榜', '榜', 0),
+(15891, 3, '月榜', '月榜', '榜', 0),
 (15892, 3, '周排行', '周排行', '週排行', 0),
 (15892, 3, '周排行', '周排行', '週排行', 0),
 (15893, 3, '月排行', '月排行', '月排行', 0),
 (15893, 3, '月排行', '月排行', '月排行', 0),
 (15894, 3, '语言切换', '语言切换', '語言切換', 0),
 (15894, 3, '语言切换', '语言切换', '語言切換', 0),
@@ -25755,7 +25755,27 @@ INSERT INTO `eb_lang_code` (`id`, `type_id`, `code`, `remarks`, `lang_explain`,
 (24217, 10, '提醒付款通知', '提醒付款通知', 'Ghi nhớ thông báo thanh toán', 0),
 (24217, 10, '提醒付款通知', '提醒付款通知', 'Ghi nhớ thông báo thanh toán', 0),
 (24218, 10, '短信验证码', '短信验证码', 'Mã kiểm tra SMS', 0),
 (24218, 10, '短信验证码', '短信验证码', 'Mã kiểm tra SMS', 0),
 (24219, 10, '到店自提', '到店自提', 'Tự rút lui', 0),
 (24219, 10, '到店自提', '到店自提', 'Tự rút lui', 0),
-(24220, 10, '快递配送', '快递配送', 'Name', 0);
+(24220, 10, '快递配送', '快递配送', 'Name', 0),
+(24221, 1, '退款发起查询失败', '退款发起失败', '微信支付:发起退款查询失败', 0),
+(24222, 2, '退款发起查询失败', '退款发起失败', 'WeChat payment: Failed to launch refund query', 0),
+(24223, 3, '退款发起查询失败', '退款发起失败', '微信支付:發起退款査詢失敗', 0),
+(24224, 4, '退款发起查询失败', '退款发起失败', 'Paiement Wechat: impossible dinitier la requête de remboursement', 0),
+(24225, 5, '退款发起查询失败', '退款发起失败', 'Pagamento WeChat: avvio della richiesta di rimborso non riuscito', 0),
+(24226, 6, '退款发起查询失败', '退款发起失败', 'WeChat Pay:返金照会の開始に失敗しました', 0),
+(24227, 7, '退款发起查询失败', '退款发起失败', '위챗페이: 환불 조회 실패', 0),
+(24228, 8, '退款发起查询失败', '退款发起失败', '', 0),
+(24229, 9, '退款发起查询失败', '退款发起失败', 'ล้มเหลวในการเริ่มต้นการสอบถามเกี่ยวกับการคืนเงิน', 0),
+(24230, 10, '退款发起查询失败', '退款发起失败', 'Dịch vụ WeChat: không thể khởi chạy yêu cầu hoàn to àn', 0),
+(24231, 1, '500000', '发起退款查询失败', '微信支付:发起退款查询失败', 1),
+(24232, 2, '500000', '发起退款查询失败', 'WeChat payment: Failed to launch refund query', 1),
+(24233, 3, '500000', '发起退款查询失败', '微信支付:發起退款査詢失敗', 1),
+(24234, 4, '500000', '发起退款查询失败', 'Paiement Wechat: impossible dinitier la requête de remboursement', 1),
+(24235, 5, '500000', '发起退款查询失败', 'Pagamento WeChat: avvio della richiesta di rimborso non riuscito', 1),
+(24236, 6, '500000', '发起退款查询失败', 'WeChat Pay:返金照会の開始に失敗しました', 1),
+(24237, 7, '500000', '发起退款查询失败', '위챗페이: 환불 조회 실패', 1),
+(24238, 8, '500000', '发起退款查询失败', '', 1),
+(24239, 9, '500000', '发起退款查询失败', 'ล้มเหลวในการเริ่มต้นการสอบถามเกี่ยวกับการคืนเงิน', 1),
+(24240, 10, '500000', '发起退款查询失败', 'Dịch vụ WeChat: không thể khởi chạy yêu cầu hoàn to àn', 1);
 
 
 -- --------------------------------------------------------
 -- --------------------------------------------------------
 
 
@@ -26140,24 +26160,25 @@ CREATE TABLE IF NOT EXISTS `eb_lang_type` (
   `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '1启用0禁用',
   `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '1启用0禁用',
   `is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否默认语言',
   `is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否默认语言',
   `is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
   `is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
-  PRIMARY KEY (`id`)
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `id` (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='语言类型';
 ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='语言类型';
 
 
 --
 --
 -- 转存表中的数据 `eb_lang_type`
 -- 转存表中的数据 `eb_lang_type`
 --
 --
 
 
-INSERT INTO `eb_lang_type` (`id`, `language_name`, `file_name`, `status`, `is_default`) VALUES
-(1, '中文', 'zh-CN', 1, 1),
-(2, '英文', 'en-US', 1, 0),
-(3, '繁体中文', 'zh-Hant', 1, 0),
-(4, '法语', 'fr-FR', 1, 0),
-(5, '意大利语', 'it-IT', 1, 0),
-(6, '日语', 'ja-JP', 1, 0),
-(7, '韩语', 'ko-KR', 1, 0),
-(8, '蒙古语', 'mn-MN', 1, 0),
-(9, '泰语', 'th-TH', 1, 0),
-(10, '越南语', 'vi-VN', 1, 0);
+INSERT INTO `eb_lang_type` (`id`, `language_name`, `file_name`, `status`, `is_default`, `is_del`) VALUES
+(1, '中文', 'zh-CN', 1, 1, 0),
+(2, '英文', 'en-US', 1, 0, 0),
+(3, '繁体中文', 'zh-Hant', 1, 0, 0),
+(4, '法语', 'fr-FR', 1, 0, 0),
+(5, '意大利语', 'it-IT', 1, 0, 0),
+(6, '日语', 'ja-JP', 1, 0, 0),
+(7, '韩语', 'ko-KR', 1, 0, 0),
+(8, '蒙古语', 'mn-MN', 1, 0, 0),
+(9, '泰语', 'th-TH', 1, 0, 0),
+(10, '越南语', 'vi-VN', 1, 0, 0);
 
 
 -- --------------------------------------------------------
 -- --------------------------------------------------------
 
 
@@ -32353,7 +32374,10 @@ INSERT INTO `eb_system_config` (`id`, `menu_name`, `type`, `input_type`, `config
 (409, 'pay_new_weixin_open', 'radio', 'input', 4, '0=>关闭\n1=>打开', 1, '', 0, 0, '0', '小程序支付管理', '小程序开发后台有支付管理的请打开此开关', 0, 1),
 (409, 'pay_new_weixin_open', 'radio', 'input', 4, '0=>关闭\n1=>打开', 1, '', 0, 0, '0', '小程序支付管理', '小程序开发后台有支付管理的请打开此开关', 0, 1),
 (410, 'pay_new_weixin_mchid', 'text', 'input', 4, '', 1, '', 0, 0, '\"\"', '小程序支付商户号', '小程序开通支付管理生成的商户号', 0, 1),
 (410, 'pay_new_weixin_mchid', 'text', 'input', 4, '', 1, '', 0, 0, '\"\"', '小程序支付商户号', '小程序开通支付管理生成的商户号', 0, 1),
 (411, 'system_comment_time', 'text', 'number', 27, '', 1, '', 100, 0, '\"0\"', '自动评价时间', '自动评价时间(天),0为不开启自动评价', 0, 1),
 (411, 'system_comment_time', 'text', 'number', 27, '', 1, '', 100, 0, '\"0\"', '自动评价时间', '自动评价时间(天),0为不开启自动评价', 0, 1),
-(412, 'comment_content', 'text', 'input', 27, '', 1, '', 100, 0, '\"\\u6b64\\u7528\\u6237\\u672a\\u505a\\u8bc4\\u4ef7\"', '自动评价文字', '自动评价显示的评价文字', 0, 1);
+(412, 'comment_content', 'text', 'input', 27, '', 1, '', 100, 0, '\"\\u6b64\\u7528\\u6237\\u672a\\u505a\\u8bc4\\u4ef7\"', '自动评价文字', '自动评价显示的评价文字', 0, 1),
+(413, 'pay_wechat_type', 'radio', 'input', 4, '0=>v2\n1=>v3', 1, '', 0, 0, '0', '支付接口类型', '支付接口类型v2对应微信支付旧版v2支付。v3对应微信支付v3支付接口。支付证书可以通用一个。支付秘钥和v2旧版支付有区别', 10, 1),
+(414, 'pay_weixin_serial_no', 'text', 'input', 4, '', 1, '', 100, 0, '\"\"', '证书序列号', '「商户API证书」的「证书序列号」,可以在证书管理里面查看', 0, 1),
+(415, 'pay_weixin_key_v3', 'text', 'input', 4, '', 1, '', 100, 0, '\"\"', 'V3支付Key', 'V3支付秘钥', 0, 1);
 
 
 -- --------------------------------------------------------
 -- --------------------------------------------------------
 
 

+ 6 - 6
crmeb/vendor/composer/installed.php

@@ -1,9 +1,9 @@
 <?php return array(
 <?php return array(
     'root' => array(
     'root' => array(
         'name' => 'topthink/think',
         'name' => 'topthink/think',
-        'pretty_version' => 'dev-master',
-        'version' => 'dev-master',
-        'reference' => '13adf9a931c339ffb7910da803bf28c4ed4d1d9b',
+        'pretty_version' => '4.6.0.x-dev',
+        'version' => '4.6.0.9999999-dev',
+        'reference' => '7894ca3eaf91b7c54ffb20443b7b0d51541546d3',
         'type' => 'project',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
         'aliases' => array(),
@@ -764,9 +764,9 @@
             'dev_requirement' => false,
             'dev_requirement' => false,
         ),
         ),
         'topthink/think' => array(
         'topthink/think' => array(
-            'pretty_version' => 'dev-master',
-            'version' => 'dev-master',
-            'reference' => '13adf9a931c339ffb7910da803bf28c4ed4d1d9b',
+            'pretty_version' => '4.6.0.x-dev',
+            'version' => '4.6.0.9999999-dev',
+            'reference' => '7894ca3eaf91b7c54ffb20443b7b0d51541546d3',
             'type' => 'project',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
             'aliases' => array(),

+ 1 - 5
crmeb/vendor/overtrue/wechat/src/Foundation/Application.php

@@ -105,11 +105,7 @@ class Application extends Container
         ServiceProviders\ShakeAroundServiceProvider::class,
         ServiceProviders\ShakeAroundServiceProvider::class,
         ServiceProviders\OpenPlatformServiceProvider::class,
         ServiceProviders\OpenPlatformServiceProvider::class,
         ServiceProviders\MiniProgramServiceProvider::class,
         ServiceProviders\MiniProgramServiceProvider::class,
-        ServiceProviders\CommentServiceProvider::class,
-        \crmeb\services\easywechat\subscribe\ProgramProvider::class,
-        \crmeb\services\easywechat\wechatlive\ProgramProvider::class,
-        \crmeb\services\easywechat\Open3rd\ProgramProvider::class,
-        \crmeb\services\easywechat\miniPayment\ServiceProvider::class
+        ServiceProviders\CommentServiceProvider::class
     ];
     ];
 
 
     /**
     /**

+ 1 - 1
crmeb/vendor/services.php

@@ -1,5 +1,5 @@
 <?php 
 <?php 
-// This file is automatically generated at:2022-09-15 10:07:34
+// This file is automatically generated at:2022-09-20 16:14:20
 declare (strict_types = 1);
 declare (strict_types = 1);
 return array (
 return array (
   0 => 'think\\captcha\\CaptchaService',
   0 => 'think\\captcha\\CaptchaService',