1. 登陆商户平台 查看APPID , https://pay.weixin.qq.com
在产品中心->我的产品 中查看当前商户开的支付类型。
然后在appID授权里边查看APPID。
然后在 产品中心>api安全 中设置API密钥
微信APP支付接口文档 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/10/20
* Time: 18:48
*/
namespace app\v1\controller;
use app\common\controller\Checking;
use think\console\Command;
use think\Controller;
use think\Db;
class WechatPay extends Controller
{
protected $values;
const APPID='';
const MCHID='';
const KEY='tv0ov06vgvavh1ml5enx9dgocetsvbyg';//自己设置的微信商家key
public function wx_apppay(){
$data=Db::name('account_recharge_orders')->where(['pay_order_num'=>$_GET['orderId']])->find();
// $data['pay_order_num']=time();
$notify_url = "http://api.chachuanqi.cn/v1/pay/wechatNotify"; //回调地址
$out_trade_no=$data['pay_order_num'];
$nonce_str=MD5($out_trade_no);//随机字符串
$userip =$this->get_client_ip();
$appid = self::APPID;//微信
$mch_id = self::MCHID;//微信官方的
// $key = self::KEY;//自己设置的微信商家key
$setAttr = [
'body' => '充值',
'appid' => $appid,
'out_trade_no' => $data['pay_order_num'],
'total_fee' => $data['money']*100,
'notify_url' => $notify_url,
'mch_id' => $mch_id,
'spbill_create_ip' => $userip,
'nonce_str' => $nonce_str,
'trade_type' => 'APP'
];
$this->values = $setAttr; //配置赋给values属性
$sign = $this->MakeSign(); //获取签名
$this->values['sign'] = $sign;
$xmlstr = $this->ToXml(); //生成微信请求xml数据格式
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //请求url参数
$dataxml = self::postXmlCurl( $xmlstr, $url );//传参调用curl请求
$objectxml = $this->xmlToArray( $dataxml );
if ( $objectxml['return_code'] == 'FAIL' && $objectxml['result_code'] == 'FAIL' ) {
Checking::response( -200, '获取签名失败' );
} else {
$time = time();
//如果上一次请求成功,那么我们将返回的数据重新拼装,进行第二次签名
$resignData = array(
'appid' => $appid,
'noncestr' => $setAttr['nonce_str'],
'package' => 'Sign=WXPay',
'partnerid' => $mch_id,
'prepayid' => $objectxml['prepay_id'],
'timestamp' => "$time"
);
$this->values = $resignData;
$secondSign = $this->MakeSign( $resignData );
$this->values['sign'] = $secondSign;
// $this->values['notify_url'] = $notify_url;
Checking::response( 200, 'ok', $this->values );
}
}
/**
* 获取客户端ip
* @return string
*/
public function get_client_ip() {
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
$ip = getenv('HTTP_CLIENT_IP');
} elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
$ip = getenv('HTTP_X_FORWARDED_FOR');
} elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
$ip = getenv('REMOTE_ADDR');
} elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$ip = $_SERVER['REMOTE_ADDR'];
}
return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : '';
}
/**
* 订单查询
*
*/
public function orderQuery( $out_trade_no = '' )
{
$config = [
'out_trade_no' => $out_trade_no,
'appid' => self::APPID,
'mch_id' => self::MCHID,
'nonce_str' => self::getNonceStr()
];
$this->values = $config;
$sign = $this->MakeSign(); //生成sign签名
$this->values['sign'] = $sign;
$xmlstr = $this->ToXml(); //生成微信请求xml数据格式
$url = "https://api.mch.weixin.qq.com/pay/orderquery"; //查询订单url
$dataxml = self::postXmlCurl( $xmlstr, $url, false );//传参调用curl请求
$objectxml = $this->xmlToArray( $dataxml );
if ( $objectxml['return_code'] == 'SUCCESS' && $objectxml['result_code'] == 'SUCCESS' && $objectxml['trade_state'] == 'SUCCESS' ) {
return true;
} else {
return false;
}
}
/**
* 作用:将xml转为array
*/
public function xmlToArray( $xml )
{
//将XML转为array
$array_data = json_decode( json_encode( simplexml_load_string( $xml, 'SimpleXMLElement', LIBXML_NOCDATA ) ), true );
return $array_data;
}
/**
* 以post方式提交xml到对应的接口url
*
* @param string $xml 需要post的xml数据
* @param string $url url
* @param bool $useCert 是否需要证书,默认不需要
* @param int $second url执行超时时间,默认30s
* @throws WxPayException
*/
private static function postXmlCurl( $xml, $url, $useCert = false, $second = 30 )
{
$ch = curl_init();
//设置超时
curl_setopt( $ch, CURLOPT_TIMEOUT, $second );
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, TRUE );
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );//严格校验
//设置header
curl_setopt( $ch, CURLOPT_HEADER, FALSE );
//要求结果为字符串且输出到屏幕上
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );
if ( $useCert == true ) {
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt( $ch, CURLOPT_SSLCERTTYPE, 'PEM' );
curl_setopt( $ch, CURLOPT_SSLCERT, self::SSLCERT_PATH );
curl_setopt( $ch, CURLOPT_SSLKEYTYPE, 'PEM' );
curl_setopt( $ch, CURLOPT_SSLKEY, self::SSLKEY_PATH );
}
//post提交方式
curl_setopt( $ch, CURLOPT_POST, TRUE );
curl_setopt( $ch, CURLOPT_POSTFIELDS, $xml );
//运行curl
$data = curl_exec( $ch );
//返回结果
if ( $data ) {
curl_close( $ch );
return $data;
} else {
$error = curl_errno( $ch );
curl_close( $ch );
throw new \Exception( "curl出错,错误码:$error" );
}
}
/**
* 输出xml字符
**/
public function ToXml()
{
if ( !is_array( $this->values )
|| count( $this->values ) <= 0 ) {
throw new Exception( '数组异常', 1 );
}
$xml = "<xml>";
foreach ( $this->values as $key => $val ) {
if ( is_numeric( $val ) ) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
/**
*
* 产生随机字符串,不长于32位
* @param int $length
* @return 产生的随机字符串
*/
public static function getNonceStr( $length = 32 )
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr( $chars, mt_rand( 0, strlen( $chars ) - 1 ), 1 );
}
return $str;
}
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
protected function MakeSign(){
//签名步骤一:按字典序排序参数
ksort( $this->values );
$string = $this->ToUrlParams( ksort( $this->values ) );
//签名步骤二:在string后加入KEY
$string = $string . "&key=" . self::KEY;
//签名步骤三:MD5加密
$string = md5( $string );
//签名步骤四:所有字符转为大写
$result = strtoupper( $string );
return $result;
}
/**
* 格式化参数格式化成url参数
*/
public function ToUrlParams()
{
$buff = "";
foreach ( $this->values as $k => $v ) {
if ( $k != "sign" && $v != "" && !is_array( $v ) ) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim( $buff, "&" );
return $buff;
}
}
客户确认商品点击支付—>后台提供前端调用的加签接口---->前端拿到后台加签的参数调起支付-->微信支付--->微信回调后台提供的回调接口
这样微信支付就完成了,总体看起来,后台只需要做两件事
1.给前端提供接口把订单进行加签在返还给前端
2.给微信提供回调接口确认支付信息。
到此支付验签成功,然后结束回调数据
/**
* 微信回调
*/
public function wechatNotify()
{
Checking::writeLog('回调开始','回调','wx_cz.txt');
$xml = file_get_contents('php://input');
$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
//用户http_build_query()将数据转成URL键值对形式
$sign = http_build_query($arr);
//md5处理
$sign = md5($sign);
//转大写
$sign = strtoupper($sign);
Checking::writeLog(json_encode($arr),'回调开始','wx_cz.txt');
//验签名。默认支持MD5
Checking::writeLog('开始验签','开始验签','wx_cz.txt');
// if ( $sign === $arr['sign']) {
//校验返回的订单金额是否与商户侧的订单金额一致。修改订单表中的支付状态。
//逻辑
Checking::writeLog('开始验签','验签成功'.$arr['return_code'],'wx_cz.txt');
// $arr=json_decode();
if ( $arr['return_code'] == 'SUCCESS' ) {
$orderCode = htmlspecialchars( $arr['out_trade_no'] );
$money = htmlspecialchars( $arr['total_fee'] ); //微信回调过来的支付金额
$where = [
'pay_order_num' => $orderCode,
'status' => '1'
];
Checking::writeLog($orderCode.'>>'.'','查询订单','wx_cz.txt');
$info = Db::name('account_recharge_orders')->where( $where )->find();
if (!empty($info)){
Db::startTrans();
try{
#处理逻辑
$return = ['return_code'=>'SUCCESS','return_msg'=>'OK'];
$xml = '<xml>';
foreach($return as $k=>$v){
$xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>';
}
$xml.='</xml>';
echo $xml;
}catch (Exception $exception){
Db::rollback();
Checking::writeLog($exception->getMessage(),'程序出错','wx_cz.txt');
echo $exception->getMessage();
}
}
} else {
echo 'fail';
}
// }
}