""" 支付宝 OpenAPI 封装(从 payment 迁入 app) """ from app.core.logging import get_logger from typing import Dict, Optional from app.features.payment.payment_config import AlipayConfig from app.features.payment.schemas import NotifyResult, PaymentResult, PaymentStatus from app.features.payment.payment_exceptions import ( PaymentConfigError, PaymentCreateError, PaymentNotifyError, PaymentQueryError, ) logger = get_logger(__name__) class AlipayClient: def __init__(self, config: AlipayConfig): self._config = config self._client = None def _ensure_client(self): if not self._config.is_configured: raise PaymentConfigError("支付宝配置不完整,请检查环境变量") if self._client is None: try: from alipay import AliPay self._client = AliPay( appid=self._config.app_id, app_notify_url=self._config.notify_url, app_private_key_string=self._config.private_key, alipay_public_key_string=self._config.alipay_public_key, sign_type=self._config.sign_type, debug=False, ) logger.info("支付宝客户端初始化成功") except Exception as e: raise PaymentConfigError(f"支付宝客户端初始化失败: {e}") def create_app_order( self, out_trade_no: str, total_amount: int, subject: str, ) -> PaymentResult: self._ensure_client() try: amount_yuan = f"{total_amount / 100:.2f}" order_string = self._client.api_alipay_trade_app_pay( out_trade_no=out_trade_no, total_amount=amount_yuan, subject=subject, notify_url=self._config.notify_url, ) if order_string: logger.info("支付宝订单创建成功: %s", out_trade_no) return PaymentResult( success=True, payment_method="alipay", out_trade_no=out_trade_no, alipay_order_string=order_string, ) raise PaymentCreateError("支付宝下单失败: 返回空订单字符串") except PaymentCreateError: raise except Exception as e: logger.error("支付宝订单创建异常: %s", e) raise PaymentCreateError(f"支付宝下单异常: {e}") def verify_notify(self, params: Dict[str, str]) -> NotifyResult: self._ensure_client() try: sign = params.pop("sign", None) params.pop("sign_type", None) if not sign: raise PaymentNotifyError("支付宝回调缺少签名参数") success = self._client.verify(params, sign) if success: trade_status = params.get("trade_status", "") out_trade_no = params.get("out_trade_no", "") trade_no = params.get("trade_no", "") total_amount_str = params.get("total_amount", "0") total_amount = int(float(total_amount_str) * 100) return NotifyResult( success=True, out_trade_no=out_trade_no, trade_no=trade_no, total_amount=total_amount, trade_status=trade_status, ) raise PaymentNotifyError("支付宝回调验签失败") except PaymentNotifyError: raise except Exception as e: logger.error("支付宝回调处理异常: %s", e) raise PaymentNotifyError(f"支付宝回调处理失败: {e}") def query_order(self, out_trade_no: str) -> PaymentStatus: self._ensure_client() try: result = self._client.api_alipay_trade_query(out_trade_no=out_trade_no) if result and result.get("code") == "10000": trade_status = result.get("trade_status", "") trade_no = result.get("trade_no", "") total_amount_str = result.get("total_amount", "0") total_amount = int(float(total_amount_str) * 100) unified_status = self._map_trade_status(trade_status) return PaymentStatus( success=True, out_trade_no=out_trade_no, trade_no=trade_no, trade_status=unified_status, total_amount=total_amount, ) error_msg = ( result.get("sub_msg", result.get("msg", "未知错误")) if result else "空结果" ) raise PaymentQueryError(f"查询支付宝订单失败: {error_msg}") except PaymentQueryError: raise except Exception as e: logger.error("查询支付宝订单异常: %s", e) raise PaymentQueryError(f"查询支付宝订单异常: {e}") def close_order(self, out_trade_no: str) -> bool: self._ensure_client() try: result = self._client.api_alipay_trade_close(out_trade_no=out_trade_no) if result and result.get("code") == "10000": logger.info("支付宝订单已关闭: %s", out_trade_no) return True return False except Exception as e: logger.error("关闭支付宝订单异常: %s", e) return False @staticmethod def _map_trade_status(alipay_status: str) -> str: status_map = { "WAIT_BUYER_PAY": "NOTPAY", "TRADE_CLOSED": "CLOSED", "TRADE_SUCCESS": "SUCCESS", "TRADE_FINISHED": "SUCCESS", } return status_map.get(alipay_status, alipay_status)