service.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. from datetime import datetime
  2. from decimal import Decimal
  3. from typing import Any
  4. from redis.asyncio import Redis
  5. from app.api.v1.module_system.auth.schema import AuthSchema
  6. from app.core.alipay import AlipayClient
  7. from app.core.exceptions import CustomException
  8. from app.core.logger import log
  9. from app.utils.snowflake import get_snowflake_id_str
  10. from app.plugin.module_payment.enterprise.crud import EnterpriseCRUD
  11. from .crud import AccountCRUD, TransferCRUD, DepositCRUD, WithdrawCRUD
  12. from .enums import (
  13. DepositStatusEnum,
  14. )
  15. from .schema import (
  16. AccountAuthorizeApplySchema,
  17. AccountAuthorizeApplyOutSchema,
  18. AccountCreateSchema,
  19. AccountDepositSchema,
  20. AccountDepositOutSchema,
  21. AccountOperationOutSchema,
  22. AccountQuerySchema,
  23. AccountTransferSchema,
  24. AccountTransferOutSchema,
  25. TransferListOutSchema,
  26. TransferOutSchema,
  27. TenantTransferCreate,
  28. TenantTransferResponse,
  29. )
  30. class AccountService:
  31. """资金专户服务层"""
  32. @classmethod
  33. async def authorize_apply_service(
  34. cls,
  35. auth: AuthSchema,
  36. data: AccountAuthorizeApplySchema
  37. ) -> AccountAuthorizeApplyOutSchema:
  38. """
  39. 申请转账授权签约(✅)
  40. 调用: alipay.commerce.ec.trans.authorize.apply
  41. """
  42. from alipay.aop.api.request.AlipayCommerceEcTransAuthorizeApplyRequest import (
  43. AlipayCommerceEcTransAuthorizeApplyRequest,
  44. )
  45. from alipay.aop.api.domain.AlipayCommerceEcTransAuthorizeApplyModel import (
  46. AlipayCommerceEcTransAuthorizeApplyModel,
  47. )
  48. from alipay.aop.api.response.AlipayCommerceEcTransAuthorizeApplyResponse import (
  49. AlipayCommerceEcTransAuthorizeApplyResponse,
  50. )
  51. model = AlipayCommerceEcTransAuthorizeApplyModel()
  52. model.enterprise_id = data.enterprise_id
  53. request = AlipayCommerceEcTransAuthorizeApplyRequest()
  54. request.biz_model = model
  55. client = AlipayClient.get_client()
  56. response = client.execute(request)
  57. if not response:
  58. raise CustomException(msg="申请转账授权失败: 无响应")
  59. result = AlipayCommerceEcTransAuthorizeApplyResponse()
  60. result.parse_response_content(response)
  61. if not result.is_success():
  62. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  63. raise CustomException(msg=f"申请转账授权失败: {result.msg}")
  64. return AccountAuthorizeApplyOutSchema(
  65. sign_url=result.sign_url,
  66. )
  67. @classmethod
  68. async def create_account_service(
  69. cls,
  70. auth: AuthSchema,
  71. data: AccountCreateSchema
  72. ) -> AccountOperationOutSchema:
  73. """
  74. 开通资金专户(✅)
  75. 调用: alipay.commerce.ec.trans.account.create
  76. """
  77. from alipay.aop.api.request.AlipayCommerceEcTransAccountCreateRequest import (
  78. AlipayCommerceEcTransAccountCreateRequest,
  79. )
  80. from alipay.aop.api.domain.AlipayCommerceEcTransAccountCreateModel import (
  81. AlipayCommerceEcTransAccountCreateModel,
  82. )
  83. from alipay.aop.api.response.AlipayCommerceEcTransAccountCreateResponse import (
  84. AlipayCommerceEcTransAccountCreateResponse,
  85. )
  86. model = AlipayCommerceEcTransAccountCreateModel()
  87. model.enterprise_id = data.enterprise_id
  88. model.account_type = data.account_type or "ALL" # 收支全能户
  89. model.scene = data.scene or "B2B_TRANS" # ToB转账场景
  90. request = AlipayCommerceEcTransAccountCreateRequest()
  91. request.biz_model = model
  92. client = AlipayClient.get_client()
  93. response = client.execute(request)
  94. if not response:
  95. raise CustomException(msg="开通资金专户失败: 无响应")
  96. result = AlipayCommerceEcTransAccountCreateResponse()
  97. result.parse_response_content(response)
  98. if not result.is_success():
  99. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  100. raise CustomException(msg=f"开通资金专户失败: {result.msg}")
  101. account_data = AccountCreateSchema(
  102. enterprise_id=model.enterprise_id,
  103. account_book_id=result.account_book_id,
  104. account_type=model.account_type,
  105. scene=model.scene,
  106. )
  107. if result.account_book_id:
  108. account_data.account_book_id = result.account_book_id
  109. await AccountCRUD(auth).create(account_data)
  110. return AccountOperationOutSchema(
  111. enterprise_id=account_data.enterprise_id,
  112. account_book_id=account_data.account_book_id,
  113. )
  114. @classmethod
  115. async def deposit_service(
  116. cls,
  117. auth: AuthSchema,
  118. data: AccountDepositSchema
  119. ) -> AccountDepositOutSchema:
  120. """
  121. 资金专户充值(✅)
  122. 调用: alipay.commerce.ec.trans.account.deposit
  123. """
  124. from alipay.aop.api.request.AlipayCommerceEcTransAccountDepositRequest import (
  125. AlipayCommerceEcTransAccountDepositRequest,
  126. )
  127. from alipay.aop.api.domain.AlipayCommerceEcTransAccountDepositModel import (
  128. AlipayCommerceEcTransAccountDepositModel,
  129. )
  130. from alipay.aop.api.response.AlipayCommerceEcTransAccountDepositResponse import (
  131. AlipayCommerceEcTransAccountDepositResponse,
  132. )
  133. model = AlipayCommerceEcTransAccountDepositModel()
  134. model.enterprise_id = data.enterprise_id
  135. model.account_book_id = data.account_book_id
  136. model.amount = str(data.amount)
  137. model.out_biz_no = get_snowflake_id_str(auth.tenant_id)
  138. request = AlipayCommerceEcTransAccountDepositRequest()
  139. request.biz_model = model
  140. client = AlipayClient.get_client()
  141. response = client.execute(request)
  142. if not response:
  143. raise CustomException(msg="充值失败: 无响应")
  144. result = AlipayCommerceEcTransAccountDepositResponse()
  145. result.parse_response_content(response)
  146. if not result.is_success():
  147. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  148. raise CustomException(msg=f"充值失败: {result.msg}")
  149. deposit_crud = DepositCRUD(auth)
  150. deposit_data = {
  151. "enterprise_id": data.enterprise_id,
  152. "out_biz_no": model.out_biz_no,
  153. "account_book_id": data.account_book_id,
  154. "amount": data.amount,
  155. "url": result.url,
  156. "status": DepositStatusEnum.DEALING.value,
  157. "remark": data.remark,
  158. }
  159. await deposit_crud.create(deposit_data)
  160. return AccountDepositOutSchema(
  161. url=result.url,
  162. )
  163. @classmethod
  164. async def transfer_service(
  165. cls,
  166. auth: AuthSchema,
  167. data: AccountTransferSchema
  168. ) -> AccountTransferOutSchema:
  169. """
  170. 资金专户转账(✅)
  171. 调用: alipay.commerce.ec.trans.account.transfer
  172. """
  173. from alipay.aop.api.request.AlipayCommerceEcTransAccountTransferRequest import (
  174. AlipayCommerceEcTransAccountTransferRequest,
  175. )
  176. from alipay.aop.api.domain.AlipayCommerceEcTransAccountTransferModel import (
  177. AlipayCommerceEcTransAccountTransferModel,
  178. )
  179. from alipay.aop.api.response.AlipayCommerceEcTransAccountTransferResponse import (
  180. AlipayCommerceEcTransAccountTransferResponse,
  181. )
  182. from alipay.aop.api.domain.TransParticipant import (
  183. TransParticipant,
  184. )
  185. from alipay.aop.api.domain.BankCardExtInfoDTO import (
  186. BankCardExtInfoDTO,
  187. )
  188. # 检查资金专户是否存在
  189. account = await AccountCRUD(auth).get_by_account_book_id(data.account_book_id)
  190. if not account:
  191. raise CustomException(msg="资金账户不存在")
  192. if account.tenant_id != auth.tenant_id:
  193. raise CustomException(msg="无权限操作")
  194. if data.enterprise_id and account.enterprise_id != data.enterprise_id:
  195. raise CustomException(msg="参数错误")
  196. if not data.order_title and account.enterprise_id:
  197. enterprise = await EnterpriseCRUD(auth).get_by_enterprise_id(account.enterprise_id)
  198. if not enterprise:
  199. raise CustomException(msg="资金账户所属企业不存在")
  200. data.order_title = f"来自{enterprise.name}转账"
  201. model = AlipayCommerceEcTransAccountTransferModel()
  202. model.enterprise_id = account.enterprise_id
  203. model.account_book_id = account.account_book_id
  204. model.out_biz_no = get_snowflake_id_str(auth.tenant_id)
  205. # 转账总金额,单位为元,精确到小数点后两位
  206. model.amount = str(data.amount)
  207. model.order_title = data.order_title
  208. payee_info = TransParticipant()
  209. payee_info.identity_type = data.payee_info.identity_type
  210. payee_info.name = data.payee_info.name
  211. payee_info.identity = data.payee_info.identity
  212. if data.payee_info.bankcard_ext_info:
  213. payee_info.bankcard_ext_info = BankCardExtInfoDTO.from_alipay_dict(
  214. data.payee_info.bankcard_ext_info.model_dump(exclude_none=True)
  215. )
  216. model.payee_info = payee_info
  217. request = AlipayCommerceEcTransAccountTransferRequest()
  218. request.biz_model = model
  219. client = AlipayClient.get_client()
  220. response = client.execute(request)
  221. if not response:
  222. raise CustomException(msg="转账失败: 无响应")
  223. result = AlipayCommerceEcTransAccountTransferResponse()
  224. result.parse_response_content(response)
  225. if not result.is_success():
  226. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  227. raise CustomException(msg=f"转账失败: {result.sub_msg or result.msg or result.code}")
  228. transfer_crud = TransferCRUD(auth)
  229. transfer_data = {
  230. "enterprise_id": model.enterprise_id,
  231. "out_biz_no": model.out_biz_no,
  232. "account_book_id": model.account_book_id,
  233. "amount": model.amount,
  234. "order_title": model.order_title,
  235. "payee_info": data.payee_info.model_dump() if data.payee_info else None,
  236. "status": result.status,
  237. "order_no": result.order_no,
  238. "fund_order_id": result.fund_order_id,
  239. }
  240. await transfer_crud.create(transfer_data)
  241. return AccountTransferOutSchema(
  242. status=result.status,
  243. order_no=result.order_no,
  244. fund_order_id=result.fund_order_id,
  245. )
  246. @classmethod
  247. async def tenant_transfer_service(
  248. cls,
  249. auth: AuthSchema,
  250. tenant_id: int,
  251. data: TenantTransferCreate,
  252. request_ip: str,
  253. api_key_id: int | None = None,
  254. ) -> TenantTransferResponse:
  255. """
  256. 租户API转账(通过API Key认证)
  257. 调用: alipay.commerce.ec.trans.account.transfer
  258. """
  259. from alipay.aop.api.request.AlipayCommerceEcTransAccountTransferRequest import (
  260. AlipayCommerceEcTransAccountTransferRequest,
  261. )
  262. from alipay.aop.api.domain.AlipayCommerceEcTransAccountTransferModel import (
  263. AlipayCommerceEcTransAccountTransferModel,
  264. )
  265. from alipay.aop.api.response.AlipayCommerceEcTransAccountTransferResponse import (
  266. AlipayCommerceEcTransAccountTransferResponse,
  267. )
  268. from alipay.aop.api.domain.TransParticipant import (
  269. TransParticipant,
  270. )
  271. from alipay.aop.api.domain.BankCardExtInfoDTO import (
  272. BankCardExtInfoDTO,
  273. )
  274. # 检查资金专户是否存在
  275. account = await AccountCRUD(auth).get_by_account_book_id(data.account_book_id)
  276. if not account:
  277. raise CustomException(msg="资金账户不存在")
  278. if account.tenant_id != tenant_id:
  279. raise CustomException(msg="无权限操作")
  280. if data.enterprise_id and account.enterprise_id != data.enterprise_id:
  281. raise CustomException(msg="参数错误")
  282. if not data.order_title and account.enterprise_id:
  283. enterprise = await EnterpriseCRUD(auth).get_by_enterprise_id(account.enterprise_id)
  284. if not enterprise:
  285. raise CustomException(msg="资金账户所属企业不存在")
  286. data.order_title = f"来自{enterprise.name}转账"
  287. model = AlipayCommerceEcTransAccountTransferModel()
  288. model.enterprise_id = account.enterprise_id
  289. model.account_book_id = account.account_book_id
  290. model.out_biz_no = get_snowflake_id_str(tenant_id)
  291. # 转账总金额,单位为元,精确到小数点后两位
  292. model.amount = str(data.amount)
  293. model.order_title = data.order_title
  294. payee_info = TransParticipant()
  295. payee_info.identity_type = data.payee_info.identity_type
  296. payee_info.name = data.payee_info.name
  297. payee_info.identity = data.payee_info.identity
  298. if data.payee_info.bankcard_ext_info:
  299. payee_info.bankcard_ext_info = BankCardExtInfoDTO.from_alipay_dict(
  300. data.payee_info.bankcard_ext_info.model_dump(exclude_none=True)
  301. )
  302. model.payee_info = payee_info
  303. request = AlipayCommerceEcTransAccountTransferRequest()
  304. request.biz_model = model
  305. client = AlipayClient.get_client()
  306. response = client.execute(request)
  307. if not response:
  308. raise CustomException(msg="转账失败: 无响应")
  309. result = AlipayCommerceEcTransAccountTransferResponse()
  310. result.parse_response_content(response)
  311. if not result.is_success():
  312. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  313. raise CustomException(msg=f"转账失败: {result.sub_msg or result.msg or result.code}")
  314. transfer_crud = TransferCRUD(auth)
  315. transfer_data = {
  316. "enterprise_id": model.enterprise_id,
  317. "out_biz_no": model.out_biz_no,
  318. "account_book_id": model.account_book_id,
  319. "amount": model.amount,
  320. "order_title": model.order_title,
  321. "payee_info": data.payee_info.model_dump() if data.payee_info else None,
  322. "status": result.status,
  323. "order_no": result.order_no,
  324. "fund_order_id": result.fund_order_id,
  325. }
  326. await transfer_crud.create(transfer_data)
  327. return TenantTransferResponse(
  328. status=result.status,
  329. order_no=result.order_no,
  330. fund_order_id=result.fund_order_id,
  331. )
  332. # @classmethod
  333. # async def withdraw_service(
  334. # cls,
  335. # auth: AuthSchema,
  336. # data: AccountWithdrawSchema
  337. # ) -> AccountOperationOutSchema:
  338. # """
  339. # 资金专户提现
  340. # 调用: alipay.commerce.ec.trans.account.withdraw
  341. # """
  342. # from alipay.aop.api.request.AlipayCommerceEcTransAccountWithdrawRequest import (
  343. # AlipayCommerceEcTransAccountWithdrawRequest,
  344. # )
  345. # from alipay.aop.api.domain.AlipayCommerceEcTransAccountWithdrawModel import (
  346. # AlipayCommerceEcTransAccountWithdrawModel,
  347. # )
  348. # from alipay.aop.api.response.AlipayCommerceEcTransAccountWithdrawResponse import (
  349. # AlipayCommerceEcTransAccountWithdrawResponse,
  350. # )
  351. # crud = AccountCRUD(auth)
  352. # enterprise = await crud.get_by_enterprise_id(data.enterprise_id)
  353. # if not enterprise:
  354. # raise CustomException(msg="企业不存在")
  355. # model = AlipayCommerceEcTransAccountWithdrawModel()
  356. # model.enterprise_id = enterprise.alipay_enterprise_id
  357. # model.account_book_id = data.account_book_id
  358. # model.amount = str(data.amount)
  359. # model.out_biz_no = data.out_biz_no
  360. # request = AlipayCommerceEcTransAccountWithdrawRequest()
  361. # request.biz_model = model
  362. # client = AlipayClient.get_client()
  363. # response = client.execute(request)
  364. # if not response:
  365. # raise CustomException(msg="提现失败: 无响应")
  366. # result = AlipayCommerceEcTransAccountWithdrawResponse()
  367. # result.parse_response_content(response)
  368. # if not result.is_success():
  369. # log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  370. # raise CustomException(msg=f"提现失败: {result.msg}")
  371. # withdraw_crud = WithdrawCRUD(auth)
  372. # withdraw_data = {
  373. # "enterprise_id": data.enterprise_id,
  374. # "out_biz_no": data.out_biz_no,
  375. # "account_book_id": data.account_book_id,
  376. # "amount": data.amount,
  377. # "status": WithdrawStatusEnum.DEALING.value,
  378. # "order_no": result.order_no,
  379. # }
  380. # await withdraw_crud.create(withdraw_data)
  381. # return AccountOperationOutSchema(
  382. # enterprise_id=data.enterprise_id,
  383. # account_book_id=data.account_book_id,
  384. # out_biz_no=data.out_biz_no,
  385. # status=WithdrawStatusEnum.DEALING.value,
  386. # )
  387. @classmethod
  388. async def query_account_service(
  389. cls,
  390. auth: AuthSchema,
  391. data: AccountQuerySchema
  392. ) -> list[Any]:
  393. """
  394. 查询资金专户(调用支付宝接口)
  395. 调用: alipay.commerce.ec.trans.account.query
  396. """
  397. from alipay.aop.api.request.AlipayCommerceEcTransAccountQueryRequest import (
  398. AlipayCommerceEcTransAccountQueryRequest,
  399. )
  400. from alipay.aop.api.domain.AlipayCommerceEcTransAccountQueryModel import (
  401. AlipayCommerceEcTransAccountQueryModel,
  402. )
  403. from alipay.aop.api.response.AlipayCommerceEcTransAccountQueryResponse import (
  404. AlipayCommerceEcTransAccountQueryResponse,
  405. )
  406. from alipay.aop.api.domain.FundAccountApiDTO import (
  407. FundAccountApiDTO,
  408. )
  409. model = AlipayCommerceEcTransAccountQueryModel()
  410. model.enterprise_id = data.enterprise_id
  411. request = AlipayCommerceEcTransAccountQueryRequest()
  412. request.biz_model = model
  413. client = AlipayClient.get_client()
  414. response = client.execute(request)
  415. if not response:
  416. raise CustomException(msg="查询资金专户失败: 无响应")
  417. result = AlipayCommerceEcTransAccountQueryResponse()
  418. result.parse_response_content(response)
  419. if not result.is_success():
  420. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  421. raise CustomException(msg=f"查询资金专户失败: {result.msg}")
  422. collect = []
  423. for v in list(result.account_list or []):
  424. account = FundAccountApiDTO.to_alipay_dict(v)
  425. collect.append(account)
  426. return collect
  427. @classmethod
  428. async def transfer_detail_service(
  429. cls,
  430. auth: AuthSchema,
  431. out_biz_no: str
  432. ) -> TransferOutSchema:
  433. """
  434. 查询转账记录详情
  435. """
  436. crud = TransferCRUD(auth)
  437. transfer = await crud.get_by_out_biz_no(out_biz_no)
  438. if not transfer:
  439. raise CustomException(msg="转账记录不存在")
  440. return TransferOutSchema.model_validate(transfer)
  441. @classmethod
  442. async def transfer_list_service(
  443. cls,
  444. auth: AuthSchema,
  445. page_no: int = 1,
  446. page_size: int = 20,
  447. search: dict | None = None,
  448. ) -> dict:
  449. """
  450. 查询转账记录列表
  451. """
  452. log.info(f"查询转账记录列表: {page_no}, {page_size}, {search}")
  453. crud = TransferCRUD(auth)
  454. offset = (page_no - 1) * page_size
  455. return await crud.page(
  456. offset=offset,
  457. limit=page_size,
  458. order_by=[{"id": "desc"}],
  459. search=search or {},
  460. out_schema=TransferListOutSchema,
  461. )
  462. @classmethod
  463. async def update_transfer_status_service(
  464. cls,
  465. auth: AuthSchema,
  466. order_no: str,
  467. status: str,
  468. ext_info: dict = {}
  469. ) -> None:
  470. """
  471. 更新转账状态(由通知处理器调用)
  472. """
  473. crud = TransferCRUD(auth)
  474. transfer = await crud.get_by_order_no(order_no)
  475. if not transfer:
  476. log.warning(f"转账记录不存在: {order_no}")
  477. return
  478. update_data = {}
  479. update_data["status"] = status
  480. if ext_info:
  481. update_data["ext_info"] = ext_info
  482. await crud.update_by_order_no(order_no, update_data)
  483. @classmethod
  484. async def update_deposit_status_service(
  485. cls,
  486. auth: AuthSchema,
  487. out_biz_no: str,
  488. status: str,
  489. ) -> None:
  490. """
  491. 更新充值状态(由通知处理器调用)
  492. """
  493. crud = DepositCRUD(auth)
  494. deposit = await crud.get_by_out_biz_no(out_biz_no)
  495. if not deposit:
  496. log.warning(f"充值记录不存在: {out_biz_no}")
  497. return
  498. update_data = {"status": status}
  499. await crud.update_by_out_biz_no(out_biz_no, update_data)
  500. @classmethod
  501. async def update_withdraw_status_service(
  502. cls,
  503. auth: AuthSchema,
  504. out_biz_no: str,
  505. status: str,
  506. error_code: str | None = None,
  507. error_msg: str | None = None,
  508. ) -> None:
  509. """
  510. 更新提现状态(由通知处理器调用)
  511. """
  512. crud = WithdrawCRUD(auth)
  513. withdraw = await crud.get_by_out_biz_no(out_biz_no)
  514. if not withdraw:
  515. log.warning(f"提现记录不存在: {out_biz_no}")
  516. return
  517. update_data = {"status": status}
  518. if error_code:
  519. update_data["error_code"] = error_code
  520. if error_msg:
  521. update_data["error_msg"] = error_msg
  522. await crud.update_by_out_biz_no(out_biz_no, update_data)
  523. @classmethod
  524. async def consume_detail_query_service(
  525. cls,
  526. auth: AuthSchema,
  527. pay_no: str,
  528. enterprise_id: str | None = None,
  529. ant_shop_id: str | None = None,
  530. query_options: list[str] | None = None,
  531. ) -> dict:
  532. """
  533. 账单详情查询(✅)
  534. 调用: alipay.commerce.ec.consume.detail.query
  535. 用于查询企业码账单详情,支持查询关联退款、订单、票据等信息。
  536. """
  537. from alipay.aop.api.request.AlipayCommerceEcConsumeDetailQueryRequest import (
  538. AlipayCommerceEcConsumeDetailQueryRequest,
  539. )
  540. from alipay.aop.api.domain.AlipayCommerceEcConsumeDetailQueryModel import (
  541. AlipayCommerceEcConsumeDetailQueryModel,
  542. )
  543. from alipay.aop.api.response.AlipayCommerceEcConsumeDetailQueryResponse import (
  544. AlipayCommerceEcConsumeDetailQueryResponse,
  545. )
  546. model = AlipayCommerceEcConsumeDetailQueryModel()
  547. model.pay_no = pay_no
  548. if enterprise_id:
  549. model.enterprise_id = enterprise_id
  550. if ant_shop_id:
  551. model.ant_shop_id = ant_shop_id
  552. if query_options:
  553. model.query_options = query_options
  554. request = AlipayCommerceEcConsumeDetailQueryRequest()
  555. request.biz_model = model
  556. client = AlipayClient.get_client()
  557. response = client.execute(request)
  558. if not response:
  559. raise CustomException(msg="账单详情查询失败: 无响应")
  560. result = AlipayCommerceEcConsumeDetailQueryResponse()
  561. result.parse_response_content(response)
  562. if not result.is_success():
  563. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  564. raise CustomException(msg=f"账单详情查询失败: {result.msg}")
  565. consume_info = result.consume_info
  566. if not consume_info:
  567. raise CustomException(msg="账单详情查询失败: 无账单信息")
  568. return {
  569. "account_id": consume_info.account_id,
  570. "pay_no": consume_info.pay_no,
  571. "consume_type": consume_info.consume_type,
  572. "gmt_biz_create": consume_info.gmt_biz_create,
  573. "consume_biz_type": consume_info.consume_biz_type,
  574. "consume_amount": consume_info.consume_amount,
  575. "order_complete_label": consume_info.order_complete_label,
  576. "refund_status": consume_info.refund_status,
  577. "refund_amount": consume_info.refund_amount,
  578. "peer_payer_card_name": consume_info.peer_payer_card_name,
  579. "user_id": getattr(consume_info, 'user_id', None),
  580. "open_id": getattr(consume_info, 'open_id', None),
  581. "enterprise_id": consume_info.enterprise_id,
  582. "employee_id": consume_info.employee_id,
  583. "enterprise_name": getattr(consume_info, 'enterprise_name', None),
  584. "employee_name": getattr(consume_info, 'employee_name', None),
  585. "consume_scene_code": getattr(consume_info, 'consume_scene_code', None),
  586. "consume_type_sub_category": getattr(consume_info, 'consume_type_sub_category', None),
  587. "consume_title": getattr(consume_info, 'consume_title', None),
  588. "gmt_pay": getattr(consume_info, 'gmt_pay', None),
  589. "gmt_refund": getattr(consume_info, 'gmt_refund', None),
  590. "pay_amount": getattr(consume_info, 'pay_amount', None),
  591. "invoice_amount": getattr(consume_info, 'invoice_amount', None),
  592. "peer_pay_amount": getattr(consume_info, 'peer_pay_amount', None),
  593. "subsidy_amount": getattr(consume_info, 'subsidy_amount', None),
  594. "ext_infos": getattr(consume_info, 'ext_infos', None),
  595. }