From 64a2a89929bd9b1f08be7c77dd4aa29919858203 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Thu, 18 Dec 2025 12:16:47 +0800 Subject: [PATCH] bugfix --- models/payment_log.xlsx | Bin 18725 -> 18783 bytes unipay/init.py | 41 +++++++++++++++---------------- unipay/paylog.py | 37 ++++++++++++++++++++++++++++ unipay/providers/alipay.py | 8 ++++-- wwwroot/notify/alipay/index.dspy | 3 ++- 5 files changed, 65 insertions(+), 24 deletions(-) diff --git a/models/payment_log.xlsx b/models/payment_log.xlsx index 97334b9a919745b0f4f2b7d042594da201799397..d559b3a2844ce2b623109f8bba81bf52e4c488ba 100644 GIT binary patch delta 4454 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 delta 4439 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 NWo`MhRO)|2{{t+8QStx) diff --git a/unipay/init.py b/unipay/init.py index cb2d351..d61e658 100644 --- a/unipay/init.py +++ b/unipay/init.py @@ -3,7 +3,7 @@ import os from appPublic.log import debug,exception from ahserver.serverenv import ServerEnv from .notify import get_provider, get_provider_channel -from .paylog import PaymentLog +from .paylog import PaymentLog, unipay_accounting from .payfee import get_pay_fee, sor_get_pay_fee, get_paychannels, get_pay_feerate # 从 env 或配置载入 provider conf(这里只示例) @@ -124,32 +124,31 @@ async def refund_payment(request, params_kw=None): raise e # 回调入口:你可把厂商回调用各自 endpoint 再转发到这里,或在厂商控制台按各自 URL 配置 -async def payment_notify(request, callback, params_kw=None): +async def payment_notify(request, params_kw=None): if params_kw is None: params_kw = request.params_kw - data = params_kw provider = params_kw.provider - headers = dict(request.headers) - body = await request.text() + if PROVIDERS[provider] is None: + e = Exception(f'{provider} cannot pay') + exception(f'{e}') + return try: - if PROVIDERS[provider] is None: - e = Exception(f'{provider} cannot pay') - exception(f'{e}') - raise e + headers = dict(request.headers) + body = await request.text() data = await PROVIDERS[provider].handle_notify(headers, body) - # 这里 data 应包含标准化字段:out_trade_no/status/attach 等 - # TODO: 业务幂等处理 - # 返回厂商要求的固定成功响应 - logid = data['out_trade_no'] - pl = PaymentLog(request._run_ns) - plog = await pl.payed_log(logid) - await callback(request, data) - if provider == "wechat": - return {"code":"SUCCESS", "message":"OK"} - else: - return "OK" except Exception as e: - return web.Response(status=500, text=str(e)) + e = Exception(f'{provider} cannot pay') + exception(f'{e}') + return + + logid = data.params.out_trade_no + pl = PaymentLog(request._run_ns) + plog = await pl.payed_log(logid) + await unipay_accounting(request, data) + if provider == "wechat": + return {"code":"SUCCESS", "message":"OK"} + else: + return "OK" # callback url= "/unipay/notify/{provider}" diff --git a/unipay/paylog.py b/unipay/paylog.py index fb9c1a1..fc8be4a 100644 --- a/unipay/paylog.py +++ b/unipay/paylog.py @@ -2,6 +2,7 @@ from sqlor.dbpools import DBPools from appPublic.timeUtils import timestampstr from appPublic.log import debug, exception from appPublic.dictObject import DictObject +from ahserver.serverenv import ServerEnv class PaymentLog: def __init__(self, env): @@ -68,4 +69,40 @@ class PaymentLog: return None return None +async def unipay_accounting(request, data): + logid = data.params.out_trade_no + trade_id = data.params.trade_no + env = request._run_ns + db = DBPools() + dbname = env.get_module_dbname('unipay') + async with db.sqlorContext(dbname) as sor: + recs = await sor.R('payment_log', {'id', logid}) + if len(recs) < 1: + e = Exception(f'{logid} not found {data=}') + exception(f'{e}') + raise e + r = recs[0] + if r.status == '2': + e = Exception(f'{logid} payment_log status({r.status}) is already accounted') + exception(f'{e}') + return True + if r.status != '1': + e = Exception(f'{logid} payment_log status({r.status}) not correct') + exception(f'{e}') + return False + await env.recharge_accounting(sor, + r.customerid, + 'RECHARGE', + r.id, + await env.get_business_date(sor), + r.amount_total, + r.pay_feerate + ) + await sor.U('payment_log', {'id': logid, + 'status': '2', + 'channel_trade_id': trade_id + }) + return True + exception(f'{db.e_except}') + return False diff --git a/unipay/providers/alipay.py b/unipay/providers/alipay.py index 838d0cb..3cee049 100644 --- a/unipay/providers/alipay.py +++ b/unipay/providers/alipay.py @@ -6,6 +6,7 @@ import hashlib import urllib.parse from typing import Any, Dict, Optional from appPublic.log import debug, exception +from appPublic.dictObject import DictObject import aiohttp from cryptography.hazmat.primitives import hashes @@ -205,13 +206,16 @@ class AlipayGateway(Gateway): unsigned_str = self._build_sign_content(params) if not sign: - return {"verified": False, "msg": "no sign"} + e = Exception(f'notify/alipay: verify failed') + exception(f'{e}') + raise e ok = self._verify(unsigned_str, sign) - return { + ret = { "verified": ok, "provider": "alipay", "data": params, } + return DictObject(**ret) diff --git a/wwwroot/notify/alipay/index.dspy b/wwwroot/notify/alipay/index.dspy index c61d682..ac1eb90 100644 --- a/wwwroot/notify/alipay/index.dspy +++ b/wwwroot/notify/alipay/index.dspy @@ -1,5 +1,6 @@ debug(f'/unipay/notify/alipay/index.dspy:{params_kw}') ns = params_kw.copy() ns.provider = 'alipay' -return await payment_notify(request, recharge_accounting, params_kw=ns) +return await payment_notify(request, params_kw=ns) +