From 0b8d9f4bd60f16a01cb5b74251ff45fcc2b7e09a Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 16 Dec 2025 18:29:59 +0800 Subject: [PATCH] bugfix --- json/transfercode.json | 15 +++++ models/transfercode.xlsx | Bin 0 -> 18342 bytes unipay/init.py | 7 +++ unipay/notify.py | 10 ++- unipay/providers/transfer.py | 114 +++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 json/transfercode.json create mode 100644 models/transfercode.xlsx create mode 100644 unipay/providers/transfer.py diff --git a/json/transfercode.json b/json/transfercode.json new file mode 100644 index 0000000..b08e411 --- /dev/null +++ b/json/transfercode.json @@ -0,0 +1,15 @@ +{ + "tblname": "transfercode", + "title": "转账码", + "params": { + "sortby": "id", + "browserfields": { + "exclouded": ["id"], + "alters": { + } + }, + "editexclouded": [ + "id" + ] + } +} diff --git a/models/transfercode.xlsx b/models/transfercode.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..15201c54aff27afb5ea16174059ac44a24088f98 GIT binary patch literal 18342 zcmeHvWmH_*wl2Zl9Rh(saCZsr?(XjH?iSpg;O_3hT@u{go#6T^={~RfaQck#e!em8 z-9M^o)n4=4tM;C@rj(302q-ELI1nTd5D)>7+?x1J;yWOqYQPW$2ohMG-`dK-(8@tu z!PUmlUX$9z(&9r7C@|?)AYj1!|2_T(-+|G@C8-`-*r2l*mtgWnlQHH-?0y8@=&YON( zUBlyGA>@@)D*d54*b6Na9#lx;-vFg^Pz;GXsQ8LJ ztC(!itI?g<-WZC~v1k^tr|YxCbX(rG?oj0%k-om5UE`{Tw@>Pv=lGH1Um!Jo^BV;t?5SAX^N%V zhcNfu38e6#Z9yF-L}utxV@jT4BGI#401;^LBi_D-$ktyT6RBXI z_oAxp(&)#J+#msP;_E92kj%e{Gm2J+AuvFk*8tKA0}y9zJ3|Y58tPxK|0~A-gJt(` zKfNqgQXGU1G3d@)CJ3ZBJE4eUk#1i?i;|<#YlaB&o$UE*uCTJr zoB(R#S07AAVWmF5&&?X9tSSk1m}*yI2qnZC2|MqD*05#982iRWAUW(oa=7l=?%SAI zBr8ov+^mQ4KT<>&2#iOo-bF9)9sXp>QOeFI>ZIIOIJYXu$W*L+qT|Kp27-}VW`bG+dJVqw_FjQts7qtTlYaI8-!pI z`=P$@Bo&xBew<&rxIfJl+|R2pFlK~g2Cnh6b+2{zm11uRD0xpyh!){TwihHkUac@G z9zF(?DrKhceo#~ghi~8-3wKT_YG?IoR;5>f^`5-zIGgZwi#7V^>taF05^|=*{i?r{ zGG)_8>JPl8I5vKgJJJjhQ1KT>u%*OUfYoFJ+h9c6!`wU6Ty|N2^jLV(s_G<9o(6|TMM;-qB9VsVO}-K*;< zeyQK91)}Ky@Wp>BWN!XZVH*%2pbLP1g#ch10C)YJukw^Mtdz|3A=33`bc`gp7*9Go2Z*8>QwqwyAmc1W;g1h&ZE1L_?RBfsE840G zglMo1>Io9cYqbgNFHtr;4wf)d$&nj$5!UIVJvcmH^vsBr%r(bXq9dXz2GEy}Aa0Ql zsYsQ=sMY{0q08+k^vJZYmnei6DE23hgE_q?`Np;bwn!gvhzsAWavmUQAh%aGsgB$S zI-}#2a8YBnaJh}!l3p^`Qiqa~#6!2|cO=OlR>lcstp#1Yui>*~zNu~wwg%+Y>)UU+ z1}aWcIqaHaK#cJKTy!zJkMbJo59NyDcWnwgu79Z8m$#?c*e*A7N4u|nUrD2;YA>qeG@+s zWGLS%7%P?> zm2bmiQ&)W4PRp+M+7u*LJ_j~N^ifW8lF(T5y(m%j+AJO?+;;8qI%U|R!J3zN@MI80 z8B4Md+5=*cYU=+1*fyyso$ug5z71Rj_j#h!tED?#-197rV_FChYVT z5RB?+)r)n=20J^?E&OB`9lL%G=eN|Y)u8PfBsZMlfUs>X)dF)p79TZQTF8dk#sn)j z)*xIt=F7*A12|1iG@q(hK7tJ3Mfhc&Nb5~>5~0YrdV7#);kCD{KN3 z0Us}D&3Ni{k?OATU3WBpk-p_8+eho22XBDr{iks5nsd|506Zm3fa=HmOE~RK3=JLZ zX@0*k{?h*GaZ8bDv;dPyas58}ER@Z%ftR3^2ieiOq-3e5o{qmtPv49q_@v}))tB*D ztT|K(*p#z>?P=>ig*!d|3SOsd(o(2fl!w!#xk8|IY%uB!=N09mOy9CE&x+vVgw9aU z=2q~%Y+8mTP5D7dC9@u} zP|vLH0NXQz^U(7M8pEI`MPlNn?@Du4egwL56{&$sZ^dC%U&c= z?k0SLy47?AKMberad?l?l8o?jPquEd)|;ungCepHUnL~c#;sOLGXC&gkRa7f!}|b^ z?A6#EB}whfZTTY}Y*od3T2IaX3|5|Z$lTg18{X&cGJXWZUvncHF9fHIs-Bg%5=Qy5 zcW7jr!2`2nq0JhSPSd!&z^g5BjZi>e0Q&az_4gCEh>?Zw{s7BU$>;Vi{Yy7&bkhd! zU`;O#lmFH@Sd8lj7e(yM&C#AbShcxq*f;t>9dWpc4v3S5SpME!bVIXFU7q`G_sBFkb@z#IGG zr9A74L5r*U3g?d|w#6e=H)wEZ3*d%#Pu`cmOu?T^WE_=*?xscTyC@A{^Fb3>W?X5q zBX9{cA;B;D*=RfQp-oYU%%u$Q{F5QObf>4u1vAAMHo1EYjKTxcb) ze9W#E?%0%$WmJk1Er|Or0OhrI} zfP_%~t0*x2DGFgrzeEAX6C&GPn5$2cFP#`?&f!e#;6N#6pgP2$Io3`^Y|V3E{1Xmj ze3g!?LFj9mciH!+jUu!Kf5#tnY!N~P#FNnSLr!s1_-i9Ne4#(v%A4q^wy&^oJykrO zXt^<49pQXPL`*cLaL!t>^raZTHshqgt1TBt%q(veuF%e@uFrh<5!R=hOgCmIE*+54 z9;|z)22C11Z))-krN?U<^`iNa!@0oJ7D39sb;Z$_wuQe}jr<@Bf|OWoG?Tf!+M~Sc zR6pL1HxfMyQ`k<5p={0Ihn&im#EGzo5y?d3j`Tvjtu4XItT)Cdv6R5}V}x@!w9W}R z#*V5@7L`jW6(`^8&XIAan&E>r2RW(ki#^lmgg*KZ5>s5t+ABKJO~aUks8S-zg4caL zEm6*m4syNh@dNtQ@|yUXJ`aODc8aTBlESa>@A&?Nz0&7?S`tv=|@qEaM zyD#ZqFOHp64OF8HF;1_a(sOYi?n;TxPjw8ZX;kmQX<%2Apv^kuU((n--Zh(YR={%w z$`UL#UU2O{Upm-U;sCsRD&T;=roSwDfK&4} zJKhD{A9+jl-W1i)H2M0#I?&?$6`&6PWK55w6{&6>Oablu1DNS&K}pk`JHmiyoBSYT z7f)yIgnU{C=K^DrT2q8L*74xym#gZ}mW3C0jt7xPtJf<}?a%2W+LhT^{hFS4N?C~{ z(w@$0xAv|cnRa-=KN;a$I~mS8jWewy%bqR=cP~V=G^1d5z~{{G`j}eyi?8R7EXb+m zY@cPQZE#@dmB_*cu}V?}jip+0!ITu~o!SA&N3QalcV6XRa{?4qnVs5eniz zjk`9=%iD9lb7uq(D`)S)00CaiW*GWH9L(Fo(2?@T1Yh13rawMcj?sm42Qv@I+qW?f z(BgSoN)=&3)U)}fqSUbk+2%5n$k_VwET;3YkeJxRZV6dReN98D zXA9ERp9Y-ZwUo;HJ^V%WV=QzL7qH4Sl!ILX%ShejqOnj1`y2q0g#_RPYtfjfgMI8D zz(g%=x;0Hv0tla!qgxupW<9ERj1YFSQo4n zrI;0uRWE2}bdVlgCWci{l>0UvmH>`&g9s4@F}kK)a|4lzp0v*BJfgUQq%;?jEKb~0 z;VYLT$gGZebf=-avgGg$Y=Qmwv&Ta)ZNw{OY+|>n^~j_k{nDO?)=M-T76#Qp+shZ) z10Ob(5f>j}g)?umd={!w=YZ>1s8&ne3+HAF;xFl5B;Muz_SnIBaaUV5KfLx`6ht^T zRM2c?cCQG=3J!c~E{c#Qg99SCt3{``mKO~*l6BKTk?>GMieS5K)p|hVhY$4K{8L}d zioOdGnWq%q_`Xh?-1Wt<-O^Sc%A;p@kN7#~`@~i%u(Ai2pUqfh@=}-hX!o~y|F$2$ z?UzIRMi}661ZWFh0yA0Glbdebc*GEPKg(73xj!!r#@Hr~b+Y59gmbJE?CiN|PW)t- zK5%_q;T%BhF>)Pk6wH$HO#VUqMZ2UzU*+o7JWSI}OWut7RR;0~wiD_UGZX3*#)45C zYP!W&LGWuAC}9(mFC?DC2R~tMnLTj1dQh{m3_jXa9#D0ZkPqO}D2FRNC4|wYf{=?! zkV-ZXkf6YrB4$#T3X}Sgk6ET;5ToO!*nSXya5lOUdsFD1>Vm)NXwxnzpaY`N-f8%QyKye*o(>(uy z6~S;k`^J0@k|bhsNg~ir_$6O-t+XH3+gjX~rOINPsm=5@8JIzxnwQO$q>^NFj6w&(D|fRWAT=P)za&Qt)hNgkDAE!F-} z)ZUOEGQNO3v?UJdK90*n)61Q%W1ZEWxerhDdE)~rrI)*?E#MsXPf;dAqjnIz9_x1P*G4ORC8;vZmF2)oyE>756UQBo%@dg9F z$h@Vyv+nb7HrAr#Z!qMZYl(L~gJLvQ3mm#XkW5JlYJ^$$Uw&*S5oXWb++RU-O1&^ zbTx*3(lmTr-88SW0asp;V}0SpJ7$)?$YO#E2r(JY)%- z=CF!n+UI0DfnsFafbDB1xMX{Q1LTWtjfjc4WJX8Uw?Hqhm zz;=FWkg|&VR=Rj4D5Lt&yH#w7AF;7_Acw}7oOpS3Xg?DyYkb+I1GAr}8bN~7KY?^L zr~W`2n2Bg*CGayjareCxmoQ^pAJXNl22Vq^7B#FiBnx-QFM>8r)*Rb$sG%`+%*~jO z*r`WQ8@s~aISX{)CF^j%fBkg{I0q{tB8wxpax7M80HPQ27+?jH0wYKumbL+P7fLGF z+q=u<(`tI>-9Jfa!4Y)y=ou34;vubG+x!lyhf^ncDJ*-i{G-J6>{!(5iMig{8nGUV&#z#@ySB2Dmrt%I+ue>);+kic@I$%(+AtVIh(mYtAc9rWU%d;L6@X0f z>5qWvqUg=r^B=mJoFZRcB|_9}a##G~w&P<_-QOWyk}r%1hf_3MsTeNQLqHSBX5ig1 z{$=X8R=a>aH`ix#iGqTkcRVAAiqutj8nLM0TtF(b8#O$lPrPR~IM0o7>)EXyqHndu z5T60b()@m<`=wisRi&eFm{2=EUc48wFBEn9S*XgSezGf#;nY~(Vk#Ro9?r1U&w#fu zZe`DZf&>L6D?=j^0s{f6U`8BGnq?#|OD5L|JdtV8zshQ%-XO;-NsTpe_L;l$_Q~CA zJn%~+%HexGYDjSouM>@#TN~`E=sekhPA*&zo7LL@0n-ptaod3`-3{$!kJok>6aIeI zF1*1)JrKtym0^oqIiz>6A8jKISp%K*`%#ECVuTZ-b3aa|O%AKoFP0I7+ zMTz>flXN~k94|e-?DDy1b)y!entk~C#B0#;#BG>cI>~_ajkOt)?N$ z>zgl;!4_l;l2Y|X<(kRokR85UyaPgwCKQ#E9;fFNdY5O5Axpp6yYx|(a)!|aR8i2N9` zyQkKP*M|p`vx(p}&d%F>92$wIGj6Yk?M9kqo_XPXLFrDn^MPCl=`-(}<41S+J9OcX zC|pEP=&Obv+nV}NWs)5I4Y*NvrJ}I<0+djw{#L<<9pO~#J6Ji?%ghZK&i){QO+HBl zjg;yLdRz&;bU&@qRla%l?=x>nK^&Jf!y+IQnr>&NOG_>@1;A_CJIk5{HgJ#maD<8* zvh*jWD3gg1huMVX7p_)A^+(RE5cOUjaY3=7SWvd7^)3nZ9>fx0sfH>*pG1Rh(%WM} zW;b}$diN|DP1hgdan;3C5gqYu0I%lwT zI1i>{;`n_|CILLDCe|qqsQLz124n?@ed?zKsGL=a=8uUC z^qpt8zU6BWAR%_&}(p_-`-MrqywwuvpeWpoLfTh&T zY`VVZP<4I6LlHyR4&VHoExis5EyL==yMn#{Qe`qwgXuMn@Q6;mDKg@cJ`@!ij-VIc zb{B6&aMXdGHCgWNV_GxO-F8$qqY#`D7tB+GTge<|yN_uQY1J?+ zRU%XAJ|*JZ1_Kg*T#BhHVT4nCN}E%U89@3Jhva5xtS7jn+vl3^g;XMl3Zv{}DyXm? zu-(;Mvc+=f%7S2tlEbQCJB;%v=%8pb&iJD=U33h#sC%WK6J%JVF`=9ysZc4Ya8X=f zk-l4mWeM^loIdKNt5fP6zA@I36PDZv=T2Ck73r8shBr#J5|N!Bj)@sPm}Gly=T)AwGVu(l&*NYn*BHi5tpO|U#2(Tev69upw#!O!Kz)$;NY?cx!O4k(Y% z)c6*p^4D3`GkpkXoNZ#B99}ZQ1zmxs$BVN4Fjj^0@qt|k#An04!lq+pvl2lm)&hEi zE2QV6wLsw$wjZi_o!PK20g_Fh{75?%a#I3jNZDv7N}#D!>u&wm@oW#=@03D6!}MZZ z;pd3dl5?{z1&_!f;OjJWxF=vXNaet8w3Sz;#Op89c~cF?>Qn?3C0SBBnhC8{6wzfz zO43^#mp+J9nA0@DBhRQ3OBJ`!$Rff{4G&RH#rKUcma)2XnWZ@eK>+uljG=i^m<3Fs zRI1A)hI6FD~)Qgu)vjUAKKlvY_;Q_WroOR*w3+ zT5{C8OlR<*ndc#FC!jqhW-#eJDC0>|O2=A?yQs6MT7>lb4{37P7GKvV*6*Jmo?B;- zR#xYLe37y*z$*Do>ihOfoydQKD>ICt8@P-YXLXTUvfT?$hL&4o)HS(!y8Ufyq~JzC20)W+NxQl^5N;e zcw=AtdI4YHQF6#d@gO$V1=~9k%0#P-&a^S)vFSN9T~uVi{~g*?u&5b))jR$Xd{VDh zcbV}^N%Vw1qURRwmWz4g2aJJ+A3fk>G@3!F4nrx(J@~+2%OOhWVED?4_t7LH~_EeY9FC!bCafmA>o7FQ%C>p9L)dSZH>lBM*RxXzlNEg zBw$|l`2j%lmh#a+M0+%WvP z5Q`q}y}aij82Y;UB^|q{%L8Rrlr|nsbpl~CbjhDFlJxsSdeE0-WD0~8!?WzH8XU3A zF89f#A`}mrW3uiWDxXf!dFAkzxSw$({t0ZJ>Z?38V{@Lkk&%!G#~d*qHvYUr6G)(fiCRQAKo*Y(uQ8T5h>* zn=z-?(Z+-Ec|9w@zUY2p+rM7NTQZXQJrUP}xr^4jhn=AS-VmRu)60{S@CCma|4k&m zqdF?=d2#zY8|76hdB&x1NmB>Xp;ooqnXrbU99pST($KWcr8*%^;`QW>-?|y!0FJE+>mg2m-oR`8{7!)m{ z9>+PPvu<5G3l3m%E8;M_@JPaq!l#S#V!lo5F=yY0+nyZe`_>Bi!3bDx(}pqhW9|a< z{Dr>hxFGPUTqYefA8N2foI2Psp;s6hU0@vUPYt5=sgbm{eO-f}!2R8hqg@7<;Rl3# z5dnQ3@}5L0cWut?;atIQ1!Mu!~mE%|cQw($3netL9seG2ES>=;_hpycEYNZWVN zdxJ<&B3XP`P(+Npe9&Z5Y-`6k)1hsw__q+zO;Ere`D9-Maw{?Yalm$p~ zNmX<*QyGEHgcIK1Mv9Oj%B+jUAnugp+K<4QUn$TF9?RB8&Mc0*_s-t);tX-e`MBCDrSsjkNFEbNYUENdp!1>MZ026ocp==ITP%#&skb@REd>K2DY61%IO{5lT|Y@68ZB^$k%;6wr%gT*fe zTSgIij@QCs)^A}?O(R65!v&oiIMuDUfsu-s7h|&IJr~cuYzuHb@Ijt|6OBhJ%x#bJ z8Nx;mYyt~HN7*1o++qx>f|B38py1|xy?;hwssvcViBUefGl>GtJ zCH>>rfu**N+;SVRsaKAKc1b_HFLuw$`0s#S{M!yOg z>>XS!41Y`MsLGht7BgZ8#y$sR8^uF4k$BKfAA6(^n9~=qSm5>_$c8BSB1t(Qu3uJf zX7%xN8Z&QyG%kSM(6PrGh^IAGZxLC65FrSNb?YK1EDs<4T|u z(37Mw2uy)F9H>}9)TLUCfi`Mq$x#I@!Dhx79(ILE43@}Uscb|ZhNk2og{7FLhCv_Z zaAnjL*lQ0iLov`(EX(HW5Fut2Fhi`I(H|B8JuHC(d! zMk88ib40t-`{gl8%`DUqYRD2Dp7XPEVM78XDX5yX+Mxe$3aWmdHk5Dz-&&^-;Gt1RDJNRYihY)s9t`8yLx~NUE`}0~V zt+$tuK@%Zg;lT{NsSUsl>Md=l1v*kv!?A|Yrlaz{D0}s8+>Jy>zjcWLeT$x~^o|iwrhf&_!x-+R6Z?WaWh0@{YcC->Dq}xO_@V9tp8Ji3Q# zGxl&JqCGt?A=Nq6V!uJJ#(mhbR?=*Phses~1AV{^Dg88J1nz z&h~VNaM*X()fhUr)xuUfoj327B)}W=-QK~E(PG{iZE~Dao);*4ZQfQrl4$8$j@YKc zRD+*Ip;AHSdZd+F20he96Jc_#2h@)R%skiX{h9WD8zkbeq!;f3s1iOvX5r%>D&<#= z)SqP-e-z{W9?<{t1b&y|1w?fLG7qSNw;(V4F79!&fw27YEQC#pdmwrbi{ML1(YL72 z?zOmv-*$FvCU%(99yyC~jNoG^r(1vnxAiehs?OaPsXBS9Sr3;{p{O{cb*HF1QbDAo z#6kz6RnTCn17(SxF?+mv7pfznile!97eqr{X@~B=;7pd4m|0$GL)6ho)t^yrdn)_0 zf0k2yIga$i=d{Z092dEq2oPn9*5S$#VH*@Yx%AQFJ#xOuH1~uDEwCIN`FR7_0R3kn zO8!J%{xHA;nuqwWarob{vh=t$Yd{=+V2$L&o2PAH-s+G_Dc#IWxtwOI4*!IqYY!7F z9D-Gp<=T5~qt_|_n4rWEgsW-dc0F=ujF+MY$-Y6e@zk@Mo>RK0Xpg}W@oA45%}*USmRPf22`%PKv{n5OLH(F z3HDND8opy5_{c?MPd(VRnYQW7GgA(R}h3hlJaol9DJi}=FdNR_~+$=gR1+R#`FJY%vT6860aym+ZoVPOfavyeS5RwZW^DlA2J^dnCtXj>wcJg~Kr!&xz)3FLC(3WtNy!d=C@9nmDkmOAn@%) zIm;HG3n8Ko9SK8xjXYqcMi$xbij*$fO5ajk@{unp<(M$DacLjBOJ?veRqTI{x}KSU zfQ^fBUy{J#NbxMy`YpVKZtDV2sWjKR>mY0b;V`}c^1<)3VnnR^4tEJzp{P%xpNnD? z%e&xF_DY5qHPoap*B%|=u{#AJMhF<9b<`|hiX_4e)XL`4OkJ=0RjngnOt~`++juSH z3HWA6DS8GEhxvW5h0#>rIhIop^Bo;vWXnRABgL^w2@sY|NA6J&iZ&wev@@pClfjqy zjmOYlZn1_;KN&|JQZSvUAHveP8L|I8>g5zYvkadnIItxTo8k$C9c^PwsyHJ z86Oc<8%O7#d&lbV!btN92OIFC0>O5^Y2M!(7d^HUAm#rl|8*CM*9d?x`R;$u!~NEM z>2ZK^QCifXGm;Z-l@4jxfT~Z>#U)aT?hKNThxPjKU11R^Cms${FYZxL(IPP|MWh#W zLAPvY)d*jf{FMwKW<}-q81aPTd8i}^jvS@DUtBO(WHvb~VJalx*gW3}TPNXjQ{MDj zX@IRmA)6&l4L=%jZA9@t}HZqgO~fO;-;FIMD!E zk=_^+tPWudelMQ0_+t(o=Ez#c;aDHplT-AYl-F9Ses<7UOZBm=2DRVc9R#>1kkaGg z5q($UA4jmPQj9~CrWiGzuQoy=HkvXC`0gAYAW;f2f~;hxQOi2k&O5>dLFQp~~3GeD;P)N*BVvevn@urCxEb;{UZC(%Jg zh%Myu(;R-}wHq_Wn;`BX+n=n|y?T*Z7M?gv-BfYTR~z2{yj^HM<$;PHL)fbSYEw%k zBEtDpF35{YE7KWJjf|RYSaC14Y*!mM?AC5l{h`Z3l=GwW%1+eRLJPh&2%dlqWJf>i zKsF)!&1fK+uI96hZ?f*G=DMfb_z;=M2;r#EZ0vV_CbT9^0>Me5Ecpu-JzFxHK5JA` zt2hyqgDCoQUmRm8pB+@`xV=>JUs@{Ay)tSX#%GVrwAMIL&SZMS_bM#4M*m zeU}zK;DDg3r8CjWn5Zv6n$_E)T9AOke~$3`kWco$Ng;1kGtOcpX6>gI``K>27D%pL zi2x0cRM^CN2>-`|moK-iS+`T-N89V32#+Ouhqh`NCpw+7ZoTTdVI7+1gfD23KU8>B>U=eQm?@)hhB_#@G)J#^Vh#W3t+gEr0orfF7v-?cf01y!6QtvjAFD@O97ZGsK#xP%6S~d5K5@nCn(B5L%@nj}24!zPh7HdwaNs9n=1t9JRxa>=tx`+A&!3uwf#`5;RTi z#s734Bo|bP)zgV)7F3y4`p>*mE`%@K4&a+&fO$szJMZY)*!(Z!066B)BR#ImYWi0J zo=6-2w)>nZFO?cwg$&H0Z&-rU(&M@WGkWBE*S7D}6WxIz$kyLN8{dTv=)@K+QEE~z z8|~spaZiNL>@?V_2FF;O2eM}D5b|5|nLf85I+r$*R0;?|c|cLSQ1-%$Y8>uOOcMs7 z>(_{2C7mmTu;)8b6Q^L~e8n2cOd#(CM{c#H;#XAlpX;)~z7oycxo_^Z zATnA;Ky_r4snA6fi_g@{L?ADly&0O5<*`GE`*@-rP3E$CrBr{#QH>R2Z^%8FR~C0W zyrR6}gE|zCA{iJ8yh~NZqJw7W#O{j|+^sI&ga$?QZL3*DK<(-*fop|1@ds5m8{!ws z@a61&Jb3!huKA9BSy!CToYY1|!%*)F^F&NLzBY!Vhet^L_n9 zEE$7^#k)(@3z4!zsrdj@>qN}ugY&#LPdJWBwkgEQlJ3rR+Pt^>(u51c1&>G|5M>vF znYA%_i$vr!S`?OCq%%GF>8iNXX7`!~n{jmg$v?(w>LKJ=AAp_Hn+l(n+Q?WqZa>1SnhD23^&$7u%Afwv6RD6ZfhTn~}kft66sNGN7UDpX6e zvF>8#L%jXe5Se@EsX_Rn^+}o?Br1cn2DrzoC12brAx*Kjy zp1o^#V5^LiXU|VDYFCa)3RYwshBJ%`BaZZs=1#8W0=Y|#mBVMi%|*Cp(WshxpGgwxD>F*}0Y;m+4Yfw8E{Zt_N zf)z&q8sLM72+YR=3HgrT!(RhmTS!ize{Fx?0aF2zegFJ76o5(pcKpMiQpkw^JHWp; zzW<$i0$|er)&~EU@ZZ~i|4s-D2*&;QX5hCtZyP=TLec?*{NA*Ez9oL!i1rupGax7W zC-Hx@rM*RY+r#r03M3#|^A9M$J9^%tysgFm3nd7Uoc#xs-x|!`ye%L63q>3EH_F>$!nXi#>rVaxFed!RW&Ex}d5iKkLH!p>z^C6R|Bu->Bl`=0GD3?QHpRv@5%*ulL8_;<(s?*I@Se*^f_jekr1?>^AqsUJT7jrw06 o(OdGjcIz*IK%U?C;Qz&n$%umi+E0INSy6ys0P%`IzF(vN1BlZe^#A|> literal 0 HcmV?d00001 diff --git a/unipay/init.py b/unipay/init.py index 50c997b..6c2bf46 100644 --- a/unipay/init.py +++ b/unipay/init.py @@ -8,6 +8,12 @@ from .payfee import get_pay_fee, sor_get_pay_fee, get_paychannels, get_pay_feera # 从 env 或配置载入 provider conf(这里只示例) CONF = { + "transfer":{ + "pop3server": os.getenv("POP3SERVER", ""), + "mail": os.getenv("MAIL", ""), + "password": os.getenv("PASSWORD", "") + "from_mail": os.getenv("FROM_MAIL", "") + } "wechat": { "mchid": os.getenv("WXP_MCHID",""), "appid": os.getenv("WXP_APPID", ""), @@ -132,6 +138,7 @@ async def payment_notify(request, callback, params_kw=None): # 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"]), diff --git a/unipay/notify.py b/unipay/notify.py index 96ba22c..9202df2 100644 --- a/unipay/notify.py +++ b/unipay/notify.py @@ -1,5 +1,7 @@ # unipay/notify.py from typing import Dict +from appPublic.log import exception +from .providers.transfer import TransferGateway from .providers.wechat import WechatGateway from .providers.paypal import PaypalGateway from .providers.alipay import AlipayGateway @@ -12,12 +14,15 @@ def get_provider_channel(name:str): "wechat":"0", "paypal":"1", "alipay":"2", - "stripe":"3" + "stripe":"3", + "transfer":"4" } return channels.get(name, '9') def get_provider(name: str, conf: Dict): try: + if name == "transfer": + return TransferGateway(**conf) if name == "wechat": return WechatGateway(**conf) if name == "paypal": @@ -26,7 +31,8 @@ def get_provider(name: str, conf: Dict): return AlipayGateway(**conf) if name == "stripe": return StripeGateway(**conf) - except: + except Exception as e: + exception(f'get_proveder() error {name=}, {conf=}, {e}') return None raise ValueError("unknown provider") diff --git a/unipay/providers/transfer.py b/unipay/providers/transfer.py new file mode 100644 index 0000000..729523e --- /dev/null +++ b/unipay/providers/transfer.py @@ -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) +