#### 1. 登錄商戶平臺,將商戶和小程序進(jìn)行關(guān)聯(lián) --- 在 `產(chǎn)品中心` 中的 `APPID賬號管理` 中,添加關(guān)聯(lián)的小程序 `appid` ![](https://img.itqaq.com/art/content/46f28aaf496fc53046d16706cf406e28.png) #### 2. 小程序調(diào)起支付API --- 小程序調(diào)起支付API:[https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5](https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5) 小程序API開發(fā)文檔:[https://developers.weixin.qq.com/miniprogram/dev/api/api-pay.html#wxrequestpaymentobject](https://developers.weixin.qq.com/miniprogram/dev/api/api-pay.html#wxrequestpaymentobject) #### 3. 封裝類 --- ```php <?php class Wechat { // 公眾號的或者小程序支付參數(shù) private $appId = "wx5117bexxxxxx"; private $appSecret = "136ae3xxxxxxx"; // 微信商戶號 private $mch_id = "15xxxx"; private $mch_key = "xxxasdfghjkxxxxxx"; // 回調(diào)地址 public $notify_url = ''; // 退款回掉地址 public $refund_notify_url = ''; private $request; private $nonce_str; public function __construct() { $this->request = request(); $this->notify_url = 'notice.php'; $this->refund_notify_url = 'refund.php'; $this->nonce_str = md5(date('YmdHis') . time() . rand(1000, 9999)); } //小程序登錄 /** * @param $code 獲取微信支付的登錄code * @return mixed */ public function wxLogin($code) { $url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $this->appId . "&secret=" . $this->appSecret . "&js_code=" . $code . "&grant_type=authorization_code"; return json_decode($this->execute($url), true); } /** * @param $out_trade_no 微信支付唯一訂單 * @param $openid 微信支付用戶的openid * @param $price 訂單支付的價格,(單位,元) * @param string $desc 訂單描述 * @return array|mixed 組裝支付參數(shù) */ public function getPayParameter($out_trade_no, $openid, $price, $desc = '') { header("Content-type:text/html;charset=utf-8"); //此處進(jìn)行字符集初始化, $data = [ 'appid' => $this->appId, 'body' => $desc, 'mch_id' => $this->mch_id, 'nonce_str' => $this->nonce_str,// 隨機字符串 'notify_url' => $this->notify_url,//異步回調(diào)地址 'openid' => $openid,//用戶登錄時獲取的code中含有的值 'out_trade_no' => $out_trade_no,//商家訂單號 'spbill_create_ip' => $this->get_real_ip(),//APP和網(wǎng)頁支付提交用戶端ip 'total_fee' => $price * 100,//訂單總額,單位:分 'attach' => 'order',//確定是哪個商家進(jìn)行的支付 'trade_type' => 'JSAPI'//交易類型 ]; //將數(shù)組轉(zhuǎn)化為Xml $data['sign'] = $this->makeSign($data); $abc_xml = $this->arrayToXml($data); //統(tǒng)一下單接口prepay_id $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $xml = $this->execute($url, $abc_xml, 1); //將XMl轉(zhuǎn)化為數(shù)組 $info = $this->xml2array($xml); if (!isset($info['prepay_id'])) { return $info; } $params = array( 'appId' => $this->appId, 'nonceStr' => $data['nonce_str'], 'package' => 'prepay_id=' . $info['prepay_id'], 'signType' => 'MD5', 'timeStamp' => '' . time(), ); $_info['paySign'] = $this->makeSign($params); $_info['timeStamp'] = "" . $params['timeStamp']; $_info['nonceStr'] = $params['nonceStr']; $_info['package'] = $params['package']; $_info['signType'] = $params['signType']; $_info['notify_url'] = $this->notify_url; // 請求成功后進(jìn)行返回數(shù)據(jù)信息 if ($info['return_code'] == 'SUCCESS' || $info['result_code'] == 'SUCCESS') { return $_info; } else { return []; } } /** * @param $openid 支付的用戶的openid * @return mixed */ public function userInfo($openid) { $token = $this->getWxAccessToken(); $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" . $token . "&openid=$openid&lang=zh_CN"; return json_decode($this->execute($url), true); } //獲取微信的token public function getWxAccessToken() { $key = $this->appId . 'miniProgram_access_token'; $accessToken = Cache::get($key); if ($accessToken) return $accessToken; $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $this->appId . "&secret=" . $this->appSecret; $con = json_decode($this->execute($url)); Cache::set($key, $con->access_token, $con->expires_in); return $con->access_token; } /** * @param $url 請求url地址 * @param string $data 請求參數(shù) * @param int $post 是否為post * @param int $cert 是否為微信的cert * @return mixed */ public function execute($url, $data = '', $post = 0, $cert = 0) { if (is_array($data)) { $data = json_encode($data); } $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); curl_setopt($ch, CURLOPT_POST, $post); if ($post) curl_setopt($ch, CURLOPT_POSTFIELDS, $data); if ($cert) { curl_setopt($ch, CURLOPT_SSLCERT, WEB_PATH . 'cert' . DS . 'apiclient_cert.pem'); curl_setopt($ch, CURLOPT_SSLKEY, WEB_PATH . 'cert' . DS . 'apiclient_key.pem'); } // curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type:application/json']); curl_setopt($ch, CURLOPT_URL, $url); $result = curl_exec($ch); curl_close($ch); return $result; } function http_post_data($url, $data_string, $header = [], $is_array = 0, $is_key_pem = false) { if (is_array($data_string)) { $data_string = json_encode($data_string); } if (!$header) { $header = [ "Content-Type: application/json; charset=utf-8", "Content-Length: " . strlen($data_string) ]; } else { $header = array_merge($header, [ "Content-Type: application/json; charset=utf-8", "Content-Length: " . strlen($data_string) ]); } $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_TIMEOUT, 3); if ($is_key_pem) { $isdir = '/cert/'; curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 終止從服務(wù)端進(jìn)行驗證 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);// curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//證書類型 curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//證書位置 curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中規(guī)定的私鑰的加密類型 curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//證書位置 // curl_setopt($ch, CURLOPT_CAINFO, 'PEM'); // curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem'); } ob_start(); curl_exec($ch); $return_content = ob_get_contents(); ob_end_clean(); $return_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($is_array) { if (!is_array($return_content)) { $return_content = json_decode($return_content, true); } } return $return_content; } // 數(shù)組轉(zhuǎn)化為 xlm public function arrayToXml($data) { $data['sign'] = $this->makeSign($data); ksort($data); //進(jìn)行拼接數(shù)據(jù) $abc_xml = "<xml>"; foreach ($data as $key => $val) { if (is_numeric($val)) { $abc_xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } else { $abc_xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">"; } } $abc_xml .= "</xml>"; return $abc_xml; } //xlm 轉(zhuǎn)化為數(shù)組 public function xml2array($xml) { if (empty($xml)) { return array(); } //禁止引用外部xml實體 libxml_disable_entity_loader(true); $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); $val = json_decode(json_encode($xmlstring), true); return $val; } //微信進(jìn)行拼接sign public function makeSign($params) { //簽名步驟一:按字典序排序數(shù)組參數(shù) ksort($params); $buff1 = ''; foreach ($params as $k => $v) { if ($k != "sign" && $v != "" && !is_array($v)) { $buff1 .= $k . "=" . $v . "&"; } } $buff1 = trim($buff1, "&"); //簽名步驟二:在string后加入KEY $string = $buff1 . "&key=" . $this->mch_key; //簽名步驟三:MD5加密 $string = md5($string); //簽名步驟四:所有字符轉(zhuǎn)為大寫 $result = strtoupper($string); return $result; } //驗證圖片或者視頻是否涉黃 public function imgSecCheck($media_url, $media_type = 2) { $url = 'https://api.weixin.qq.com/wxa/media_check_async?access_token=' . $this->getWxAccessToken(); $return = $this->execute($url, [ 'media_url' => $media_url, 'media_type' => $media_type, ], 1); $return = json_decode($return, true); if ($return && $return['errcode'] == 0) { return true; } return false; } //驗證發(fā)布內(nèi)容是否違法以及敏感詞匯 public function msgSecCheck($content = '') { $url = 'https://api.weixin.qq.com/wxa/msg_sec_check?access_token=' . $this->getWxAccessToken(); $return = $this->execute($url, ['content' => $content], 1); $return = json_decode($return, true); if ($return && $return['errcode'] == 0) { return true; } return false; } /** * [sendMoney 企業(yè)付款到零錢] * @param [type] $amount [發(fā)送的金額(分)目前發(fā)送金額不能少于1元] * @param [type] $re_openid [發(fā)送人的 openid] * @param string $desc [企業(yè)付款描述信息 (必填)] * @param string $check_name [收款用戶姓名 (選填)] * @return [type] [description] * https://www.cnblogs.com/echoppy/p/8603286.html */ public function sendMoney($amount, $re_openid) { $total_amount = (100) * $amount; $data = array( 'mch_appid' => $this->appId,//商戶賬號appid 'mchid' => $this->mch_id,//商戶號 'nonce_str' => $this->nonce_str,//隨機字符串 'partner_trade_no' => date('YmdHis') . rand(1000, 9999),//商戶訂單號 'openid' => $re_openid,//用戶openid 'check_name' => 'NO_CHECK',//校驗用戶姓名選項, 'amount' => $total_amount,//付款金額,單位為分 'desc' => '商戶提現(xiàn)',//企業(yè)付款描述信息 'spbill_create_ip' => $this->get_real_ip(),//Ip地址 ); //生成簽名算法 $secrect_key = $this->mch_key; ///這個就是個API密碼。MD5 32位。 $data = array_filter($data); ksort($data); $str = ''; foreach ($data as $k => $v) { $str .= $k . '=' . $v . '&'; } $str .= 'key=' . $secrect_key; $data['sign'] = md5($str); //生成簽名算法 $xml = $this->arraytoxml($data); $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; //調(diào)用接口 //$res = $this->http_post_data($url, $xml) $res = $this->execute($url, $xml, 1, 1); $return = $this->xml2array($res); return $return; } /** * @param $amount 退款金額 * @param $re_openid 退款用戶的openid * @return bool 退款成功失敗 * 開發(fā)文檔 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 */ public function refundUserMoney($transaction_id, $total_fee, $refund_fee, $out_refund_no) { $data = array( 'appid' => $this->appId,//商戶賬號appid 'mch_id' => $this->mch_id,//商戶號 'nonce_str' => $this->nonce_str,//隨機字符串 'transaction_id' => $transaction_id,//微信支付商戶號 'total_fee' => $total_fee * 100,//訂單金額 'refund_fee' => $refund_fee * 100,//退款金額 'notify_url' => $this->refund_notify_url,//退款金額 'out_refund_no' => $out_refund_no,//退款金額 ); //生成簽名算法 $secrect_key = $this->mch_key; ///這個就是個API密碼。MD5 32位。 $data = array_filter($data); ksort($data); $str = ''; foreach ($data as $k => $v) { $str .= $k . '=' . $v . '&'; } $str .= 'key=' . $secrect_key; $data['sign'] = md5($str); //生成簽名算法 $xml = $this->arraytoxml($data); $url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; $res = $this->execute($url, $xml, 1, 1); if (!$res) { return false; } //$res = $this->http_post_data($url, $xml); $return = $this->xml2array($res); if ($return['return_code'] == 'SUCCESS') { return true; } return false; } /** * 獲取客戶端IP */ public function get_real_ip() { $ip = $_SERVER['REMOTE_ADDR']; if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) { foreach ($matches[0] AS $xip) { if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) { $ip = $xip; break; } } } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (isset($_SERVER['HTTP_CF_CONNECTING_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CF_CONNECTING_IP'])) { $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; } elseif (isset($_SERVER['HTTP_X_REAL_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_REAL_IP'])) { $ip = $_SERVER['HTTP_X_REAL_IP']; } return $ip ? $ip : '127.0.0.1'; } } ``` #### 相關(guān)文章 --- 微信小程序調(diào)用支付接口支付:[https://blog.csdn.net/qishubiao/article/details/80804052](https://blog.csdn.net/qishubiao/article/details/80804052)