unipay/unipay/init.py
2025-12-17 14:43:50 +08:00

174 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# init.py
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 .payfee import get_pay_fee, sor_get_pay_fee, get_paychannels, get_pay_feerate
# 从 env 或配置载入 provider conf这里只示例
CONF = {
"transfer":{
"pop3server": os.getenv("POP3SERVER", ""),
"mail": os.getenv("MAIL", ""),
"password": os.getenv("PASSWORD", ""),
"from_mail": os.getenv("FROM_MAIL", ""),
"account_no": os.getenv("ACCOUNT_NO", "")
},
"wechat": {
"mchid": os.getenv("WXP_MCHID",""),
"appid": os.getenv("WXP_APPID", ""),
"cert_serial_no": os.getenv("WXP_SERIAL",""),
"private_key_pem": open(os.getenv("WXP_PRIVKEY","./merchant_private_key.pem"),"rb").read(),
"api_v3_key": os.getenv("WXP_API_V3_KEY","")
},
"paypal": {
"client_id": os.getenv("PP_ID",""),
"client_secret": os.getenv("PP_SECRET",""),
"sandbox": True
},
"alipay": {
"app_id": os.getenv("ALIPAY_APPID",""),
"app_private_key_pem": open(os.getenv("ALIPAY_PRIV","./alipay_priv.pem"),"rb").read(),
"alipay_public_key_pem": open(os.getenv("ALIPAY_PUB",""),"rb").read()
},
"stripe": {
"api_key": os.getenv("STRIPE_KEY","")
}
}
PROVIDERS = {}
# 下单接口(统一)
async def create_payment(request, params_kw=None):
env = request._run_ns
if params_kw is None:
params_kw = request.params_kw
data = params_kw
data.request = request
provider = data.get("provider")
if provider not in PROVIDERS:
debug(f'{provider=} is not a valid payment channel')
return {"error":"unknown provider"}
try:
if PROVIDERS[provider] is None:
e = Exception(f'{provider} cannot pay')
exception(f'{e}')
raise e
notify_url = env.entire_url(f'providers/{provider}')
pl = PaymentLog(request._run_ns)
fee = await get_pay_fee(provider, data.amount)
channel = get_provider_channel(provider)
userid = await env.get_user()
orgid = await env.get_userorgid()
client_ip = request['client_ip']
# userid, customerid, channel, payment_name, amount, client_ip, currency='CNY'
payment_name = data.payment_name or "充值",
amount = data.amount
currency = data.currency
plog = await pl.new_log(userid, orgid, provider,
payment_name, amount,
fee, client_ip, currency=currency)
if plog:
data.out_trade_no = plog.id
data.customerid = orgid
data.userid = userid
data.payment_name = payment_name
data.notify_url = notify_url
data.client_ip = client_ip
res = await PROVIDERS[provider].create_payment(data)
debug(f'{provider=} instance return {res}')
return res
raise Exception('write payment_log error')
except Exception as e:
exception(f'create_payment():{params_kw=}, {e}')
raise e
# 查询
async def query_payment(request, params_kw=None):
if params_kw is None:
params_kw = request.params_kw
data = params_kw
provider = data.get("provider")
if provider not in PROVIDERS:
return {"error":"unknown provider"}
try:
if PROVIDERS[provider] is None:
e = Exception(f'{provider} cannot pay')
exception(f'{e}')
raise e
res = await PROVIDERS[provider].query(data)
return res
except Exception as e:
exception(f'query_payment():{params_kw}, {e}')
raise e
# 退款
async def refund_payment(request, params_kw=None):
if params_kw is None:
params_kw = request.params_kw
data = params_kw
provider = data.get("provider")
if provider not in PROVIDERS:
return {"error":"unknown provider"}
try:
if PROVIDERS[provider] is None:
e = Exception(f'{provider} cannot pay')
exception(f'{e}')
raise e
res = await PROVIDERS[provider].refund(data)
return res
except Exception as e:
exception(f'query_payment():{params_kw}, {e}')
raise e
# 回调入口:你可把厂商回调用各自 endpoint 再转发到这里,或在厂商控制台按各自 URL 配置
async def payment_notify(request, callback, 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()
try:
if PROVIDERS[provider] is None:
e = Exception(f'{provider} cannot pay')
exception(f'{e}')
raise e
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))
# callback url= "/unipay/notify/{provider}"
def load_unipay():
PROVIDERS["transfer"] = get_provider("transfer", CONF["transfer"])
PROVIDERS["wechat"] = get_provider("wechat", CONF["wechat"])
PROVIDERS["paypal"] = get_provider("paypal", CONF["paypal"])
PROVIDERS["alipay"] = get_provider("alipay", CONF["alipay"])
PROVIDERS["stripe"] = get_provider("stripe", CONF["stripe"])
env = ServerEnv()
env.get_paychannels = get_paychannels
env.get_pay_feerate = get_pay_feerate
env.payment_notify = payment_notify
env.create_payment = create_payment
env.query_payment = query_payment
env.refund_payment = refund_payment
env.get_pay_fee = get_pay_fee
env.sor_get_pay_fee = sor_get_pay_fee
env.PaymentLog = PaymentLog