diff --git a/app/Console/Commands/OrderRefundCommand.php b/app/Console/Commands/OrderRefundCommand.php index 14457001..92614003 100644 --- a/app/Console/Commands/OrderRefundCommand.php +++ b/app/Console/Commands/OrderRefundCommand.php @@ -2,14 +2,15 @@ namespace App\Console\Commands; +use App\Enums\PayWay; use App\Exceptions\BizException; use App\Models\BalanceLog; use App\Models\OrderRefundLog; use App\Models\WalletLog; use App\Services\AlipayService; use App\Services\BalanceService; +use App\Services\Payment\WxpayService; use App\Services\WalletService; -use App\Services\WeChatPayService; use Illuminate\Console\Command; use Illuminate\Support\Str; use Throwable; @@ -43,7 +44,11 @@ class OrderRefundCommand extends Command OrderRefundLog::pending()->chunkById(200, function ($logs) use (&$page) { foreach ($logs as $log) { try { - $method = 'refundBy'.Str::studly($log->order->pay_way->value); + $method = match ($log->order->pay_way) { + PayWay::WxpayApp, PayWay::WxpayH5, PayWay::WxpayJsApi, PayWay::WxpayMiniProgram => 'refundByWxpay', + PayWay::AlipayApp => 'refundByAlipay', + default => 'refundBy'.Str::studly($log->order->pay_way->value), + }; if (! method_exists($this, $method)) { throw new BizException('退款方式暂不支持'); @@ -79,20 +84,22 @@ class OrderRefundCommand extends Command * @param \App\Models\OrderRefundLog $log * @return void */ - protected function refundByWxpayApp(OrderRefundLog $log) + protected function refundByWxpay(OrderRefundLog $log) { $order = $log->order; - (new WeChatPayService())->refundByOutTradeNumber( - $order->pay_sn, - $log->sn, - $order->total_amount, - $log->amount, - [ + (new WxpayService())->refundByOutTradeNumber([ + 'number' => $order->pay_sn, + 'refund_number' => $log->sn, + 'total_fee' => $order->total_amount, + 'refund_fee' => $log->amount, + 'optional' => [ 'refund_desc' => $log->reason, - 'notify_url' => url(route('wxpay.order_refund_notify', [], false), [], true), - ] - ); + ], + ], match ($order->pay_way) { + PayWay::WxpayMiniProgram => 'mini_program', + default => 'default', + }); $log->update([ 'status' => OrderRefundLog::STATUS_SUCCESS, @@ -106,7 +113,7 @@ class OrderRefundCommand extends Command * @param \App\Models\OrderRefundLog $log * @return void */ - protected function refundByAlipayApp(OrderRefundLog $log) + protected function refundByAlipay(OrderRefundLog $log) { $order = $log->order; diff --git a/app/Endpoint/Api/Http/Controllers/Auth/SocialiteAuthController.php b/app/Endpoint/Api/Http/Controllers/Auth/SocialiteAuthController.php index e1caa91f..19d6a5c7 100644 --- a/app/Endpoint/Api/Http/Controllers/Auth/SocialiteAuthController.php +++ b/app/Endpoint/Api/Http/Controllers/Auth/SocialiteAuthController.php @@ -4,6 +4,7 @@ namespace App\Endpoint\Api\Http\Controllers\Auth; use App\Constants\Device; use App\Endpoint\Api\Http\Controllers\Controller; +use App\Enums\SocialiteType; use App\Exceptions\BizException; use App\Helpers\PhoneNumber; use App\Models\SmsCode; @@ -135,7 +136,7 @@ class SocialiteAuthController extends Controller $config = config('socialite', []); $socialite = new SocialiteManager($config); switch ($provider) { - case 'wechat-mini'://微信小程序 + case SocialiteType::WechatMiniProgram://微信小程序 $user = $socialite->create('wehcat-mini')->userFromCode($code); break; default: diff --git a/app/Endpoint/Callback/Http/Controllers/WeChatPayController.php b/app/Endpoint/Callback/Http/Controllers/WeChatPayController.php deleted file mode 100644 index 336806cf..00000000 --- a/app/Endpoint/Callback/Http/Controllers/WeChatPayController.php +++ /dev/null @@ -1,110 +0,0 @@ -handlePaidNotify(function ($message, $fail) { - $this->log('paid notify', $message); - - // 通信失败 - if (data_get($message, 'return_code') !== 'SUCCESS') { - return $fail('通信失败'); - } - - try { - $payLog = DB::transaction(function () use ($message) { - $payService = new PayService(); - - if (data_get($message, 'result_code') === 'SUCCESS') { - return $payService->handleSuccessByPaySerialNumber($message['out_trade_no'], [ - 'out_trade_no' => $message['transaction_id'], - 'pay_at' => Carbon::parse($message['time_end']), - ]); - } elseif (data_get($message, 'result_code') === 'FAIL') { - return $payService->handleFailedByPaySerialNumber($message['out_trade_no'], [ - 'out_trade_no' => $message['transaction_id'] ?? null, - 'failed_reason' => '['.$message['err_code'].']'.$message['err_code_des'], - ]); - } - }); - - $payable = $payLog?->payable; - - if ($payable instanceof Order) { - OrderPaid::dispatchIf($payable->isPaid(), $payable); - } - } catch (ModelNotFoundException | BizException $e) { - } catch (Throwable $e) { - throw $e; - } - - return true; - }); - } - - /** - * 订单退款结果通知 - * - * @param \App\Services\WeChatPayService $weChatPayService - * @return \Illuminate\Http\Response - */ - public function orderRefundedNotify(WeChatPayService $weChatPayService) - { - return $weChatPayService->handleRefundedNotify(function ($message, $reqInfo, $fail) { - $this->log('refund notify', $reqInfo); - - // 通信失败 - if (data_get($message, 'return_code') !== 'SUCCESS') { - return $fail('通信失败'); - } - - // 退款失败 - if ($reqInfo['refund_status'] !== 'SUCCESS') { - $log = OrderRefundLog::where('sn', $reqInfo['out_refund_no'])->first(); - - $log?->update([ - 'status' => OrderRefundLog::STATUS_FAILED, - 'failed_reason' => $reqInfo['refund_status'] === 'CHANGE' ? '退款异常' : '退款关闭', - ]); - } - - return true; - }); - } - - /** - * 微信回调日志 - * - * @param string $message - * @param array $context - * @return void - */ - protected function log(string $message, array $context = []) - { - return Log::build([ - 'driver' => 'daily', - 'path' => storage_path('logs/wxpay-notify.log'), - ])->info($message, $context); - } -} diff --git a/app/Endpoint/Callback/Http/Controllers/WxpayController.php b/app/Endpoint/Callback/Http/Controllers/WxpayController.php index 50e582e1..62e610da 100644 --- a/app/Endpoint/Callback/Http/Controllers/WxpayController.php +++ b/app/Endpoint/Callback/Http/Controllers/WxpayController.php @@ -9,10 +9,8 @@ use App\Models\OrderRefundLog; use App\Services\Payment\WxpayService; use App\Services\PayService; use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; use Throwable; class WxpayController extends Controller @@ -20,13 +18,14 @@ class WxpayController extends Controller /** * 支付结果通知 * + * @param string $payment * @param \App\Services\Payment\WxpayService $wxpayService * @return \Illuminate\Http\Response */ - public function paidNotify(WxpayService $wxpayService, Request $request) + public function paidNotify($payment, WxpayService $wxpayService) { - return $wxpayService->payment()->handlePaidNotify(function ($message, $fail) use ($request) { - $this->log('paid notify--------'.$request->url(), $message); + return $wxpayService->payment($payment)->handlePaidNotify(function ($message, $fail) { + $this->log('paid notify', $message); // 通信失败 if (data_get($message, 'return_code') !== 'SUCCESS') { @@ -67,12 +66,13 @@ class WxpayController extends Controller /** * 订单退款结果通知 * + * @param string $payment * @param \App\Services\Payment\WxpayService $wxpayService * @return \Illuminate\Http\Response */ - public function orderRefundedNotify(WxpayService $wxpayService) + public function orderRefundedNotify($payment, WxpayService $wxpayService) { - return $wxpayService->payment()->handleRefundedNotify(function ($message, $reqInfo, $fail) { + return $wxpayService->payment($payment)->handleRefundedNotify(function ($message, $reqInfo, $fail) { $this->log('refund notify', $reqInfo); // 通信失败 @@ -93,19 +93,4 @@ class WxpayController extends Controller return true; }); } - - /** - * 微信回调日志 - * - * @param string $message - * @param array $context - * @return void - */ - protected function log(string $message, array $context = []) - { - return Log::build([ - 'driver' => 'daily', - 'path' => storage_path('logs/wxpay-notify.log'), - ])->info($message, $context); - } } diff --git a/app/Endpoint/Callback/Http/Controllers/YzkWeChatPayController.php b/app/Endpoint/Callback/Http/Controllers/YzkWeChatPayController.php deleted file mode 100644 index ca5387d4..00000000 --- a/app/Endpoint/Callback/Http/Controllers/YzkWeChatPayController.php +++ /dev/null @@ -1,73 +0,0 @@ -handlePaidNotify(function ($message, $fail) { - $this->log('paid notify', $message); - - // 通信失败 - if (data_get($message, 'return_code') !== 'SUCCESS') { - return $fail('通信失败'); - } - - try { - DB::transaction(function () use ($message) { - $payService = new PayService(); - - if (data_get($message, 'result_code') === 'SUCCESS') { - return $payService->handleSuccessByPaySerialNumber($message['out_trade_no'], [ - 'out_trade_no' => $message['transaction_id'], - 'pay_at' => Carbon::parse($message['time_end']), - ]); - } elseif (data_get($message, 'result_code') === 'FAIL') { - return $payService->handleFailedByPaySerialNumber($message['out_trade_no'], [ - 'out_trade_no' => $message['transaction_id'] ?? null, - 'failed_reason' => '['.$message['err_code'].']'.$message['err_code_des'], - ]); - } - }); - } catch (ModelNotFoundException | BizException $e) { - } catch (Throwable $e) { - throw $e; - } - - return true; - }); - } - - /** - * 微信回调日志 - * - * @param string $message - * @param array $context - * @return void - */ - protected function log(string $message, array $context = []) - { - return Log::build([ - 'driver' => 'daily', - 'path' => storage_path('logs/yzk-wxpay-notify.log'), - ])->info($message, $context); - } -} diff --git a/app/Endpoint/Callback/routes.php b/app/Endpoint/Callback/routes.php index d7958851..26c68d31 100644 --- a/app/Endpoint/Callback/routes.php +++ b/app/Endpoint/Callback/routes.php @@ -2,17 +2,14 @@ use App\Endpoint\Callback\Http\Controllers\AlipayController; use App\Endpoint\Callback\Http\Controllers\Kuaidi100Controller; -use App\Endpoint\Callback\Http\Controllers\WeChatPayController; use App\Endpoint\Callback\Http\Controllers\WxpayController; -use App\Endpoint\Callback\Http\Controllers\YzkWeChatPayController; use Illuminate\Support\Facades\Route; //快递100物流推送 Route::post('kuaidi100', [Kuaidi100Controller::class, 'notify']); + // 微信支付通知 -Route::post('wxpay/paid-notify', [WxpayController::class, 'paidNotify'])->name('wxpay.paid_notify'); -Route::post('wxpay/order-refund-notify', [WeChatPayController::class, 'orderRefundedNotify'])->name('wxpay.order_refund_notify'); +Route::post('wxpay/{payment}/paid-notify', [WxpayController::class, 'paidNotify'])->name('wxpay.paid_notify'); +Route::post('wxpay/order-refund-notify', [WxpayController::class, 'orderRefundedNotify'])->name('wxpay.order_refund_notify'); Route::post('alipay', AlipayController::class)->name('alipay.notify'); - -Route::post('yzk-wxpay/paid-notify', [YzkWeChatPayController::class, 'paidNotify'])->name('yzk_wxpay.paid_notify'); diff --git a/app/Enums/SocialiteType.php b/app/Enums/SocialiteType.php new file mode 100644 index 00000000..e404f5f2 --- /dev/null +++ b/app/Enums/SocialiteType.php @@ -0,0 +1,7 @@ +hasMany(DealerUserProductLog::class, 'user_id'); } + public function socialites() + { + return $this->hasMany(SocialiteUser::class, 'user_id'); + } + /** * 确认此用户是否是 VIP * diff --git a/app/Services/Dealer/OrderService.php b/app/Services/Dealer/OrderService.php index 4ddbba4c..558b586e 100644 --- a/app/Services/Dealer/OrderService.php +++ b/app/Services/Dealer/OrderService.php @@ -15,9 +15,8 @@ use App\Models\DealerProduct; use App\Models\DealerUserProductLog; use App\Models\ShippingAddress; use App\Models\User; +use App\Services\Payment\WxpayService; use App\Services\PayService; -use App\Services\WeChatPayService; -use EasyWeChat\Factory as EasyWeChatFactory; use Illuminate\Database\QueryException; class OrderService @@ -351,15 +350,15 @@ class OrderService throw new BizException('支付方式 非法'); } - $app = EasyWeChatFactory::payment(config('wechat.payment.yzk')); - - $data = (new WeChatPayService($app))->pay([ + $params = [ 'body' => app_settings('app.app_name').'-批零订单', 'out_trade_no' => $payLog->pay_sn, 'total_fee' => bcmul($order->total_amount, '100'), 'trade_type' => $tradeType->value, - 'notify_url' => url(route('yzk_wxpay.paid_notify', [], false), [], true), - ]); + ]; + + $data = (new WxpayService())->pay($params, 'yzk_h5'); + break; default: diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index c5544955..99fa04cf 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -6,6 +6,7 @@ use App\Endpoint\Api\Http\Resources\ProductSkuSimpleResource; use App\Endpoint\Api\Http\Resources\ShippingAddressResource; use App\Endpoint\Api\Http\Resources\UserCouponResource; use App\Enums\PayWay; +use App\Enums\SocialiteType; use App\Enums\WxpayTradeType; use App\Exceptions\BizException; use App\Exceptions\ShippingNotSupportedException; @@ -15,6 +16,7 @@ use App\Models\OrderProduct; use App\Models\ProductGift; use App\Models\ProductSku; use App\Models\ShippingAddress; +use App\Models\SocialiteUser; use App\Models\User; use App\Models\UserCoupon; use App\Services\Payment\WxpayService; @@ -781,16 +783,17 @@ class OrderService } } while ($payLog === null); + $data = [ + 'pay_sn' => $payLog->pay_sn, + ]; + // 如果支付方式为线下支付,或支付金额为 0,则按支付完成处理 if ($order->total_amount === 0 || $payLog->isOffline()) { (new PayService())->handleSuccess($payLog, [ 'pay_at' => now(), ]); - return [ - 'pay_sn' => $payLog->pay_sn, - 'data' => null, - ]; + $data = null; } elseif ($payLog->isWxpay()) { if (is_null($tradeType = WxpayTradeType::tryFromPayWay($payLog->pay_way))) { throw new BizException('支付方式 非法'); @@ -804,13 +807,24 @@ class OrderService ]; if ($tradeType === WxpayTradeType::JSAPI) { - $params['openid'] = 'o-BdO46p2hRnk-SQIXa1TEdBFtfs'; + $socialite = match ($payLog->pay_way) { + PayWay::WxpayMiniProgram => SocialiteUser::where([ + 'user_id' => $order->user_id, + 'socialite_type' => SocialiteType::WechatMiniProgram, + ])->first(), + }; + + if ($socialite === null) { + throw new BizException('未绑定微信小程序'); + } + + $params['openid'] = $socialite->socialite_id; } - return [ - 'pay_sn' => $payLog->pay_sn, - 'data' => (new WxpayService())->pay($params), - ]; + $data = (new WxpayService())->pay($params, match ($payLog->pay_way) { + PayWay::WxpayMiniProgram => 'mini_program', + default => 'default', + }); } elseif ($payLog->isAlipay()) { $params = [ 'subject' => app_settings('app.app_name').'-商城订单', @@ -818,13 +832,13 @@ class OrderService 'total_amount' => bcdiv($order->total_amount, 100, 2), ]; - return [ - 'pay_sn' => $payLog->pay_sn, - 'data' => app(AlipayService::class)->pay($params), - ]; + $data = app(AlipayService::class)->pay($params); } - throw new BizException('【-1】支付方式 非法'); + return [ + 'pay_way' => $payLog->pay_way, + 'data' => $data, + ]; } /** diff --git a/app/Services/Payment/WxpayService.php b/app/Services/Payment/WxpayService.php index 3ea9f928..7b6de61f 100644 --- a/app/Services/Payment/WxpayService.php +++ b/app/Services/Payment/WxpayService.php @@ -19,19 +19,22 @@ class WxpayService * 支付 * * @param array $params + * @param string|null $payment * @return array */ - public function pay(array $params, string $paymentName = null): array + public function pay(array $params, ?string $payment = null): array { if (! isset($params['notify_url'])) { - $params['notify_url'] = url(route('wxpay.paid_notify', ['payment' => $paymentName], false), [], true); + $path = route('wxpay.paid_notify', ['payment' => $payment ?: 'default'], false); + + $params['notify_url'] = url($path, [], true); } if (! isset($params['trade_type'])) { $params['trade_type'] = WxpayTradeType::App->value; } - $app = $this->payment($paymentName); + $app = $this->payment($payment); $result = $app->order->unify($params); @@ -44,6 +47,45 @@ class WxpayService }; } + /** + * 根据商户订单号退款 + * + * @param string $number + * @param string $refundNumber + * @param int $totalFee + * @param int $refundFee + * @param array $optional + * @return array + */ + public function refundByOutTradeNumber(array $params, ?string $payment = null) + { + $optional = Arr::get($params, 'optional', []); + + if (! is_array($optional)) { + $optional = []; + } + + if (! isset($optional['notify_url'])) { + $path = route('wxpay.order_refund_notify', [ + 'payment' => $payment ?: 'default', + ], false); + + $optional['notify_url'] = url($path, [], true); + } + + $result = $this->payment($payment)->refund->byOutTradeNumber( + $params['number'], + $params['refund_number'], + $params['total_fee'], + $params['refund_fee'], + $optional, + ); + + $this->validateResult($result, $params); + + return $result; + } + /** * @param string|null $name * @return \EasyWeChat\Payment\Application @@ -100,7 +142,7 @@ class WxpayService throw new WeChatPayException(sprintf( '[%s] %s', Arr::get($result, 'err_code', '-1'), - Arr::get($result, 'err_code_des', '结果异常') + Arr::get($result, 'err_code_des', '交易失败') ), $raw); } } diff --git a/app/Services/WeChatPayService.php b/app/Services/WeChatPayService.php deleted file mode 100644 index 978432d1..00000000 --- a/app/Services/WeChatPayService.php +++ /dev/null @@ -1,172 +0,0 @@ -app = $app; - } - - /** - * 支付 - * - * @param array $params - * @return array - * - * @throws \App\Exceptions\WeChatPayException - */ - public function pay(array $params) - { - if (! isset($params['notify_url'])) { - $params['notify_url'] = url(route('wxpay.paid_notify', [], false), [], true); - } - - // 如果交易类型不存在,则使用 App 支付 - if (! isset($params['trade_type'])) { - $params['trade_type'] = static::TRADE_TYPE_APP; - } - - $result = $this->app->order->unify($params); - - if (data_get($result, 'return_code') !== 'SUCCESS') { - throw new WeChatPayException( - data_get($result, 'return_msg', '请求失败'), - $params - ); - } - - if (data_get($result, 'result_code') !== 'SUCCESS') { - throw new WeChatPayException( - sprintf( - '[%s] %s', - data_get($result, 'err_code', '-1'), - data_get($result, 'err_code_des', '交易失败') - ), - $params - ); - } - - return match ($params['trade_type']) { - static::TRADE_TYPE_APP => $this->app->jssdk->appConfig($result['prepay_id']), - static::TRADE_TYPE_H5 => Arr::only($result, ['mweb_url']), - default => $this->app->jssdk->bridgeConfig($result['prepay_id'], false), - }; - } - - /** - * 支付结果通知 - * - * @param \Closure $closure - * @return \Symfony\Component\HttpFoundation\Response - */ - public function handlePaidNotify(Closure $closure) - { - return $this->app->handlePaidNotify($closure); - } - - /** - * 根据微信订单号退款 - * - * @param string $transactionId - * @param string $refundNumber - * @param int $totalFee - * @param int $refundFee - * @param array $optional - * @return array - */ - public function refundByTransactionId(string $transactionId, string $refundNumber, int $totalFee, int $refundFee, array $optional = []) - { - $result = $this->app->refund->byTransactionId($transactionId, $refundNumber, $totalFee, $refundFee, $optional); - - if (data_get($result, 'return_code') !== 'SUCCESS') { - throw new WeChatPayException( - data_get($result, 'return_msg', '请求失败') - ); - } - - if (data_get($result, 'result_code') !== 'SUCCESS') { - throw new WeChatPayException( - sprintf( - '[%s] %s', - data_get($result, 'err_code', '-1'), - data_get($result, 'err_code_des', '退款失败') - ) - ); - } - - return $result; - } - - /** - * 根据商户订单号退款 - * - * @param string $number - * @param string $refundNumber - * @param int $totalFee - * @param int $refundFee - * @param array $optional - * @return array - */ - public function refundByOutTradeNumber(string $number, string $refundNumber, int $totalFee, int $refundFee, array $optional = []) - { - $result = $this->app->refund->byOutTradeNumber($number, $refundNumber, $totalFee, $refundFee, $optional); - - if (data_get($result, 'return_code') !== 'SUCCESS') { - throw new WeChatPayException( - data_get($result, 'return_msg', '请求失败') - ); - } - - if (data_get($result, 'result_code') !== 'SUCCESS') { - throw new WeChatPayException( - sprintf( - '[%s] %s', - data_get($result, 'err_code', '-1'), - data_get($result, 'err_code_des', '退款失败') - ) - ); - } - - return $result; - } - - /** - * 退款结果通知 - * - * @param \Closure $closure - * @return \Symfony\Component\HttpFoundation\Response - */ - public function handleRefundedNotify(Closure $closure) - { - return $this->app->handleRefundedNotify($closure); - } -} diff --git a/config/wechat.php b/config/wechat.php index a9d85446..60af8281 100644 --- a/config/wechat.php +++ b/config/wechat.php @@ -50,6 +50,7 @@ return [ ], ], 'payment' => [ + // 商城 - 微信 App支付 'default' => [ 'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false), 'app_id' => env('WECHAT_PAYMENT_APPID'), @@ -71,7 +72,29 @@ return [ ], ], ], - 'yzk' => [ + // 商城 - 微信小程序支付 + 'mini_program' => [ + 'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false), + 'app_id' => env('WECHAT_MINI_PROGRAM_APPID'), + 'mch_id' => env('WECHAT_PAYMENT_MCH_ID'), + 'key' => env('WECHAT_PAYMENT_KEY'), + 'cert_path' => env('WECHAT_PAYMENT_CERT_PATH'), // 绝对地址 + 'key_path' => env('WECHAT_PAYMENT_KEY_PATH'), // 绝对地址 + 'notify_url' => env('WECHAT_PAYMENT_NOTIFY_URL'), + + // 日志 + 'log' => [ + 'default' => 'daily', + 'channels' => [ + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/wxpay-mini-program.log'), + 'level' => 'info', + ], + ], + ], + ], + 'yzk_h5' => [ 'sandbox' => env('WECHAT_PAYMENT_SANDBOX_YZK', false), 'app_id' => env('WECHAT_PAYMENT_APPID_YZK'), 'mch_id' => env('WECHAT_PAYMENT_MCH_ID_YZK'),