validate([ 'bill_ids' => 'required|array|min:1', 'bill_ids.*' => 'integer|distinct', 'code' => 'required|string|max:64', 'remark' => 'nullable|string|max:255', ], [ 'bill_ids.required' => '请选择要付款的账单', 'bill_ids.min' => '请选择要付款的账单', 'code.required' => '微信登录凭证缺失,请重新进入小程序', 'remark.max' => '备注超过最大长度', ]); $store = $this->currentStore($request); [$payment, $payParams] = $this->onlinePayment->create( $store, array_map('intval', $data['bill_ids']), (string) $data['code'], (string) ($data['remark'] ?? ''), ); return $this->success([ 'id' => $payment->id, 'payment_no' => $payment->payment_no, 'amount' => $payment->amount, 'pay_params' => $payParams, ], '下单成功,请调起支付'); } /** * 查询支付结果:网关回调可能延迟/丢失,小程序完成支付后主动调用同步结账 * @throws Throwable */ #[GetRoute('/payment/online/{paymentNo}/query', authorize: true)] public function query(string $paymentNo, Request $request): JsonResponse { $store = $this->currentStore($request); $payment = PaymentModel::query() ->where('store_id', $store->id) ->where('payment_no', $paymentNo) ->where('pay_type', PaymentModel::TYPE_ONLINE) ->first(); if ($payment === null) { throw new RepositoryException('支付记录不存在'); } $result = $this->onlinePayment->queryAndSettle($payment); $payment = $result['payment']; return $this->success([ 'payment_no' => $payment->payment_no, 'status' => $payment->status, 'status_name' => PaymentModel::ONLINE_STATUS_NAMES[$payment->status] ?? '待支付', 'paid_at' => $payment->paid_at, 'trade_no' => $payment->trade_no, ], $payment->status === PaymentModel::STATUS_APPROVED ? '支付成功' : '支付结果确认中'); } /** * 旺铺支付结果后台通知(公开路由,验签后幂等结账) * * 网关约定:应答 {"code":"00"} 视为通知成功,否则按 2^n 分钟重试 7 次; * 重复通知必须幂等(settle 内部行锁 + 状态判断)。 */ #[PostRoute('/payment/notify', authorize: false)] public function notify(Request $request): JsonResponse { $params = $request->all(); if (! $this->wangpu->verifyNotifySign($params)) { Log::warning('旺铺支付通知验签失败', ['params' => $params]); return $this->notifyAck('01', '验签失败'); } try { $this->onlinePayment->settleByNotify($params); } catch (Throwable $e) { Log::error('旺铺支付通知处理失败', ['params' => $params, 'error' => $e->getMessage()]); return $this->notifyAck('01', $e->getMessage()); } return $this->notifyAck(WangpuPayService::NOTIFY_ACK_OK, '成功'); } /** * 通知应答报文(网关约定格式,timestamp:yyyyMMddHHmmssSSS) */ protected function notifyAck(string $code, string $msg): JsonResponse { return response()->json([ 'code' => $code, 'msg' => mb_substr($msg, 0, 100), 'timestamp' => now()->format('YmdHisv'), ]); } }