diff --git a/app/Admin/Actions/Order/RowWxShareFinish.php b/app/Admin/Actions/Order/RowWxShareFinish.php new file mode 100644 index 00000000..213ec44b --- /dev/null +++ b/app/Admin/Actions/Order/RowWxShareFinish.php @@ -0,0 +1,34 @@ +primaryKey); + $service = new WxpayService(); + $sn = serial_number(); + $result = $service->finishShare($order->out_trade_no, $sn); + if (data_get($result, 'return_code') != 'SUCCESS') { + return $this->response()->error(data_get($result, 'return_msg')); + } + if (data_get($result, 'result_code') != 'SUCCESS') { + return $this->response()->error(data_get($result, 'err_code') . ':' . data_get($result, 'err_code_des')); + } + $attributes = ['finish_sn' => $sn, 'status' => 'Y']; + $order->update($order->wx_share ? array_merge($order->wx_share, $attributes) : $attributes); + return $this->response()->success('成功!'); + } + + public function confirm() + { + return ['确定要解冻资金?', '解冻资金后将无法进行分账']; + } +} diff --git a/app/Admin/Controllers/OrderController.php b/app/Admin/Controllers/OrderController.php index 17a6cde0..d1bcbe8b 100644 --- a/app/Admin/Controllers/OrderController.php +++ b/app/Admin/Controllers/OrderController.php @@ -5,6 +5,7 @@ namespace App\Admin\Controllers; use App\Admin\Actions\Grid\Exports\ShippingOrder as ExportShippingOrder; use App\Admin\Actions\Grid\KuaidiInfo; use App\Admin\Actions\Grid\OrderSetTag; +use App\Admin\Actions\Order\RowWxShareFinish; use App\Admin\Actions\Show\OrderConsigneeInfo; use App\Admin\Actions\Show\OrderCreatePackage; use App\Admin\Actions\Show\OrderPay; @@ -253,24 +254,30 @@ class OrderController extends AdminController $filter->like('pay_sn')->width(3); }); - $grid->tools(function (Grid\Tools $tools) { - if (Admin::user()->can('dcat.admin.orders.export_shipping_orders')) { + $user = Admin::user(); + $grid->tools(function (Grid\Tools $tools) use ($user) { + if ($user->can('dcat.admin.orders.export_shipping_orders')) { $tools->append(new ExportShippingOrder()); } - if (Admin::user()->can('dcat.admin.orders.export_order_products')) { + if ($user->can('dcat.admin.orders.export_order_products')) { $tools->append(new ExportProduct()); } }); - $grid->actions(function (Grid\Displayers\Actions $actions) { - if (Admin::user()->can('dcat.admin.orders.show')) { + $grid->actions(function (Grid\Displayers\Actions $actions) use ($user) { + $row = $actions->row; + if ($user->can('dcat.admin.orders.show')) { $actions->disableView(false); } - if (Admin::user()->can('dcat.admin.orders.tags')) { + if ($user->can('dcat.admin.orders.tags')) { $actions->append(new OrderSetTag()); } + + if (data_get($row->wx_share, 'status') != 'Y') { + $actions->append(new RowWxShareFinish()); + } }); $grid->footer(function ($collection) use ($grid) { diff --git a/app/Admin/Controllers/Store/OrderController.php b/app/Admin/Controllers/Store/OrderController.php index 0a9d871f..77ffdd2d 100644 --- a/app/Admin/Controllers/Store/OrderController.php +++ b/app/Admin/Controllers/Store/OrderController.php @@ -189,7 +189,7 @@ class OrderController extends AdminController $show->field('cost_price')->as(fn ($value) => bcdiv($value, 100, 2)); $show->field('profit_price')->as(fn () => round(($this->total_amount - $this->cost_price) / 100, 2, PHP_ROUND_HALF_DOWN)); $show->field('vip_discount_amount')->as(fn ($v) => bcdiv($v, 100, 2))->prepend('- ¥'); - + // 优惠券 $show->field('user_coupon_id')->as(fn() => data_get($this->userCoupon, 'coupon_name')); $show->field('coupon_discount_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('- ¥'); @@ -199,11 +199,11 @@ class OrderController extends AdminController $show->field('total_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('¥'); $show->field('sales_value'); $show->field('order_status')->as(fn() => $this->order_status)->using($this->statusMap)->dot($this->statusColor); - $show->field('pay_way')->as(fn() => $this->pay_way?->mallText())->circleDot(PayWay::colors()); + $show->field('pay_way')->as(fn() => $this->pay_way?->text())->circleDot(PayWay::colors()); $show->field('created_at'); $show->field('pay_at'); $show->field('completed_at'); - + if ($model->source_type == Desk::class) { $show->field('desk_id')->as(fn() => data_get($model->source, 'name')); } diff --git a/app/Enums/PayWay.php b/app/Enums/PayWay.php index 1a3548ad..f7d92b23 100644 --- a/app/Enums/PayWay.php +++ b/app/Enums/PayWay.php @@ -76,6 +76,7 @@ enum PayWay: string { static::WxpayH5->value => '#21b978', static::WxpayJsApi->value => '#21b978', static::WxpayMiniProgram->value => '#21b978', + static::WxPayShare->value => '#21b978', // 支付宝 static::AlipayApp->value => '#3085d6', ]; diff --git a/app/Listeners/OrderDistribute.php b/app/Listeners/OrderDistribute.php index 8bd3bac1..92c62616 100644 --- a/app/Listeners/OrderDistribute.php +++ b/app/Listeners/OrderDistribute.php @@ -2,8 +2,10 @@ namespace App\Listeners; +use App\Enums\PayWay; use App\Models\Order; use App\Services\DistributeService; +use App\Services\Payment\WxpayService; use Exception; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; @@ -33,9 +35,27 @@ class OrderDistribute try { DB::beginTransaction(); $service = new DistributeService(); - $service->storeByOrder($order); + $profits = $service->storeByOrder($order); DB::commit(); + + // 如果没有提成记录 + if ($profits->count() <= 0 && in_array($order->pay_way, [PayWay::WxpayApp, PayWay::WxpayH5, PayWay::WxpayJsApi, PayWay::WxpayMiniProgram])) { + // 解冻资金 + $sn = serial_number(); + $result = (new WxpayService())->finishShare($order->out_trade_no, $sn); + if (data_get($result, 'return_code') != 'SUCCESS') { + logger()->error('微信分账-解冻资金失败', $result); + } + else if (data_get($result, 'result_code') != 'SUCCESS') { + logger()->error('微信分账-解冻资金失败', $result); + } else { + $attributes = ['finish_sn' => $sn, 'status' => 'Y']; + $order->update($order->wx_share ? array_merge($order->wx_share, $attributes) : $attributes); + } + } } catch (Exception $e) { + logger('添加提成记录失败'); + logger()->error($e); DB::rollBack(); } } diff --git a/app/Models/Order.php b/app/Models/Order.php index 969e4725..0f6e6b83 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -59,6 +59,7 @@ class Order extends Model 'profit_paid' => 'datetime', 'status' => 'int', 'is_change' => 'bool', + 'wx_share' => 'json', ]; /** @@ -100,6 +101,7 @@ class Order extends Model 'cost_price', 'profit_paid', 'point_discount_amount', + 'wx_share', ]; /** diff --git a/app/Services/DistributeService.php b/app/Services/DistributeService.php index 8f839b31..0b262f13 100644 --- a/app/Services/DistributeService.php +++ b/app/Services/DistributeService.php @@ -241,6 +241,9 @@ class DistributeService $result = (new WxpayService())->share($order->out_trade_no, $sn, $receivers); + $attributes = ['share_sn' => $sn, 'status' => 'N']; + $order->update($order->wx_share ? array_merge($order->wx_share, $attributes) : $attributes); + $status = data_get($result, 'status'); if ($status == 'FINISHED') { $this->success($list, $result); diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 74d27b0b..5076298e 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -1267,6 +1267,9 @@ class OrderService PayWay::WxpayMiniProgram => 'mini_program', default => 'default', }); + + // 更新订单冻结状态 + $order->update(['wx_share' => $order->wx_share ? array_merge($order->wx_share, ['status' => 'Y']) : ['status' => 'Y']]); } elseif ($payLog->isAlipay()) { $params = [ 'subject' => app_settings('app.app_name').'-商城订单', diff --git a/app/Services/Payment/WxpayService.php b/app/Services/Payment/WxpayService.php index 64242590..8335e113 100644 --- a/app/Services/Payment/WxpayService.php +++ b/app/Services/Payment/WxpayService.php @@ -5,7 +5,10 @@ namespace App\Services\Payment; use App\Enums\WxpayTradeType; use App\Exceptions\WeChatPayException; use EasyWeChat\Factory; +use EasyWeChat\Kernel\Exceptions\InvalidArgumentException; +use EasyWeChat\Kernel\Exceptions\InvalidConfigException; use EasyWeChat\Payment\Application; +use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Arr; class WxpayService @@ -154,6 +157,31 @@ class WxpayService return $app->profit_sharing->query($transaction_id, $share_sn); } + /** + * 微信支付-完结分账, 解冻订单剩余资金 + * + * @param string $transaction_id 微信支付流水号 + * @param string $out_trade_sn 分账订单号, 自动生成 + * @return mixed + */ + public function finishShare(string $transaction_id, string $out_trade_sn) + { + $app = $this->payment(); + // 服务商模式 (子商户) + $appId = config('wechat.payment.sub.app_id'); + $mchId = config('wechat.payment.sub.mch_id'); + + if ($appId && $mchId) { + $app->setSubMerchant($mchId, $appId); + } + + return $app->profit_sharing->markOrderAsFinished([ + 'transaction_id' => $transaction_id, + 'out_order_no' => $out_trade_sn, + 'description' => '订单分账完结, 解冻资金' + ]); + } + /** * 根据商户订单号退款 * diff --git a/database/migrations/2023_10_23_131209_add_wx_share_to_orders.php b/database/migrations/2023_10_23_131209_add_wx_share_to_orders.php new file mode 100644 index 00000000..6a59198a --- /dev/null +++ b/database/migrations/2023_10_23_131209_add_wx_share_to_orders.php @@ -0,0 +1,35 @@ +json('wx_share')->nullable()->comment('微信分账信息'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('orders', function (Blueprint $table) { + $table->dropColumn(['wx_share']); + }); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php index 5aebe412..6e5eb091 100644 --- a/tests/Feature/ExampleTest.php +++ b/tests/Feature/ExampleTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use App\Enums\PayWay; use App\Services\DistributeService; use App\Services\Payment\WxpayService; use Illuminate\Support\Facades\DB; @@ -17,18 +18,8 @@ class ExampleTest extends TestCase */ public function test_example() { - // $order = Order::findOrFail(3146); - // try { - // DB::beginTransaction(); - // (new DistributeService())->wechatShare($order); - // $order->update(['profit_paid' => now()]); - // DB::commit(); - // } catch (\Exception $e) { - // DB::rollBack(); - // report($e); - // } - $result = (new WxpayService())->queryShare('4200002029202310133548206841', '20231013084421898946'); - dump($result); + $result = ['status' => 'N', 'sn' => '21322132']; + dump($result ? array_merge($result, ['status' => 'Y']) : ['status' => 'Y']); $this->assertTrue(true); } }