card.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, unicode_literals
  3. from library.wechatpy.client.api.base import BaseWeChatAPI
  4. class WeChatCard(BaseWeChatAPI):
  5. API_BASE_URL = 'https://api.weixin.qq.com/'
  6. def create(self, card_data):
  7. """
  8. 创建卡券
  9. :param card_data: 卡券信息
  10. :return: 创建的卡券 ID
  11. """
  12. result = self._post(
  13. 'card/create',
  14. data=card_data,
  15. result_processor=lambda x: x['card_id']
  16. )
  17. return result
  18. def batch_add_locations(self, location_data):
  19. """
  20. 批量导入门店信息
  21. :param location_data: 门店信息
  22. :return: 门店 ID 列表,插入失败的门店元素值为 -1
  23. """
  24. result = self._post(
  25. 'card/location/batchadd',
  26. data=location_data,
  27. result_processor=lambda x: x['location_id_list']
  28. )
  29. return result
  30. def batch_get_locations(self, offset=0, count=0):
  31. """
  32. 批量获取门店信息
  33. """
  34. return self._post(
  35. 'card/location/batchget',
  36. data={
  37. 'offset': offset,
  38. 'count': count
  39. }
  40. )
  41. def get_colors(self):
  42. """
  43. 获得卡券的最新颜色列表,用于创建卡券
  44. :return: 颜色列表
  45. """
  46. result = self._get(
  47. 'card/getcolors',
  48. result_processor=lambda x: x['colors']
  49. )
  50. return result
  51. def create_qrcode(self, qrcode_data):
  52. """
  53. 创建卡券二维码
  54. :param qrcode_data: 二维码信息
  55. :return: 二维码 ticket,可使用 :func:show_qrcode 换取二维码文件
  56. """
  57. result = self._post(
  58. 'card/qrcode/create',
  59. data=qrcode_data,
  60. result_processor=lambda x: x['ticket']
  61. )
  62. return result
  63. def create_landingpage(self, buffer_data):
  64. """
  65. 创建货架
  66. """
  67. result = self._post(
  68. 'card/landingpage/create',
  69. data=buffer_data
  70. )
  71. return result
  72. def get_html(self, card_id):
  73. """
  74. 图文消息群发卡券
  75. """
  76. result = self._post(
  77. 'card/mpnews/gethtml',
  78. data={
  79. 'card_id': card_id
  80. },
  81. result_processor=lambda x: x['content']
  82. )
  83. return result
  84. def consume_code(self, code, card_id=None):
  85. """
  86. 消耗 code
  87. """
  88. card_data = {
  89. 'code': code
  90. }
  91. if card_id:
  92. card_data['card_id'] = card_id
  93. return self._post(
  94. 'card/code/consume',
  95. data=card_data
  96. )
  97. def decrypt_code(self, encrypt_code):
  98. """
  99. 解码加密的 code
  100. """
  101. result = self._post(
  102. 'card/code/decrypt',
  103. data={
  104. 'encrypt_code': encrypt_code
  105. },
  106. result_processor=lambda x: x['code']
  107. )
  108. return result
  109. def delete(self, card_id):
  110. """
  111. 删除卡券
  112. """
  113. return self._post(
  114. 'card/delete',
  115. data={
  116. 'card_id': card_id
  117. }
  118. )
  119. def get_code(self, code, card_id=None, check_consume=True):
  120. """
  121. 查询 code 信息
  122. """
  123. card_data = {
  124. 'code': code
  125. }
  126. if card_id:
  127. card_data['card_id'] = card_id
  128. if not check_consume:
  129. card_data['check_consume'] = check_consume
  130. return self._post(
  131. 'card/code/get',
  132. data=card_data
  133. )
  134. def get_card_list(self, openid, card_id=None):
  135. """
  136. 用于获取用户卡包里的,属于该appid下的卡券。
  137. """
  138. card_data = {
  139. 'openid': openid
  140. }
  141. if card_id:
  142. card_data['card_id'] = card_id
  143. return self._post(
  144. 'card/user/getcardlist',
  145. data=card_data
  146. )
  147. def batch_get(self, offset=0, count=50, status_list=None):
  148. """
  149. 批量查询卡券信息
  150. """
  151. card_data = {
  152. 'offset': offset,
  153. 'count': count
  154. }
  155. if status_list:
  156. card_data['status_list'] = status_list
  157. return self._post(
  158. 'card/batchget',
  159. data=card_data
  160. )
  161. def get(self, card_id):
  162. """
  163. 查询卡券详情
  164. """
  165. result = self._post(
  166. 'card/get',
  167. data={
  168. 'card_id': card_id
  169. },
  170. result_processor=lambda x: x['card']
  171. )
  172. return result
  173. def update_code(self, card_id, old_code, new_code):
  174. """
  175. 更新卡券 code
  176. """
  177. return self._post(
  178. 'card/code/update',
  179. data={
  180. 'card_id': card_id,
  181. 'code': old_code,
  182. 'new_code': new_code
  183. }
  184. )
  185. def invalid_code(self, code, card_id=None):
  186. """
  187. 设置卡券失效
  188. """
  189. card_data = {
  190. 'code': code
  191. }
  192. if card_id:
  193. card_data['card_id'] = card_id
  194. return self._post(
  195. 'card/code/unavailable',
  196. data=card_data
  197. )
  198. def update(self, card_data):
  199. """
  200. 更新卡券信息
  201. """
  202. return self._post(
  203. 'card/update',
  204. data=card_data
  205. )
  206. def set_paycell(self, card_id, is_open):
  207. """
  208. 更新卡券信息
  209. """
  210. return self._post(
  211. 'card/paycell/set',
  212. data={
  213. 'card_id': card_id,
  214. 'is_open': is_open
  215. }
  216. )
  217. def set_test_whitelist(self, openids=None, usernames=None):
  218. """
  219. 设置卡券测试用户白名单
  220. """
  221. openids = openids or []
  222. usernames = usernames or []
  223. return self._post(
  224. 'card/testwhitelist/set',
  225. data={
  226. 'openid': openids,
  227. 'username': usernames
  228. }
  229. )
  230. def activate_membercard(self, membership_number, code, **kwargs):
  231. """
  232. 激活会员卡 - 接口激活方式
  233. 详情请参见
  234. https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
  235. 参数示例:
  236. {
  237. "init_bonus": 100,
  238. "init_bonus_record":"旧积分同步",
  239. "init_balance": 200,
  240. "membership_number": "AAA00000001",
  241. "code": "12312313",
  242. "card_id": "xxxx_card_id",
  243. "background_pic_url": "https://mmbiz.qlogo.cn/mmbiz/0?wx_fmt=jpeg",
  244. "init_custom_field_value1": "xxxxx",
  245. "init_custom_field_value2": "xxxxx",
  246. "init_custom_field_value3": "xxxxx"
  247. }
  248. 返回示例:
  249. {"errcode":0, "errmsg":"ok"}
  250. :param membership_number: 必填,会员卡编号,由开发者填入,作为序列号显示在用户的卡包里。可与Code码保持等值
  251. :param code: 必填,领取会员卡用户获得的code
  252. :param kwargs: 其他非必填字段,包含则更新对应字段。详情参见微信文档 “6 激活会员卡” 部分
  253. :return: 参见返回示例
  254. """
  255. kwargs['membership_number'] = membership_number
  256. kwargs['code'] = code
  257. return self._post(
  258. 'card/membercard/activate',
  259. data=kwargs
  260. )
  261. def update_membercard(self, code, card_id, **kwargs):
  262. """
  263. 更新会员信息
  264. 详情请参见
  265. https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
  266. 注意事项:
  267. 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时
  268. add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。
  269. 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。
  270. 参数示例:
  271. {
  272. "code": "179011264953",
  273. "card_id": "p1Pj9jr90_SQRaVqYI239Ka1erkI",
  274. "background_pic_url": "https://mmbiz.qlogo.cn/mmbiz/0?wx_fmt=jpeg",
  275. "record_bonus": "消费30元,获得3积分",
  276. "bonus": 3000,
  277. "add_bonus": 30,
  278. "balance": 3000,
  279. "add_balance": -30,
  280. "record_balance": "购买焦糖玛琪朵一杯,扣除金额30元。",
  281. "custom_field_value1": "xxxxx",
  282. "custom_field_value2": "xxxxx",
  283. "notify_optional": {
  284. "is_notify_bonus": true,
  285. "is_notify_balance": true,
  286. "is_notify_custom_field1":true
  287. }
  288. }
  289. 返回示例:
  290. {
  291. "errcode": 0,
  292. "errmsg": "ok",
  293. "result_bonus": 100,
  294. "result_balance": 200,
  295. "openid": "oFS7Fjl0WsZ9AMZqrI80nbIq8xrA"
  296. }
  297. :param code: 必填,卡券Code码
  298. :param card_id: 必填,卡券ID
  299. :param kwargs: 其他非必填字段,包含则更新对应字段。详情参见微信文档 “7 更新会员信息” 部分
  300. :return: 参见返回示例
  301. """
  302. kwargs.update({
  303. 'code': code,
  304. 'card_id': card_id,
  305. })
  306. return self._post(
  307. 'card/membercard/updateuser',
  308. data=kwargs
  309. )
  310. def get_membercard_user_info(self, card_id, code):
  311. """
  312. 查询会员卡的会员信息
  313. 详情请参见
  314. https://mp.weixin.qq.com/wiki?id=mp1466494654_K9rNz
  315. :param card_id: 查询会员卡的 Card ID
  316. :param code: 所查询用户领取到的 code 值
  317. :return: 会员信息,包括激活资料、积分信息以及余额等信息
  318. """
  319. return self._post(
  320. 'card/membercard/userinfo/get',
  321. data={
  322. 'card_id': card_id,
  323. 'code': code,
  324. },
  325. )
  326. def add_pay_giftcard(self, base_info, extra_info, is_membercard):
  327. """
  328. 新增支付后投放卡券的规则,支持支付后领卡,支付后赠券
  329. 详情请参见
  330. https://mp.weixin.qq.com/wiki?id=mp1466494654_K9rNz
  331. :param base_info: 营销规则结构体
  332. :type base_info: dict
  333. :param extra_info: 支付规则结构体
  334. :type extra_info: dict
  335. :param is_membercard: 本次规则是否是领卡。(领卡传入 True, 赠券传入 False)
  336. :type is_membercard: bool
  337. :return: 规则 ID, 设置成功的列表,以及设置失败的列表
  338. """
  339. if is_membercard:
  340. rule_key = 'member_rule'
  341. rule_type = 'RULE_TYPE_PAY_MEMBER_CARD'
  342. else:
  343. rule_key = 'single_pay'
  344. rule_type = 'RULE_TYPE_SINGLE_PAY'
  345. return self._post(
  346. 'card/paygiftcard/add',
  347. data={
  348. 'rule_info': {
  349. 'type': rule_type,
  350. 'base_info': base_info,
  351. rule_key: extra_info,
  352. }
  353. }
  354. )
  355. def del_pay_giftcard(self, rule_id):
  356. """
  357. 删除支付后投放卡券的规则
  358. 详情请参见
  359. https://mp.weixin.qq.com/wiki?id=mp1466494654_K9rNz
  360. :param rule_id: 支付即会员的规则 ID
  361. """
  362. return self._post(
  363. 'card/paygiftcard/delete',
  364. data={
  365. 'rule_id': rule_id,
  366. },
  367. )
  368. def get_pay_giftcard(self, rule_id):
  369. """
  370. 查询支付后投放卡券的规则
  371. 详情请参见
  372. https://mp.weixin.qq.com/wiki?id=mp1466494654_K9rNz
  373. :param rule_id: 支付即会员的规则 ID
  374. :return: 支付后投放卡券的规则
  375. :rtype: dict
  376. """
  377. return self._post(
  378. 'card/paygiftcard/getbyid',
  379. data={
  380. 'rule_id': rule_id,
  381. },
  382. result_processor=lambda x: x['rule_info'],
  383. )
  384. def batch_get_pay_giftcard(self, effective=True, offset=0, count=10):
  385. """
  386. 批量查询支付后投放卡券的规则
  387. 详情请参见
  388. https://mp.weixin.qq.com/wiki?id=mp1466494654_K9rNz
  389. :param effective: 是否仅查询生效的规则
  390. :type effective: bool
  391. :param offset: 起始偏移量
  392. :type offset: int
  393. :param count: 查询的数量
  394. :type count: int
  395. :return: 支付后投放卡券规则的总数,以及查询到的列表
  396. """
  397. return self._post(
  398. 'card/paygiftcard/batchget',
  399. data={
  400. 'type': 'RULE_TYPE_PAY_MEMBER_CARD',
  401. 'effective': effective,
  402. 'offset': offset,
  403. 'count': count,
  404. },
  405. )
  406. def update_movie_ticket(self, code, ticket_class, show_time, duration,
  407. screening_room, seat_number, card_id=None):
  408. """
  409. 更新电影票
  410. """
  411. ticket = {
  412. 'code': code,
  413. 'ticket_class': ticket_class,
  414. 'show_time': show_time,
  415. 'duration': duration,
  416. 'screening_room': screening_room,
  417. 'seat_number': seat_number
  418. }
  419. if card_id:
  420. ticket['card_id'] = card_id
  421. return self._post(
  422. 'card/movieticket/updateuser',
  423. data=ticket
  424. )
  425. def checkin_boardingpass(self, code, passenger_name, seat_class,
  426. etkt_bnr, seat='', gate='', boarding_time=None,
  427. is_cancel=False, qrcode_data=None, card_id=None):
  428. """
  429. 飞机票接口
  430. """
  431. data = {
  432. 'code': code,
  433. 'passenger_name': passenger_name,
  434. 'class': seat_class,
  435. 'etkt_bnr': etkt_bnr,
  436. 'seat': seat,
  437. 'gate': gate,
  438. 'is_cancel': is_cancel
  439. }
  440. if boarding_time:
  441. data['boarding_time'] = boarding_time
  442. if qrcode_data:
  443. data['qrcode_data'] = qrcode_data
  444. if card_id:
  445. data['card_id'] = card_id
  446. return self._post(
  447. 'card/boardingpass/checkin',
  448. data=data
  449. )
  450. def update_luckymoney_balance(self, code, balance, card_id=None):
  451. """
  452. 更新红包余额
  453. """
  454. card_data = {
  455. 'code': code,
  456. 'balance': balance
  457. }
  458. if card_id:
  459. card_data['card_id'] = card_id
  460. return self._post(
  461. 'card/luckymoney/updateuserbalance',
  462. data=card_data
  463. )
  464. def get_redirect_url(self, url, encrypt_code, card_id):
  465. """
  466. 获取卡券跳转外链
  467. """
  468. from library.wechatpy.utils import WeChatSigner
  469. code = self.decrypt_code(encrypt_code)
  470. signer = WeChatSigner()
  471. signer.add_data(self.secret)
  472. signer.add_data(code)
  473. signer.add_data(card_id)
  474. signature = signer.signature
  475. r = '{url}?encrypt_code={code}&card_id={card_id}&signature={signature}'
  476. return r.format(
  477. url=url,
  478. code=encrypt_code,
  479. card_id=card_id,
  480. signature=signature
  481. )
  482. def deposit_code(self, card_id, codes):
  483. """
  484. 导入code
  485. """
  486. card_data = {
  487. 'card_id': card_id,
  488. 'code': codes
  489. }
  490. return self._post(
  491. 'card/code/deposit',
  492. data=card_data
  493. )
  494. def get_deposit_count(self, card_id):
  495. """
  496. 查询导入code数目
  497. """
  498. card_data = {
  499. 'card_id': card_id,
  500. }
  501. return self._post(
  502. 'card/code/getdepositcount',
  503. data=card_data
  504. )
  505. def check_code(self, card_id, codes):
  506. """
  507. 核查code
  508. """
  509. card_data = {
  510. 'card_id': card_id,
  511. 'code': codes
  512. }
  513. return self._post(
  514. 'card/code/checkcode',
  515. data=card_data
  516. )
  517. def modify_stock(self, card_id, n):
  518. """
  519. 修改库存
  520. """
  521. if n == 0:
  522. return
  523. card_data = {
  524. 'card_id': card_id,
  525. }
  526. if n > 0:
  527. card_data['increase_stock_value'] = n
  528. elif n < 0:
  529. card_data['reduce_stock_value'] = -n
  530. return self._post(
  531. 'card/modifystock',
  532. data=card_data
  533. )
  534. def get_activate_url(self, card_id, outer_str=None):
  535. """
  536. 获取开卡插件 Url, 内含调用开卡插件所需的参数
  537. 详情请参考
  538. https://mp.weixin.qq.com/wiki?id=mp1499332673_Unm7V
  539. :param card_id: 会员卡的card_id
  540. :param outer_str: 渠道值,用于统计本次领取的渠道参数
  541. :return: 内含调用开卡插件所需的参数的 Url
  542. """
  543. return self._post(
  544. 'card/membercard/activate/geturl',
  545. data={
  546. 'card_id': card_id,
  547. 'outer_str': outer_str,
  548. },
  549. result_processor=lambda x: x['url'],
  550. )
  551. def get_activate_info(self, activate_ticket):
  552. """
  553. 获取用户开卡时提交的信息
  554. 详情请参考
  555. https://mp.weixin.qq.com/wiki?id=mp1499332673_Unm7V
  556. :param activate_ticket: 跳转型开卡组件开卡后回调中的激活票据,可以用来获取用户开卡资料
  557. :return: 用户开卡时填写的字段值
  558. """
  559. return self._post(
  560. 'card/membercard/activatetempinfo/get',
  561. data={
  562. 'activate_ticket': activate_ticket,
  563. },
  564. result_processor=lambda x: x['info'],
  565. )
  566. def set_activate_user_form(self, card_id, **kwargs):
  567. """
  568. 设置开卡字段接口
  569. 详情请参考
  570. https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
  571. "6 激活会员卡" -> "6.2 一键激活" -> "步骤二:设置开卡字段接口"
  572. 参数示例:
  573. {
  574. "card_id": "pbLatjnrwUUdZI641gKdTMJzHGfc",
  575. "service_statement": {
  576. "name": "会员守则",
  577. "url": "https://www.qq.com"
  578. },
  579. "bind_old_card": {
  580. "name": "老会员绑定",
  581. "url": "https://www.qq.com"
  582. },
  583. "required_form": {
  584. "can_modify":false,
  585. "rich_field_list": [
  586. {
  587. "type": "FORM_FIELD_RADIO",
  588. "name": "兴趣",
  589. "values": [
  590. "钢琴",
  591. "舞蹈",
  592. "足球"
  593. ]
  594. },
  595. {
  596. "type": "FORM_FIELD_SELECT",
  597. "name": "喜好",
  598. "values": [
  599. "郭敬明",
  600. "韩寒",
  601. "南派三叔"
  602. ]
  603. },
  604. {
  605. "type": "FORM_FIELD_CHECK_BOX",
  606. "name": "职业",
  607. "values": [
  608. "赛车手",
  609. "旅行家"
  610. ]
  611. }
  612. ],
  613. "common_field_id_list": [
  614. "USER_FORM_INFO_FLAG_MOBILE"
  615. ]
  616. },
  617. "optional_form": {
  618. "can_modify":false,
  619. "common_field_id_list": [
  620. "USER_FORM_INFO_FLAG_LOCATION",
  621. "USER_FORM_INFO_FLAG_BIRTHDAY"
  622. ],
  623. "custom_field_list": [
  624. "喜欢的电影"
  625. ]
  626. }
  627. }
  628. common_field_id_list 值见常量 `wechatpy.constants.UserFormInfoFlag`
  629. :param card_id: 卡券ID
  630. :param kwargs: 其他非必填参数,见微信文档
  631. """
  632. kwargs['card_id'] = card_id
  633. return self._post(
  634. 'card/membercard/activateuserform/set',
  635. data=kwargs
  636. )