service.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  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. out_biz_no=model.out_biz_no,
  246. )
  247. @classmethod
  248. async def tenant_transfer_service(
  249. cls,
  250. auth: AuthSchema,
  251. tenant_id: int,
  252. data: TenantTransferCreate,
  253. request_ip: str,
  254. api_key_id: int | None = None,
  255. ) -> TenantTransferResponse:
  256. """
  257. 租户API转账(通过API Key认证)
  258. 调用: alipay.commerce.ec.trans.account.transfer
  259. """
  260. from alipay.aop.api.request.AlipayCommerceEcTransAccountTransferRequest import (
  261. AlipayCommerceEcTransAccountTransferRequest,
  262. )
  263. from alipay.aop.api.domain.AlipayCommerceEcTransAccountTransferModel import (
  264. AlipayCommerceEcTransAccountTransferModel,
  265. )
  266. from alipay.aop.api.response.AlipayCommerceEcTransAccountTransferResponse import (
  267. AlipayCommerceEcTransAccountTransferResponse,
  268. )
  269. from alipay.aop.api.domain.TransParticipant import (
  270. TransParticipant,
  271. )
  272. from alipay.aop.api.domain.BankCardExtInfoDTO import (
  273. BankCardExtInfoDTO,
  274. )
  275. # 检查资金专户是否存在
  276. account = await AccountCRUD(auth).get_by_account_book_id(data.account_book_id)
  277. if not account:
  278. raise CustomException(msg="资金账户不存在")
  279. if account.tenant_id != tenant_id:
  280. raise CustomException(msg="无权限操作")
  281. if data.enterprise_id and account.enterprise_id != data.enterprise_id:
  282. raise CustomException(msg="参数错误")
  283. if not data.order_title and account.enterprise_id:
  284. enterprise = await EnterpriseCRUD(auth).get_by_enterprise_id(account.enterprise_id)
  285. if not enterprise:
  286. raise CustomException(msg="资金账户所属企业不存在")
  287. data.order_title = f"来自{enterprise.name}转账"
  288. model = AlipayCommerceEcTransAccountTransferModel()
  289. model.enterprise_id = account.enterprise_id
  290. model.account_book_id = account.account_book_id
  291. model.out_biz_no = get_snowflake_id_str(tenant_id)
  292. # 转账总金额,单位为元,精确到小数点后两位
  293. model.amount = str(data.amount)
  294. model.order_title = data.order_title
  295. payee_info = TransParticipant()
  296. payee_info.identity_type = data.payee_info.identity_type
  297. payee_info.name = data.payee_info.name
  298. payee_info.identity = data.payee_info.identity
  299. if data.payee_info.bankcard_ext_info:
  300. payee_info.bankcard_ext_info = BankCardExtInfoDTO.from_alipay_dict(
  301. data.payee_info.bankcard_ext_info.model_dump(exclude_none=True)
  302. )
  303. model.payee_info = payee_info
  304. request = AlipayCommerceEcTransAccountTransferRequest()
  305. request.biz_model = model
  306. client = AlipayClient.get_client()
  307. response = client.execute(request)
  308. if not response:
  309. raise CustomException(msg="转账失败: 无响应")
  310. result = AlipayCommerceEcTransAccountTransferResponse()
  311. result.parse_response_content(response)
  312. if not result.is_success():
  313. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  314. raise CustomException(msg=f"转账失败: {result.sub_msg or result.msg or result.code}")
  315. transfer_crud = TransferCRUD(auth)
  316. transfer_data = {
  317. "enterprise_id": model.enterprise_id,
  318. "out_biz_no": model.out_biz_no,
  319. "account_book_id": model.account_book_id,
  320. "amount": model.amount,
  321. "order_title": model.order_title,
  322. "payee_info": data.payee_info.model_dump() if data.payee_info else None,
  323. "status": result.status,
  324. "order_no": result.order_no,
  325. "fund_order_id": result.fund_order_id,
  326. }
  327. await transfer_crud.create(transfer_data)
  328. return TenantTransferResponse(
  329. status=result.status,
  330. order_no=result.order_no,
  331. fund_order_id=result.fund_order_id,
  332. )
  333. # @classmethod
  334. # async def withdraw_service(
  335. # cls,
  336. # auth: AuthSchema,
  337. # data: AccountWithdrawSchema
  338. # ) -> AccountOperationOutSchema:
  339. # """
  340. # 资金专户提现
  341. # 调用: alipay.commerce.ec.trans.account.withdraw
  342. # """
  343. # from alipay.aop.api.request.AlipayCommerceEcTransAccountWithdrawRequest import (
  344. # AlipayCommerceEcTransAccountWithdrawRequest,
  345. # )
  346. # from alipay.aop.api.domain.AlipayCommerceEcTransAccountWithdrawModel import (
  347. # AlipayCommerceEcTransAccountWithdrawModel,
  348. # )
  349. # from alipay.aop.api.response.AlipayCommerceEcTransAccountWithdrawResponse import (
  350. # AlipayCommerceEcTransAccountWithdrawResponse,
  351. # )
  352. # crud = AccountCRUD(auth)
  353. # enterprise = await crud.get_by_enterprise_id(data.enterprise_id)
  354. # if not enterprise:
  355. # raise CustomException(msg="企业不存在")
  356. # model = AlipayCommerceEcTransAccountWithdrawModel()
  357. # model.enterprise_id = enterprise.alipay_enterprise_id
  358. # model.account_book_id = data.account_book_id
  359. # model.amount = str(data.amount)
  360. # model.out_biz_no = data.out_biz_no
  361. # request = AlipayCommerceEcTransAccountWithdrawRequest()
  362. # request.biz_model = model
  363. # client = AlipayClient.get_client()
  364. # response = client.execute(request)
  365. # if not response:
  366. # raise CustomException(msg="提现失败: 无响应")
  367. # result = AlipayCommerceEcTransAccountWithdrawResponse()
  368. # result.parse_response_content(response)
  369. # if not result.is_success():
  370. # log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  371. # raise CustomException(msg=f"提现失败: {result.msg}")
  372. # withdraw_crud = WithdrawCRUD(auth)
  373. # withdraw_data = {
  374. # "enterprise_id": data.enterprise_id,
  375. # "out_biz_no": data.out_biz_no,
  376. # "account_book_id": data.account_book_id,
  377. # "amount": data.amount,
  378. # "status": WithdrawStatusEnum.DEALING.value,
  379. # "order_no": result.order_no,
  380. # }
  381. # await withdraw_crud.create(withdraw_data)
  382. # return AccountOperationOutSchema(
  383. # enterprise_id=data.enterprise_id,
  384. # account_book_id=data.account_book_id,
  385. # out_biz_no=data.out_biz_no,
  386. # status=WithdrawStatusEnum.DEALING.value,
  387. # )
  388. @classmethod
  389. async def query_account_service(
  390. cls,
  391. auth: AuthSchema,
  392. data: AccountQuerySchema
  393. ) -> list[Any]:
  394. """
  395. 查询资金专户(调用支付宝接口)
  396. 调用: alipay.commerce.ec.trans.account.query
  397. """
  398. from alipay.aop.api.request.AlipayCommerceEcTransAccountQueryRequest import (
  399. AlipayCommerceEcTransAccountQueryRequest,
  400. )
  401. from alipay.aop.api.domain.AlipayCommerceEcTransAccountQueryModel import (
  402. AlipayCommerceEcTransAccountQueryModel,
  403. )
  404. from alipay.aop.api.response.AlipayCommerceEcTransAccountQueryResponse import (
  405. AlipayCommerceEcTransAccountQueryResponse,
  406. )
  407. from alipay.aop.api.domain.FundAccountApiDTO import (
  408. FundAccountApiDTO,
  409. )
  410. model = AlipayCommerceEcTransAccountQueryModel()
  411. model.enterprise_id = data.enterprise_id
  412. request = AlipayCommerceEcTransAccountQueryRequest()
  413. request.biz_model = model
  414. client = AlipayClient.get_client()
  415. response = client.execute(request)
  416. if not response:
  417. raise CustomException(msg="查询资金专户失败: 无响应")
  418. result = AlipayCommerceEcTransAccountQueryResponse()
  419. result.parse_response_content(response)
  420. if not result.is_success():
  421. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  422. raise CustomException(msg=f"查询资金专户失败: {result.msg}")
  423. collect = []
  424. for v in list(result.account_list or []):
  425. account = FundAccountApiDTO.to_alipay_dict(v)
  426. collect.append(account)
  427. return collect
  428. @classmethod
  429. async def transfer_detail_service(
  430. cls,
  431. auth: AuthSchema,
  432. out_biz_no: str
  433. ) -> TransferOutSchema:
  434. """
  435. 查询转账记录详情
  436. """
  437. crud = TransferCRUD(auth)
  438. transfer = await crud.get_by_out_biz_no(out_biz_no)
  439. if not transfer:
  440. raise CustomException(msg="转账记录不存在")
  441. return TransferOutSchema.model_validate(transfer)
  442. @classmethod
  443. async def transfer_list_service(
  444. cls,
  445. auth: AuthSchema,
  446. page_no: int = 1,
  447. page_size: int = 20,
  448. search: dict | None = None,
  449. ) -> dict:
  450. """
  451. 查询转账记录列表
  452. """
  453. log.info(f"查询转账记录列表: {page_no}, {page_size}, {search}")
  454. crud = TransferCRUD(auth)
  455. offset = (page_no - 1) * page_size
  456. return await crud.page(
  457. offset=offset,
  458. limit=page_size,
  459. order_by=[{"id": "desc"}],
  460. search=search or {},
  461. out_schema=TransferListOutSchema,
  462. )
  463. @classmethod
  464. async def update_transfer_status_service(
  465. cls,
  466. auth: AuthSchema,
  467. order_no: str,
  468. status: str,
  469. ext_info: dict = {}
  470. ) -> None:
  471. """
  472. 更新转账状态(由通知处理器调用)
  473. """
  474. crud = TransferCRUD(auth)
  475. transfer = await crud.get_by_order_no(order_no)
  476. if not transfer:
  477. log.warning(f"转账记录不存在: {order_no}")
  478. return
  479. update_data = {}
  480. update_data["status"] = status
  481. if ext_info:
  482. update_data["ext_info"] = ext_info
  483. await crud.update_by_order_no(order_no, update_data)
  484. @classmethod
  485. async def update_deposit_status_service(
  486. cls,
  487. auth: AuthSchema,
  488. out_biz_no: str,
  489. status: str,
  490. ) -> None:
  491. """
  492. 更新充值状态(由通知处理器调用)
  493. """
  494. crud = DepositCRUD(auth)
  495. deposit = await crud.get_by_out_biz_no(out_biz_no)
  496. if not deposit:
  497. log.warning(f"充值记录不存在: {out_biz_no}")
  498. return
  499. update_data = {"status": status}
  500. await crud.update_by_out_biz_no(out_biz_no, update_data)
  501. @classmethod
  502. async def update_withdraw_status_service(
  503. cls,
  504. auth: AuthSchema,
  505. out_biz_no: str,
  506. status: str,
  507. error_code: str | None = None,
  508. error_msg: str | None = None,
  509. ) -> None:
  510. """
  511. 更新提现状态(由通知处理器调用)
  512. """
  513. crud = WithdrawCRUD(auth)
  514. withdraw = await crud.get_by_out_biz_no(out_biz_no)
  515. if not withdraw:
  516. log.warning(f"提现记录不存在: {out_biz_no}")
  517. return
  518. update_data = {"status": status}
  519. if error_code:
  520. update_data["error_code"] = error_code
  521. if error_msg:
  522. update_data["error_msg"] = error_msg
  523. await crud.update_by_out_biz_no(out_biz_no, update_data)
  524. @classmethod
  525. async def consume_detail_query_service(
  526. cls,
  527. auth: AuthSchema,
  528. pay_no: str,
  529. enterprise_id: str | None = None,
  530. ant_shop_id: str | None = None,
  531. query_options: list[str] | None = None,
  532. ) -> dict:
  533. """
  534. 账单详情查询(✅)
  535. 调用: alipay.commerce.ec.consume.detail.query
  536. 用于查询企业码账单详情,支持查询关联退款、订单、票据等信息。
  537. """
  538. from alipay.aop.api.request.AlipayCommerceEcConsumeDetailQueryRequest import (
  539. AlipayCommerceEcConsumeDetailQueryRequest,
  540. )
  541. from alipay.aop.api.domain.AlipayCommerceEcConsumeDetailQueryModel import (
  542. AlipayCommerceEcConsumeDetailQueryModel,
  543. )
  544. from alipay.aop.api.response.AlipayCommerceEcConsumeDetailQueryResponse import (
  545. AlipayCommerceEcConsumeDetailQueryResponse,
  546. )
  547. model = AlipayCommerceEcConsumeDetailQueryModel()
  548. model.pay_no = pay_no
  549. if enterprise_id:
  550. model.enterprise_id = enterprise_id
  551. if ant_shop_id:
  552. model.ant_shop_id = ant_shop_id
  553. if query_options:
  554. model.query_options = query_options
  555. request = AlipayCommerceEcConsumeDetailQueryRequest()
  556. request.biz_model = model
  557. client = AlipayClient.get_client()
  558. response = client.execute(request)
  559. if not response:
  560. raise CustomException(msg="账单详情查询失败: 无响应")
  561. result = AlipayCommerceEcConsumeDetailQueryResponse()
  562. result.parse_response_content(response)
  563. if not result.is_success():
  564. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  565. raise CustomException(msg=f"账单详情查询失败: {result.msg}")
  566. consume_info = result.consume_info
  567. if not consume_info:
  568. raise CustomException(msg="账单详情查询失败: 无账单信息")
  569. return {
  570. "account_id": consume_info.account_id,
  571. "pay_no": consume_info.pay_no,
  572. "consume_type": consume_info.consume_type,
  573. "gmt_biz_create": consume_info.gmt_biz_create,
  574. "consume_biz_type": consume_info.consume_biz_type,
  575. "consume_amount": consume_info.consume_amount,
  576. "order_complete_label": consume_info.order_complete_label,
  577. "refund_status": consume_info.refund_status,
  578. "refund_amount": consume_info.refund_amount,
  579. "peer_payer_card_name": consume_info.peer_payer_card_name,
  580. "user_id": getattr(consume_info, 'user_id', None),
  581. "open_id": getattr(consume_info, 'open_id', None),
  582. "enterprise_id": consume_info.enterprise_id,
  583. "employee_id": consume_info.employee_id,
  584. "enterprise_name": getattr(consume_info, 'enterprise_name', None),
  585. "employee_name": getattr(consume_info, 'employee_name', None),
  586. "consume_scene_code": getattr(consume_info, 'consume_scene_code', None),
  587. "consume_type_sub_category": getattr(consume_info, 'consume_type_sub_category', None),
  588. "consume_title": getattr(consume_info, 'consume_title', None),
  589. "gmt_pay": getattr(consume_info, 'gmt_pay', None),
  590. "gmt_refund": getattr(consume_info, 'gmt_refund', None),
  591. "pay_amount": getattr(consume_info, 'pay_amount', None),
  592. "invoice_amount": getattr(consume_info, 'invoice_amount', None),
  593. "peer_pay_amount": getattr(consume_info, 'peer_pay_amount', None),
  594. "subsidy_amount": getattr(consume_info, 'subsidy_amount', None),
  595. "ext_infos": getattr(consume_info, 'ext_infos', None),
  596. }