from alipay import AliPay from flask import current_app import os import logging logger = logging.getLogger(__name__) class AlipayService: def __init__(self): self.app_id = current_app.config.get('ALIPAY_APP_ID') self.private_key = current_app.config.get('ALIPAY_APP_PRIVATE_KEY') self.public_key = current_app.config.get('ALIPAY_PUBLIC_KEY') self.return_url = current_app.config.get('ALIPAY_RETURN_URL') self.notify_url = current_app.config.get('ALIPAY_NOTIFY_URL') self.debug = current_app.config.get('ALIPAY_DEBUG', True) def get_alipay_client(self): """获取支付宝客户端实例""" return AliPay( appid=self.app_id, app_notify_url=self.notify_url, app_private_key_string=self.private_key, alipay_public_key_string=self.public_key, sign_type="RSA2", debug=self.debug ) def create_order_url(self, out_trade_no, total_amount, subject): """生成支付链接 (电脑网站支付)""" alipay = self.get_alipay_client() order_string = alipay.api_alipay_trade_page_pay( out_trade_no=out_trade_no, total_amount=str(total_amount), subject=subject, return_url=self.return_url, notify_url=self.notify_url ) # 拼接支付网关 (使用最新的支付宝沙箱域名) gateway = "https://openapi-sandbox.dl.alipaydev.com/gateway.do" if self.debug else "https://openapi.alipay.com/gateway.do" return f"{gateway}?{order_string}" def verify_notify(self, data, signature): """验证通知签名""" try: if not signature or not data: logger.error("签名或数据为空") return False # 创建数据副本 verify_data = data.copy() # python-alipay-sdk 的 verify 方法会自动处理 sign 的移除 # 但为了保险,我们手动移除它,保留其他所有字段 verify_data.pop('sign', None) alipay = self.get_alipay_client() # 对于同步回调,sign_type 实际上是参与签名的(某些版本/接口) # 对于异步通知,sign_type 通常不参与签名 # alipay.verify 会根据情况处理 sign_type result = alipay.verify(verify_data, signature) if not result: logger.error("签名验证失败") logger.error(f"待验证数据: {verify_data}") logger.error(f"签名值: {signature}") logger.error(f"使用的公钥前50位: {self.public_key[:50]}...") # 检查公钥是否可能是应用公钥而非支付宝公钥 if self.private_key and self.public_key: logger.warning("提示:如果签名持续验证失败,请确认 ALIPAY_PUBLIC_KEY 是“支付宝公钥”而非“应用公钥”") else: logger.info("签名验证成功") return result except Exception as e: logger.error(f"验证签名时发生异常: {str(e)}", exc_info=True) return False