From c0c04b2bd08833a7e9f01c189c3467f9ab338fe4 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 17 Jul 2026 13:56:58 +0800 Subject: [PATCH] feat(transfer): process ALL matching emails, not just current tcode --- unipay/providers/transfer.py | 95 +++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/unipay/providers/transfer.py b/unipay/providers/transfer.py index 4f9ebad..534ec0a 100644 --- a/unipay/providers/transfer.py +++ b/unipay/providers/transfer.py @@ -221,14 +221,15 @@ class TransferGateway(Gateway): await asyncio.sleep(30) async def check_transfer(self, tcode: str, env): - """扫描最近2天邮件,查找指定转账码并完成入账。 + """扫描最近2天邮件,对所有匹配的转账码完成入账。 返回: (title, message)""" from datetime import datetime, timedelta + from email.utils import parsedate_to_datetime cutoff = datetime.now() - timedelta(days=2) ec = EmailClient(self.pop3server, self.email, self.password) count, _ = ec.stat() - debug(f'check_transfer: tcode={tcode}, total {count} mails, cutoff={cutoff}') + debug('check_transfer: tcode=' + tcode + ' total=' + str(count) + ' mails, cutoff=' + str(cutoff)) db = DBPools() dbname = env.get_module_dbname('unipay') @@ -240,8 +241,8 @@ class TransferGateway(Gateway): tc = recs[0] return ('已入账', '转账码 ' + tcode + ' 已入账 ' + str(tc.amount) + ' 元') - log_lines = [] - matched = None + done = [] + failed = [] scanned = 0 for i in range(count, 0, -1): @@ -250,12 +251,10 @@ class TransferGateway(Gateway): scanned += 1 idx = str(i) - # 日期过滤 mail_date_str = getattr(mail, 'date', None) skip_date = False if mail_date_str: try: - from email.utils import parsedate_to_datetime mail_date = parsedate_to_datetime(mail_date_str) if mail_date < cutoff: skip_date = True @@ -284,52 +283,60 @@ class TransferGateway(Gateway): code = str(data.code) amount = data.amount - debug('mail[' + idx + '] code=' + code + ' amount=' + str(amount) + ' looking_for=' + tcode) + debug('mail[' + idx + '] code=' + code + ' amount=' + str(amount)) - if code == tcode: - matched = data - debug('mail[' + idx + '] *** MATCHED tcode=' + tcode + ' amount=' + str(amount) + ' ***') - break - debug('mail[' + idx + '] EXIT: code mismatch ' + code + ' != ' + tcode) + # 在transfercode表中查找 status=0 的记录并入账 + async with db.sqlorContext(dbname) as sor: + recs = await sor.R('transfercode', {'tcode': code, 'status': '0'}) + if len(recs) < 1: + debug('mail[' + idx + '] EXIT: code ' + code + ' not in transfercode or already processed') + continue + + tc = recs[0] + diff = abs(float(amount) - float(tc.amount)) + if diff > 0.01: + await sor.U('transfercode', {'id': tc.id, 'status': '9', + 'remark': '金额不匹配: 到账' + str(amount) + ' 期望' + str(tc.amount)}) + debug('mail[' + idx + '] amount mismatch: ' + str(amount) + ' != ' + str(tc.amount)) + failed.append(code + '(金额不匹配)') + continue + + biz_date = await env.get_business_date(sor) + await sor.U('transfercode', {'id': tc.id, 'status': '1'}) + + recs2 = await sor.R('payment_log', {'id': tc.id, 'payment_status': '0'}) + if len(recs2) > 0: + plog = recs2[0] + await env.recharge_accounting(sor, + plog.customerid, 'RECHARGE', plog.id, + biz_date, plog.amount_total, plog.pay_feerate) + await sor.U('payment_log', { + 'id': plog.id, + 'payment_status': '1', + 'payed_timestamp': timestampstr() + }) + + done.append(code + '(' + str(amount) + '元)') + debug('mail[' + idx + '] ACCOUNTING DONE: code=' + code + ' amount=' + str(amount)) except Exception as e: debug('mail[' + str(i) + '] ERROR: ' + str(e)) - debug('check_transfer: scanned ' + str(scanned) + ' mails, matched=' + str(matched is not None)) + debug('check_transfer: scanned=' + str(scanned) + ' done=' + str(done) + ' failed=' + str(failed)) - if matched is None: + # 构造返回消息 + msg_parts = [] + if done: + msg_parts.append('入账成功 ' + str(len(done)) + ' 笔: ' + ', '.join(done)) + if failed: + msg_parts.append('失败 ' + str(len(failed)) + ' 笔: ' + ', '.join(failed)) + + if tcode in [d.split('(')[0] for d in done]: + return ('充值成功', '\n'.join(msg_parts)) + if not done and not failed: return ('尚未到账', '扫描了 ' + str(scanned) + ' 封邮件,暂未收到转账码 ' + tcode + ' 的到账通知') - # 执行入账 - async with db.sqlorContext(dbname) as sor: - recs = await sor.R('transfercode', {'tcode': tcode, 'status': '0'}) - if len(recs) < 1: - return ('已处理', '转账码 ' + tcode + ' 已处理或不存在') - - tc = recs[0] - diff = abs(float(matched.amount) - float(tc.amount)) - if diff > 0.01: - await sor.U('transfercode', {'id': tc.id, 'status': '9', - 'remark': '金额不匹配: 到账' + str(matched.amount) + ' 期望' + str(tc.amount)}) - return ('金额不匹配', '到账金额 ' + str(matched.amount) + ' 与充值金额 ' + str(tc.amount) + ' 不一致') - - biz_date = await env.get_business_date(sor) - await sor.U('transfercode', {'id': tc.id, 'status': '1'}) - - recs2 = await sor.R('payment_log', {'id': tc.id, 'payment_status': '0'}) - if len(recs2) > 0: - plog = recs2[0] - await env.recharge_accounting(sor, - plog.customerid, 'RECHARGE', plog.id, - biz_date, plog.amount_total, plog.pay_feerate) - await sor.U('payment_log', { - 'id': plog.id, - 'payment_status': '1', - 'payed_timestamp': timestampstr() - }) - debug('check_transfer: ACCOUNTING DONE for ' + tcode + ' amount=' + str(matched.amount)) - - return ('充值成功', '转账码 ' + tcode + ' 已确认到账 ' + str(matched.amount) + ' 元') + return ('转账检查结果', '\n'.join(msg_parts) + '\n\n当前转账码 ' + tcode + ' 尚未到账') async def query(self, out_trade_no: str) -> Dict[str, Any]: pass