post(); // 验证参数 if (!isset($params['amount']) || !isset($params['order_no']) || !isset($params['subject'])) { return $this->error('缺少必要参数'); } $amount = $params['amount']; $orderNo = $params['order_no']; $subject = $params['subject']; $body = $params['body'] ?? $subject; $type = $params['type'] ?? 'goods'; // 获取支付宝配置 $config = config('payment.alipay.default'); // 构建支付参数 $payOrder = [ 'out_trade_no' => $orderNo, 'total_amount' => $amount, 'subject' => $subject, 'body' => $body, ]; // 发起支付 $result = Pay::alipay($config)->web($payOrder); // 记录支付订单 $this->record_payment_order($orderNo, 'alipay', $amount, $subject, $type); return $this->success('ok',[ 'pay_url' => $result->getTargetUrl(), 'order_no' => $orderNo ]); } catch (\Exception $e) { WebmanLog::error('支付宝支付下单失败: ' . $e->getMessage()); return $this->error('支付下单失败: ' . $e->getMessage()); } } /** * 支付宝同步回调 * @Apidoc\Title("支付宝同步回调") * @Apidoc\Url("/api/payment/alipay/return") * @Apidoc\Method("GET") * @Apidoc\Param("out_trade_no", "string", "订单号", true) * @Apidoc\Param("trade_no", "string", "支付宝交易号", true) * @Apidoc\Param("trade_status", "string", "交易状态", true) * @Apidoc\Return("redirect", "string", "跳转到成功或失败页面") * @param Request $request * @return Response */ public function alipay_return(Request $request): Response { try { $config = config('payment.alipay.default'); $data = $request->all(); // 验证回调数据 $result = Pay::alipay($config)->verify($data); // 处理支付结果 $orderNo = $result->out_trade_no; $tradeNo = $result->trade_no; $status = $result->trade_status; // 更新订单状态 $this->update_payment_order($orderNo, $tradeNo, $status); // 跳转到成功页面 return redirect('/api/payment/success?order_no=' . $orderNo); } catch (\Exception $e) { WebmanLog::error('支付宝同步回调失败: ' . $e->getMessage()); return redirect('/api/payment/fail?error=' . urlencode($e->getMessage())); } } /** * 支付宝异步回调 * @Apidoc\Title("支付宝异步回调") * @Apidoc\Url("/api/payment/alipay/notify") * @Apidoc\Method("POST") * @Apidoc\Param("out_trade_no", "string", "订单号", true) * @Apidoc\Param("trade_no", "string", "支付宝交易号", true) * @Apidoc\Param("trade_status", "string", "交易状态", true) * @Apidoc\Return("string", "string", "返回success或fail") * @param Request $request * @return string */ public function alipay_notify(Request $request): string { try { $config = config('payment.alipay.default'); $data = $request->all(); // 验证回调数据 $result = Pay::alipay($config)->verify($data); // 处理支付结果 $orderNo = $result->out_trade_no; $tradeNo = $result->trade_no; $status = $result->trade_status; // 更新订单状态 $this->update_payment_order($orderNo, $tradeNo, $status); // 返回成功 return 'success'; } catch (\Exception $e) { WebmanLog::error('支付宝异步回调失败: ' . $e->getMessage()); return 'fail'; } } /** * 微信支付下单 * @Apidoc\Title("微信支付下单") * @Apidoc\Url("/api/payment/wechat/order") * @Apidoc\Method("POST") * @Apidoc\Param("amount", "float", "支付金额", true, "0.01") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("body", "string", "订单描述", true, "测试商品描述") * @Apidoc\Param("trade_type", "string", "交易类型: JSAPI, NATIVE, APP, MWEB", false, "JSAPI") * @Apidoc\Param("openid", "string", "微信用户openid(JSAPI模式需要)", false, "o123456") * @Apidoc\Param("type", "string", "订单类型: recharge(充值), goods(商品), service(服务), other(其他)", false, "goods") * @Apidoc\Return("success", "object", "成功响应", "pay_data|object|支付数据,order_no|string|订单号") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function wechat_order(Request $request): Response { try { $params = $request->post(); // 验证参数 if (!isset($params['amount']) || !isset($params['order_no']) || !isset($params['body'])) { return $this->error('缺少必要参数'); } $amount = $params['amount']; $orderNo = $params['order_no']; $body = $params['body']; $tradeType = $params['trade_type'] ?? 'JSAPI'; $openid = $params['openid'] ?? ''; $type = $params['type'] ?? 'goods'; // 获取微信支付配置 $config = config('payment.wechat.default'); // 构建支付参数 $payOrder = [ 'out_trade_no' => $orderNo, 'total_fee' => $amount * 100, // 微信支付金额单位为分 'body' => $body, 'trade_type' => $tradeType, ]; // JSAPI支付需要openid if ($tradeType === 'JSAPI' && $openid) { $payOrder['openid'] = $openid; } // 发起支付 $result = Pay::wechat($config)->order($payOrder); // 记录支付订单 $this->record_payment_order($orderNo, 'wechat', $amount, $body, $type); return $this->success('ok',[ 'pay_data' => $result->toArray(), 'order_no' => $orderNo ]); } catch (\Exception $e) { WebmanLog::error('微信支付下单失败: ' . $e->getMessage()); return $this->error('支付下单失败: ' . $e->getMessage()); } } /** * 微信支付回调 * @Apidoc\Title("微信支付回调") * @Apidoc\Url("/api/payment/wechat/notify") * @Apidoc\Method("POST") * @Apidoc\Param("out_trade_no", "string", "订单号", true) * @Apidoc\Param("transaction_id", "string", "微信交易号", true) * @Apidoc\Param("result_code", "string", "业务结果", true) * @Apidoc\Return("xml", "string", "返回XML格式的成功或失败响应") * @param Request $request * @return string */ public function wechat_notify(Request $request): string { try { $config = config('payment.wechat.default'); $data = $request->all(); // 验证回调数据 $result = Pay::wechat($config)->verify($data); // 处理支付结果 $orderNo = $result->out_trade_no; $tradeNo = $result->transaction_id; $status = $result->result_code === 'SUCCESS' ? 'SUCCESS' : 'FAIL'; // 更新订单状态 $this->update_payment_order($orderNo, $tradeNo, $status); // 返回成功 return Pay::wechat($config)->success(); } catch (\Exception $e) { WebmanLog::error('微信支付回调失败: ' . $e->getMessage()); return Pay::wechat(config('payment.wechat.default'))->fail(); } } /** * 记录支付订单 * @param string $orderNo * @param string $payType * @param float $amount * @param string $subject * @param string $type */ private function record_payment_order(string $orderNo, string $payType, float $amount, string $subject, string $type = 'goods'): void { try { Db::name('payment_order')->insert([ 'order_no' => $orderNo, 'pay_type' => $payType, 'type' => $type, 'amount' => $amount, 'subject' => $subject, 'status' => Status::CREATED->value, 'created_at' => time(), 'updated_at' => time() ]); } catch (\Exception $e) { WebmanLog::error('记录支付订单失败: ' . $e->getMessage()); } } /** * 更新支付订单状态 * @param string $orderNo * @param string $tradeNo * @param string $status */ private function update_payment_order(string $orderNo, string $tradeNo, string $status): void { try { // 映射支付状态到我们的状态枚举 $mappedStatus = $this->map_payment_status($status); Db::name('payment_order')->where('order_no', $orderNo)->update([ 'trade_no' => $tradeNo, 'status' => $mappedStatus, 'updated_at' => time() ]); } catch (\Exception $e) { WebmanLog::error('更新支付订单状态失败: ' . $e->getMessage()); } } /** * 映射支付状态到枚举 * @param string $status * @return string */ private function map_payment_status(string $status): string { // 支付宝状态映射 $alipayStatusMap = [ 'TRADE_SUCCESS' => Status::SUCCESS->value, 'TRADE_FINISHED' => Status::COMPLETE->value, 'TRADE_CLOSED' => Status::FAIL->value, 'WAIT_BUYER_PAY' => Status::CREATED->value ]; // 微信支付状态映射 $wechatStatusMap = [ 'SUCCESS' => Status::SUCCESS->value, 'FAIL' => Status::FAIL->value, 'REFUND' => Status::REFUNDED->value ]; // 先尝试支付宝映射 if (isset($alipayStatusMap[$status])) { return $alipayStatusMap[$status]; } // 再尝试微信映射 if (isset($wechatStatusMap[$status])) { return $wechatStatusMap[$status]; } // 默认返回成功 return Status::SUCCESS->value; } /** * 查询支付订单状态 * @Apidoc\Title("查询支付订单状态") * @Apidoc\Url("/api/payment/order/query") * @Apidoc\Method("GET") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("pay_type", "string", "支付类型: alipay, wechat", true, "alipay") * @Apidoc\Return("success", "object", "成功响应", "order|object|订单信息,pay_status|object|支付状态") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function query_order(Request $request): Response { try { $orderNo = $request->get('order_no'); $payType = $request->get('pay_type'); if (!$orderNo || !$payType) { return $this->error('缺少必要参数'); } // 查询订单 $order = Db::name('payment_order')->where('order_no', $orderNo)->find(); if (!$order) { return $this->error('订单不存在'); } // 根据支付类型查询支付状态 if ($payType === \app\enum\Payment\Method::ALIPAY->value) { $config = config('payment.alipay.default'); $result = Pay::alipay($config)->find($orderNo); } elseif ($payType === \app\enum\Payment\Method::WECHAT->value) { $config = config('payment.wechat.default'); $result = Pay::wechat($config)->find($orderNo); } else { return $this->error('不支持的支付类型'); } return $this->success('ok',[ 'order' => $order, 'pay_status' => $result->toArray() ]); } catch (\Exception $e) { WebmanLog::error('查询支付订单失败: ' . $e->getMessage()); return $this->error('查询失败: ' . $e->getMessage()); } } /** * 关闭支付订单 * @Apidoc\Title("关闭支付订单") * @Apidoc\Url("/api/payment/order/close") * @Apidoc\Method("POST") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("pay_type", "string", "支付类型: alipay, wechat", true, "alipay") * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function close_order(Request $request): Response { try { $orderNo = $request->post('order_no'); $payType = $request->post('pay_type'); if (!$orderNo || !$payType) { return $this->error('缺少必要参数'); } // 关闭订单 if ($payType === 'alipay') { $config = config('payment.alipay.default'); $result = Pay::alipay($config)->close($orderNo); } elseif ($payType === 'wechat') { $config = config('payment.wechat.default'); $result = Pay::wechat($config)->close($orderNo); } else { return $this->error('不支持的支付类型'); } // 更新订单状态 Db::name('payment_order')->where('order_no', $orderNo)->update([ 'status' => \app\enum\Payment\Status::COMPLETE->value, 'updated_at' => time() ]); return $this->success('订单关闭成功'); } catch (\Exception $e) { WebmanLog::error('关闭支付订单失败: ' . $e->getMessage()); return $this->error('关闭失败: ' . $e->getMessage()); } } /** * 退款 * @Apidoc\Title("退款") * @Apidoc\Url("/api/payment/refund") * @Apidoc\Method("POST") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("pay_type", "string", "支付类型: alipay, wechat", true, "alipay") * @Apidoc\Param("amount", "float", "退款金额", true, "0.01") * @Apidoc\Param("refund_no", "string", "退款单号", false, "20260409001_refund") * @Apidoc\Param("reason", "string", "退款原因", false, "退款") * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function refund(Request $request): Response { try { $params = $request->post(); if (!isset($params['order_no']) || !isset($params['pay_type']) || !isset($params['amount'])) { return $this->error('缺少必要参数'); } $orderNo = $params['order_no']; $payType = $params['pay_type']; $amount = $params['amount']; $refundNo = $params['refund_no'] ?? $orderNo . '_' . time(); $reason = $params['reason'] ?? '退款'; // 发起退款 if ($payType === Method::ALIPAY->value) { $config = config('payment.alipay.default'); $result = Pay::alipay($config)->refund([ 'out_trade_no' => $orderNo, 'refund_amount' => $amount, 'out_request_no' => $refundNo, 'refund_reason' => $reason, ]); } elseif ($payType === Method::WECHAT->value) { $config = config('payment.wechat.default'); $result = Pay::wechat($config)->refund([ 'out_trade_no' => $orderNo, 'out_refund_no' => $refundNo, 'total_fee' => $amount * 100, 'refund_fee' => $amount * 100, 'refund_desc' => $reason, ]); } else { return $this->error('不支持的支付类型'); } // 记录退款信息 Db::name('payment_refund')->insert([ 'order_no' => $orderNo, 'refund_no' => $refundNo, 'pay_type' => $payType, 'amount' => $amount, 'reason' => $reason, 'status' => 'SUCCESS', 'created_at' => time() ]); // 更新订单状态 Db::name('payment_order')->where('order_no', $orderNo)->update([ 'status' => Status::REFUNDED->value, 'updated_at' => time() ]); return $this->success('退款成功'); } catch (\Exception $e) { WebmanLog::error('退款失败: ' . $e->getMessage()); return $this->error('退款失败: ' . $e->getMessage()); } } /** * 统一支付接口 * @Apidoc\Title("统一支付接口") * @Apidoc\Url("/api/payment/unified/order") * @Apidoc\Method("POST") * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") * @Apidoc\Param("amount", "float", "支付金额", true, "0.01") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("subject", "string", "订单标题", true, "测试商品") * @Apidoc\Param("body", "string", "订单描述", false, "测试商品描述") * @Apidoc\Param("type", "string", "订单类型: recharge(充值), goods(商品), service(服务), other(其他)", false, "goods") * @Apidoc\Param("trade_type", "string", "交易类型(微信支付需要): JSAPI, NATIVE, APP, MWEB", false, "JSAPI") * @Apidoc\Param("openid", "string", "微信用户openid(JSAPI模式需要)", false, "o123456") * @Apidoc\Return("success", "object", "成功响应", "pay_url|string|支付宝支付链接,pay_data|object|微信支付数据,order_no|string|订单号") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function unified_order(Request $request): Response { try { $params = $request->post(); // 验证必要参数 if (!isset($params['payment']) || !isset($params['amount']) || !isset($params['order_no']) || !isset($params['subject'])) { return $this->error('缺少必要参数'); } $payment = strtolower($params['payment']); $amount = $params['amount']; $orderNo = $params['order_no']; $subject = $params['subject']; $body = $params['body'] ?? $subject; $type = $params['type'] ?? 'goods'; // 根据payment参数选择支付方式 switch ($payment) { case Method::ALIPAY->value: // 调用支付宝支付 return $this->alipay_order($request); case Method::WECHAT->value: // 调用微信支付 return $this->wechat_order($request); default: return $this->error('不支持的支付方式'); } } catch (\Exception $e) { WebmanLog::error('统一支付接口失败: ' . $e->getMessage()); return $this->error('支付失败: ' . $e->getMessage()); } } /** * 统一支付回调接口 * @Apidoc\Title("统一支付回调接口") * @Apidoc\Url("/api/payment/unified/notify") * @Apidoc\Method("ANY") * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", false) * @Apidoc\Param("out_trade_no", "string", "订单号", true) * @Apidoc\Param("trade_no|transaction_id", "string", "支付交易号", true) * @Apidoc\Param("trade_status|result_code", "string", "交易状态", true) * @Apidoc\Return("string", "string", "返回success/fail或XML格式响应") * @param Request $request * @return Response */ public function unified_notify(Request $request): Response { try { $payment = $request->get('payment') ?? $request->post('payment'); if (!$payment) { // 尝试从请求参数中自动识别 $data = $request->all(); if (isset($data['app_id']) && strpos($data['app_id'], '20') === 0) { $payment = 'alipay'; } elseif (isset($data['mch_id'])) { $payment = 'wechat'; } else { return $this->error('无法识别支付方式'); } } $payment = strtolower($payment); // 根据payment参数选择回调处理 switch ($payment) { case Method::ALIPAY->value: // 调用支付宝回调 return $this->alipay_notify($request); case Method::WECHAT->value: // 调用微信回调 return $this->wechat_notify($request); default: return $this->error('不支持的支付方式'); } } catch (\Exception $e) { WebmanLog::error('统一支付回调接口失败: ' . $e->getMessage()); return $this->error('回调处理失败: ' . $e->getMessage()); } } /** * 统一支付查询接口 * @Apidoc\Title("统一支付查询接口") * @Apidoc\Url("/api/payment/unified/query") * @Apidoc\Method("GET") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") * @Apidoc\Return("success", "object", "成功响应", "order|object|订单信息,pay_status|object|支付状态") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function unified_query(Request $request): Response { try { $params = $request->all(); // 验证必要参数 if (!isset($params['order_no']) || !isset($params['payment'])) { return $this->error('缺少必要参数'); } // 设置pay_type参数 $request->withAddedHeader('pay_type', $params['payment']); // 调用查询接口 return $this->query_order($request); } catch (\Exception $e) { WebmanLog::error('统一支付查询接口失败: ' . $e->getMessage()); return $this->error('查询失败: ' . $e->getMessage()); } } /** * 统一支付关闭接口 * @Apidoc\Title("统一支付关闭接口") * @Apidoc\Url("/api/payment/unified/close") * @Apidoc\Method("POST") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function unified_close(Request $request): Response { try { $params = $request->post(); // 验证必要参数 if (!isset($params['order_no']) || !isset($params['payment'])) { return $this->error('缺少必要参数'); } // 设置pay_type参数 $request->withAddedHeader('pay_type', $params['payment']); // 调用关闭接口 return $this->close_order($request); } catch (\Exception $e) { WebmanLog::error('统一支付关闭接口失败: ' . $e->getMessage()); return $this->error('关闭失败: ' . $e->getMessage()); } } /** * 统一退款接口 * @Apidoc\Title("统一退款接口") * @Apidoc\Url("/api/payment/unified/refund") * @Apidoc\Method("POST") * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") * @Apidoc\Param("amount", "float", "退款金额", true, "0.01") * @Apidoc\Param("refund_no", "string", "退款单号", false, "20260409001_refund") * @Apidoc\Param("reason", "string", "退款原因", false, "退款") * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") * @param Request $request * @return Response */ public function unified_refund(Request $request): Response { try { $params = $request->post(); // 验证必要参数 if (!isset($params['order_no']) || !isset($params['payment']) || !isset($params['amount'])) { return $this->error('缺少必要参数'); } // 设置pay_type参数 $params['pay_type'] = $params['payment']; $request->withAddedHeader('pay_type', $params['payment']); // 调用退款接口 return $this->refund($request); } catch (\Exception $e) { WebmanLog::error('统一退款接口失败: ' . $e->getMessage()); return $this->error('退款失败: ' . $e->getMessage()); } } }