php微信公眾號JSAPI支付
阿新 • • 發佈:2019-01-01
首先是生成簽名方法
//生成簽名
private function MakeSign($params, $KEY)
{
//簽名步驟一:按字典序排序陣列引數
ksort($params);
$string = $this->ToUrlParams($params); //引數進行拼接key=value&k=v
//簽名步驟二:在string後加入KEY
$string = $string . "&key=" . $KEY;
//簽名步驟三:MD5加密
$string = md5($string);
//簽名步驟四:所有字元轉為大寫
$result = strtoupper($string);
return $result;
}
//模擬post提交(調介面用)
模擬post提交
//傳送http請求
private function http_request($url, $data = null, $headers = array())
{
$curl = curl_init();
if (count($headers) >= 1) {
curl_setopt($curl , CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//
$zs1 = APP_PATH . "app/cert/apiclient_cert.pem";
$zs2 = APP_PATH . "app/cert/apiclient_key.pem" ;
// curl_setopt ( $curl, CURLOPT_SSLCERT, $zs1 );
// curl_setopt ( $curl, CURLOPT_SSLKEY, $zs2 );
//設定證書
//使用證書:cert 與 key 分別屬於兩個.pem檔案
curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($curl, CURLOPT_SSLCERT, $zs1);
curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($curl, CURLOPT_SSLKEY, $zs2);
if (!empty($data)) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
xml轉換陣列
//獲取xml裡面資料,轉換成array
private function xml2array($xml)
{
$p = xml_parser_create();
xml_parse_into_struct($p, $xml, $vals, $index);
xml_parser_free($p);
$data = "";
foreach ($index as $key => $value) {
if ($key == 'xml' || $key == 'XML') continue;
$tag = $vals[$value[0]]['tag'];
$value = $vals[$value[0]]['value'];
$data[$tag] = $value;
}
return $data;
}
然後是統一下單方法
//構建訂單/* 首先在伺服器端呼叫微信【統一下單】介面,返回prepay_id和sign簽名等資訊給前端,前端呼叫微信支付介面 */
private function construct_order($total_fee, $openid, $order_sn, $order_id)
{
//獲取系統的配置
$appid = '';//如果是公眾號 就是公眾號的appid;小程式就是小程式的appid
$body = '商戶號構造訂單';
$mch_id = '';
$KEY = '';
$nonce_str = $this->rand_str(12);//隨機字串
$notify_url = config('app_url') . "pay/notify"; //支付完成回撥地址url,不能帶引數
$out_trade_no = $order_sn;//商戶訂單號
$spbill_create_ip = $_SERVER['SERVER_ADDR'];
$trade_type = 'JSAPI';//交易型別 預設JSAPI
//這裡是按照順序的 因為下面的簽名是按照(字典序)順序 排序錯誤 肯定出錯
$post['appid'] = $appid;
$post['body'] = $body;
$post['mch_id'] = $mch_id;
$post['nonce_str'] = $nonce_str;//隨機字串
$post['notify_url'] = $notify_url;
$post['openid'] = $openid;
$post['out_trade_no'] = $out_trade_no;
$post['spbill_create_ip'] = $spbill_create_ip;//伺服器終端的ip
$post['total_fee'] = intval($total_fee); //總金額 最低為一分錢 必須是整數
$post['trade_type'] = $trade_type;
$sign = $this->MakeSign($post, $KEY); //簽名
$this->sign = $sign;
//$post_xml = '<xml><appid>'.$appid.'</appid><body>'.$body.'</body><mch_id>'.$mch_id.'</mch_id><nonce_str>'.$nonce_str.'</nonce_str><notify_url>'.$notify_url.'</notify_url><openid>'.$openid.'</openid><out_trade_no>'.$out_trade_no.'</out_trade_no><spbill_create_ip>'.$spbill_create_ip.'</spbill_create_ip><total_fee>'.$total_fee.'</total_fee><trade_type>'.$trade_type.'</trade_type><sign>'.$sign.'</sign></xml>';
$post_xml = "
<xml>
<appid>$appid</appid>
<body>$body</body>
<mch_id>$mch_id</mch_id>
<nonce_str>$nonce_str</nonce_str>
<notify_url>$notify_url</notify_url>
<openid>$openid</openid>
<out_trade_no>$out_trade_no</out_trade_no>
<spbill_create_ip>$spbill_create_ip</spbill_create_ip>
<total_fee>{$post["total_fee"]}</total_fee>
<trade_type>$trade_type</trade_type>
<sign>$sign</sign>
</xml>";
//統一下單介面prepay_id
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$xml = $this->http_request($url, $post_xml); //POST方式請求http
$array = $this->xml2array($xml); //將【統一下單】api返回xml資料轉換成陣列,全要大寫
$array['my_sign'] = $sign;
$array['post_xml'] = $post_xml;
$array['source_xml'] = $xml;
if ($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS') {
$time = time();
$tmp = ''; //臨時陣列用於簽名
$tmp['appId'] = $appid;
$tmp['nonceStr'] = $nonce_str;
$tmp['package'] = 'prepay_id=' . $array['PREPAY_ID'];
$tmp['signType'] = 'MD5';
$tmp['timeStamp'] = "$time";
$data['state'] = 1;
$data['timeStamp'] = "$time"; //時間戳
$data['nonceStr'] = $nonce_str; //隨機字串
$data['signType'] = 'MD5'; //簽名演算法,暫支援 MD5
$data['package'] = 'prepay_id=' . $array['PREPAY_ID']; //統一下單介面返回的 prepay_id 引數值,提交格式如:prepay_id=*
$data['paySign'] = $this->MakeSign($tmp, $KEY); //簽名,具體簽名方案參見微信公眾號支付幫助文件;
$data['prepay_id'] = $array['PREPAY_ID'];
$data['out_trade_no'] = $out_trade_no;
$data['order_id'] = $order_id;
} else {
$data['statusCode'] = USER_ERROR;
$data['statusMsg'] = "請求錯誤";
$data['data']['RETURN_CODE'] = $array['RETURN_CODE'];
$data['data']['RETURN_MSG'] = $array['RETURN_MSG'];
}
return $data;
}
回撥函式
//訂單支付回撥
function notify()
{
$post = $this->post_data(); //接受POST資料XML個數
$post_data = $this->xml_to_array($post); //微信支付成功,返回回撥地址url的資料:XML轉陣列Array
//輸出訂單號
$order_sn = $post_data['out_trade_no'];
$key = '';//key
$postSign = $post_data['sign'];
unset($post_data['sign']);
/* 微信官方提醒:
* 商戶系統對於支付結果通知的內容一定要做【簽名驗證】,
* 並校驗返回的【訂單金額是否與商戶側的訂單金額】一致,
* 防止資料洩漏導致出現“假通知”,造成資金損失。
*/
ksort($post_data);// 對資料進行排序
$str = $this->ToUrlParams($post_data);//對陣列資料拼接成key=value字串
$KEY = $key;
$str .= '&key=';
$str .= $KEY;
$user_sign = strtoupper(md5($str)); //再次生成簽名,與$postSign比較
if ($post_data['return_code'] == 'SUCCESS') {
//判斷證書是否正確
if ($postSign != $user_sign) {
Log::write('簽名不匹配');
exit;
}
$order_info = Db::table('cs_hborders')->where('order_sn', $order_sn)->find();
if ($order_info['pay_status'] != 1) {
//支付成功後操作(如推送、更改訂單狀態等)
}
}
}
echo 'success';
} else {
echo '微信支付失敗';
}
}