From baffcaa04362a7bf50a68f4d8d5583ff258ffd9a Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 17 Jul 2026 13:53:31 +0800 Subject: [PATCH] feat(transfer): 2-day email filter, trace each mail exit point with full debug --- unipay/providers/transfer.py | 75 +++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/unipay/providers/transfer.py b/unipay/providers/transfer.py index 1237167..4f9ebad 100644 --- a/unipay/providers/transfer.py +++ b/unipay/providers/transfer.py @@ -45,6 +45,7 @@ class EmailClient: "mailfrom": msg.get('From'), "mailto": msg.get('To'), "subject": msg.get('Subject'), + "date": msg.get('Date'), "body": self.get_body(msg) } return DictObject(**mail) @@ -220,11 +221,14 @@ class TransferGateway(Gateway): await asyncio.sleep(30) async def check_transfer(self, tcode: str, env): - """手动检查指定转账码的邮件并完成入账。 + """扫描最近2天邮件,查找指定转账码并完成入账。 返回: (title, message)""" + from datetime import datetime, timedelta + cutoff = datetime.now() - timedelta(days=2) + ec = EmailClient(self.pop3server, self.email, self.password) count, _ = ec.stat() - debug(f'check_transfer: tcode={tcode}, got {count} mails') + debug(f'check_transfer: tcode={tcode}, total {count} mails, cutoff={cutoff}') db = DBPools() dbname = env.get_module_dbname('unipay') @@ -234,48 +238,80 @@ class TransferGateway(Gateway): recs = await sor.R('transfercode', {'tcode': tcode}) if recs and recs[0].status == '1': tc = recs[0] - return ('已入账', f'转账码 {tcode} 已入账 {tc.amount} 元') + return ('已入账', '转账码 ' + tcode + ' 已入账 ' + str(tc.amount) + ' 元') - # 扫描邮件 + log_lines = [] matched = None + scanned = 0 + for i in range(count, 0, -1): try: mail = ec.get_mail(i) - debug(f'check_transfer: mail[{i}] from={mail.mailfrom} body_head={mail.body[:20]}') - if mail.mailfrom != '95555@message.cmbchina.com': - debug(f'check_transfer: mail[{i}] skipped, wrong sender') + 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 + except: + pass + if skip_date: + debug('mail[' + idx + '] date=' + str(mail_date_str) + ' older than 2 days, skip') continue - if not mail.body.startswith('动账业务通知'): - debug(f'check_transfer: mail[{i}] skipped, not 动账业务通知') + + from_addr = getattr(mail, 'mailfrom', '?') + subject = getattr(mail, 'subject', '?') + body_head = (getattr(mail, 'body', '') or '')[:50].replace('\n', ' ') + debug('mail[' + idx + '] date=' + str(mail_date_str) + ' from=' + str(from_addr) + ' subj=' + str(subject) + ' body=' + body_head) + + if from_addr != '95555@message.cmbchina.com': + debug('mail[' + idx + '] EXIT: wrong sender') continue + if not (getattr(mail, 'body', '') or '').startswith('动账业务通知'): + debug('mail[' + idx + '] EXIT: not 动账业务通知') + continue + data = self.get_transfer_data(mail) if data is None: - debug(f'check_transfer: mail[{i}] get_transfer_data returned None') + debug('mail[' + idx + '] EXIT: get_transfer_data returned None') continue - debug(f'check_transfer: mail[{i}] code={data.code} amount={data.amount}, looking for {tcode}') - if str(data.code) == tcode: + + code = str(data.code) + amount = data.amount + debug('mail[' + idx + '] code=' + code + ' amount=' + str(amount) + ' looking_for=' + tcode) + + if code == tcode: matched = data - debug(f'check_transfer: mail[{i}] MATCHED!') + debug('mail[' + idx + '] *** MATCHED tcode=' + tcode + ' amount=' + str(amount) + ' ***') break - debug(f'check_transfer: mail[{i}] code mismatch: {data.code} != {tcode}') + debug('mail[' + idx + '] EXIT: code mismatch ' + code + ' != ' + tcode) + except Exception as e: - debug(f'check_transfer: skip mail {i}: {e}') + debug('mail[' + str(i) + '] ERROR: ' + str(e)) + + debug('check_transfer: scanned ' + str(scanned) + ' mails, matched=' + str(matched is not None)) if matched is None: - return ('尚未到账', f'暂未收到转账码 {tcode} 的到账通知') + 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 ('已处理', f'转账码 {tcode} 已处理或不存在') + 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 ('金额不匹配', f'到账金额 {matched.amount} 与充值金额 {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'}) @@ -291,8 +327,9 @@ class TransferGateway(Gateway): 'payment_status': '1', 'payed_timestamp': timestampstr() }) + debug('check_transfer: ACCOUNTING DONE for ' + tcode + ' amount=' + str(matched.amount)) - return ('充值成功', f'转账码 {tcode} 已确认到账 {matched.amount} 元') + return ('充值成功', '转账码 ' + tcode + ' 已确认到账 ' + str(matched.amount) + ' 元') async def query(self, out_trade_no: str) -> Dict[str, Any]: pass