【程序目录】优化程序

This commit is contained in:
吴昊天 2022-09-27 16:04:04 +08:00
parent 6f9dc32e8a
commit 75174a99df
42 changed files with 1395 additions and 121 deletions

View File

@ -111,7 +111,7 @@ class Common extends AuthController
} catch (\Throwable $e) {
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:
return app('json')->success(['status' => -9, 'force_reminder' => $upgradeStatus['force_reminder'] ?? 0]);
}

View File

@ -216,7 +216,7 @@ class StoreIntegralOrder extends AuthController
$data['delivery_name'] = $orderInfo['delivery_name'];
$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);
}

View File

@ -510,7 +510,7 @@ class StoreOrder extends AuthController
$data['delivery_name'] = $orderInfo['delivery_name'];
$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);
}

View File

@ -359,6 +359,11 @@ class StoreProduct extends AuthController
return app('json')->success(100002);
}
/**
* 导入卡密
* @return mixed
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
public function import_card()
{
$data = $this->request->getMore([
@ -371,4 +376,27 @@ class StoreProduct extends AuthController
$cardData = $readExcelService->readExcel($file);
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);
}
}

View File

@ -50,22 +50,29 @@ class LangCountry extends AuthController
* @throws \think\db\exception\DbException
* @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
* @return mixed
*/
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);
}
public function langCountryDel($id)
{
$this->services->langCountryDel($id);
return app('json')->success(100002);
}
}

View File

@ -168,8 +168,6 @@ class SystemAdmin extends AuthController
['pwd', ''],
['new_pwd', ''],
['conf_pwd', ''],
['file_pwd', ''],
['conf_file_pwd', ''],
]);
if(!preg_match('/^(?![^a-zA-Z]+$)(?!\D+$).{6,}$/',$data['new_pwd'])){
return app('json')->fail(400183);
@ -179,6 +177,24 @@ class SystemAdmin extends AuthController
else
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);
}
/**
* 退出登陆

View File

@ -12,6 +12,7 @@ namespace app\adminapi\controller\v1\setting;
use app\adminapi\controller\AuthController;
use app\Request;
use app\services\order\StoreOrderServices;
use app\services\system\config\SystemConfigServices;
use app\services\system\config\SystemConfigTabServices;
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) {
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'])) {
$from = public_path() . $post['weixin_ckeck_file'];
$to = public_path() . array_reverse(explode('/', $post['weixin_ckeck_file']))[0];

View File

@ -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/import_card', 'v1.product.StoreProduct/import_card')->option(['real_name' => '导入虚拟商品卡密']);
/** 商品批量操作 */
Route::post('batch/setting', 'v1.product.StoreProduct/batchSetting')->option(['real_name' => '商品批量设置']);
})->middleware([
\app\http\middleware\AllowOriginMiddleware::class,
\app\adminapi\middleware\AdminAuthTokenMiddleware::class,

View File

@ -25,6 +25,8 @@ Route::group('setting', function () {
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('set_file_password', 'v1.setting.SystemAdmin/set_file_password')->name('SystemAdminSetFilePassword')->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/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' => '语言类型列表']);
//新增修改语言类型表单

View File

@ -94,7 +94,7 @@ Route::group('system', function () {
Route::get('upgrade_log/list', 'UpgradeController/upgradeLogList')->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' => '文件管理登录']);
})->middleware([
\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::post('file/savefile', 'v1.system.SystemFile/savefile')->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/delFolder', 'v1.system.SystemFile/delFolder')->option(['real_name' => '删除文件夹']);
//重命名文件
Route::get('file/rename', 'v1.system.SystemFile/rename')->option(['real_name' => '重命名文件夹']);
})->middleware([
\app\http\middleware\AllowOriginMiddleware::class,

View File

@ -38,8 +38,13 @@ class PayController
return $pay->handleNotify();
case 'wechat':
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();
}
}

View File

@ -142,7 +142,7 @@ class StoreIntegralOrderController
return app('json')->success([
'order' => $order,
'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'])
]
]
]);

View File

@ -409,7 +409,7 @@ class StoreOrderController
case 'routine':
if ($type == 1 || in_array($order['is_channel'], [0, 2, 3, 4])) {
$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;
case 'app':
@ -690,7 +690,7 @@ class StoreOrderController
return app('json')->success([
'order' => $orderInfo,
'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'])
]
]
]);

View File

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

View File

@ -15,6 +15,7 @@ use think\Response;
Route::any('wechat/serve', 'v1.wechat.WechatController/serve');//公众号服务
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('pay/notify/:type', 'v1.PayController/notify');//支付回调
Route::get('get_script', 'v1.PublicController/getScript');//获取统计代码

View File

@ -160,7 +160,7 @@ class StoreProductDao extends BaseDao
}
})->when(!$page && $limit, function ($query) use ($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) {
$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();
}

View File

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

View File

@ -1581,6 +1581,7 @@ HTML;
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
$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;
}

View File

@ -76,6 +76,10 @@ class PayServices
//这些全都是微信支付
if (in_array($payType, ['routine', 'weixinh5', 'weixin', 'pc', 'store'])) {
$payType = 'wechat_pay';
//判断是否使用v3
if (sys_config('pay_wechat_type') == 1) {
$payType = 'v3_wechat_pay';
}
}
if ($payType == 'alipay') {
@ -85,7 +89,7 @@ class PayServices
/** @var Pay $pay */
$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) {
throw new ApiException($e->getMessage());

View File

@ -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;
}
}

View File

@ -190,7 +190,7 @@ class ExpressServices extends BaseServices
* @param string|null $com
* @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);
if ($resultData === null || !is_array($resultData)) {
@ -200,7 +200,7 @@ class ExpressServices extends BaseServices
case 1:
/** @var ServeServices $services */
$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) {
$cacheTime = 0;
} else {

View File

@ -138,6 +138,7 @@ class SystemAdminServices extends BaseServices
'id' => $adminInfo->getData('id'),
'account' => $adminInfo->getData('account'),
'head_pic' => $adminInfo->getData('head_pic'),
'level' => $adminInfo->getData('level'),
],
'logo' => sys_config('site_logo'),
'logo_square' => sys_config('site_logo_square'),
@ -358,6 +359,28 @@ class SystemAdminServices extends BaseServices
throw new AdminException(400264);
$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 ($adminInfo->level != 0) throw new AdminException(400611);
if (!$data['conf_file_pwd'])
@ -366,9 +389,6 @@ class SystemAdminServices extends BaseServices
throw new AdminException(400264);
$adminInfo->file_pwd = $this->passwordHash($data['file_pwd']);
}
$adminInfo->real_name = $data['real_name'];
$adminInfo->head_pic = $data['head_pic'];
if ($adminInfo->save())
return true;
else

View File

@ -139,7 +139,19 @@ class SystemConfigServices extends BaseServices
'pay_weixin_open' => [
'son_type' => [
'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_key' => '',
'paydir' => '',

View File

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

View File

@ -19,7 +19,7 @@ class LangCountryServices extends BaseServices
}
/**
* 国家语言列表
* 地区语言列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
@ -35,7 +35,7 @@ class LangCountryServices extends BaseServices
}
/**
* 设置国家语言类型表单
* 添加语言地区表单
* @param $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
@ -43,32 +43,41 @@ class LangCountryServices extends BaseServices
* @throws \think\db\exception\DbException
* @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 $typeId
* @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);
return true;
}
/**
* 删除语言地区
* @param $id
* @return bool
*/
public function langCountryDel($id)
{
$res = $this->dao->delete($id);
if (!$res) throw new AdminException(100008);
return true;
}
}

View File

@ -21,6 +21,7 @@ use crmeb\services\CacheService;
use crmeb\services\CacheService as Cache;
use crmeb\services\app\WechatService as WechatAuthService;
use crmeb\services\oauth\OAuth;
use crmeb\services\pay\Pay;
use crmeb\utils\Canvas;
/**
@ -60,6 +61,21 @@ class WechatServices extends BaseServices
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

View File

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

View File

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

View File

@ -49,7 +49,7 @@ class WeChatClient extends AbstractAPI
/**
* 支付
* @param array $params[
* @param array $params [
* 'openid'=>'支付者的openid',
* 'out_trade_no'=>'商家合单支付总交易单号',
* 'total_fee'=>'支付金额',
@ -64,15 +64,15 @@ class WeChatClient extends AbstractAPI
public function createorder($order)
{
$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',
* 'trade_no'=>'商家交易单号',
* 'transaction_id'=>'支付单号',
@ -95,13 +95,13 @@ class WeChatClient extends AbstractAPI
public function refundorder(array $order)
{
$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)]);
}

View File

@ -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');
}
}

View File

@ -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;
}
}

View File

@ -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');
}
}

View File

@ -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);
};
}
}

View File

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

View File

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

View File

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

View File

@ -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;
}
});
}
}

View File

@ -64,9 +64,8 @@ class WechatPay extends BasePay implements PayInterface
}
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);
}
@ -114,10 +113,9 @@ class WechatPay extends BasePay implements PayInterface
if (isset($opt['wechat'])) {
return WechatService::refund($outTradeNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount);
} else {
if($opt['pay_new_weixin_open'])
{
if ($opt['pay_new_weixin_open']) {
return MiniProgramService::miniRefund($outTradeNo, $totalFee, $refundFee, $opt);
}else{
} else {
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
* @throws \EasyWeChat\Core\Exceptions\FaultException
*/
public static function handleNotify()
public function handleNotify()
{
return WechatService::handleNotify();
}

View File

@ -15057,7 +15057,7 @@ INSERT INTO `eb_lang_code` (`id`, `type_id`, `code`, `remarks`, `lang_explain`,
(13530, 1, '账户充值', '账户充值', '账户充值', 0),
(13531, 1, '佣金转入', '佣金转入', '佣金转入', 0),
(13532, 1, '周榜', '周榜', '周榜', 0),
(13533, 1, '月榜', '月榜', '周榜', 0),
(13533, 1, '月榜', '月榜', '月榜', 0),
(13534, 1, '周排行', '周排行', '周排行', 0),
(13535, 1, '月排行', '月排行', '月排行', 0),
(13536, 1, '语言切换', '语言切换', '语言切换', 0),
@ -17418,7 +17418,7 @@ INSERT INTO `eb_lang_code` (`id`, `type_id`, `code`, `remarks`, `lang_explain`,
(15888, 3, '账户充值', '账户充值', '賬戶充值', 0),
(15889, 3, '佣金转入', '佣金转入', '佣金轉入', 0),
(15890, 3, '周榜', '周榜', '周榜', 0),
(15891, 3, '月榜', '月榜', '周榜', 0),
(15891, 3, '月榜', '月榜', '月榜', 0),
(15892, 3, '周排行', '周排行', '週排行', 0),
(15893, 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),
(24218, 10, '短信验证码', '短信验证码', 'Mã kiểm tra SMS', 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禁用',
`is_default` 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='语言类型';
--
-- 转存表中的数据 `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),
(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),
(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);
-- --------------------------------------------------------

View File

@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'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',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -764,9 +764,9 @@
'dev_requirement' => false,
),
'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',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

View File

@ -105,11 +105,7 @@ class Application extends Container
ServiceProviders\ShakeAroundServiceProvider::class,
ServiceProviders\OpenPlatformServiceProvider::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
];
/**

View File

@ -1,5 +1,5 @@
<?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);
return array (
0 => 'think\\captcha\\CaptchaService',