from sqlor.dbpools import DBPools, get_sor_context from appPublic.timeUtils import timestampstr from appPublic.log import debug, exception from appPublic.dictObject import DictObject from ahserver.serverenv import ServerEnv RECHARGE="充值" REFUND ="退费" payment_names = [ RECHARGE, REFUND ] class PaymentLog: def __init__(self, env): self.db = DBPools() self.env = env async def new_log(self, userid, customerid, channel, amount, feerate, client_ip, currency='CNY'): dbname = self.env.get_module_dbname('unipay') async with self.db.sqlorContext(dbname) as sor: return await self.sor_new_log(sor, userid, customerid, channel, amount, feerate, client_ip, currency=currency) return None async def sor_new_log(self, sor, userid, customerid, channel, amount, feerate, client_ip, currency='CNY'): debug(f"{userid=}, {customerid=}, {amount=},{feerate=},{client_ip=}") ns = { "id": self.env.uuid(), "customerid": customerid, "channelid": channel, "payment_name": RECHARGE, "payer_client_ip": client_ip, "amount_total": amount, "pay_feerate": feerate, "pay_fee": feerate * amount, "currency": currency, "payment_status": '0', "init_timestamp": timestampstr(), "payed_timestamp": "2000-01-01 00:00:00.001", "cancel_timestamp": "2000-01-01 00:00:00.001", "userid": userid } await sor.C('payment_log', ns.copy()) return DictObject(**ns) async def cancel_log(self, logid): dbname = self.env.get_module_dbname('unipay') async with self.db.sqlorContext(dbname) as sor: ns = { "id": logid, "cancel_timestamp": timestampstr() } await sor.U('payment_log', ns) recs = await sor.R('payment_log', {"id": logid}) if len(recs) > 0: return recs[0] return None return None async def payed_log(self, logid): dbname = self.env.get_module_dbname('unipay') async with self.db.sqlorContext(dbname) as sor: ns = { "id": logid, "payment_status":'1', "payed_timestamp": timestampstr() } await sor.U('payment_log', ns) recs = await sor.R('payment_log', {"id": logid}) if len(recs) > 0: return recs[0] return None return None async def get_refundable_plog(request, id): db = DBPools() env = request._run_ns orgid = await get_userorgid() dbname = env.get_module_dbname('unipay') async with db.sqlorContext(dbname) as sor: recs = await sor.R('payment_log', {'id':id, 'customerid': orgid }) if len(recs) < 1: debug(f'id({id}) not exists in payment_log or not belong you') return None origin_plog = recs[0] if origin_plog.payment_status == '0': debug(f'id({id}) payment status 0') return None if origin_plog.origin_id: debug(f'id({id}) is a refund plog') return None recs = await sor.R('payment_log', {'origin_id': id}) amt = origin_plog.total_amount for l in recs: amt -= l.total_amount if amt <= 0: return None origin_plog.total_amount = amt return origin_plog exception(f'{db.e_except}') return None async def new_refund_log(request, amount, origin_id): env = ServerEnv() async with get_sor_context(env, 'unipay') as sor: recs = await sor.R('payment_log', {'id': origin_id}) if len(recs) < 1: raise Exception(f'{origin_id} not a payment_log') ol = recs[0] ns = { "id": env.uuid(), "customerid": ol.customerid, "channelid": ol.channel, "payment_name": REFUND, "payer_client_ip": ol.client_ip, "amount_total": amount, "pay_feerate": 0.0, "pay_fee": 0.0, "currency": ol.currency, "payment_status": '0', "init_timestamp": timestampstr(), "payed_timestamp": "2000-01-01 00:00:00.001", "cancel_timestamp": "2000-01-01 00:00:00.001", "userid": userid, "origin_id": origin_id } await sor.C('payment_log', ns.copy()) return DictObject(**ns) async def unipay_accounting(request, data): logid = data.out_trade_no trade_id = data.trade_no env = ServerEnv() db = DBPools() dbname = env.get_module_dbname('unipay') async with db.sqlorContext(dbname) as sor: recs = await sor.R('payment_log', {'id': logid, 'payment_status': '0'}) if len(recs) < 1: e = Exception(f'{logid} not found {data=} or accounted') exception(f'{e}') raise e r = recs[0] 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, 'payment_status': '1', 'payed_timestamp': timestampstr(), 'channel_trade_id': trade_id }) return True exception(f'{db.e_except}') return False