diff --git a/app/api/controller/Config.php b/app/api/controller/Config.php index 7729fe6..131d688 100755 --- a/app/api/controller/Config.php +++ b/app/api/controller/Config.php @@ -29,5 +29,15 @@ class Config extends Base 'version' => '111' ]); } - + /** + * 获取平台配置 + * @return \think\response\Json + */ + public function getPlatformConfig() + { + $platform = \app\common\server\platform\PlatformFactory::create(); + $config = $platform->get_config(); + return $this->renderSuccess("", $config); + } + } \ No newline at end of file diff --git a/app/api/controller/Login.php b/app/api/controller/Login.php index 07eccef..ee12434 100755 --- a/app/api/controller/Login.php +++ b/app/api/controller/Login.php @@ -15,6 +15,7 @@ use app\common\model\UserLoginLog; use think\facade\Log; use app\common\server\TencentCosUploader; use app\common\model\ProfitMoney; +use app\common\helper\EnvHelper; class Login extends Base { private $uploader; @@ -886,13 +887,15 @@ class Login extends Base $redis = (new RedisHelper())->getRedis(); $redisKey = "VerificationCode:{$mobile}"; $redisCode = $redis->get($redisKey); - // if ($code != "9999") { + $is_test = EnvHelper::getIsTest(); + if (!$is_test) { if (empty($redisCode) || $redisCode != $code) { $logMessages[] = '验证码错误: ' . $code . ',正确验证码: ' . $redisCode . '==>' . $mobile; Log::error(end($logMessages)); return $this->renderError('验证码错误'); } - // } + } + // 验证通过后删除Redis中的验证码 $redis->del($redisKey); diff --git a/app/api/controller/Mall.php b/app/api/controller/Mall.php index 157a864..b36802d 100755 --- a/app/api/controller/Mall.php +++ b/app/api/controller/Mall.php @@ -949,6 +949,7 @@ class Mall extends Base 'user_id' => $user['id'], 'diamond_id' => $diamond_product['id'], 'product_id' => $product_id, + 'product_name' => $diamond_product['name'], 'amount_paid' => $price, 'pay_method' => $pay_type == 1 || $pay_type == 3 ? DiamondOrder::PAY_METHOD_WECHAT : DiamondOrder::PAY_METHOD_ALIPAY, 'is_first_charge' => $diamond_order_count == 0 ? 1 : 0, diff --git a/app/api/controller/Notify.php b/app/api/controller/Notify.php index d8fe4ce..9d354c0 100755 --- a/app/api/controller/Notify.php +++ b/app/api/controller/Notify.php @@ -2716,8 +2716,8 @@ class Notify extends Base } if ($payment_type == "zfbpay") { $postData = request()->post(); - $h5 = new \app\common\server\platform\H5Platform(); - $zfb_result = $h5->verify($order_num, $postData); + $platform = \app\common\server\platform\PlatformFactory::getOrderNumPlatform($order_num); + $zfb_result = $platform->verify($order_num, $postData); if (!$zfb_result) { writelog('pay_notify_error', "支付宝支付验证失败: " . $order_num); $this->CallbackSuccess_new($payment_type); // 返回成功避免重复通知 @@ -2866,7 +2866,7 @@ class Notify extends Base // 给用户钱包加钻石 $reward_res = RewardService::sendReward($user_id, $diamondProduct['base_reward'], '购买商品' . $diamondProduct['name']); if ($reward_res) { - + foreach ($reward_res['data'] as $item) { $reward_log .= $item['msg'] . ','; } @@ -2880,14 +2880,14 @@ class Notify extends Base if ($diamond_order_count == 0 && $diamondProduct['first_bonus_reward'] != '') { $reward_res = RewardService::sendReward($user_id, $diamondProduct['first_bonus_reward'], '首充赠送'); foreach ($reward_res['data'] as $item) { - $reward_log .='首充赠送' . $item['msg'] . ','; + $reward_log .= '首充赠送' . $item['msg'] . ','; } // $reward_log = '首充赠送' . implode(',', $reward_res['data']); } } //判断$reward_log结尾是否是,号,是的话,去除 - if(substr($reward_log, -1) == ','){ + if (substr($reward_log, -1) == ',') { $reward_log = substr($reward_log, 0, -1); } // 更新订单状态为支付成功 @@ -3086,4 +3086,5 @@ class Notify extends Base } } + } \ No newline at end of file diff --git a/app/api/controller/Order.php b/app/api/controller/Order.php index dcb9452..c473af7 100644 --- a/app/api/controller/Order.php +++ b/app/api/controller/Order.php @@ -4,8 +4,9 @@ namespace app\api\controller; use app\common\model\Order as OrderModel; use app\common\model\OrderList; +use app\common\model\OrderNotify; use think\facade\Db; - +use app\common\helper\EnvHelper; /** * 订单控制器 */ @@ -20,11 +21,11 @@ class Order extends Base { // 获取当前登录用户 $user = $this->getUser(); - + // 获取分页参数 $page = $this->request->param('page', 1); $pageSize = $this->request->param('page_size', 10); - + // 获取订单列表 $orderQuery = Db::name('order') ->field('goods_id, goods_title, goods_price, order_total, order_zhe_total, price, use_money, use_integral, use_money2, addtime, pay_time, prize_num, use_coupon, order_num') @@ -33,40 +34,40 @@ class Order extends Base 'status' => 1 ]) ->order('addtime desc'); - + // 获取总数 $total = $orderQuery->count(); - + // 获取分页数据 $orderList = $orderQuery->page($page, $pageSize)->select()->toArray(); - + // 格式化数据 foreach ($orderList as &$order) { // 格式化时间 $order['addtime'] = date('Y-m-d H:i:s', $order['addtime']); - $order['pay_time'] = $order['pay_time'] ? date('Y-m-d H:i:s', $order['pay_time']) : ''; + $order['pay_time'] = $order['pay_time'] ? date('Y-m-d H:i:s', $order['pay_time']) : ''; // 格式化价格数据 - $order['goods_price'] = (float)$order['goods_price']; - $order['order_total'] = (float)$order['order_total']; - $order['order_zhe_total'] = (float)$order['order_zhe_total']; - $order['price'] = (float)$order['price']; - $order['use_money'] = (float)$order['use_money']; - $order['use_money2'] = (float)$order['use_money2']; + $order['goods_price'] = (float) $order['goods_price']; + $order['order_total'] = (float) $order['order_total']; + $order['order_zhe_total'] = (float) $order['order_zhe_total']; + $order['price'] = (float) $order['price']; + $order['use_money'] = (float) $order['use_money']; + $order['use_money2'] = (float) $order['use_money2']; } unset($order); - + // 返回分页数据 $data = [ 'list' => $orderList, 'total' => $total, - 'page' => (int)$page, - 'page_size' => (int)$pageSize, + 'page' => (int) $page, + 'page_size' => (int) $pageSize, 'total_pages' => ceil($total / $pageSize) ]; - + return $this->renderSuccess('获取成功', $data); } - + /** * 获取订单详情 * @@ -78,12 +79,12 @@ class Order extends Base $orderNum = $this->request->param('order_num', ''); if (empty($orderNum)) { return $this->renderError('订单编号不能为空'); - + } - + // 获取当前登录用户 $user = $this->getUser(); - + // 获取订单详情 $orderInfo = Db::name('order') ->field('order_num,goods_id, goods_title, goods_price, id, order_total, order_zhe_total, price, use_money, use_integral, use_money2, addtime, pay_time, prize_num, use_coupon') @@ -92,24 +93,24 @@ class Order extends Base 'order_num' => $orderNum ]) ->find(); - + if (!$orderInfo) { return $this->renderError('订单不存在'); - + } - + // 格式化时间 $orderInfo['addtime'] = date('Y-m-d H:i:s', $orderInfo['addtime']); $orderInfo['pay_time'] = $orderInfo['pay_time'] ? date('Y-m-d H:i:s', $orderInfo['pay_time']) : ''; - + // 格式化价格数据 - $orderInfo['goods_price'] = (float)$orderInfo['goods_price']; - $orderInfo['order_total'] = (float)$orderInfo['order_total']; - $orderInfo['order_zhe_total'] = (float)$orderInfo['order_zhe_total']; - $orderInfo['price'] = (float)$orderInfo['price']; - $orderInfo['use_money'] = (float)$orderInfo['use_money']; - $orderInfo['use_money2'] = (float)$orderInfo['use_money2']; - + $orderInfo['goods_price'] = (float) $orderInfo['goods_price']; + $orderInfo['order_total'] = (float) $orderInfo['order_total']; + $orderInfo['order_zhe_total'] = (float) $orderInfo['order_zhe_total']; + $orderInfo['price'] = (float) $orderInfo['price']; + $orderInfo['use_money'] = (float) $orderInfo['use_money']; + $orderInfo['use_money2'] = (float) $orderInfo['use_money2']; + // 获取订单商品列表 $orderGoods = Db::name('order_list') ->field('goodslist_title, goodslist_imgurl, goodslist_price, addtime') @@ -117,18 +118,171 @@ class Order extends Base 'order_id' => $orderInfo['id'] ]) ->select()->toArray(); - + // 格式化订单商品数据 foreach ($orderGoods as &$goods) { $goods['addtime'] = date('Y-m-d H:i:s', $goods['addtime']); - $goods['goodslist_price'] = (float)$goods['goodslist_price']; + $goods['goodslist_price'] = (float) $goods['goodslist_price']; $goods['goodslist_imgurl'] = imageUrl($goods['goodslist_imgurl']); } unset($goods); - + $orderInfo['goods_list'] = $orderGoods; - + return $this->renderSuccess('获取成功', $orderInfo); } - -} \ No newline at end of file + + + /** + * 发送网页支付订单 + * + * @return \think\Response + */ + public function sendWebPayOrder() + { + // sendCustomerServiceMessage + // 获取当前登录用户 + $user = $this->getUser(); + // 获取订单编号 + $orderNum = $this->request->param('order_num', ''); + if (empty($orderNum)) { + return $this->renderError('订单编号不能为空'); + } + $platform = \app\common\server\platform\PlatformFactory::create(); + $res = $platform->sendCustomerServiceMessage($user, $orderNum); + if ($res['status'] == 1) { + return $this->renderSuccess('发送成功',$res['data']); + } else { + return $this->renderError($res['msg']); + } + } + + /** + * 获取订单状态 + * + * @return \think\Response + */ + public function getOrderStatus() + { + // 获取当前登录用户 + $user = $this->getUser(); + // 获取订单编号 + $orderNum = $this->request->param('order_num', ''); + if (empty($orderNum)) { + return $this->renderError('订单编号不能为空'); + } + $orderNotify = OrderNotify::where([ + 'order_no' => $orderNum, + ])->find(); + if (!$orderNotify) { + return $this->renderError('订单不存在'); + } + if ($orderNotify['status'] == 1) { + return $this->renderSuccess('支付成功', $orderNotify['status']); + } + return $this->renderSuccess('支付未完成,请10秒后重试!', $orderNotify['status']); + } + + /** + * 创建H5支付订单通知 + * + * @return \think\Response + */ + public function createWebPayOrderNotify() + { + $orderNum = $this->request->param('order_num', ''); + if (empty($orderNum)) { + return $this->renderError('订单编号不能为空'); + } + $cache_key = "order:webpay:" . $orderNum; + $redis = (new \app\common\server\RedisHelper())->getRedis(); + $redis_key_info = $redis->get($cache_key); + $orderNotify = OrderNotify::where([ + 'order_no' => $orderNum, + ])->find(); + if (!$orderNotify) { + if ($redis_key_info) { + $redis->del($cache_key); + } + return $this->renderError('订单不存在'); + } + if ($orderNotify['status'] == 1) { + if ($redis_key_info) { + $redis->del($cache_key); + } + return $this->renderError('订单已支付'); + } + if ($orderNotify['status'] == 2) { + if ($redis_key_info) { + $redis->del($cache_key); + } + return $this->renderError('订单已失效'); + } + + $res = []; + if ($redis_key_info) { + $res = json_decode($redis_key_info, true); + } else { + $platform = new \app\common\server\platform\H5Platform(); + $res = $platform->createWebPayOrderNotify($orderNum); + if ($res['status'] == 1) { + $redis->set($cache_key, json_encode($res), 60 * 30); + } + } + if ($res['status'] == 1) { + return $this->renderSuccess('创建成功', $res['data']); + } else { + return $this->renderError($res['msg']); + } + } + public function getOrderUrlLink() + { + $order_num = $this->request->param('order_num', ''); + if (!empty($orderNum)) { + return $this->renderError('订单编号不能为空'); + } + $order_notify = OrderNotify::where('order_no', $order_num)->find(); + $user_id = 0; + $extend = []; + if ($order_notify == null) { + return ['status' => 0, 'msg' => '未找到订单']; + } + if ($order_notify['status'] == 2) { + return ['status' => 0, 'msg' => '订单已失效']; + } + + // $this->appid + $web_domain = EnvHelper::getWebDomain(); + $returnUrl = ''; + if ($order_notify) { + if ($order_notify['extend'] == '') { + return $this->renderError('订单号错误'); + } + $extend = json_decode($order_notify['extend'], true); + $user_id = $extend['userId']; + $order_type = $extend['orderType']; + $returnUrl = $extend['returnUrl']; + // 1. 提取路径部分 + $path = parse_url($returnUrl, PHP_URL_PATH); + $path = ltrim($path, '/'); + // 2. 提取查询部分 + $queryString = parse_url($returnUrl, PHP_URL_QUERY); + parse_str($queryString, $paramsArray); + $paramsArray['order_num'] = $order_num; + + $query = http_build_query($paramsArray); + $platform = new \app\common\server\platform\MiniProgramPlatform(); + $env_version = 'release'; + if (EnvHelper::getIsTest()) { + $env_version = 'develop'; + } + $res = $platform->getUrlLink($path, $query, $env_version); + if ($res['status'] == 1) { + return $this->renderSuccess('获取成功', $res['data']); + } + + } + return $this->renderError('获取失败'); + } + +} \ No newline at end of file diff --git a/app/api/route/app.php b/app/api/route/app.php index 3127e16..46a6bf2 100755 --- a/app/api/route/app.php +++ b/app/api/route/app.php @@ -181,6 +181,8 @@ Route::rule('goods_receive_sync', 'Goods/receive_sync', 'POST'); // 配置信息接口 Route::rule('config', 'Config/index', 'GET'); +Route::rule('getPlatformConfig', 'Config/getPlatformConfig', 'GET'); + #============================ #WelfareHouse.php福利屋 #============================ @@ -215,7 +217,10 @@ Route::any('getRankList', 'Index/getRankList'); #============================ Route::any('order_list', 'Order/getOrderList'); Route::any('order_detail', 'Order/getOrderDetail'); - +Route::any('send_web_pay_order', 'Order/sendWebPayOrder'); +Route::any('get_order_status', 'Order/getOrderStatus'); +Route::any('create_web_pay_order', 'Order/createWebPayOrderNotify'); +Route::any('get_order_url_link', 'Order/getOrderUrlLink'); #============================ #FloatBall.php悬浮球 #============================ diff --git a/app/common/helper/ConfigHelper.php b/app/common/helper/ConfigHelper.php index a8ec88f..7518373 100755 --- a/app/common/helper/ConfigHelper.php +++ b/app/common/helper/ConfigHelper.php @@ -30,6 +30,28 @@ class ConfigHelper private static $systemTest = null; + private static $youda_env = null; + + /** + * 获取友达环境 + * + * @return string 友达环境 + */ + public static function getYoudaEnv() + { + if (self::$youda_env !== null) { + return self::$youda_env; + } + $youda_env = env('youda.youda_env', 'production '); + if ($youda_env) { + self::$youda_env = $youda_env; + } + return self::$youda_env; + } + + public static function getIsTest(){ + + } /** * 获取应用设置 * diff --git a/app/common/helper/EnvHelper.php b/app/common/helper/EnvHelper.php new file mode 100644 index 0000000..a9d418f --- /dev/null +++ b/app/common/helper/EnvHelper.php @@ -0,0 +1,58 @@ +code = 'APP_ANDROID'; + $this->env = 'app'; + Factory::setOptions($this->getOptions()); + } + + /** + * 获取系统配置 + * @return void + */ + public function get_config(): array + { + return ['isWebPay' => false]; + } + /** + * Summary of pay + * @param array $data + * @throws \Exception + * @return array|array{data: array, status: int} + */ + public function pay(array $data): array + { + // echo "H5支付:{$amount}分,订单号:{$orderId}\n"; + // 实际项目中这里调用支付SDK + + try { + $data += ['user' => null, 'price' => 0, 'title' => '', 'attach' => 'order_wxs', 'pre' => 'MH_']; + [ + 'user' => $user, + 'price' => $price, + 'title' => $title, + 'attach' => $attach, + 'pre' => $pre, + 'quitUrl' => $quitUrl, + 'returnUrl' => $returnUrl + ] = $data; + if ($user == null) { + //抛出异常 + throw new \Exception('用户信息为空'); + } + $project_prefix = "AD"; + if ($price <= 0) { + //生成订单号,订单号为:ML_DRAYDMP02025018.... ML_ 前缀(固定3位),DRA 商户号(固定3位),YD 项目,固定2位,MP0 表示微信小程序支付 + $order_no = create_order_no_new($pre, "MON", $project_prefix, "APP"); + return [ + 'status' => 1, + 'data' => [ + 'order_no' => $order_no, + 'res' => [] + ] + ]; + } + + $prefix = "ZFA";//$this->GetPrefix(); + $config = $this->get_config(); + if ($config['isWebPay']) { + $domain = Request::domain(); + // Request::param('return_url') + $host = parse_url($domain, PHP_URL_HOST); + $order_no = create_order_no_new($pre, "ZFA", 'H5', "ZFB"); + $extend = [ + 'orderType' => $attach, + ]; + $extend_str = json_encode($extend, JSON_UNESCAPED_UNICODE); + // 将通知URL和随机字符串保存到order_notify表中 + Db::name('order_notify')->insert([ + 'order_no' => $order_no, + 'notify_url' => '', + 'nonce_str' => '', + 'pay_time' => date('Y-m-d H:i:s'), + 'pay_amount' => $price, + 'status' => 0, + 'retry_count' => 0, + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s'), + 'extend' => $extend_str, + 'title' => $title, + ]); + return [ + 'status' => 1, + 'data' => [ + 'order_no' => $order_no, + 'res' => ['data' => ['order_num' => $order_no], 'requestPay' => $domain . '/api/send_web_pay_order', 'tips' => ''] + ] + ]; + } + $title = mb_substr($title, 0, 30); + + + //生成订单号,订单号为:ML_DRAYDMP02025018.... ML_ 前缀(固定3位),DRA 商户号(固定3位),YD 项目,固定2位,MP0 表示微信小程序支付 + $order_no = create_order_no_new($pre, $prefix, $project_prefix, "ZFB"); + $openid = $user['openid']; + $payment_type = 'zfbpay'; + $order_type = $attach; + $user_id = $user ? $user['id'] : 0; + // 回调使用的随机数(与支付随机数分离) + $callback_nonce_str = $this->genRandomString(); + // 生成新的支付通知URL + $notifyUrl = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_no, $callback_nonce_str); + $is_env_test = EnvHelper::getIsTest(); + if ($is_env_test) { + $is_test = $user['istest']; + if ($is_test == 2) { + $price = 0.01; + } + } + $returnUrl = urldecode($returnUrl); + //2. 发起API调用(以支付能力下的统一收单交易创建接口为例) + $result = Factory::payment() + ->app() + ->asyncNotify($notifyUrl) + ->pay( + $title, // 支付标题 + $order_no, // 商户订单号(唯一) + $price, // 金额 + $quitUrl,// 取消地址 + $returnUrl// 支付后返回地址 + ); + //3. 处理响应或异常 + if (!empty($result->body)) { + // echo "调用成功" . PHP_EOL; + // 将通知URL和随机字符串保存到order_notify表中 + Db::name('order_notify')->insert([ + 'order_no' => $order_no, + 'notify_url' => $notifyUrl, + 'nonce_str' => $callback_nonce_str, + 'pay_time' => date('Y-m-d H:i:s'), + 'pay_amount' => $price, + 'status' => 0, + 'retry_count' => 0, + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s'), + 'title' => $title, + ]); + return [ + 'status' => 1, + 'data' => [ + 'order_no' => $order_no, + 'res' => $result->body + ] + ]; + } else { + return ['status' => 0, 'msg' => '支付失败:' . $result]; + } + } catch (\Exception $e) { + // echo "调用失败," . $e->getMessage() . PHP_EOL; + // ; + } + return ['status' => 0, 'msg' => '支付失败:']; + } + + + /** + * 客服发送消息 + * @param mixed $user_id + * @param mixed $order_num + * @return array{status: int, data: array, msg: string} + */ + public function sendCustomerServiceMessage($user, $order_num): array + { + // 获取订单编号 + $return_url = Request::param('return_url', ''); + + + $message = "用户ID:{$user['id']},订单号:{$order_num}"; + $order_title = ''; + $order_price = 0; + if ($user['openid'] == null) { + return ['status' => 0, 'msg' => '用户openid为空']; + } + $orderNotify = OrderNotify::where([ + 'order_no' => $order_num, + ])->find(); + $extend = []; + if ($orderNotify) { + if ($orderNotify['extend'] != '') { + $extend = json_decode($orderNotify['extend'], true); + } + $order_title = $orderNotify['title']; + $order_price = $orderNotify['pay_amount']; + } + if ($order_title == "") { + return ['status' => 0, 'msg' => '订单号错误']; + } + + // $this->appid + $web_domain = EnvHelper::getWebDomain(); + $extend['returnUrl'] = $return_url; + $extend['openId'] = $user['openid']; + $extend['userId'] = $user['id']; + $extend_str = json_encode($extend, JSON_UNESCAPED_UNICODE); + OrderNotify::where('order_no', $order_num)->update(['extend' => $extend_str]); + return ['status' => 1, 'data' => $web_domain . '/pages/other/web-pay-order?order_num=' . $order_num]; + + } + + /** + * 发货 + * @param mixed $openid + * @param mixed $access_token + * @param mixed $order_num + * @param mixed $title + * @return int + */ + public function post_order($user, $order_num): int + { + + return 1; + } + + public function verify($order_no, $data): bool + { + $result = Factory::payment()->common()->verifyNotify($data); + if ($result) { + return true; + } + return false; + } + + /** + * 生成URL链接 + * @param int $userId 用户ID + * @return array + */ + public function generateUrlLink($userId): array + { + + return ['status' => 1, 'data' => ""]; + } + + /** + * 获取手机号 + * @param string $code 手机号获取凭证 + * @return array + */ + public function getMobile($code = ''): array + { + + + return ['status' => 1, 'data' => ""]; + } + + /** + * 获取openid + * @param string $code 登录凭证 + * @return array + */ + public function getOpenid($code): array + { + + return ['status' => 1, 'data' => ""]; + } + + + + public function getOptions() + { + $options = new Config(); + $options->protocol = 'https'; + $options->gatewayHost = 'openapi.alipay.com'; + $options->signType = 'RSA2'; + + $options->appId = '2021005153608270'; + + // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中 + $options->merchantPrivateKey = 'MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDt4poXku4KIym89gds3U6nbzLUxky1x47G8cgloYdIObXDV8oDPiPVf7kaJGcVU5R3eU7myJHEkurlAnSzb40oyg2xV5qsHjjV4AO/9v5VPCQNl5qbL83PYC79eh7z4vGOwitSK3AjHilQJkeD8WNaX6D/UMZKz4KcXbcNbBjnu9FvwpiVwY6HZPxrTlR5QTxxuPWVUAbnMkBP9nLys0jMrIShns+PA4GD7Jhx32SUwMPwmjTmv93CR1KTgKe1I0WQgkCG2/5nyD2j+hm1SFC/5rnF0dElZbJcZxIXgvyyTEdbNuf2mzaVYpFZNSc6v/kQKCevL3sNPfHy5XWqFydxAgMBAAECggEBANyV6rN/cLH/xz1MmrgNQ1kpWOZK92j8ol3CaAjVDuAfe3enWVDGd24LYLZoGRqChUKAP3TreZfhcGVpcJPPFgND0YyoImoGIEfa0T9Zpp+dBAWClj/fBSaOFyS+8CLRR5NMY+VvGC3IUyDaTiiAVtO/p0f5O9a1M3URxaNxoGqISlMxpn/LIyWc1elpT2/RY0nkKQCXk2rav3yZZe9+td1ESHtKOFE4l9qPAP6H9AaE401qKhASa8vuLm1nUE35XqoGSxASLnckAq3Z+v6WnO2rli59DMiFFB45+XJxMSwumdfpKxsI/OO/VXBwpgNaLGXBO47op8SxqswYZiVRyVECgYEA9xK1SmCbGjyUJAiJI77zF3SQotz39PK2pe1l+5YUXTGRx5D9iOR9o2zMfHwlgGpPWB9XJU23ZOjPhNsz0pGXTfEelp0VJtmbX1SqKjGhAZ+sJxMmY5iP72MFfu+aIsfPnWPh3G9s2fDGxlmoXnCKbptrgM7T3TRsRPPx+7zvZ+0CgYEA9nrpGIipOn0n2lxHH1eSlLVYMvRT/gt1EWePlWXK/7D71iCL09SdbY1DKvZxRwcd+c3LBuTI+zWJiW+8b78B7Z/mMntoQokbB3EMM/C3voVfsuCOSea5ARDNbgQ0pRFw9Qov/dLlliDdYrSKiYJYwjnfXKE+qTX9dXNOAK2cBRUCgYEA7mAwZSg7vN6BlxpdJg3O/+xIt7k1yjB6JDCdWlR8JUXz/nVXB8JbrVcFG32zuOfY0Y67R5RpwoQT43yRzTEGp/5gorO/epIso5dN7hOf4a8qKzEAssq45B/HZ6bIMZJSLun1OfaPMN5rCWfrV+KAzSJKYCYsppkzdHtgFp885CkCgYEA9Kf0D+I2+FOa52iJQFcQrIOE1K8pYBXHUktVfpnX8g2fLGCJ6u40hbWeYlrU/gfWfUsEqAcYaCIwLze198XFCDWbrahJSSIGrlBMKJJcEMUaxNeY5UobgS9Iele6Wc8CLHi8QlrAgVCF75/9k5jKuZ/wUmXLaPKqb5bQamPpZjECgYEAuSTyMwJmOTLJ/LRGCzVKOUEetfjQU+RhDvp7K/9zAuhUx9iY869lzIdCV9gzrk92+inhmTxXBxxM+zcml7VfHxbsNoG0jnlMtC7f4jdzg1qMhk3vPlRDIC+TIB/kaNu+lAScg3BzYSniKVBu9zwZ1OIlVWFwAKNvBGXtRSdUN7k='; + + // $options->alipayCertPath = '<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->'; + // $options->alipayRootCertPath = '<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->'; + // $options->merchantCertPath = '<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'; + + //注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可 + $options->alipayPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlAGEC6+urK0+aC3HGy1KqxqK2lD8G+0/oNN3AcZpsXrztMNHbSpfw9zdea1jldF5HZsFD5JH/mb3ngMaepry/DnEyTpxqVfeBfWDhXgM+isQqm8mc/SpX4W1uh0edXh984U0YHNabg+HKdc4EdXpIW/0bRBUjmDQJPLDl6vjn7aFU1KTFSo06+e5PsVAwZuYZTEsgqn05zZxh+nccLVsjO64Kywv6NbbZRFRG3Dxequ35gRHtZOUvzt5o8bzpEnH0/8lNJqVtfpu41tFxinSdH8R4WFa8YKPqLqRnxCw7o5pTuznS2akOhPQ7ElO5tvotgFwOfVUGc/7yXxizGVgMwIDAQAB'; + //可设置异步通知接收服务地址(可选) + //如果需要使用文件上传接口,请不要设置该参数 + $options->notifyUrl = ""; + + //可设置AES密钥,调用AES加解密相关接口时需要(可选) + // $options->encryptKey = "<-- 请填写您的AES密钥,例如:aa4BtZ4tspm2wnXLb1ThQA== -->"; + return $options; + } + +} \ No newline at end of file diff --git a/app/common/server/platform/BasePlatform.php b/app/common/server/platform/BasePlatform.php index bdac4b3..9b7d17f 100644 --- a/app/common/server/platform/BasePlatform.php +++ b/app/common/server/platform/BasePlatform.php @@ -14,6 +14,11 @@ abstract class BasePlatform $this->code = ''; $this->env = ''; } + /** + * 获取系统配置 + * @return void + */ + abstract public function get_config(): array; /** * 抽象方法:支付(子类必须实现) @@ -47,6 +52,15 @@ abstract class BasePlatform * @return void */ abstract public function getMobile($code = ''): array; + /** + * 验证加签 + * @param mixed $order_no + * @param mixed $data + * @return void + */ + abstract function verify($order_no, $data): bool; + + /** * 普通方法(子类自动继承) */ @@ -139,5 +153,11 @@ abstract class BasePlatform } return $output; } - + /** + * 客服发送消息 + * @param mixed $user_id + * @param mixed $order_num + * @return array{status: int, data: array, msg: string} + */ + abstract public function sendCustomerServiceMessage($user, $order_num): array; } \ No newline at end of file diff --git a/app/common/server/platform/H5Platform.php b/app/common/server/platform/H5Platform.php index a7adad7..4eec5c1 100644 --- a/app/common/server/platform/H5Platform.php +++ b/app/common/server/platform/H5Platform.php @@ -3,9 +3,13 @@ namespace app\common\server\platform; use Alipay\EasySDK\Kernel\Factory; use Alipay\EasySDK\Kernel\Config; +use app\common\model\User; use think\App; use think\facade\Db; use think\facade\Log; +use app\common\model\Order; +use app\common\model\OrderNotify; +use app\common\helper\EnvHelper; /** * 多端平台支付抽象基类 */ @@ -18,6 +22,14 @@ class H5Platform extends BasePlatform $this->env = 'h5'; Factory::setOptions($this->getOptions()); } + /** + * 获取系统配置 + * @return void + */ + public function get_config(): array + { + return ['isWebPay' => false]; + } /** * Summary of pay @@ -99,7 +111,8 @@ class H5Platform extends BasePlatform 'status' => 0, 'retry_count' => 0, 'create_time' => date('Y-m-d H:i:s'), - 'update_time' => date('Y-m-d H:i:s') + 'update_time' => date('Y-m-d H:i:s'), + 'title' => $title, ]); return [ 'status' => 1, @@ -131,7 +144,7 @@ class H5Platform extends BasePlatform return 1; } - public function verify($order_no, $data) + public function verify($order_no, $data): bool { $result = Factory::payment()->common()->verifyNotify($data); if ($result) { @@ -173,7 +186,17 @@ class H5Platform extends BasePlatform return ['status' => 1, 'data' => ""]; } - + /** + * 客服发送消息 + * @param mixed $user_id + * @param mixed $order_num + * @return array{status: int, data: array, msg: string} + */ + public function sendCustomerServiceMessage($user, $order_num): array + { + $message = "用户ID:{$user['id']},订单号:{$order_num}"; + return ['status' => 1, 'data' => $message]; + } public function getOptions() { @@ -202,4 +225,98 @@ class H5Platform extends BasePlatform return $options; } + /** + * 根据订单号创建订单 + * @param string $order_num 订单号 + * @return array{status: int, data: array, msg: string} + */ + public function createWebPayOrderNotify($order_num) + { + // echo "H5支付:{$amount}分,订单号:{$orderId}\n"; + // 实际项目中这里调用支付SDK + try { + $order_notify = OrderNotify::where('order_no', $order_num)->find(); + $user_id = 0; + $extend = []; + $order_type = ''; + if ($order_notify == null) { + return ['status' => 0, 'msg' => '未找到订单']; + } + if ($order_notify['status'] == 2) { + return ['status' => 0, 'msg' => '订单已失效']; + } + if ($order_notify['status'] == 1) { + return ['status' => 0, 'msg' => '订单已支付']; + } + // $this->appid + $web_domain = EnvHelper::getWebDomain(); + $returnUrl = $web_domain . '/pages/other/web-pay-success'; + $quitUrl = $web_domain . '/pages/other/web-pay-order'; + if ($order_notify) { + $title = $order_notify['title']; + $price = $order_notify['pay_amount']; + if ($order_notify['extend'] == '') { + return ['status' => 0, 'msg' => '订单号错误']; + } + $extend = json_decode($order_notify['extend'], true); + $user_id = $extend['userId']; + $order_type = $extend['orderType']; + } + if ($price <= 0) { + return ['status' => 0, 'msg' => '金额错误']; + } + $user = User::where('id', $user_id)->find(); + if ($user == null) { + //抛出异常 + throw new \Exception('用户信息为空'); + } + $title = mb_substr($title, 0, 30); + $order_no = $order_num; + $payment_type = 'zfbpay'; + // 回调使用的随机数(与支付随机数分离) + $callback_nonce_str = $this->genRandomString(); + // 生成新的支付通知URL + $notifyUrl = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_no, $callback_nonce_str); + $is_env_test = EnvHelper::getIsTest(); + if ($is_env_test) { + $is_test = $user['istest']; + if ($is_test == 2) { + $price = 0.01; + } + } + //2. 发起API调用(以支付能力下的统一收单交易创建接口为例) + $result = Factory::payment() + ->wap() + ->asyncNotify($notifyUrl) + ->pay( + $title, // 支付标题 + $order_no, // 商户订单号(唯一) + $price, // 金额 + $quitUrl,// 取消地址 + $returnUrl// 支付后返回地址 + ); + //3. 处理响应或异常 + if (!empty($result->body)) { + // 将通知URL和随机字符串保存到order_notify表中 + if ($order_notify) { + $order_notify->notify_url = $notifyUrl; + $order_notify->nonce_str = $callback_nonce_str; + } + $order_notify->save(); + return [ + 'status' => 1, + 'data' => [ + 'order_no' => $order_no, + 'res' => $result->body + ] + ]; + } else { + return ['status' => 0, 'msg' => '支付失败:' . $result]; + } + } catch (\Exception $e) { + } + return ['status' => 0, 'msg' => '支付失败:']; + + } + } \ No newline at end of file diff --git a/app/common/server/platform/MiniProgramPlatform.php b/app/common/server/platform/MiniProgramPlatform.php index a87e309..2c292b7 100644 --- a/app/common/server/platform/MiniProgramPlatform.php +++ b/app/common/server/platform/MiniProgramPlatform.php @@ -1,9 +1,16 @@ wx_secret = $this->mp_miniprogram['app_secret']; } } + /** + * 获取系统配置 + * @return void + */ + public function get_config(): array + { + return ['isWebPay' => true]; + } /** * Summary of pay [$user,$price,title,attach] * @param mixed $data @@ -97,16 +112,48 @@ class MiniProgramPlatform extends BasePlatform 'data' => [], 'msg' => '小程序支付通道维护中,请联系客服下载app。' ]; - if ($currentTime < 800 || $currentTime >= 2200) { - return [ - 'status' => 0, - 'data' => [], - 'msg' => '支付未开放,请在08:00-22:00范围内购买' - ]; - } + // if ($currentTime < 800 || $currentTime >= 2200) { + // return [ + // 'status' => 0, + // 'data' => [], + // 'msg' => '支付未开放,请在08:00-22:00范围内购买' + // ]; + // } $title = mb_substr($title, 0, 30); $prefix = $this->GetPrefix(); + $mp_config = $this->get_config(); + if ($mp_config['isWebPay']) { + $domain = Request::domain(); + // Request::param('return_url') + $host = parse_url($domain, PHP_URL_HOST); + $order_no = create_order_no_new($pre, "ZFA", 'H5', "ZFB"); + $extend = [ + 'orderType' => $attach, + ]; + $extend_str = json_encode($extend, JSON_UNESCAPED_UNICODE); + // 将通知URL和随机字符串保存到order_notify表中 + Db::name('order_notify')->insert([ + 'order_no' => $order_no, + 'notify_url' => '', + 'nonce_str' => '', + 'pay_time' => date('Y-m-d H:i:s'), + 'pay_amount' => $price, + 'status' => 0, + 'retry_count' => 0, + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s'), + 'extend' => $extend_str, + 'title' => $title, + ]); + return [ + 'status' => 1, + 'data' => [ + 'order_no' => $order_no, + 'res' => ['data' => ['order_num' => $order_no], 'requestPay' => $domain . '/api/send_web_pay_order', 'tips' => '您即将进入客服聊天界面完成支付,也可前往「我的」页面下载官方APP,享受更便捷的购物及充值服务。'] + ] + ]; + } //生成订单号,订单号为:ML_DRAYDMP02025018.... ML_ 前缀(固定3位),DRA 商户号(固定3位),YD 项目,固定2位,MP0 表示微信小程序支付 $order_no = create_order_no_new($pre, $prefix, $project_prefix, "MP0"); $openid = $user['openid']; @@ -155,7 +202,8 @@ class MiniProgramPlatform extends BasePlatform 'status' => 0, 'retry_count' => 0, 'create_time' => date('Y-m-d H:i:s'), - 'update_time' => date('Y-m-d H:i:s') + 'update_time' => date('Y-m-d H:i:s'), + 'title' => $title, ]); $time = time(); $res['appId'] = $this->wx_appid; @@ -252,6 +300,89 @@ class MiniProgramPlatform extends BasePlatform return ['status' => 0, 'msg' => '支付失败:' . $error_message]; } + + /** + * 客服发送消息 + * @param mixed $user_id + * @param mixed $order_num + * @return array{status: int, data: array, msg: string} + */ + public function sendCustomerServiceMessage($user, $order_num): array + { + // 获取订单编号 + $return_url = Request::param('return_url', ''); + + + $message = "用户ID:{$user['id']},订单号:{$order_num}"; + $order_title = ''; + $order_price = 0; + if ($user['openid'] == null) { + return ['status' => 0, 'msg' => '用户openid为空']; + } + $orderNotify = OrderNotify::where([ + 'order_no' => $order_num, + ])->find(); + $extend = []; + if ($orderNotify) { + if ($orderNotify['extend'] != '') { + $extend = json_decode($orderNotify['extend'], true); + } + $order_title = $orderNotify['title']; + $order_price = $orderNotify['pay_amount']; + } + if ($order_title == "") { + return ['status' => 0, 'msg' => '订单号错误']; + } + + // $this->appid + $web_domain = EnvHelper::getWebDomain(); + $extend['returnUrl'] = $return_url; + $extend['openId'] = $user['openid']; + $extend['userId'] = $user['id']; + $extend_str = json_encode($extend, JSON_UNESCAPED_UNICODE); + + $access_token = $this->get_access_token(); + $request_url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" . $access_token; + $param = [ + 'touser' => $user['openid'], + 'msgtype' => 'link', + 'link' => [ + 'title' => $order_title, + 'description' => "¥{$order_price}【点击去支付】", + 'thumb_url' => 'https://image.zfunbox.cn/icon_80.png', + 'url' => $web_domain . '/pages/other/web-pay-order?order_num=' . $order_num + ] + ]; + $postData = json_encode($param, JSON_UNESCAPED_UNICODE); + $result = []; + for ($i = 0; $i < 5; $i++) { + $result = curlPost($request_url, $postData); + $result = json_decode($result, true); + if (isset($result['errcode']) && $result['errcode'] == 0) { + //发送成功,退出循环 + $extend_str = json_encode($extend, JSON_UNESCAPED_UNICODE); + OrderNotify::where('order_no', $order_num)->update(['extend' => $extend_str]); + return ['status' => 1, 'data' => '']; + + } + // 记录响应结果日志 + writelog('post_order_log', json_encode([ + 'method' => 'sendCustomerServiceMessage', + 'order_num' => $order_num, + 'response' => $result, + 'time' => date('Y-m-d H:i:s') + ])); + //延迟一秒 + sleep(1); + // usleep(500000); + } + //发送成功,退出循环 + $extend_str = json_encode($extend, JSON_UNESCAPED_UNICODE); + OrderNotify::where('order_no', $order_num)->update(['extend' => $extend_str]); + return ['status' => 1, 'msg' => $result['errmsg'] ?? '发送失败']; + + } + /** * 发货 * @param mixed $openid @@ -377,11 +508,39 @@ class MiniProgramPlatform extends BasePlatform $result = curlPost($url, json_encode($param)); $result = json_decode($result, true); if (isset($result['errcode']) && $result['errcode'] != 0) { + return ['status' => 0, 'msg' => $result['errmsg'] ?? '生成链接失败']; } return ['status' => 1, 'data' => $result]; } + /** + * 生成URL链接 + * @param string $path 路径 + * @param string $query 查询参数 + * @return array + */ + public function getUrlLink($path, $query, $env_version = "release"): array + { + // 获取access_token + $access_token = $this->get_access_token(); + + $url = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=" . $access_token; + $param = [ + 'path' => $path, + 'query' => $query, + 'env_version' => $env_version + ]; + + $result = curlPost($url, json_encode($param)); + $result = json_decode($result, true); + if (isset($result['errcode']) && $result['errcode'] != 0) { + + return ['status' => 0, 'msg' => $result['errmsg'] ?? '生成链接失败']; + } + + return ['status' => 1, 'data' => $result['url_link']]; + } /** * 获取手机号 @@ -681,6 +840,10 @@ class MiniProgramPlatform extends BasePlatform return $this->mp_merchant['order_prefix']; } + public function verify($order_no, $data): bool + { + return true; + } /** * 实际加载商户信息 diff --git a/app/common/server/platform/PlatformFactory.php b/app/common/server/platform/PlatformFactory.php index 33d4af7..cb1f1b8 100644 --- a/app/common/server/platform/PlatformFactory.php +++ b/app/common/server/platform/PlatformFactory.php @@ -22,6 +22,12 @@ class PlatformFactory if ($client == "WEB_APP") { return new H5Platform(); } + if ($client == "APP_ANDROID") { + return new AppPlatform(); + } + if ($client == "APP_IOS") { + return new AppPlatform(); + } if ($client == "MP-WEIXIN") { return new MiniProgramPlatform(); } @@ -57,4 +63,28 @@ class PlatformFactory } return $platform->pay($data); } + + /** + * 根据订单号获取平台实例 + * @param string $order_num 订单号 + * @return BasePlatform 平台实例 + */ + public static function getOrderNumPlatform($order_num = null): BasePlatform + { + + if ($order_num == null) { + throw new \Exception('订单号不能为空'); + } + $platform = substr($order_num, 6, 2); + if ($platform == "H5") { + return new H5Platform(); + } + if ($platform == "MP") { + return new MiniProgramPlatform(); + } + if ($platform == "AD") { + return new AppPlatform(); + } + throw new \Exception('订单号错误'); + } } \ No newline at end of file