Files
xin-procurement/app/Http/Controllers/Mini/PaymentController.php
T
2026-09-04 21:22:46 +08:00

177 lines
6.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Http\Controllers\Mini;
use App\Exceptions\RepositoryException;
use App\Models\BillModel;
use App\Models\PaymentModel;
use App\Services\BillNumberService;
use App\Services\WechatMpService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\AnnoRoute\Attribute\GetRoute;
use Modules\AnnoRoute\Attribute\PostRoute;
use Modules\AnnoRoute\Attribute\RequestAttribute;
use Modules\SystemTool\Models\SysFileModel;
use Throwable;
/**
* 小程序门店支付(选择本店账单合并付款,提交汇款凭证,后台审核通过后账单置已支付)
*/
#[RequestAttribute('/mini', 'mini', authGuard: 'users')]
class PaymentController extends BaseMiniController
{
/** 支付配置:收款码图片与对公汇款信息(付款页展示) */
#[GetRoute('/payment/config', authorize: true)]
public function config(): JsonResponse
{
// 配置值支持图片URL或文件ID(文件ID解析为预览地址)
$resolve = static function (mixed $value): string {
$value = trim((string) $value);
if ($value === '') {
return '';
}
if (is_numeric($value)) {
return (string) (SysFileModel::query()->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,
]);
}
}