order.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, unicode_literals
  3. import time
  4. import random
  5. import datetime
  6. from library.wechatpy.pay.utils import get_external_ip
  7. from library.wechatpy.pay.base import BaseWeChatPayAPI
  8. from library.wechatpy.utils import json
  9. from library import to_text, timezone, random_string
  10. from library.wechatpy.pay.utils import calculate_signature
  11. class WeChatOrder(BaseWeChatPayAPI):
  12. def create(self, trade_type, body, total_fee, notify_url, client_ip=None,
  13. user_id=None, out_trade_no=None, detail=None, attach=None,
  14. fee_type='CNY', time_start=None, time_expire=None,
  15. goods_tag=None, product_id=None, device_info=None,
  16. limit_pay=None, scene_info=None, sub_user_id=None, **kwargs):
  17. """
  18. 统一下单接口
  19. :param trade_type: 交易类型,取值如下:JSAPI,NATIVE,APP,WAP, MWEB
  20. :param body: 商品描述
  21. :param total_fee: 总金额,单位分
  22. :param notify_url: 接收微信支付异步通知回调地址
  23. :param client_ip: 可选,APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
  24. :param user_id: 可选,用户在商户appid下的唯一标识。trade_type=JSAPI和appid已设定,此参数必传
  25. :param sub_user_id: 可选,小程序appid下的唯一标识。trade_type=JSAPI和sub_appid已设定,此参数必传
  26. :param out_trade_no: 可选,商户订单号,默认自动生成
  27. :param detail: 可选,商品详情
  28. :param attach: 可选,附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
  29. :param fee_type: 可选,符合ISO 4217标准的三位字母代码,默认人民币:CNY
  30. :param time_start: 可选,订单生成时间,默认为当前时间
  31. :param time_expire: 可选,订单失效时间,默认为订单生成时间后两小时
  32. :param goods_tag: 可选,商品标记,代金券或立减优惠功能的参数
  33. :param product_id: 可选,trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义
  34. :param device_info: 可选,终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
  35. :param limit_pay: 可选,指定支付方式,no_credit--指定不能使用信用卡支付
  36. :param scene_info: 可选,上报支付的场景信息
  37. :param kwargs: 其他未列举在上述参数中的统一下单接口调用参数,例如电子发票入口开放标识receipt
  38. :type scene_info: dict
  39. :return: 返回的结果数据
  40. """
  41. now = datetime.datetime.fromtimestamp(time.time(), tz=timezone('Asia/Shanghai'))
  42. hours_later = now + datetime.timedelta(hours=2)
  43. if time_start is None:
  44. time_start = now
  45. if time_expire is None:
  46. time_expire = hours_later
  47. if not out_trade_no:
  48. out_trade_no = '{0}{1}{2}'.format(
  49. self.mch_id,
  50. now.strftime('%Y%m%d%H%M%S'),
  51. random.randint(1000, 10000)
  52. )
  53. if scene_info is not None:
  54. scene_info = json.dumps(scene_info, ensure_ascii=False)
  55. data = {
  56. 'appid': self.appid,
  57. 'sub_appid': self.sub_appid,
  58. 'device_info': device_info,
  59. 'body': body,
  60. 'detail': detail,
  61. 'attach': attach,
  62. 'out_trade_no': out_trade_no,
  63. 'fee_type': fee_type,
  64. 'total_fee': total_fee,
  65. 'spbill_create_ip': client_ip or get_external_ip(),
  66. 'time_start': time_start.strftime('%Y%m%d%H%M%S'),
  67. 'time_expire': time_expire.strftime('%Y%m%d%H%M%S'),
  68. 'goods_tag': goods_tag,
  69. 'notify_url': notify_url,
  70. 'trade_type': trade_type,
  71. 'limit_pay': limit_pay,
  72. 'product_id': product_id,
  73. 'openid': user_id,
  74. 'sub_openid': sub_user_id,
  75. 'scene_info': scene_info,
  76. }
  77. data.update(kwargs)
  78. return self._post('pay/unifiedorder', data=data)
  79. def query(self, transaction_id = None, out_trade_no = None):
  80. """
  81. 查询订单
  82. :param transaction_id: 微信的订单号,优先使用
  83. :param out_trade_no: 商户系统内部的订单号,当没提供transaction_id时需要传这个。
  84. :return: 返回的结果数据
  85. """
  86. data = {
  87. 'appid': self.appid,
  88. 'transaction_id': transaction_id,
  89. 'out_trade_no': out_trade_no,
  90. }
  91. return self._post('pay/orderquery', data=data)
  92. def close(self, out_trade_no):
  93. """
  94. 关闭订单
  95. :param out_trade_no: 商户系统内部的订单号
  96. :return: 返回的结果数据
  97. """
  98. data = {
  99. 'appid': self.appid,
  100. 'out_trade_no': out_trade_no,
  101. }
  102. return self._post('pay/closeorder', data=data)
  103. def get_appapi_params(self, prepay_id, timestamp=None, nonce_str=None):
  104. """
  105. 获取 APP 支付参数
  106. :param prepay_id: 统一下单接口返回的 prepay_id 参数值
  107. :param timestamp: 可选,时间戳,默认为当前时间戳
  108. :param nonce_str: 可选,随机字符串,默认自动生成
  109. :return: 签名
  110. """
  111. data = {
  112. 'appid': self.appid,
  113. 'partnerid': self.mch_id,
  114. 'prepayid': prepay_id,
  115. 'package': 'Sign=WXPay',
  116. 'timestamp': timestamp or to_text(int(time.time())),
  117. 'noncestr': nonce_str or random_string(32)
  118. }
  119. sign = calculate_signature(data, self._client.api_key)
  120. data['sign'] = sign
  121. return data
  122. def reverse(self, transaction_id=None, out_trade_no=None):
  123. """
  124. 撤销订单
  125. :param transaction_id: 可选,微信的订单号,优先使用
  126. :param out_trade_no: 可选,商户系统内部的订单号,
  127. transaction_id、out_trade_no二选一,
  128. 如果同时存在优先级:transaction_id> out_trade_no
  129. :return: 返回的结果数据
  130. """
  131. data = {
  132. 'appid': self.appid,
  133. 'transaction_id': transaction_id,
  134. 'out_trade_no': out_trade_no,
  135. }
  136. return self._post('secapi/pay/reverse', data=data)