WxPay.Api.php
9.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
<?php
require_once IA_ROOT . '/addons/zh_cjdianc/inc/Log.php';
require_once IA_ROOT . "/addons/zh_cjdianc/inc/wxlib/WxPay.Exception.php";
require_once IA_ROOT . "/addons/zh_cjdianc/inc/wxlib/WxPay.Data.php";
require_once IA_ROOT . '/addons/zh_cjdianc/inc/wxlib/WxUnifiedOrder.php';
/**
*
* 接口访问类,包含所有微信支付API列表的封装,类中方法为static方法,
* 每个接口有默认超时时间(除提交被扫支付为10s,上报超时时间为1s外,其他均为6s)
* @author widyhu
*
*/
class WxPayApi
{
public static $orderQueryURL = "https://api.mch.weixin.qq.com/pay/orderquery";
public static $refundURL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
public static $transferURL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
public static $unifiedOrderURL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
/**
*
* 查询订单,WxPayOrderQuery中out_trade_no、transaction_id至少填一个
* appid、mchid、spbill_create_ip、nonce_str不需要填入
* @param WxPayOrderQuery $inputObj
* @param int $timeOut
* @throws WxPayException
* @return 成功时返回,其他抛异常
*/
public static function orderQuery($inputObj, $timeOut = 6, $key = '')
{
//检测必填参数
if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) {
throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
}
if(!$inputObj->IsAppidSet() ) {
throw new WxPayException("APPID 未填!");
}
if(!$inputObj->IsMch_idSet() ) {
throw new WxPayException("MCHID 未填!");
}
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串
$inputObj->SetSign($key); //签名
$xml = $inputObj->ToXml();
$startTimeStamp = self::getMillisecond();//请求开始时间
$response = self::postXmlCurl($xml, self::$orderQueryURL, false, $timeOut,'1','2');
$result = WxPayResults::Init($response);
return $result;
}
/**
*
*退款接口
*
*/
public static function refund($inputObj, $timeOut = 6,$f1,$f2,$key)
{
//检测必填参数
if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) {
throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
} else if(!$inputObj->IsOut_refund_noSet()) {
throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!");
} else if(!$inputObj->IsTotal_feeSet()) {
throw new WxPayException("退款申请接口中,缺少必填参数total_fee!");
} else if(!$inputObj->IsRefund_feeSet()) {
throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!");
} else if(!$inputObj->IsOp_user_idSet()) {
throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!");
}
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串
$inputObj->SetSign($key);//签名
$xml = $inputObj->ToXml();
$startTimeStamp = self::getMillisecond();//请求开始时间
$response = self::postXmlCurl($xml, self::$refundURL, true, $timeOut,$f1,$f2);
$result = WxPayResults::Init($response);
return $result;
}
/**
*
* 产生随机字符串,不长于32位
* @param int $length
* @return 产生的随机字符串
*/
public static function getNonceStr($length = 32)
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 以post方式提交xml到对应的接口url
*
* @param string $xml 需要post的xml数据
* @param string $url url
* @param bool $useCert 是否需要证书,默认不需要
* @param int $second url执行超时时间,默认30s
* @throws WxPayException
*/
private static function postXmlCurl($xml, $url, $useCert, $second = 30, $f1 = '', $f2 = '')
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
// //如果有配置代理这里就设置代理
// if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
// && WxPayConfig::CURL_PROXY_PORT != 0){
// curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
// curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
// }
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
if($useCert == true){
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $f1);
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $f2);
}
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
message("退款失败,CURL出错,错误码:".$error);
exit;
// throw new WxPayException("curl出错,错误码:$error");
}
}
/**
* 获取毫秒级别的时间戳
*/
private static function getMillisecond()
{
//获取毫秒的时间戳
$time = explode ( " ", microtime () );
$time = $time[1] . ($time[0] * 1000);
$time2 = explode( ".", $time );
$time = $time2[0];
return $time;
}
/**
* @param $inputObj
* @param int $timeOut
* @param $f1
* @param $f2
* @param $key
* @return array
* @throws WxPayException
*/
public static function transferTo($inputObj, $timeOut = 6, $f1, $f2, $key)
{
//检测必填参数
if(!$inputObj->IsMchAppIdSet()) {
throw new WxPayException("转账接口未填 mch_appid!");
} else if(!$inputObj->IsMchIdSet()) {
throw new WxPayException("转账接口未填 mchid!");
} else if(!$inputObj->IsPartnerTradeNoSet()) {
throw new WxPayException("订单号未设置!");
} else if(!$inputObj->IsSpbilCreateIpSet()) {
throw new WxPayException("IP 地址未设置!");
} else if(!$inputObj->IsOpenIdSet()) {
throw new WxPayException("未指定收款人微信openid!");
} else if(!$inputObj->IsAmountSet()) {
throw new WxPayException("未指定金额!");
} else if(!$inputObj->IsReUserNameSet()) {
throw new WxPayException("未指收款人名称!");
}
$inputObj->SetNonceStr(self::getNonceStr());//随机字符串
$inputObj->SetSign($key);//签名
$xml = $inputObj->ToXml();
$response = self::postXmlCurl($xml, self::$transferURL, true, $timeOut, $f1, $f2);
$result = WxPayResults::Init($response);
return $result;
}
public static function buildUnifiedOrder($inputObj, $key)
{
if(!$inputObj->IsAppIdSet()) {
throw new WxPayException("转账接口未填 appid!");
} else if(!$inputObj->IsMchIdSet()) {
throw new WxPayException("转账接口未填 mchid!");
} else if(!$inputObj->IsOutTradeNoSet()) {
throw new WxPayException("内部交易号未设置!");
} else if(!$inputObj->IsSpbilCreateIpSet()) {
throw new WxPayException("IP 地址未设置!");
} else if(!$inputObj->IsOpenIdSet()) {
throw new WxPayException("未指定收款人微信openid!");
} else if(!$inputObj->IsTotalFeeSet()) {
throw new WxPayException("未指定金额!");
}
$inputObj->SetNonceStr(self::getNonceStr());//随机字符串
$inputObj->SetSign($key);//签名
$xmlData = $inputObj->ToXml();
//Log::debug(json_encode('toxml:'.$xmlData, JSON_UNESCAPED_UNICODE));
$response = self::postXmlCurl($xmlData, self::$unifiedOrderURL, false, 60);
$result = WxPayResults::Init($response);
//Log::debug(json_encode('toxml - result:'. $result, JSON_UNESCAPED_UNICODE));
return $result;
}
/**
* @param $inputObj
* @param $key
* @return array
* @throws WxPayException
*/
public static function payParams($inputObj, $key)
{
$unifiedOrderResult = self::buildUnifiedOrder($inputObj, $key);
$parameters = array(
'appId' => $inputObj->GetAppId(), //小程序ID
'timeStamp' => '' . time() . '', //时间戳
'nonceStr' => self::getNonceStr(),
'package' => 'prepay_id=' . $unifiedOrderResult['prepay_id'], //数据包
'signType' => 'MD5'//签名方式
);
//签名
$parameters['paySign'] = $inputObj->MakeSign($key, $parameters);
return $parameters;
}
/**
*
* 支付结果通用通知
* @param function $callback
* 直接回调函数使用方法: notify(you_function);
* 回调类成员函数方法:notify(array($this, you_function));
* $callback 原型为:function function_name($data){}
*/
public static function notify($callback, &$msg)
{
//获取通知的数据
$xml = file_get_contents("php://input") ;
//如果返回成功则验证签名
try {
$result = WxPayResults::Init($xml);
} catch (WxPayException $e){
$msg = $e->errorMessage();
return false;
}
//Log::debug('notify----:'.$xml);
return call_user_func($callback, $result);
}
/**
* 直接输出xml
* @param string $xml
*/
public static function replyNotify($xml)
{
echo $xml;
}
/*
* $params = [$appid,$openid,$mch_id,$key,$out_trade_no,$body,$total_fee,$notify_url, ip]
*
* @param array $params
* @return array
*/
public static function payJsParams($params = [])
{
$unifiedOrder = new WxUnifiedOrder();
$unifiedOrder->SetAppId($params['appid']);
$unifiedOrder->SetMchId($params['mch_id']);
$unifiedOrder->SetBody($params['body']);
$unifiedOrder->SetOpenId($params['openid']);
$unifiedOrder->SetOutTradeNo($params['out_trade_no']);
$unifiedOrder->SetAttach($params['attach']);
$unifiedOrder->SetNotifyUrl($params['notify_url']);
$unifiedOrder->SetTotalFee($params['total_fee']);
$unifiedOrder->SetSpbilCreateIp($params['ip']);
$unifiedOrder->SetTradeType();
$wxPayApi = new WxPayApi();
$parameters = $wxPayApi->payParams($unifiedOrder, $params['key']);
return $parameters;
}
}