From e5c797f5ded07198b47cfa2edb9758c9c1a3ddba Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 10 Mar 2026 05:25:08 -0500 Subject: [PATCH] 's' --- .gitignore | 12 ++++++++ app/main.py | 7 ++--- app/payment/__pycache__/zpay.cpython-312.pyc | Bin 7149 -> 8698 bytes app/payment/zpay.py | 21 +++++++++++++ .../__pycache__/_digital_base.cpython-312.pyc | Bin 12804 -> 11689 bytes .../__pycache__/nomadvip.cpython-312.pyc | Bin 11145 -> 10072 bytes app/routers/_digital_base.py | 28 ++++-------------- app/routers/nomadvip.py | 25 +++------------- payment_notify.log | 11 +++++++ 9 files changed, 56 insertions(+), 48 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0d8313 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# 日志文件(不产生,避免 git 本地/线上不一致) +*.log +payment_notify.log +xorpay_notify.log + +# Python +__pycache__/ +*.py[cod] +venv/ +.venv/ +.env +.env.local diff --git a/app/main.py b/app/main.py index 67a49f8..3c79f1c 100755 --- a/app/main.py +++ b/app/main.py @@ -51,14 +51,11 @@ async def global_exception_handler(request: Request, exc: Exception): logging.error(f"未处理异常: {request.url.path} - {exc}", exc_info=True) return JSONResponse(status_code=500, content={"detail": str(exc)}) -# 日志 +# 日志(仅控制台,不写文件,避免 git 本地/线上不一致) logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(message)s", - handlers=[ - logging.FileHandler("payment_notify.log"), - logging.StreamHandler(), - ], + handlers=[logging.StreamHandler()], ) # 挂载路由 diff --git a/app/payment/__pycache__/zpay.cpython-312.pyc b/app/payment/__pycache__/zpay.cpython-312.pyc index 721fbd4a58afcb47275bb4c606f0deca1b4ae56e..d8c52579ca96fa2294b2efa836e8dbdea138de41 100644 GIT binary patch delta 1572 zcmZ`(Z%kWN6uVj4x(YF)`zd_jWv3SiB#8=j8l; z=iGBo&U=3pKdw~$7#|-8@CimI9rJ0^s!9mo4_$;W!;F~_G{H>t z&C!Dy+7Cy-$4r1`fglxmCnI`?)=&GXdyC{3jk~rNM;5Q zl;Yi)0Hp>PUiwt4jF`zYqm+%Z0$vti-eqd`Z5QkU-Lw^SL0l(QtRtimFG!%7!bPPJ zkf&I|2j2geA5qJ(`YlmyPV`>H{Pvx@JCoza`sy=Q*0reUv6K! z{%F4U!M7tuS4TU?cRJj8LEdr2A#iPZwphsK;__lnTf4`&bz}PBVEEDeP2USGg5dU) z=H>a^T#ql;=E~#Tj$C((yPUHLWgUpOBa_>~m-(coR(nsGPi5-x3QYpyczctpqs$Cr zJ6vU^HY9G&!J`J;=LJ-UF^I^XA+m<(Z46#A=O<6t`gD3{_O6*i0g*(nU{4Ihd=(-e z8d4BwB=8!6N&*~#cL;0lHfnZpx%c@Q~BtObLaMJnPSd~+ML7qy{Q5^KY- z0qwDtV_kSy95IRqk+t1OzUsB3o~GD1nmht0cs;%o_0?QYHR|Ye;2GsUD+o^DOph6_ z`Ze%dV)2~hJNZ}gDc^!-LHNG=e)obiQr#G7YK=6uMO5v*bd-{?g_~-_(5Zn_V|4h^ zn)+m~Wj!lVKfkYJ{aAX~G@1Q*_Eg>6 zh1KlWSJdYt%JWf*W_6pI)M4$NwA*Q8uZFo*{fn!bd~z+&pBPKOtq6CnBosyDMgQ&^ zK%)LH0NFxZ>t5v)oTyq+7yPU&*o)FQxEI`w14)L_bCCoSQQ@9b`YeRf?CmO_W_0Ts zZC@2(bdNn1cxa!TtjqxSb!Su-8CV1c%W<%n!CEBrVorQzIlWi{iC-?oxFk!m80e)m zNbq=)q0-J-eh7May|5>J{|7Oc=W%(qQ3!@>i=Za(&Dfu z3b2{0iJ9fhWlyB`(sK6?xw|Bc$ZrmjwJ str: return "success" + + def query_order_status(self, out_trade_no: str, timeout: int = 15) -> Dict[str, Any]: + """查询 ZPAY 订单支付状态(供 nomadvip/digital/cnomadcna/nomadlms 共用)""" + url = f"https://zpayz.cn/api.php?act=order&pid={self.pid}&key={self.key}&out_trade_no={out_trade_no}" + try: + resp = requests.get(url, timeout=timeout) + result = resp.json() if resp.ok else {} + except Exception as e: + return {"paid": False, "error": str(e)} + try: + code = result.get("code", 0) + code = int(code) if isinstance(code, (int, float)) or str(code).isdigit() else 0 + except (ValueError, TypeError): + code = 0 + if code != 1: + return {"paid": False, "msg": result.get("msg", "查询失败")} + try: + status = int(result.get("status", 0)) + except (ValueError, TypeError): + status = 0 + return {"paid": status == 1, "status": status} diff --git a/app/routers/__pycache__/_digital_base.cpython-312.pyc b/app/routers/__pycache__/_digital_base.cpython-312.pyc index 9df91901a5a7cf53418fd40064f3c71d822d46c4..25846bc60463260ba6270b184c84d8f1bf87e723 100644 GIT binary patch delta 2127 zcmZ8iT}%{L6rQ_3vkN;5ECLJ4&mszJ10q_gB8q5=)@Y$p+p=hvac97_JG<+hSrmeW z7Sdu{tMnEnF;-(V(TZBLnl??7+9r(;))Xsg%|z2DTWtD3qDgI^dd^+Z#!2>@?|%1Z z?)mPyXNQ%u9U0f%?sNg312Y%HUzUz$*42r>WUWI{aL!*d;2`uq`lW^qVs-VmjA0 zwY?bJx@8j^A|vGcq-1dr=LqQ{ihEc}daZ{^wgj6pZHC;fo3^-Y?4%j&TicN2;qney zSErpIL}YkE*O zDD~n{H>+_Mkvz89y_ootZ@aHJ8|Q(3zZE+R5sDD_K1z`J5by)E4gsSwMMdoihg8!N z4?%=9uPnC|`{pB*AuL2FXD9NC#KlOLvKx7oi!h>896Z*Q*yQ_9S%u9mgf$4OSebY8 zBBZ8kHx0!UumOPbMItz27XqJ(+w)s!h<)arpB?87)0xoKU{{Q4bUFLkyMTn)-`=K{ zH*ro7A!R=@JAi5Lio|4tu18@53SuY{R-=Y#+ZhA%diWSU9tj(?4adBMu#xS{FU&C0 z!+JQX8*((H(lmA{KYxH9=r$bk^n-YI6uticNn?|n%j4kDF9Xd?gPre@siG?MRUESw zfrtJYGM)_Fx;L=Nop=iwH>L+8qF%PSpbSoWcfnfHlDu3XSu-7=Gx$At1TaR}jl#JN zh7~w}`iWti<>xFZrn?QC#2=@&%30buy+>bWc9;MoUqTz`z%bhdTYdUBsGcp z4fo^u!mf;2_Vm5qdQ?yxIH&Z+F0Y^jq45^VU7Y?_{S-77&^JR8l;!16H`7 z3E(HD!w73?EMb^-mC_hZ%mvvOXl-iuO9@4(pa$4^gRJbCZbrRmG3>wSJ_>gfAn z6iq|m=bybhq0-)93@oQ`TVx}l^JFj`kyv+kINEJG;5?#0{5Cqs?+8Co?mtGTqjx@^ zZ;*oJr5gr5LX84Enid`n3J*mgr}EYu|A_NWX2E#nsZCRvwRf^h&a9hU-ZGWlIw`f@ z_7wc;sk-T@IMH^V9WlN@&57XWL5Lko@;&I z^-XSRzqLLv+1xQ%xNS<>emgUF#Ql#h3PPGN)U>Rrp3o&s^A{wql>K1I<@v*J>MGb5 ztwoU8!SXV)gC)x&zE@Cq55TmAqmY37GPqEDT&o%@@&|^oUxKm%*DFznUG=9?ya7CwwV)y!GR3My+flUfHs281Xo`G6g>onHRKQ98fSuUy-W#KF zxi=VPzf>$GN0RB4I|x~t)ck&H?jQ`HJS_B_b|T2^TvZ8#b-k*nA_^9!l^$9Zcf#PN2;nPonJ&u?jMs*C~Nx-SQ z%QNN(c03cOJ(V}-s~gBf^3Uq`Nyio#1%ESGdye4*8v^bKJ|ZE<6`G@;LJo`z|ve{oXV|h+{-bCo30IvsDK`fuK0Gr8SM;3Sb z@?nra$8-dPN-Pu%^4y>sL8O}yT3BIiWvA%?A2leIJWDV&cxw^b%P*Meguh1oztB@w z@r=UwC~o4cK=CvDnE!P)K#fmC)YbHJ(6EH)8`y=~d3ApXbvK2&dqUA&;kmm)#cx96 ZjHp{k_KC6?0pDM=rQTn)Kjdw`=6?|h0z&`* delta 3250 zcmZ`*Yj6|S6~4QxT}jr;k|oQw{FE(YEC(BidHR8|v7NXOFc^|rBnY8h8-ZTjU1Q_1 zQA(J^VUh{%dIAJe96|;Yptv>d@FUHT!Tf4xDi_*#wq(+2rw#mbfthBS{^&hdR?5)! z&g{46e)rthx#ymHF7G}rW&hf0%|fJ{{qyy3SLJwi0W&eabXk~<@P1xmBrYOEjecX) z(M|sD*4{vOSNo#0xuaJLFk{T;GY=Re zW00)J4Kx2>-)9`zWYM}o4fZR#ArVyfW4m_V@P_E5^uC}X<7};tA7YATF7c0fVIAS| zdTr9uZEpi{BS0%a$B)I=1h4^a)I#D?dl86D03Lv5?E|s&l?Ec}CiO@{4n%_q+z2fl zz*%B(HQaY3K$oxq=ruqKSXKriYI>R~V3@;tRw9UlQ6-?L7`s8$rrE3|Oo3KwUBQ&k z?6LmB)KWtDhuzRv3Qz{%0VoHk0DuYbS^(ITE=cm>a7fnKM2Ku~N1oT>g|ltFD6`rT0Pi+6+~?{>=$`fg?#zdk^UD1)!^egT-(VPrCtBspaV=scF^|PB)Wn4F<6!OZa8bc?E#l z<%b|qPoZ}8LzS8-3KF&25NsE_wOY5AxWRUJ8`C{A;TAcoKzNOOkN?0D;d*hQVT3ur zYI8ItQl!yRjFfy zl}uO7na-o!2&WzL4eel690n%J(e(2bn=M#Py;8=HcwB_<$Nvagf~YSiJcmTuC3J8`W@B zCM^SSikhX=o$U0n5OKqj&dV6b@mAdc0Re;mJ%sbc2MG*_1papZlLyx?{Pp6`9^CkZ zgoHOUjWnoah&ICdoX>;+3_gp^1auRY4-LqQs-)vVvIR)P0VN*O%^gD_IiZG$XpF@B z`@^w*-AFEDj0kuqjBpfyMuB1IW(+G*xIe7wSuY1819AtfUpMb0VV#OT6FvqVXaE?5 z4h9m(6Bw?p&d7L-{B!Bz6?-ELRl~~{b`u277L_oRKcb&d5)4av` zUh~_{r#pXVskrYbo7$9Gx9y&zJ0*6{+t+?(njXI8njN}zAl1Dm6*!pM6G>%9$G8QS zH?O+yEIQRR-ZaTg?YQS$G1fj`S$*k+3oo49d8&83cVhRnW2|$*WH7tto$gZ&$?=BM zeUrhv&g!xD`<|MqRX4mh2BwSec{a>?Z%Ma(U$*tkwe{R<+ci;tQk__z+WqQS`+|T( z`^l9j*3TE0PgPy7xm+{7>*m(GHJj(0TTPBFZT`*OjtWJwgoZBlkr60!O50TJq;zc_%udCI4Rv9L%wX z4j=QY)tg$-7xk^#?TzR*lPo0E?c$;i2fDr11agbHeH}MzTGC$6&DJs?*YSj(t>-~r z$AR3K(_!Q8h%AxsWHTV!fWPC&>8RrFcqzYx0l5lzyoyX7T8v^uL3M34ael8%g>(;|EhQuPBU*NZZ z6G;L=9@0e$ZkTRY20|fOQ3Bwo@t7j}%ov_C`YuHSQcw+I$UpFIAPvt^f-VxATo>qw z61I_w0@p3$)2|b!e#5(wximBD-N!JiXO=GUaYgiY(A}LTx_BQzh`_LWSleEat0k-X zW{5rmxdc3r%6T4s`p!4kK2OJh1`mE8x|{+yLqO-F@tAyMn6HH>)rmjQzNoH9o`dG^ z4TV31>i@G7{t2{#b?|urFbbX|uxJN-0f>(PzBfbqd8U{AV`zA0^Ss9cyYKfkF`vy` z^u5I-A(W7q2f4>(=wJYV6_H6G52YiJoR7pw5)xkn=0^Y%02cwS06++(M&7$OZRU-* zfKa+Cgyo3IwQorPCpr2|@mYPiS+?s*_KJRUTh~g98zH1O6r9;BqK1F}bb`^LLhG43ABSvHcNQwOFmt x_!ngR8m*o~tG_`j=g`WpP|a6p>DQ=bL1^O`$E0@w!GF3Z{lD4!gx2YF{{}B{8qfd$ diff --git a/app/routers/__pycache__/nomadvip.cpython-312.pyc b/app/routers/__pycache__/nomadvip.cpython-312.pyc index fa3d5389df89d3bab747ee3a90a17b3c00755d45..808d15ecdc32bc67c006899e8277cad5635e2d32 100644 GIT binary patch delta 756 zcmYjPO=uHA6rS15W|MY*l1;Y8*dHsEWm~KVD?+V8{X^6Sn_|TUDbm?UOV;RQL$##P z7SR?d8s?xNBCQ9NrdG&BPkI!*q_$uQ-qnLpjHvbGOxuLc;hS&X_q{joF*AK}HKu%& zCg|rB#vz2!wh2q3JLK4@jEhI;nRGN7hfM*Wp}eHs$k_PS zKnf!p52W2IZYGVpsel~@n1adSF~;K*((EC~iKdppNlMsk>2nmmfxqM6mlr7YpXK%? zx&1}mZ8RqI|kY%tjQ4eFRZ_H9&f-*&bFHrdB>vB>kleCZGXY|NEO1ez$ jweBXbT4(A6sJ%Y$6VR>918&b6N7-6{^tN?)=BV>O=NPhN delta 1793 zcmZuwTWl0n7(QoiJKNpa+1s?;UZBvjWwBD^CXlvJTP_K)2n3K7LpFP+FiZEs*(s%) zU5dn*wt}UOu_R)sL_(o8V9G-iec)0Oqc7XE#-9B3IO@OzjsCJF6(y`!^{2EEh}bpJQCWZs3Yiz%0W5m3_2ko zoTbebbqC#fEVg-~-k>+C1QiGvpbE%}3J$;#+b~1p0uU@DtXq{;=ZO6oSELq3UNU}@ zJPV{@IIBYr)z``(`E71^mH@#LQ)T#|-RDCt)zZo#_Z&qjQLLqLj2|RxG4iVAB!n_Y zFwYc7g=V5>K`+26IDhnO=oO)N^hax}1nWjO6-}@Wy`=Q50D=L4p#HQN06qeU(VsSz z-?3@JeJo_473jPviwFjs?_Gkp5#H=J!=#yrXbPUM*hzSsaf2->h(QfxSSv`g29%V| z1?buJWq4gNkED6M(8PMNiCP&mQPQV-Og7CALt{N(V*KqCmtdP-G}keUV-VRge40o0 zLoWk>6=a!BKVL!)GbEDFGCKij@cH?CiA~PuC7KtT5z+#QwHN@hy6Cw|pxe#7RRAx` zaJp>nBTo9?x`Z@GE|D=~keRZ~X@WC(B%Wo$%wB-38SWUj^Xb|FJHSzPFX(_IQ<)iA zpJ$?0hGgYQOw#9GaPriD)inV8uUsJVm?{5^?SKJy`!eH-pcp@u&U-CYeNfjE$#u20 zsYIwVwKN>74JEXtiGzvtp|D;b$4JHggof%9fy#*KNhdQN8elk z&RBFUsuGXkDXZ?<6kuk!L`XxpgLqqJxNw|=p#yi1J`uc+n09)dzn;5r_saQSKK=ae zwF?0Tzhydc9YH3M$4*QWVH+_sB32K#t4TeH8!5Mja$F=CkIh&&9u2FBE?tYqaFqHm zHLp@ZftJ)_Nj($`t9S!tr5z z5K}Sx;a${Q5{pMe=!llsPp;AEEt9+6CG-)hT}xWN3mzQ@IRQv3uWh-yZfw{1n(Irx zYRSHFAR9cGEzpiPrbD}AY+)EWQaf=Rimo4ZryAJj*}*H#Vfn<`uPW%zp47h2bM4cjIfwoNr`Kbbte z@?>Xr&+FYBsX(-MA3o7JU0gocdNJ}z9?z2` z9ibE6jVv5k)#L*=q((=x0&bR;5a#CU(&id)%Ow(g%V%wNv$qy5YL?h>3v4EvagisE zafzqc%~Gtmngi^(pP_g$q&Sf0Yg`)vJ6@mX*F%DD1Hz9!Df^S>@jIk~_bXp<^pO?! zQ`1X~(Ng&We~y6nMvqng0bzx4WYG==zir%Jw5#bus@hFWnwtEa(lNwzEHFKF2|OSc z APIRouter: async def zpay_order_status(out_trade_no: str): """查询 ZPAY 订单支付状态""" provider = get_payment_provider() - if provider.name != "zpay": + if not isinstance(provider, ZPayProvider): raise HTTPException(status_code=400, detail="仅 zpay 渠道支持") - url = f"https://zpayz.cn/api.php?act=order&pid={ZPAY_PID}&key={ZPAY_KEY}&out_trade_no={out_trade_no}" - try: - resp = requests.get(url, timeout=10) - result = resp.json() if resp.ok else {} - except Exception as e: - logging.warning(f"ZPAY 订单查询失败: {e}") - return {"paid": False, "error": str(e)} - try: - code = result.get("code", 0) - code = int(code) if isinstance(code, (int, float)) or str(code).isdigit() else 0 - except (ValueError, TypeError): - code = 0 - if code != 1: - return {"paid": False, "msg": result.get("msg", "查询失败")} - try: - status = int(result.get("status", 0)) - except (ValueError, TypeError): - status = 0 - return {"paid": status == 1, "status": status} + result = provider.query_order_status(out_trade_no, timeout=15) + if result.get("error"): + logging.warning(f"{project_name} ZPAY 订单查询失败: {result.get('error')}") + return result @router.post("/xorpay_notify") async def xorpay_notify(request: Request): diff --git a/app/routers/nomadvip.py b/app/routers/nomadvip.py index c836438..4df916f 100644 --- a/app/routers/nomadvip.py +++ b/app/routers/nomadvip.py @@ -7,8 +7,6 @@ import time from fastapi import APIRouter, Request, HTTPException from fastapi.responses import RedirectResponse, JSONResponse -import requests - from ..config import BASE_URL, ZPAY_PID, ZPAY_KEY from ..payment.zpay import ZPayProvider from ..services import handle_payment_success, processed_orders @@ -118,25 +116,10 @@ async def nomadvip_payh5_redirect(item: dict, request: Request): @router.get("/zpay_order_status") async def nomadvip_zpay_order_status(out_trade_no: str): """nomadvip 专用:查询 ZPAY 订单支付状态""" - url = f"https://zpayz.cn/api.php?act=order&pid={ZPAY_PID}&key={ZPAY_KEY}&out_trade_no={out_trade_no}" - try: - resp = requests.get(url, timeout=10) - result = resp.json() if resp.ok else {} - except Exception as e: - logging.warning(f"nomadvip ZPAY 订单查询失败: {e}") - return {"paid": False, "error": str(e)} - try: - code = result.get("code", 0) - code = int(code) if isinstance(code, (int, float)) or str(code).isdigit() else 0 - except (ValueError, TypeError): - code = 0 - if code != 1: - return {"paid": False, "msg": result.get("msg", "查询失败")} - try: - status = int(result.get("status", 0)) - except (ValueError, TypeError): - status = 0 - return {"paid": status == 1, "status": status} + result = _nomadvip_zpay.query_order_status(out_trade_no, timeout=15) + if result.get("error"): + logging.warning(f"nomadvip ZPAY 订单查询失败: {result.get('error')}") + return result @router.get("/zpay_notify") diff --git a/payment_notify.log b/payment_notify.log index 048bd20..0e8a393 100644 --- a/payment_notify.log +++ b/payment_notify.log @@ -4,3 +4,14 @@ 2026-03-08 06:31:18,405 - ZPAY ѯʧ: HTTPSConnectionPool(host='zpayz.cn', port=443): Read timed out. (read timeout=10) 2026-03-08 06:39:41,805 - ZPAY ѯʧ: HTTPSConnectionPool(host='zpayz.cn', port=443): Read timed out. (read timeout=10) 2026-03-08 06:40:22,362 - ZPAY ѯʧ: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +2026-03-10 05:01:08,564 - nomadvip payh5/redirect : item={'user_id': 'user1773135567826', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'pc', 'client_ip': '192.168.41.222'} +2026-03-10 05:02:23,523 - nomadvip ZPAY ѯʧ: HTTPSConnectionPool(host='zpayz.cn', port=443): Read timed out. (read timeout=10) +2026-03-10 05:02:46,901 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:03:26,325 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:04:54,606 - nomadvip ZPAY ѯʧ: ('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)) +2026-03-10 05:06:39,639 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:07:03,035 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:07:37,981 - nomadvip ZPAY ѯʧ: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +2026-03-10 05:08:31,484 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:15:55,977 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:22:16,735 - nomadvip payh5/redirect : item={'user_id': 'user1773131395284', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://localhost:3000/paid', 'device': 'pc', 'client_ip': '127.0.0.1'}