find((int) $value)?->preview_url ?? ''); } return $value; }; // 支付方式开关(Checkbox 配置自动转数组;未配置时默认全部启用) $payMethods = site_config('pay.pay_methods'); if (! is_array($payMethods)) { $payMethods = ['wechat_qrcode', 'alipay_qrcode', 'bank', 'online']; } return $this->success([ 'pay_methods' => array_values($payMethods), 'wechat_qrcode' => $resolve(site_config('pay.wechat_qrcode', '')), 'alipay_qrcode' => $resolve(site_config('pay.alipay_qrcode', '')), 'bank_info' => (string) site_config('pay.bank_info', ''), // 公众号 AppID(H5 在微信内置浏览器拼网页授权链接使用,授权回调 code 传给 /mini/payment/online scene=mp) 'mp_appid' => app(WechatMpService::class)->appid(), ]); } /** 支付记录列表:当前门店强制过滤,?status=&page=&pageSize= */ #[GetRoute('/payment', authorize: true)] public function index(Request $request): JsonResponse { $store = $this->currentStore($request); $query = PaymentModel::query() ->where('store_id', $store->id) ->withCount('bills'); if ($request->filled('status')) { $query->where('status', (int) $request->input('status')); } $data = $query->orderBy('id', 'desc') ->paginate((int) $request->input('pageSize', 10)) ->toArray(); return $this->success($data); } /** * 发起付款:合并选择本店未支付账单,提交支付方式与汇款凭证(后台审核) * @throws Throwable */ #[PostRoute('/payment', authorize: true)] public function create(Request $request): JsonResponse { $data = $request->validate([ 'bill_ids' => 'required|array|min:1', 'bill_ids.*' => 'integer|distinct', 'pay_method' => 'required|integer|in:1,2,3', 'voucher_ids' => 'required|array|min:1', 'voucher_ids.*' => 'integer|distinct', 'remark' => 'nullable|string|max:255', ], [ 'bill_ids.required' => '请选择要付款的账单', 'bill_ids.min' => '请选择要付款的账单', 'pay_method.required' => '请选择支付方式', 'pay_method.in' => '支付方式不正确', 'voucher_ids.required' => '请上传汇款凭证', 'voucher_ids.min' => '请上传汇款凭证', 'remark.max' => '备注超过最大长度', ]); $store = $this->currentStore($request); $billIds = array_map('intval', $data['bill_ids']); $payment = DB::transaction(function () use ($store, $data, $billIds) { $bills = BillModel::query() ->where('store_id', $store->id) ->whereIn('id', $billIds) ->lockForUpdate() ->get(); if ($bills->count() !== count($billIds)) { throw new RepositoryException('包含不属于本店的账单,请刷新后重试'); } foreach ($bills as $bill) { if ($bill->status === BillModel::STATUS_PAID) { throw new RepositoryException('账单 ' . $bill->bill_no . ' 已支付,请刷新后重试'); } if ((int) $bill->payment_id !== 0) { throw new RepositoryException('账单 ' . $bill->bill_no . ' 已在支付审核中,请勿重复提交'); } } $amount = $bills->reduce( static fn (string $carry, BillModel $bill): string => bcadd($carry, (string) $bill->total_amount, 2), '0' ); $payment = PaymentModel::create([ 'payment_no' => app(BillNumberService::class)->make('ZF'), 'store_id' => $store->id, 'amount' => $amount, 'pay_method' => (int) $data['pay_method'], 'voucher_ids' => array_map('intval', $data['voucher_ids']), 'status' => PaymentModel::STATUS_PENDING, 'remark' => (string) ($data['remark'] ?? ''), ]); // 锁定账单到本支付记录(审核拒绝后释放,可重新付款) BillModel::query()->whereIn('id', $bills->pluck('id'))->update(['payment_id' => $payment->id]); return $payment; }); return $this->success([ 'id' => $payment->id, 'payment_no' => $payment->payment_no, 'amount' => $payment->amount, ], '付款申请已提交,请等待商家审核'); } /** 支付记录详情(校验归属;含合并账单与凭证图片) */ #[GetRoute('/payment/{id}', authorize: true, where: ['id' => '[0-9]+'])] public function detail(int $id, Request $request): JsonResponse { $store = $this->currentStore($request); $payment = PaymentModel::query() ->where('store_id', $store->id) ->find($id); if ($payment === null) { throw new RepositoryException('支付记录不存在'); } $bills = $payment->bills() ->orderBy('id') ->get(['id', 'bill_no', 'bill_date', 'product_amount', 'delivery_fee', 'added_amount', 'after_sale', 'total_amount', 'status']) ->toArray(); $data = $payment->toArray(); $data['voucher_urls'] = $payment->voucherUrls(); return $this->success([ 'payment' => $data, 'bills' => $bills, ]); } }