人人商城

weixin.pay.class.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <?php
  2. /**
  3. * [WeEngine System] Copyright (c) 2014 WE7.CC
  4. * WeEngine is NOT a free software, it under the license terms, visited http://www.we7.cc/ for more details.
  5. */
  6. defined('IN_IA') or exit('Access Denied');
  7. class WeiXinPay extends pay{
  8. public $wxpay;
  9. public function __construct() {
  10. global $_W;
  11. $setting = uni_setting($_W['uniacid']);
  12. $wxpay = $setting['payment']['wechat'];
  13. if (intval($wxpay['switch']) == 3) {
  14. $oauth_account = uni_setting($wxpay['service'], array('payment'));
  15. $oauth_acid = pdo_getcolumn('uni_account', array('uniacid' => $wxpay['service']), 'default_acid');
  16. $oauth_appid = pdo_getcolumn('account_wechats', array('acid' => $oauth_acid), 'key');
  17. $this->wxpay = array(
  18. 'appid' => $oauth_appid,
  19. 'mch_id' => $oauth_account['payment']['wechat_facilitator']['mchid'],
  20. 'sub_mch_id' => $wxpay['sub_mch_id'],
  21. 'key' => $oauth_account['payment']['wechat_facilitator']['signkey'],
  22. 'notify_url' => $_W['siteroot'] . 'payment/wechat/notify.php',
  23. );
  24. } else {
  25. $this->wxpay = array(
  26. 'appid' => $_W['account']['key'],
  27. 'mch_id' => $wxpay['mchid'],
  28. 'key' => !empty($wxpay['apikey']) ? $wxpay['apikey'] : $wxpay['signkey'],
  29. 'notify_url' => $_W['siteroot'] . 'payment/wechat/notify.php',
  30. );
  31. }
  32. }
  33. public function array2url($params) {
  34. $str = '';
  35. $ignore = array('coupon_refund_fee', 'coupon_refund_count');
  36. foreach($params as $key => $val) {
  37. if((empty($val) || is_array($val)) && !in_array($key, $ignore)) {
  38. continue;
  39. }
  40. $str .= "{$key}={$val}&";
  41. }
  42. $str = trim($str, '&');
  43. return $str;
  44. }
  45. public function bulidSign($params) {
  46. unset($params['sign']);
  47. ksort($params);
  48. $string = $this->array2url($params);
  49. $string = $string . "&key={$this->wxpay['key']}";
  50. $string = md5($string);
  51. $result = strtoupper($string);
  52. return $result;
  53. }
  54. public function parseResult($result) {
  55. if(substr($result, 0 , 5) != "<xml>"){
  56. return $result;
  57. }
  58. $result = json_decode(json_encode(isimplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  59. if(!is_array($result)) {
  60. return error(-1, 'xml结构错误');
  61. }
  62. if((isset($result['return_code']) && $result['return_code'] != 'SUCCESS') || ($result['err_code'] == 'ERROR' && !empty($result['err_code_des']))) {
  63. $msg = empty($result['return_msg']) ? $result['err_code_des'] : $result['return_msg'];
  64. return error(-1, $msg);
  65. }
  66. if(!empty($result['sign']) && $this->bulidsign($result) != $result['sign']) {
  67. return error(-1, '验证签名出错');
  68. }
  69. return $result;
  70. }
  71. public function requestApi($url, $params, $extra = array()) {
  72. load()->func('communication');
  73. $xml = array2xml($params);
  74. $response = ihttp_request($url, $xml, $extra);
  75. if(is_error($response)) {
  76. return $response;
  77. }
  78. $result = $this->parseResult($response['content']);
  79. return $result;
  80. }
  81. public function shortUrl($url) {
  82. $params = array(
  83. 'appid' => $this->wxpay['appid'],
  84. 'mch_id' => $this->wxpay['mch_id'],
  85. 'long_url' => $url,
  86. 'nonce_str' => random(32),
  87. );
  88. $params['sign'] = $this->bulidSign($params);
  89. $result = $this->requestApi('https://api.mch.weixin.qq.com/tools/shorturl', $params);
  90. if(is_error($result)) {
  91. return $result;
  92. }
  93. return $result['short_url'];
  94. }
  95. public function bulidNativePayurl($product_id, $short_url = true) {
  96. $params = array(
  97. 'appid' => $this->wxpay['appid'],
  98. 'mch_id' => $this->wxpay['mch_id'],
  99. 'time_stamp' => TIMESTAMP,
  100. 'nonce_str' => random(32),
  101. 'product_id' => $product_id,
  102. );
  103. $params['sign'] = $this->bulidSign($params);
  104. $url = "weixin://wxpay/bizpayurl?" . $this->array2url($params);
  105. if($short_url) {
  106. $url = $this->shortUrl($url);
  107. }
  108. return $url;
  109. }
  110. public function paylog2NativeUrl($params) {
  111. $result = $this->buildPayLog($params);
  112. if(is_error($result)) {
  113. return $result;
  114. }
  115. $url = $this->bulidNativePayurl($result);
  116. if(is_error($url)) {
  117. return $url;
  118. }
  119. return array('url' => $url, 'product_id' => $result);
  120. }
  121. public function buildUnifiedOrder($params) {
  122. if(empty($params['out_trade_no'])) {
  123. return error(-1, '缺少统一支付接口必填参数out_trade_no:商户订单号');
  124. }
  125. if(empty($params['body'])) {
  126. return error(-1, '缺少统一支付接口必填参数body:商品描述');
  127. }
  128. if(empty($params['total_fee'])) {
  129. return error(-1, '缺少统一支付接口必填参数total_fee:总金额');
  130. }
  131. if(empty($params['trade_type'])) {
  132. return error(-1, '缺少统一支付接口必填参数trade_type:交易类型');
  133. }
  134. if($params['trade_type'] == 'JSAPI' && empty($params['openid'])) {
  135. return error(-1, '统一支付接口中,缺少必填参数openid!交易类型为JSAPI时,openid为必填参数!');
  136. }
  137. if($params['trade_type'] == 'NATIVE' && empty($params['product_id'])) {
  138. return error(-1, '统一支付接口中,缺少必填参数product_id!交易类型为NATIVE时,product_id为必填参数!');
  139. }
  140. if(empty($params['notify_url'])) {
  141. $params['notify_url'] = $this->wxpay['notify_url'];
  142. }
  143. $params['appid'] = $this->wxpay['appid'];
  144. $params['mch_id'] = $this->wxpay['mch_id'];
  145. $params['spbill_create_ip'] = CLIENT_IP;
  146. $params['nonce_str'] = random(32);
  147. $params['sign'] = $this->bulidSign($params);
  148. $result = $this->requestApi('https://api.mch.weixin.qq.com/pay/unifiedorder', $params);
  149. if(is_error($result)) {
  150. return $result;
  151. }
  152. return $result;
  153. }
  154. public function buildMicroOrder($params) {
  155. if(empty($params['out_trade_no'])) {
  156. return error(-1, '缺少刷卡支付接口必填参数out_trade_no:商户订单号');
  157. }
  158. if(empty($params['body'])) {
  159. return error(-1, '缺少刷卡支付接口必填参数body:商品描述');
  160. }
  161. if(empty($params['total_fee'])) {
  162. return error(-1, '缺少刷卡支付接口必填参数total_fee:总金额');
  163. }
  164. if(empty($params['auth_code'])) {
  165. return error(-1, '缺少刷卡支付接口必填参数auth_code:授权码');
  166. }
  167. $uniontid = $params['uniontid'];
  168. unset($params['uniontid']);
  169. $params['appid'] = $this->wxpay['appid'];
  170. $params['mch_id'] = $this->wxpay['mch_id'];
  171. $params['spbill_create_ip'] = CLIENT_IP;
  172. $params['nonce_str'] = random(32);
  173. if (!empty($this->wxpay['sub_mch_id'])) {
  174. $params['sub_mch_id'] = $this->wxpay['sub_mch_id'];
  175. }
  176. $params['sign'] = $this->bulidSign($params);
  177. $result = $this->requestApi('https://api.mch.weixin.qq.com/pay/micropay', $params);
  178. if(is_error($result)) {
  179. return $result;
  180. }
  181. if($result['result_code'] != 'SUCCESS') {
  182. return array('errno' => -10, 'message' => $result['err_code_des'], 'uniontid' => $uniontid);
  183. }
  184. return $result;
  185. }
  186. public function NoticeMicroSuccessOrder($result) {
  187. if(empty($result['out_trade_no'])) {
  188. return array('errno' => -1, 'message' => '交易单号错误');
  189. }
  190. $pay_log = pdo_get('core_paylog', array('uniontid' => $result['out_trade_no']));
  191. if(empty($pay_log)) {
  192. return array('errno' => -1, 'message' => '交易日志不存在');
  193. }
  194. $order = pdo_get('paycenter_order', array('uniontid' => $result['out_trade_no']));
  195. if(empty($order)) {
  196. return array('errno' => -1, 'message' => '交易订单不存在');
  197. }
  198. $data = array(
  199. 'status' => 1,
  200. 'openid' => $result['openid'],
  201. );
  202. pdo_update('core_paylog', $data, array('uniontid' => $result['out_trade_no']));
  203. $data['trade_type'] = strtolower($result['trade_type']);
  204. $data['paytime'] = strtotime($result['time_end']);
  205. $data['uniontid'] = $result['out_trade_no'];
  206. $data['follow'] = $result['is_subscribe'] == 'Y' ? 1 : 0;
  207. pdo_update('paycenter_order', $data, array('uniontid' => $result['out_trade_no']));
  208. if(!$order['credit_status'] && $order['uid'] > 0) {
  209. load()->model('mc');
  210. $member_credit = mc_credit_fetch($order['uid']);
  211. $message = '';
  212. if($member_credit['credit1'] < $order['credit1']) {
  213. $message = '会员账户积分少于需扣除积分';
  214. }
  215. if($member_credit['credit2'] < $order['credit2']) {
  216. $message = '会员账户余额少于需扣除余额';
  217. }
  218. if(!empty($message)) {
  219. return array('errno' => -10, 'message' => "该订单需要扣除会员积分:{$order['credit1']}, 扣除余额{$order['credit2']}.出错:{$message}.你需要和会员沟通解决该问题.");
  220. }
  221. if($order['credit1'] > 0) {
  222. $status = mc_credit_update($order['uid'], 'credit1', -$order['credit1'], array(0, "会员刷卡消费,使用积分抵现,扣除{$order['credit1']}积分", 'system', $order['clerk_id'], $order['store_id'], $order['clerk_type']));
  223. }
  224. if($order['credit2'] > 0) {
  225. $status = mc_credit_update($order['uid'], 'credit2', -$order['credit2'], array(0, "会员刷卡消费,使用余额支付,扣除{$order['credit2']}余额", 'system', $order['clerk_id'], $order['store_id'], $order['clerk_type']));
  226. }
  227. }
  228. pdo_update('paycenter_order', array('credit_status' => 1), array('id' => $order['id']));
  229. return true;
  230. }
  231. public function buildJsApiPrepayid($product_id) {
  232. $order = pdo_get('core_paylog', array('plid' => $product_id));
  233. if(empty($order)) {
  234. return error(-1, '订单不存在');
  235. }
  236. if($order['status'] == 1) {
  237. return error(-1, '该订单已经支付,请勿重复支付');
  238. }
  239. $jspai = array(
  240. 'out_trade_no' => $order['uniontid'],
  241. 'trade_type' => 'JSAPI',
  242. 'openid' => $order['openid'],
  243. 'body' => $order['body'],
  244. 'total_fee' => $order['fee'] * 100,
  245. 'attach' => $order['uniacid'],
  246. );
  247. $result = $this->buildUnifiedOrder($jspai);
  248. if(is_error($result)) {
  249. return $result;
  250. }
  251. $jspai = array(
  252. 'appId' => $this->wxpay['appid'],
  253. 'timeStamp' => TIMESTAMP,
  254. 'nonceStr' => random(32),
  255. 'package' => 'prepay_id=' . $result['prepay_id'],
  256. 'signType' => 'MD5',
  257. );
  258. $jspai['paySign'] = $this->bulidSign($jspai);
  259. $jspai = <<<EOF
  260. <script type="text/javascript">
  261. document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
  262. WeixinJSBridge.invoke(
  263. 'getBrandWCPayRequest', {
  264. appId:'{$jspai['appId']}',
  265. timeStamp:'{$jspai['timeStamp']}',
  266. nonceStr:'{$jspai['nonceStr']}',
  267. package:'{$jspai['package']}',
  268. signType:'MD5',
  269. paySign:'{$jspai['paySign']}'
  270. },
  271. function(res){
  272. if(res.err_msg == 'get_brand_wcpay_request:ok' ) {
  273. alert('支付成功')
  274. } else {
  275. }
  276. }
  277. );
  278. }, false);
  279. </script>
  280. EOF;
  281. return $jspai;
  282. }
  283. public function buildNativePrepayid($product_id) {
  284. $order = pdo_get('core_paylog', array('plid' => $product_id));
  285. if(empty($order)) {
  286. return error(-1, '订单不存在');
  287. }
  288. if($order['status'] == 1) {
  289. return error(-1, '该订单已经支付,请勿重复支付');
  290. }
  291. $data = array(
  292. 'body' => $order['body'],
  293. 'out_trade_no' => $order['uniontid'],
  294. 'total_fee' => $order['fee'] * 100,
  295. 'trade_type' => 'NATIVE',
  296. 'product_id' => $order['plid'],
  297. 'attach' => $order['uniacid'],
  298. );
  299. $result = $this->buildUnifiedOrder($data);
  300. if(is_error($result)) {
  301. return $result;
  302. }
  303. $params = array(
  304. 'return_code' => 'SUCCESS',
  305. 'appid' => $this->wxpay['appid'],
  306. 'mch_id' => $this->wxpay['mch_id'],
  307. 'prepay_id' => $result['prepay_id'],
  308. 'nonce_str' => random(32),
  309. 'result_code' => 'SUCCESS',
  310. 'code_url' => $result['code_url'],
  311. );
  312. $params['sign'] = $this->bulidSign($params);
  313. return $params;
  314. }
  315. public function replyErrorNotify($msg) {
  316. $result = array(
  317. 'return_code' => 'FAIL',
  318. 'return_msg' => $msg,
  319. );
  320. echo array2xml($result);
  321. }
  322. public function closeOrder($trade_no) {
  323. $params = array(
  324. 'appid' => $this->wxpay['appid'],
  325. 'mch_id' => $this->wxpay['mch_id'],
  326. 'nonce_str' => random(32),
  327. 'out_trade_no' => trim($trade_no),
  328. );
  329. $params['sign'] = $this->bulidSign($params);
  330. $result = $this->requestApi('https://api.mch.weixin.qq.com/pay/closeorder', $params);
  331. if(is_error($result)) {
  332. return $result;
  333. }
  334. if($result['result_code'] == 'SUCCESS') {
  335. pdo_update('paycenter_order', array('status' => 'CLOSED'), array('tradeno' => $result['out_trade_no']));
  336. }
  337. return true;
  338. }
  339. public function queryOrder($id, $type = 1) {
  340. $params = array(
  341. 'appid' => $this->wxpay['appid'],
  342. 'mch_id' => $this->wxpay['mch_id'],
  343. 'nonce_str' => random(32),
  344. );
  345. if($type == 1) {
  346. $params['transaction_id'] = $id;
  347. } else {
  348. $params['out_trade_no'] = $id;
  349. }
  350. $params['sign'] = $this->bulidSign($params);
  351. $result = $this->requestApi('https://api.mch.weixin.qq.com/pay/orderquery', $params);
  352. if(is_error($result)) {
  353. return $result;
  354. }
  355. if($result['result_code'] != 'SUCCESS') {
  356. return error(-1, $result['err_code_des']);
  357. }
  358. $result['total_fee'] = $result['total_fee'] / 100; return $result;
  359. }
  360. public function downloadBill($date, $type = 'ALL') {
  361. $params = array(
  362. 'appid' => $this->wxpay['appid'],
  363. 'mch_id' => $this->wxpay['mch_id'],
  364. 'nonce_str' => random(32),
  365. 'bill_date' => $date,
  366. 'bill_type' => $type
  367. );
  368. $params['sign'] = $this->bulidSign($params);
  369. $result = $this->requestApi('https://api.mch.weixin.qq.com/pay/downloadbill', $params);
  370. return $result;
  371. }
  372. public function refundOrder($date, $type = 'ALL') {
  373. $params = array(
  374. 'appid' => $this->wxpay['appid'],
  375. 'mch_id' => $this->wxpay['mch_id'],
  376. 'nonce_str' => random(32),
  377. 'bill_date' => $date,
  378. 'bill_type' => $type
  379. );
  380. $params['sign'] = $this->bulidSign($params);
  381. $result = $this->requestApi('https://api.mch.weixin.qq.com/pay/downloadbill', $params);
  382. return $result;
  383. }
  384. public function refund($params) {
  385. global $_W;
  386. $params['sign'] = $this->bulidSign($params);
  387. $result = $this->requestApi('https://api.mch.weixin.qq.com/secapi/pay/refund', $params, array(CURLOPT_SSLCERT => ATTACHMENT_ROOT . $_W['uniacid'] . '_wechat_refund_all.pem'));
  388. return $result;
  389. }
  390. }