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