This commit is contained in:
yumoqing 2025-12-16 18:29:59 +08:00
parent 08f1939601
commit 0b8d9f4bd6
5 changed files with 144 additions and 2 deletions

15
json/transfercode.json Normal file
View File

@ -0,0 +1,15 @@
{
"tblname": "transfercode",
"title": "转账码",
"params": {
"sortby": "id",
"browserfields": {
"exclouded": ["id"],
"alters": {
}
},
"editexclouded": [
"id"
]
}
}

BIN
models/transfercode.xlsx Normal file

Binary file not shown.

View File

@ -8,6 +8,12 @@ from .payfee import get_pay_fee, sor_get_pay_fee, get_paychannels, get_pay_feera
# 从 env 或配置载入 provider conf这里只示例 # 从 env 或配置载入 provider conf这里只示例
CONF = { CONF = {
"transfer":{
"pop3server": os.getenv("POP3SERVER", ""),
"mail": os.getenv("MAIL", ""),
"password": os.getenv("PASSWORD", "")
"from_mail": os.getenv("FROM_MAIL", "")
}
"wechat": { "wechat": {
"mchid": os.getenv("WXP_MCHID",""), "mchid": os.getenv("WXP_MCHID",""),
"appid": os.getenv("WXP_APPID", ""), "appid": os.getenv("WXP_APPID", ""),
@ -132,6 +138,7 @@ async def payment_notify(request, callback, params_kw=None):
# callback url= "/unipay/notify/{provider}" # callback url= "/unipay/notify/{provider}"
def load_unipay(): def load_unipay():
PROVIDERS["transfer"] = get_provider("transfer", CONF["transfer"]),
PROVIDERS["wechat"] = get_provider("wechat", CONF["wechat"]), PROVIDERS["wechat"] = get_provider("wechat", CONF["wechat"]),
PROVIDERS["paypal"] = get_provider("paypal", CONF["paypal"]), PROVIDERS["paypal"] = get_provider("paypal", CONF["paypal"]),
PROVIDERS["alipay"] = get_provider("alipay", CONF["alipay"]), PROVIDERS["alipay"] = get_provider("alipay", CONF["alipay"]),

View File

@ -1,5 +1,7 @@
# unipay/notify.py # unipay/notify.py
from typing import Dict from typing import Dict
from appPublic.log import exception
from .providers.transfer import TransferGateway
from .providers.wechat import WechatGateway from .providers.wechat import WechatGateway
from .providers.paypal import PaypalGateway from .providers.paypal import PaypalGateway
from .providers.alipay import AlipayGateway from .providers.alipay import AlipayGateway
@ -12,12 +14,15 @@ def get_provider_channel(name:str):
"wechat":"0", "wechat":"0",
"paypal":"1", "paypal":"1",
"alipay":"2", "alipay":"2",
"stripe":"3" "stripe":"3",
"transfer":"4"
} }
return channels.get(name, '9') return channels.get(name, '9')
def get_provider(name: str, conf: Dict): def get_provider(name: str, conf: Dict):
try: try:
if name == "transfer":
return TransferGateway(**conf)
if name == "wechat": if name == "wechat":
return WechatGateway(**conf) return WechatGateway(**conf)
if name == "paypal": if name == "paypal":
@ -26,7 +31,8 @@ def get_provider(name: str, conf: Dict):
return AlipayGateway(**conf) return AlipayGateway(**conf)
if name == "stripe": if name == "stripe":
return StripeGateway(**conf) return StripeGateway(**conf)
except: except Exception as e:
exception(f'get_proveder() error {name=}, {conf=}, {e}')
return None return None
raise ValueError("unknown provider") raise ValueError("unknown provider")

View File

@ -0,0 +1,114 @@
import re
from random import randint
import poplib
from appPublic.log import debug
from appPublic.dictObject import DictObject
from appPublic.timeUtils import curDateString, timestampstr
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
from ..core import Gateway
def guess_charset(msg):
charset = msg.get_charset()
if charset is None:
content_type = msg.get('Content-Type', '').lower()
pos = content_type.find('charset=')
if pos >= 0:
charset = content_type[pos + 8:].strip()
return charset
class EmailClient:
def __init__(self, pop3_server, emailaddress, password):
self.client = poplib.POP3(pop3_server)
self.client.user(emailaddress)
self.client.pass_(password)
def stat(self):
return self.client.stat()
def mail_list(self):
resp, mails, octets = self.client.list()
def get_mail(self, index):
resp, lines, octets = self.client.retr(index)
debug(f'{resp=}, {octets=}')
msg_content = b'\r\n'.join(lines).decode('utf-8')
msg = Parser().parsestr(msg_content)
mail = {
"mailfrom": msg.get('From'),
"mailto": msg.get('To'),
"subject": msg.get('Subject'),
"body": self.get_body(msg)
}
return DictObject(**mail)
def get_body(self, msg):
if msg.is_multipart():
parts = msg.get_payload()
content = ''
for part in parts:
content += self.get_body(part)
return content
else:
content_type = msg.get_content_type()
content = msg.get_payload(decode=True)
charset = guess_charset(msg)
if charset:
content = content.decode(charset)
else:
content = content.decode('utf-8')
return content
class TransferPay(Gateway):
def __init__(self, from_mail="", pop3server="", email="", password=""):
self.from_email = from_email
self.pop3server = pop3server
self.email = email
self.password = password
self.running = False
async def create_payment(self, payload: Dict[str, Any]) -> str:
"""
返回一个可以在 H5 里直接重定向的支付宝支付 URL
"""
ns = {
"id": payload["out_trade_no"],
"customerid": payload['customerid'],
"amount": payload["amount"],
"tcode": self.gen_mailcode(),
"curdate": curDateString(),
"curtime": timestampstr(),
"status": '0'
}
return
def get_transfer_data(self, mail):
assert mail.mailfrom == '95555@message.cmbchina.com'
assert mail.mailto == self.email
assert mail.body.startswith('动账业务通知')
ns = DictObject()
match = re.search(r'交易金额\s*[:]?\s*(\d+(?:\.\d+)?)', mail.body)
if match:
ns.amount = float(match.group(1))
p = r'摘要[:]\s*(\d{7})(?!\d)'
match = re.search(r'摘要[:]\s*(\d{7})(?!\d)', mail.body)
if match:
ns.code = match.group(1)
assert ns.amount
assert ns.code
return ns
def gen_mailcode(self):
c = '1'
for range(6):
c += randint(0,9)
return c
def run(self):
self.running = True
while self.running:
ec = EmailClient(self.pop3server, self.email, self.password)
mail = ec.get_mail(1)