下面是控制器Pay.php
<?php
namespace app\wechatapi\controller;
use app\wechatapi\controller\Base;
use think\Db;
use think\Controller;
use \app\wechatapi\model;
class Pay extends Base {
//吊起微信支付
public function getPrepare(){
$model=new model\Pay;
$result=$model->unifiedorder('111335555222','o-9fT5I1Zqqe2SYwNeZI0N_GQzFs',1);
var_dump($result);
}
//微信支付回調
public function notify(){
file_put_contents("wxback.txt",file_get_contents("php://input"));
$postStr = file_get_contents('php://input');
$post_data = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
Db::table('hotel_option_log')->insert(['option'=>json_encode($post_data),'type'=>'微信支付回調']);
/**
* 解析出來的數組
*Array
* (
* [appid] => wx1c870c0145984d
* [bank_type] => CFT
* [cash_fee] => 100
* [fee_type] => CNY
* [is_subscribe] => N
* [mch_id] => 12972103
* [nonce_str] => gkq1x5fxejqo5lz5eua50gg4c4la18vy
* [openid] => olSGW5BBvfep9UhlU40VFIQlcvZ0
* [out_trade_no] => 56265623
* [result_code] => SUCCESS
* [return_code] => SUCCESS
* [sign] => F6890323B0A6A3765510D152D9420EAC
* [time_end] => 20180626170839
* [total_fee] => 100
* [trade_type] => JSAPI
* [transaction_id] => 4200000134201806265483331660
*/
if($post_data != null && $post_data['return_code'] === 'SUCCESS'&& !empty($post_data)){
$out_trade_no=$post_data['out_trade_no'];
$order=Db::table('order')->where(['order_id'=>$out_trade_no,'all_price'=>($post_data['total_fee']/100),'order_status'=>1])->select();
if(!empty($order)){
//修改數據庫
$up_data=Db::table('order')->where(['order_id'=>$out_trade_no])->update(['out_order_id'=>$post_data['transaction_id'],'pay_status'=>1,'order_status'=>2,'pay_time'=>$post_data['time_end']]);
if($up_data){
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
echo $str;
exit('回調成功');
}else{
$str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[修改訂單狀態失敗]]></return_msg></xml>';
echo $str;
exit('回調失敗');
}
}
}else{
$str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>';
echo $str;
exit('回調失敗');
}
}
}
下面是model pay.php
<?php
namespace app\wechatapi\model;
use think\Model;
class Pay extends Model
{
//支付model
const app_id = ''; //小程序appid
const mchid = ''; //商戶號
const apikey= ''; //支付祕鑰
public function unifiedorder($order_sn, $openid, $total_fee)
{
// 當前時間
$time = time();
// 生成隨機字符串
$nonceStr = md5($time . $openid);
// API參數
$params = [
'appid' => self::app_id,
'attach' => 'test',
'body' => $order_sn,
'mch_id' => self::mchid,
'nonce_str' => $nonceStr,
'notify_url' => 'https://'.$_SERVER['SERVER_NAME'].url('/wechatapi/pay/notify'), // 異步通知地址
'openid' => $openid,
'out_trade_no' => $order_sn,
'spbill_create_ip' => \request()->ip(),
'total_fee' => $total_fee * 100, // 價格:單位分
'trade_type' => 'JSAPI',
];
// 生成簽名
$params['sign'] = $this->makeSign($params);
// 請求API
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$result = $this->postXmlCurl($this->toXml($params), $url);
$prepay = $this->fromXml($result);
// 請求失敗
if ($prepay['return_code'] === 'FAIL') {
return $this->renderError($prepay['err_code_des']);
}
if ($prepay['result_code'] === 'FAIL') {
return $this->renderError($prepay['err_code_des']);
}
// 生成 nonce_str 供前端使用
$paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time);
return [
'nonceStr' => $nonceStr,
'package' => "prepay_id=".$prepay['prepay_id'],
'paySign' => $paySign,
'signType'=>'MD5',
'timeStamp' => (string)$time
];
}
/**
* 以post方式提交xml到對應的接口url
* @param $xml
* @param $url
* @param int $second
* @return mixed
*/
public function postXmlCurl($xml, $url, $second = 30){
$ch = curl_init();
// 設置超時
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
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);
// post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
// 運行curl
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
/**
* 輸出xml字符
* @param $values
* @return bool|string
*/
public function toXml($values)
{
if (!is_array($values)
|| count($values) <= 0
) {
return false;
}
$xml = "<xml>";
foreach ($values as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
/**
* 將xml轉爲array
* @param $xml
* @return mixed
*/
public function fromXml($xml)
{
// 禁止引用外部xml實體
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}
/**
* 生成簽名
* @param $values
* @return string 本函數不覆蓋sign成員變量,如要設置簽名需要調用SetSign方法賦值
*/
public function makeSign($values){
//簽名步驟一:按字典序排序參數
ksort($values);
$string = $this->toUrlParams($values);
//簽名步驟二:在string後加入KEY
$string = $string . '&key=' . self::apikey;
//簽名步驟三:MD5加密
$string = md5($string);
//簽名步驟四:所有字符轉爲大寫
$result = strtoupper($string);
return $result;
}
/**
* 生成paySign
* @param $nonceStr
* @param $prepay_id
* @param $timeStamp
* @return string
*/
public function makePaySign($nonceStr, $prepay_id, $timeStamp)
{
$data = [
'appId' => self::app_id,
'nonceStr' => $nonceStr,
'package' => 'prepay_id=' . $prepay_id,
'signType' => 'MD5',
'timeStamp' => $timeStamp,
];
//簽名步驟一:按字典序排序參數
ksort($data);
$string = $this->toUrlParams($data);
//簽名步驟二:在string後加入KEY
$string = $string . '&key=' . self::apikey;
//簽名步驟三:MD5加密
$string = md5($string);
//簽名步驟四:所有字符轉爲大寫
$result = strtoupper($string);
return $result;
}
/**
* 格式化參數格式化成url參數
* @param $values
* @return string
*/
public function toUrlParams($values)
{
$buff = '';
foreach ($values as $k => $v) {
if ($k != 'sign' && $v != '' && !is_array($v)) {
$buff .= $k . '=' . $v . '&';
}
}
return trim($buff, '&');
}
/**
* 支付通知
*/
public function notify()
{
if (!$xml = file_get_contents('php://input')) {
$this->returnCode(false, 'Not found DATA');
}
// 將服務器返回的XML數據轉化爲數組
$data = $this->fromXml($xml);
// 保存微信服務器返回的簽名sign
$dataSign = $data['sign'];
// sign不參與簽名算法
unset($data['sign']);
// 生成簽名
$sign = $this->makeSign($data);
$wx_total_fee = $data['total_fee'];
// 判斷簽名是否正確 判斷支付狀態
if (($sign === $dataSign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
$order_sn = $data['out_trade_no'];
$order_amount = M('order')->where(['master_order_sn'=>"$order_sn"])->whereOr(['order_sn'=>"$order_sn"])->sum('order_amount');
if ((string)($order_amount * 100) == (string)$wx_total_fee) {
//更新訂單狀態,自身邏輯,不做贅述
}
}
}
}