序言
随着微信被越来越多的人使用,微信商城成为如今的热门。每一个商城都需要有自己的支付方式,微信商城也不例外。微信公众号支付就是微信商城的一种支付方式,微信支付随着微信的推广使用也被广泛应用。今天我主要讲的是yii2嵌入微信公众号支付,微信官网的微信支付文档比较简洁,接下来跟着我来看一下yii2如何嵌入微信公众号支付,以及需要注意的事项吧!
前期准备工作
首先必须有一个微信公众号,并且需要在微信公众平台申请微信支付。微信公众平台的地址是:https://mp.weixin.qq.com/cgi-bin/loginpage。申请的过程我这边就不写了,录公众平台以后就可以看到。
其次需要先配置一个域名,必须是80端口,然后在申请完微信支付以后配置上支付授权目录。
准备工作完成以后我们可以开始开发了。
代码分析
1、我们需要获取到关注微信公众号的人的openid。获取openid,我这边是通过网页授权获取用户基本信息接口获取的。其实在github上已经有封装好的关于微信的开发的接口,我们可以直接从上面下载,以后基于yii2的开发微信的其他的功能可以使用。网址是:https://github.com/callmez/yii2-wechat-sdk 大家可以下载然后安装。我的openid的获取也是在此基础上更改的。安装完成以后的目录结构如下图:
我这边使用的是MpWechat.php,在MpWechat.php中我增加了一个方法获取openid。
public function getOpenid($turl)
{
if (!isset($_GET['code'])){
//触发微信返回code码
$url=$this->getOauth2AuthorizeUrl($turl, $this->state, 'snsapi_userinfo');
Header("Location: $url");
exit();
} else {
//获取code码,以获取openid
$code = $_GET['code'];
$access_info = $this->getOauth2AccessToken($code);
return $access_info;
}
}
$turl是获取到code以后需要跳转的地址。
在控制其中获取openid我是这样写的:
public function actionView($id)
{
$openid=isset($_COOKIE['openid'])?$_COOKIE['openid']:'';
if(empty($openid))
{
$t_url=Url::to(['@web/clue/view?id='.$id],true);//这个是获取到code以后需要跳转的url。Url这个是在头部需要增加:use yii\helpers\Url
$info=$this->wechat->getOpenid($t_url);//注:$this->wechat安装完成yii2-wechat-sdk以后配置以后可以使用
if($info){
setcookie('access_token',$info['access_token'],time()+86400*365);
setcookie('openid',$info['openid'],time()+86400*365);
}
}
}
将获取到的openid放入的cookie中储存。
2、openid获取以后可以进行开发微信支付,我这边将微信支付封装成了一个类,可以配置访问。
第一步、我们可以在common/config/main.php中配置
return [
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'language' => 'zh-CN', // 启用国际化支持
'sourceLanguage' => 'zh-CN', // 源代码采用中文
'timeZone' => 'Asia/Shanghai', // 设置时区
'components' => [
'cache' => [
'class' => 'yii\caching\FileCache',
],
'user' => [
'identityClass' => 'login\models\User',
'enableAutoLogin' => false, //开启authTimeout失效
'identityCookie' => ['name' => '_identity', 'httpOnly' => true,'domain' => '.' . DOMAIN],
// 'returnUrl'=>'//' . DOMAIN_HOME,
'authTimeout' => 24*3600*30, // 30 days
],
'session' => [
'timeout' => 24*3600*30, // 30 days
'useCookies' => true,
'cookieParams' => [
'domain' => '.' . DOMAIN,
'lifetime' => 24*3600*30,
],
],
//这个是支付的
'payment' => [
'class'=>'common\widgets\payment\Instance',
'weixinjspi_config' => [
'code' => 2,
'appid' => 'wx88ee3ca8c06be5c6',//微信的appid
'secret' => '48180f87c2693d50b29d822d019999',//appsecret,在申请完公众号以后可以看到
'mch_id' => '13260614455',//商户号
'key' => '16ceshi',//key需要设置
'cert_path' => '',//可以不用填写
'key_path' => '',//可以不用填写
],
],
],
我上面的appid、key等都是错误信息需要你们根据自己申请的微信支付进行填写
第二步、在commonwidgets中加入payment文件夹,在文件夹中增加Instance.php
<?php
namespace common\widgets\payment;
use yii\base\Component;
class Instance extends Component{
private $weixin_config;
private $weixins_config;
private $weixinjspi_config;
private $alipay_config;
private $balance_config;
private $_weixin = null;
private $_weixins = null;
private $_alipay = null;
private $_balance = null;
private $_weixinjspi = null;
public function setWeixin_config($config)
{
$this->weixin_config = $config;
}
public function setWeixins_config($config)
{
$this->weixins_config = $config;
}
public function setWeixinjspi_config($config)
{
$this->weixinjspi_config = $config;
}
public function setAlipay_config($config)
{
$this->alipay_config = $config;
}
public function setBalance_config($config)
{
$this->balance_config = $config;
}
/**
* 组合使用余额支付和其他支付
* @param $order
* @param $payment
* @param $balance
*/
public function unionPay($order,$payment,$balance,$notify)
{
$res = [];
$total_fee = $order['total_fee'];
if($balance > 0)
{
$pay = $this->getBalance();
if($balance >= $total_fee)
{
$res['balance'] = $pay->pay($order);
return $res;
}
else
{
$order['total_fee'] = $balance;
$res['balance'] = $pay->pay($order);
$total_fee -= $balance;
}
}
$order['total_fee'] = $total_fee;
$pay = $this->$payment;
$pay->setNotifyUrl($notify);
$res[$payment] = $pay->prepay($order);
//$res[$payment]['balance'] = $balance;
return $res;
}
/**
* 获得支付宝支付
* @param null $notify_url
* @return mixed|object
*/
public function getAlipay($notify_url = null)
{
$this->alipay_config = array_merge(['class'=>Alipay::className()],$this->alipay_config);
//'var_dump($this->alipay_config);exit;
return $this->_getPayment('_alipay',$notify_url);
}
/**
* 获得微信app c端支付
* @param null $notify_url
* @return mixed|object
*/
public function getWeixin($notify_url = null)
{
$this->weixin_config = array_merge(['class'=>Weixin::className()],$this->weixin_config);
return $this->_getPayment('_weixin',$notify_url);
}
/**
* 获得手机微信端支付
* @param null $notify_url
* @return mixed|object
*/
public function getWeixinjspi($notify_url = null)
{
$this->weixinjspi_config = array_merge(['class'=>Weixinjspi::className()],$this->weixinjspi_config);
return $this->_getPayment('_weixinjspi',$notify_url);
}
/**
* 获得微信app s端支付
* @param null $notify_url
* @return mixed|object
*/
public function getWeixins($notify_url = null)
{
$this->weixins_config = array_merge(['class'=>Weixin::className()],$this->weixins_config);
return $this->_getPayment('_weixins',$notify_url);
}
/**
* 获得余额支付
* @return mixed|object
*/
public function getBalance()
{
$this->balance_config = array_merge(['class'=>Balance::className()],$this->balance_config);
return $this->_getPayment('_balance',null);
}
/**
* @param $name
* @param $notify_url
* @return mixed|object
* @throws \yii\base\InvalidConfigException
*/
private function _getPayment($name, $notify_url)
{
$config = substr($name.'_config',1);
if(is_null($this->{$name}))
{
$this->{$name} = \Yii::createObject($this->{$config});
}
if(!is_null($notify_url))
{
$this->{$name}->setNotifyUrl($notify_url);
}
return $this->{$name};
}
}
这个类其实是结合了其他的支付方式,其中现在最主要的是getWeixinjspi($notify_url = null)这个方法。
除了这个还需要增加Weixinjspi.php
<?php
namespace common\widgets\payment;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\InvalidValueException;
class Weixinjspi extends Payment
{
public $order_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
public $search_order_url = 'https://api.mch.weixin.qq.com/pay/orderquery';
public $close_order_url = 'https://api.mch.weixin.qq.com/pay/closeorder';
public $refund_url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
public $notify_url;
public $search_refund_url = 'https://api.mch.weixin.qq.com/pay/refundquery';
public $order_pre = 'Weixin';
private $appid;
private $mch_id;
private $key;
private $key_path;
private $secret;
private $cert_path;
private $notify_data;
private $curl_proxy_host;
private $curl_proxy_port;
public function setAppid($appid)
{
$this->appid = $appid;
}
public function setKey($key)
{
$this->key = $key;
}
public function setSecret($secret)
{
$this->secret = $secret;
}
public function setKey_path($key_path)
{
$this->key_path = Yii::getAlias($key_path);
}
public function setCert_path($cert_path)
{
$this->cert_path = Yii::getAlias($cert_path);
}
public function setMch_id($mch_id)
{
$this->mch_id = $mch_id;
}
public function init()
{
parent::init();
$needs = array('appid','mch_id','key','secret');
foreach($needs as $need)
{
if(empty($this->{$need}))
{
throw new InvalidConfigException(get_class($this) . " must define weixin's params {$need}.");
}
}
}
private function _checkRefund()
{
$needs = array('key_path','cert_path');
foreach($needs as $need)
{
if(empty($this->{$need}))
{
throw new InvalidConfigException(get_class($this) . " must define weixin's params {$need}.");
}
}
}
/**
* jsapi支付接口
* @param $order
* @return mixed
*/
public function pay($order)
{
$paras = [
'body' =>$order['goods_desc'],//设置商品或支付单简要描述
'attach' =>'PC',//设置附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
'out_trade_no' =>$this->order_pre.$order['order_sn'],//订单号
'total_fee' =>(int)($order['total_fee'] * 100),//订单总金额
'detail' =>$order['body'],
'time_start' =>empty($order['time_start'])? '':$order['time_start'],
'time_expire' =>empty($order['time_expire'])? '':$order['time_expire'],
'goods_tag' =>empty($order['goods_tag'])? '':$order['goods_tag'],//设置商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
'notify_url' =>empty($order['notify_url'])? '':$this->setNotifyUrl($order['notify_url']),//回调地址
'trade_type' =>'JSAPI',
'product_id' =>empty($order['order_sn'])? '':$order['order_sn'],
'openid' =>empty($order['openid'])? '':$order['openid'],
'nonce_str' =>$this->randomStr(),
];
///var_dump($paras);exit;
$timeout = empty($order['timeout'])? 6 :$order['timeout'];
if($order['total_fee'] <= 0)
{
throw new InvalidValueException(get_class($this) . " 支付金额必须大于0");
}
$trade = $this->createOrder($paras,$timeout);
if(isset($trade['return_code']) && $trade['return_code'] == 'SUCCESS')
{
if(isset($trade['result_code']) && $trade['result_code'] == 'SUCCESS')
{
$data=[
'appId' =>$trade['appid'],
'timeStamp'=>"".time()."",
'nonceStr' =>$this->randomStr(),
'signType'=>'MD5',
'package' =>'prepay_id='.$trade['prepay_id'],
];
$data['paySign']=$this->sign($data);
//$data['signature']=$order['signature'];
$parameters = json_encode($data);
return $parameters;//二维码的信息
}
else
{
return false;
}
}
else
{
return false;
}
}
public function getJs($parameters,$order_sn)
{
$str="<script type=\"text/javascript\">
//调用微信JS api 支付
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
".$parameters.",
function(res){
if(res.err_msg == \"get_brand_wcpay_request:ok\")
{
$.ajax({
type:\"get\",
url:\"/clue/pay_status?order_sn=".$order_sn."\",
datatype:\"json\",
data:{},
success: function(data_)
{
window.location.reload();
}
})
alert('支付成功!');
}else if(res.err_msg == \"get_brand_wcpay_request:cancel\")
{
alert('支付过程中用户取消!');
}else{
alert('支付失败!');
}
}
);
}
function callpay()
{
if (typeof WeixinJSBridge ==\"undefined\"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
}
</script>
";
return $str;
}
/**
* 预支付接口,在APP上发起支付
* @param $order
* @return mixed
*/
public function prepay($order)
{
$needs = array('title','order_sn','body','total_fee');
foreach($needs as $need)
{
if(!isset($order[$need]))
{
throw new InvalidConfigException(get_class($this) . " \$order 中必须包含键 {$need}.");
}
}
$paras = [
'trade_type' =>'APP',
'time_start' =>empty($order['time_start'])? '':$order['time_start'],
'time_expire' =>empty($order['time_expire'])? '':$order['time_expire'],
'goods_tag' =>empty($order['goods_tag'])? '':$order['goods_tag'],
'device_info' =>empty($order['device_info'])? '':$order['device_info'],
'out_trade_no' =>$this->order_pre.$order['order_sn'],
'detail' =>$order['body'],
'total_fee' =>(int)($order['total_fee'] * 100),
'body' =>$order['title'],
'fee_type' =>empty($order['fee_type'])? '':$order['fee_type'],
'product_id' =>empty($order['product_id'])? '':$order['product_id'],
'openid' =>empty($order['openid'])? '':$order['openid'],
'attach' =>empty($order['attach'])? '':$order['attach'],
'notify_url' =>empty($order['notify_url'])? '':$this->setNotifyUrl($order['notify_url']),//回调地址
];
$timeout = empty($order['timeout'])? 6 :$order['timeout'];
if($order['total_fee'] <= 0)
{
throw new InvalidValueException(get_class($this) . " 支付金额必须大于0");
}
$trade = $this->createOrder($paras,$timeout);
if(isset($trade['return_code']) && $trade['return_code'] == 'SUCCESS')
{
if(isset($trade['result_code']) && $trade['result_code'] == 'SUCCESS')
{
$trade['total_fee'] = $order['total_fee'];
return $this->getSign($trade);
}
else
{
throw new InvalidValueException(get_class($this) . $trade['err_code_des']);
}
}
else
{
throw new InvalidValueException(get_class($this) . $trade['return_msg']);
}
}
public function getSign($order)
{
$total_fee = $order['total_fee'];
$keys = ['appid','partnerid','prepayid','package','noncestr','timestamp','sign'];
$order['partnerid'] = $order['mch_id'];
$order['prepayid'] = $order['prepay_id'];
$order = array_intersect_key($order,array_fill_keys($keys,''));
$order['package'] = 'Sign=WXPay';
$order['timestamp'] = time();
$order['noncestr'] = $this->randomStr(30);
$order['sign'] = $this->sign($order);
$order['total_fee'] = $total_fee;
return $order;
}
public function notify()
{
//获取通知的数据
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
//如果返回成功则验证签名
//try {
$result = $this->fromXml($xml);
if($result['return_code'] == 'SUCCESS')
{
$sign = $this->sign($result);
if($sign == $result['sign'])
{
$result['trade_no']=$result['transaction_id'];
$result['out_trade_no']=substr($result['out_trade_no'],strlen($this->order_pre));
$result['trade_status']='TRADE_SUCCESS';
return $result;
}
else
{
return false;
}
}
else
{
return false;
}
// } catch (\Exception $e){
// throw new InvalidValueException(get_class($this) . $e->errorMessage());
// }
}
/**
* 退款接口
* @param $order
* @return mixed
*/
public function refund($order)
{
$this->_checkRefund();
$needs = array('order_sn','total_fee');
foreach($needs as $need)
{
if(!isset($order[$need]))
{
throw new InvalidConfigException(get_class($this) . " \$order 中必须包含键 {$need}.");
}
}
$order['out_trade_no'] = $this->order_pre.$order['order_sn'];
$order['total_fee'] = round($order['total_fee'],2) * 100;
$order['refund_fee'] = $order['total_fee'];
$order['op_user_id'] = $this->mch_id;
$need = array('out_trade_no','out_refund_no','total_fee','refund_fee','op_user_id');
$keys = ['device_info','refund_fee_type','transaction_id'];
foreach($need as $key)
{
if(empty($order[$key])) {
throw new InvalidConfigException("缺少退款申请接口必填参数{$key}!");
}
}
$order = array_intersect_key($order,array_fill_keys(array_merge($need, $keys),''));
$order['appid'] = $this->appid;
$order['mch_id'] = $this->mch_id;
$order['nonce_str'] = $this->randomStr();
$order['sign'] = $this->sign($order);
$xml = $this->toXml($order);
$response = $this->postXmlCurl($xml, $this->refund_url);
$result = $this->convertResponse($response);
return $result;
}
/**
* Notify处理完成接口
* @return mixed
*/
public function finish()
{
$arr = ['return_code'=>'SUCCESS'];
$xml = $this->toXml($arr);
return $xml;
}
/**
* 设置Notify回调接口
* @return mixed
*/
public function setNotifyUrl($url)
{
$this->notify_url = $url;
}
/**
* 获得Notify返回的支付金额
* @return mixed
*/
public function getTotalFee($total_fee = null)
{
if($total_fee)
{
return round($total_fee/100,2,PHP_ROUND_HALF_DOWN);
}
if(isset($this->notify_data['total_fee']))
{
return round($this->notify_data['total_fee']/100,2,PHP_ROUND_HALF_DOWN);
}
return false;
}
/**
* 获得Notify返回的交易号
* @return mixed
*/
public function getSerialNo($arr = null)
{
if(isset($arr['transaction_id']))
{
return $arr['transaction_id'];
}
if(isset($this->notify_data['transaction_id']))
{
return $this->notify_data['transaction_id'];
}
return false;
}
/**
* 获得Notify返回的原始数据
* @return mixed
*/
public function getNotifyRaw()
{
return $GLOBALS['HTTP_RAW_POST_DATA'];
}
public function randomStr($length = 32)
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
public function getIp()
{
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$strIp = $arr[0];
} elseif (isset($_SERVER["HTTP_CLIENT_IP"])) {
$strIp = $_SERVER["HTTP_CLIENT_IP"];
} elseif (isset($_SERVER["REMOTE_ADDR"])) {
$strIp = $_SERVER["REMOTE_ADDR"];
} else {
$strIp = "0.0.0.0";
}
return $strIp;
}
private function toXml($values)
{
if(!is_array($values) || count($values) <= 0)
{
throw new InvalidValueException("数组数据异常!");
}
//var_dump($values);exit;
$xml = "<xml>";
foreach ($values as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
//echo $xml;exit;
return $xml;
}
private function fromXml($xml)
{
if(!$xml){
throw new InvalidValueException("xml数据异常!");
}
try
{
$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}
catch(\Exception $e)
{
throw new InvalidValueException("xml数据异常!");
}
return $values;
}
public function sign($values)
{
ksort($values);
$string = "";
foreach ($values as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$string .= $k . "=" . $v . "&";
}
}
$string = trim($string, "&");
$string = $string . "&key=".$this->key;
$string = md5($string);
return strtoupper($string);
}
public function checkSign($values)
{
if($this->sign($values) == $values['sign']){
return true;
}
throw new InvalidValueException("验证签名错误!");
}
private function convertResponse($xml)
{
$result = $this->fromXml($xml);
if($result['return_code'] != 'SUCCESS')
{
throw new InvalidValueException($result['return_msg']);
}
if($this->checkSign($result) === true){
return $result;
}else{
return false;
}
}
public function searchOrder($out_trade_no, $transaction_id = '',$timeOut = 6)
{
if(empty($out_trade_no) && empty($transaction_id)) {
throw new InvalidValueException("缺少订单查询接口必填参数out_trade_no或transaction_id!");
}
$order = ['out_trade_no'=>$out_trade_no,'transaction_id'=>$transaction_id];
$order['appid'] = $this->appid;
$order['mch_id'] = $this->mch_id;
$order['nonce_str'] = $this->randomStr();
$order['sign'] = $this->sign($order);
$xml = $this->toXml($order);
$response = $this->postXmlCurl($xml, $this->search_order_url, false, $timeOut);
$result = $this->convertResponse($response);
return $result;
}
public function closeOrder($out_trade_no, $timeOut = 6)
{
if(empty($out_trade_no)) {
throw new InvalidValueException("缺少订单查询接口必填参数out_trade_no!");
}
$order = ['out_trade_no'=>$out_trade_no];
$order['appid'] = $this->appid;
$order['mch_id'] = $this->mch_id;
$order['nonce_str'] = $this->randomStr();
$order['sign'] = $this->sign($order);
$xml = $this->toXml($order);
$response = $this->postXmlCurl($xml, $this->close_order_url, false, $timeOut);
$result = $this->convertResponse($response);
return $result;
}
public function createOrder(array $order, $timeOut = 6)
{
//检测必填参数
$need = array('out_trade_no','body','total_fee','trade_type');
$keys = array('appid','mch_id','device_info','nonce_str','sign','detail','attach','fee_type',
'spbill_create_ip','time_start','time_expire','goods_tag','notify_url','product_id','openid');
$keys = array_merge($need,$keys);
foreach($need as $key)
{
if(empty($order[$key])) {
throw new InvalidValueException("缺少统一下单接口必填参数{$key}!");
}
}
//关联参数
if($order['trade_type'] == "JSAPI" && empty($order['openid'])){
throw new InvalidValueException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
}
if($order['trade_type'] == "NATIVE" && empty($order['product_id'])){
throw new InvalidValueException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
}
$order = array_intersect_key($order,array_fill_keys($keys,''));
$order['appid'] = $this->appid;
$order['mch_id'] = $this->mch_id;
$order['notify_url'] = $this->notify_url;
$order['spbill_create_ip'] = $this->getIp();
$order['nonce_str'] = $this->randomStr();
$order['sign'] = $this->sign($order);
$xml = $this->toXml($order);
// var_dump( $order['notify_url'] );exit;
$response = $this->postXmlCurl($xml, $this->order_url, false, $timeOut);
//var_dump($response);exit;
$result = $this->convertResponse($response);
return $result;
}
public function searchRefund($order, $timeOut = 6)
{
$keys = ['out_refund_no','out_trade_no','transaction_id','refund_id'];
if(empty($order['out_trade_no']) && empty($order['transaction_id']) && empty($order['out_refund_no']) && empty($order['refund_id'])) {
throw new InvalidValueException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!");
}
$order = array_intersect_key($order,array_fill_keys($keys,''));
$order['appid'] = $this->appid;
$order['mch_id'] = $this->mch_id;
$order['nonce_str'] = $this->randomStr();
$order['sign'] = $this->sign($order);
$xml = $this->toXml($order);
$response = $this->postXmlCurl($xml, $this->search_refund_url, true, $timeOut);
$result = $this->convertResponse($response);
return $result;
}
private function postXmlCurl($xml, $url, $useCert = false, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//如果有配置代理这里就设置代理
if($this->curl_proxy_host != "0.0.0.0" && $this->curl_proxy_port != 0){
curl_setopt($ch,CURLOPT_PROXY, $this->curl_proxy_host);
curl_setopt($ch,CURLOPT_PROXYPORT, $this->curl_proxy_port);
}
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);
if($useCert == true && !empty($this->cert_path) && !empty($this->key_path)){
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->cert_path);
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->key_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 InvalidValueException("curl出错,错误码:{$error}");
}
}
}
还需要增加一个Notifyurl.php
<?php
namespace common\widgets\payment;
use Yii;
use common\widgets\payment\Weixinjspi;
class Notifyurl
{
public function notify($code)
{
$funame='get'.$code;
$respmodel=Yii::$app->payment->$funame();
$result=$respmodel->notify();
if($result){
//商户订单号
$out_trade_no = $result['out_trade_no'];
//交易号
$trade_no = $result['trade_no'];
//交易状态
$trade_status = $result['trade_status'];
if ($trade_status == 'TRADE_SUCCESS')
{
$this->update_status($out_trade_no,$trade_no,1);
echo "success";
}else{
echo "fail";
}
}else{
echo "fail";
}
}
//支付状态更改
public function update_status($out_trade_no,$trade_no,$pay_status)
{
}
}
?>
第三步、我们如何使用呢,
在我们需支付的controller中,
//微信支付
public function actionWeixinpay()
{
$openid=isset($_COOKIE['openid'])?$_COOKIE['openid']:'';
if($openid)
{
$order_=[
'goods_desc'=>'测试',
'order_sn' =>'2222',
'total_fee' =>12,
'body'=>'测试',
'time_start' =>date("YmdHis"),
'time_expire'=>date("YmdHis", time() + 86400*300),
'goods_tag' =>'',
'notify_url'=>Url::to(['@web/respond/updatepay'],true),//这是回调地址,微信通知的地址
'openid'=>$openid,
];
$paymodel=Yii::$app->payment->getWeixinjspi();
$result=$paymodel->pay($order_);//生成预付单
if($result)
{
$jsstr=$paymodel->getJs($result,$order_['order_sn']);
//根据预付单信息生成js,详细的可以看上面的类的方法。
}
}
echo $jsstr;
}
第四步、在view层我们可以这样写:
<button type="button" class="btn red-sunglo" onclick="pay_(2)" >
<div id="js_">
</div>
<script>
//支付
function pay_(id)
{
$.ajax({
type:"get",
url:"/ceshi/weixinpay?id="+id,//这是访问的地址
datatype:"json",
data:{},
success: function(data)
{
if(data!='')
{
$("#js_").html(data);
callpay();
}
//alert(data);
}
})
}
</script>
我这边是采用的ajax的形式进行支付的,可以根据自己的情况进行调整
第五步、支付最重要的是需要回调,yii2增加了Csrf验证,禁止了外部的访问,我们在控制其中写回调方法的时候必须增加:
public function beforeAction($action) {
$this->enableCsrfValidation = false;
return parent::beforeAction($action);
}
不然微信将访问不到。
回调的方法使用:
use common\widgets\payment\Weixinjspi;
use common\widgets\payment\Notifyurl;
public function actionUpdatepay()
{
$model=new Notifyurl();
$model->notify('Weixinjspi');
}
以上就是我的微信支付。
第一次写文章,有不完善的地方请多包涵,有不对的地方请指出我这边再完善
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。