You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

337 lines
13 KiB

  1. <?php
  2. namespace addons\nzf;
  3. use addons\unishop\model\Config;
  4. /**
  5. * Class WxPayService
  6. * 微信交易接口调用核心
  7. * @package Base\Tool
  8. */
  9. class WeChatPay
  10. {
  11. public $unifiedOrderUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//统一下单API
  12. /************************手机浏览器内web支付**************************************/
  13. /**
  14. *
  15. */
  16. public function H5Pay($params)
  17. {
  18. if (empty($params['notify_url'])) {
  19. $params['notify_url'] = Config::getByName('notify_url')['value'];
  20. }
  21. $data = array(
  22. 'name' => $params['name'],//名称
  23. 'order_id' => (string)$params['order_id'] . '_' . rand(1000, 9999),//支付ID
  24. 'total_fee' => (string)$params['total_fee'] * 100,//总金
  25. 'notify_url' => $params['notify_url'],//支付回调接口
  26. 'trade_type' => 'MWEB',//用户的OPENID
  27. 'scene_info' => [
  28. 'h5_info' => [
  29. 'type' => 'Wap',
  30. 'wap_url' => Config::getByName('name')['value'],
  31. 'wap_name' => Config::getByName('name')['value']
  32. ]
  33. ]
  34. );
  35. $getpay = $this->unifiedOrderForWeChat($data);//统一下单API
  36. if ($getpay['flag'] == false) {
  37. return $getpay;
  38. }
  39. if (empty($getpay['data']['mweb_url'])) {
  40. return Util::returnArrEr('预支付会话异常!');
  41. }
  42. return Util::returnArrSu('', ['pay_url' => $getpay['data']['mweb_url']]);
  43. }
  44. /************************微信浏览器内web支付**************************************/
  45. /**
  46. * Function Description:去微信下单并获取返回
  47. * Function Name: unifiedOrderByOrderId
  48. * @param $params array
  49. * $order_id string 订单表 订单ID
  50. * $name string 产品名称
  51. * $total_fee int 总金额 单位分
  52. * $openid string 用户opendid
  53. * @return array
  54. *
  55. * @author nzf
  56. */
  57. public function webPay($params)
  58. {
  59. if (empty($params['notify_url'])) {
  60. $params['notify_url'] = Config::getByName('notify_url')['value'];;
  61. }
  62. $data = array(
  63. 'name' => $params['name'],//名称
  64. 'order_id' => (string)$params['order_id'] . '_' . rand(1000, 9999),//支付ID
  65. 'total_fee' => (string)$params['total_fee'] * 100,//总金
  66. 'notify_url' => $params['notify_url'],//支付回调接口
  67. 'openid' => $params['openid']//用户的OPENID
  68. );
  69. $getPrepayId = $this->unifiedOrderForWeChat($data);//统一下单API
  70. if ($getPrepayId['flag'] == false) {
  71. return $getPrepayId;
  72. }
  73. //设置成功返回的结果数
  74. $return = array(
  75. 'appId' => Config::getByName('app_id')['value'],//微信�?放平台审核�?�过的应用APPID
  76. 'package' => 'prepay_id=' . $getPrepayId['data']['prepay_id'],//微信返回的支付交易会话ID
  77. 'nonceStr' => self::getNonceStr(),//随机字符�?
  78. 'signType' => 'MD5',
  79. 'timeStamp' => strval(time()),//当前时间�?
  80. );
  81. $return['paySign'] = self::getSign($return);
  82. return Util::returnArrSu('', array('payData' => $return, 'price' => $params['total_fee']));
  83. }
  84. /**
  85. * Function Description:统一下单API
  86. * Function Name: unifiedOrder
  87. * @param $params array
  88. * attach 附加数据,在查询API和支付知中原样返回,该字段主要用于商户携带订单的自定义数
  89. * line_name 线路名称
  90. * order_id 订单ID
  91. * total_fee 总金
  92. * notify_url 回调地址
  93. *
  94. * @return array
  95. *
  96. * @author nzf
  97. */
  98. public function unifiedOrderForWeChat($params)
  99. {
  100. $data = array(
  101. 'appid' => Config::getByName('app_id')['value'],//微信开放平台审核过的应用APPID
  102. 'attach' => empty($params['attach']) ? '' : $params['attach'],
  103. 'body' => $params['name'],//产品名称
  104. 'mch_id' => Config::getByName('mch_id')['value'],//商户微信支付分配的商户号
  105. 'nonce_str' => $this->getNonceStr(),//随机字符
  106. 'notify_url' => $params['notify_url'],//通知地址
  107. 'out_trade_no' => $params['order_id'],//商户订单ID加上当前时间
  108. 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],//用户端实际ip
  109. 'total_fee' => $params['total_fee'],//订单总金额,单位为分
  110. 'trade_type' => empty($params['trade_type']) ? 'JSAPI' : $params['trade_type'],//交易类型
  111. );
  112. if (empty($params['openid']) == false) {//存在openid
  113. $data['openid'] = $params['openid'];
  114. }
  115. if (isset($params['scene_info'])) {
  116. $data['scene_info'] = json_encode($params['scene_info']);
  117. }
  118. $data['sign'] = $this->getSign($data);//交易签名
  119. $curl = new CurlInterface($data, 2);//函数
  120. $curl->setBaseUrl($this->unifiedOrderUrl);
  121. $result = $curl->execute('', 'POST');
  122. if (empty($result['prepay_id'])) {
  123. return Util::returnArrEr('预支付交易会话异常!');
  124. }
  125. return Util::returnArrSu('', array('prepay_id' => $result['prepay_id'], 'mweb_url' => empty($result['mweb_url']) ? '' : $result['mweb_url']));
  126. }
  127. /************************扫描支付**************************************/
  128. /**
  129. * Function Description:去微信下单并获取返回
  130. * Function Name: unifiedOrderByOrderId
  131. * @param $par
  132. * @return array
  133. *
  134. * @author nzf
  135. */
  136. public function unifiedOrderByOrderIdForSao($par)
  137. {
  138. $notify_url = Config::getByName('notify_url')['value'];
  139. $data = array(
  140. 'name' => $par['name'],//线路名称
  141. 'order_id' => (string)$par['order_id'] . '_' . rand(100, 999),//订单ID
  142. 'total_fee' => $par['total_fee'] * 100,//总金
  143. 'notify_url' => $notify_url
  144. );
  145. $codUrl = $this->unifiedOrderForSao($data);//统一下单API
  146. if ($codUrl['flag'] == false) {
  147. return $codUrl;
  148. }
  149. //设置成功返回的结果数�?
  150. $url = '/fx/?r=weChat/we-chat/q-code&qCode=' . urlencode($codUrl['data']['code_url']) . '&_math=' . rand(100, 999);
  151. return Util::returnArrSu('', array('codUrl' => $url, 'price' => $par['total_fee']));
  152. }
  153. /**
  154. * Function Description:统一下单API
  155. * Function Name: unifiedOrder
  156. * @param $params array
  157. * attach 附加数据,在查询API和支付知中原样返回,该字段主要用于商户携带订单的自定义数
  158. * line_name 线路名称
  159. * order_id 订单ID
  160. * total_fee 总金
  161. * notify_url 回调地址
  162. *
  163. * @return array
  164. *
  165. * @author 倪宗�?
  166. */
  167. public function unifiedOrderForSao($params)
  168. {
  169. $data = array(
  170. 'appid' => Config::getByName('app_id')['value'],//微信�?放平台审核�?�过的应用APPID
  171. 'mch_id' => Config::getByName('mch_id')['value'],//商户�? 微信支付分配的商户号
  172. 'nonce_str' => $this->getNonceStr(),//随机字符�?
  173. 'body' => $params['name'],//线路名称 �? 上订单ID
  174. 'out_trade_no' => $params['order_id'],//商户订单ID加上当前时间
  175. 'total_fee' => $params['total_fee'],//订单总金额,单位为分
  176. 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],//用户端实际ip
  177. 'notify_url' => $params['notify_url'],//通知地址
  178. 'trade_type' => 'NATIVE',//交易类型
  179. );
  180. $data['sign'] = $this->getSign($data);//交易签名
  181. $curl = new CurlInterface($data, 2);//函数�?
  182. $curl->setBaseUrl($this->unifiedOrderUrl);
  183. $result = $curl->execute('', 'POST');
  184. if (empty($result['prepay_id'])) {
  185. return Util::returnArrEr('预支付交易会话异常!');
  186. }
  187. return Util::returnArrSu('', array('prepay_id' => $result['prepay_id'], 'code_url' => $result['code_url']));
  188. }
  189. /***************************************退款****************************************/
  190. /**
  191. * Des:微信退款接口
  192. * Name: cancelOrder
  193. * @param $params
  194. * @return array
  195. * @author 倪宗锋
  196. */
  197. public static function cancelOrder($params)
  198. {
  199. $arr = array(
  200. 'appid' => Config::getByName('app_id')['value'],
  201. 'mch_id' => Config::getByName('mch_id')['value'],
  202. 'nonce_str' => static::getNonceStr(),
  203. 'out_trade_no' => (string)$params['order_id'],//订单ID
  204. 'out_refund_no' => (string)date('YmdHis') . rand(100, 999),//退款ID
  205. 'total_fee' => (string)$params['total_fee'] * 100,//订单总金额 元
  206. 'refund_fee' => (string)$params['refund_fee'] * 100,//退款金额 元
  207. 'op_user_id' => Config::getByName('mch_id')['value']
  208. );
  209. $arr['sign'] = static::getSign($arr);
  210. $curl = new CurlInterface($arr, 2);//函数类
  211. $certArr = [
  212. "SSLCERT_PATH"=>ROOT_PATH.Config::getByName('cert_path')['value'],
  213. "SSLKEY_PATH"=>ROOT_PATH.Config::getByName('key_path')['value']
  214. ];
  215. $curl->setCert($certArr);
  216. $curl->setBaseUrl('https://api.mch.weixin.qq.com/secapi/pay/refund');
  217. $result = $curl->execute('', 'POST');
  218. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  219. return Util::returnArrSu();
  220. }
  221. $msg = $result['return_msg'];
  222. if ($msg == 'OK') {
  223. $msg = $result['err_code_des'];
  224. }
  225. return Util::returnArrEr('退款失败!' . $msg);
  226. }
  227. /*************************************通用方法**************************************/
  228. /**
  229. * Des:检测是否已经支付
  230. * Name: checkIsPay
  231. * @param $orderId
  232. * @return array
  233. * @author 倪宗锋
  234. */
  235. public static function checkIsPay($orderId)
  236. {
  237. $arr = array(
  238. 'appid' => Config::getByName('app_id')['value'],
  239. 'mch_id' => Config::getByName('mch_id')['value'],
  240. 'out_trade_no' => (string)$orderId,//订单ID
  241. 'nonce_str' => static::getNonceStr(),
  242. );
  243. $arr['sign'] = static::getSign($arr);
  244. $curl = new CurlInterface($arr, 2);//函数类
  245. $certArr = [
  246. "SSLCERT_PATH"=>ROOT_PATH.Config::getByName('cert_path')['value'],
  247. "SSLKEY_PATH"=>ROOT_PATH.Config::getByName('key_path')['value']
  248. ];
  249. $curl->setCert($certArr);
  250. $curl->setBaseUrl('https://api.mch.weixin.qq.com/pay/orderquery');
  251. $result = $curl->execute('', 'POST');
  252. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  253. return Util::returnArrSu('', $result);
  254. }
  255. $msg = $result['return_msg'];
  256. if ($msg == 'OK') {
  257. $msg = $result['err_code_des'];
  258. }
  259. return Util::returnArrEr('退款失败!' . $msg);
  260. }
  261. /**
  262. * Function Description:获取签名
  263. * Function Name: getSign
  264. * @param $params
  265. * @return string
  266. *
  267. * @author nzf
  268. */
  269. public static function getSign($params)
  270. {
  271. if (isset($params['sign'])) {
  272. unset($params['sign']);
  273. }
  274. //签名步骤按字典序排序参
  275. ksort($params);
  276. $string = self::ToUrlParams($params);
  277. //签名步骤二:在string后加入KEY
  278. $string = $string . "&key=" . Config::getByName('key')['value'];
  279. //签名步骤三:MD5加密
  280. $string = md5($string);
  281. //签名步骤四:有字符转为大
  282. $result = strtoupper($string);
  283. return $result;
  284. }
  285. /**
  286. * Function Description:格式化参�? 格式化成url参数
  287. * Function Name: ToUrlParams
  288. * @param $params
  289. *
  290. * @return string
  291. *
  292. * @author 倪宗�?
  293. */
  294. public static function ToUrlParams($params)
  295. {
  296. $buff = "";
  297. foreach ($params as $k => $v) {
  298. if ($k != "sign" && $v != "" && !is_array($v)) {
  299. $buff .= $k . "=" . $v . "&";
  300. }
  301. }
  302. $buff = trim($buff, "&");
  303. return $buff;
  304. }
  305. /**
  306. * Function Description:产生的随机字符串 不长
  307. * Function Name: getNonceStr
  308. * @param int $length
  309. *
  310. * @return string
  311. *
  312. * @author 倪宗�?
  313. */
  314. public static function getNonceStr($length = 32)
  315. {
  316. $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
  317. $str = "";
  318. for ($i = 0; $i < $length; $i++) {
  319. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  320. }
  321. return $str;
  322. }
  323. }