微信开发记录
准备工作
获取accesss_token和jsapi_ticket,这个是所有开发的必备,我们使用redis存储,过期时间为返回的过期时间
##sign.php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$redis->auth('redispassword');
$redis->select(1);
$redis->setOption(Redis::OPT_PREFIX, 'WXAPI:');
$appid = "wxappid";
$secret = "wxsecret";
if(!$redis->exists('wx_global_ac_tk')){
$get_token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$secret;
$json_obj = json_decode(httpsget($get_token_url),true);
$access_token = $json_obj['access_token'];
$redis->set('wx_global_ac_tk', $access_token);
$redis->setTimeout('wx_global_ac_tk', $json_obj['expires_in']);
}else{
$access_token = $redis->get('wx_global_ac_tk');
}
if(!$redis->exists('wx_global_js')){
$jsapi_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=".$access_token."&type=jsapi";
$json_obj = json_decode(httpsget($jsapi_ticket_url),true);
$jsapi_ticket = $json_obj['ticket'];
$redis->set('wx_global_js', $jsapi_ticket);
$redis->setTimeout('wx_global_js', $json_obj['expires_in']);
}else{
$jsapi_ticket = $redis->get('wx_global_js');
}
$noncestr = getRandChar(16);
$timestamp = time();
$url = $_POST['url'];
$string = 'jsapi_ticket='.$jsapi_ticket.'&noncestr='.$noncestr.'×tamp='.$timestamp.'&url='.$url;
$signature = sha1($string);
$redis->close();
echo json_encode(array("success"=>"1","appid"=>$appid,"signature"=>$signature,"timestamp"=>$timestamp,"noncestr"=>$noncestr,"url"=>$url));
function httpsget($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
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, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
function getRandChar($length){
$str = null;
$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($strPol)-1;
for($i = 0; $i < $length; $i++){
$str .= $strPol[rand(0,$max)];
}
return $str;
}
调用接口:图片和语音
根据开发者文档,需要调用的接口需要先声明,如下:
<!-- index.html -->
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
$.post('sigin.php', {url: location.href.split('#')[0]}, function(res){
wx.config({
appId: res.appid,
timestamp: res.timestamp,
nonceStr: res.noncestr,
signature: res.signature,
jsApiList: [
'onMenuShareTimeline',//分享接口
'onMenuShareAppMessage',//分享接口
'onMenuShareQQ',//分享接口
'onMenuShareWeibo',//分享接口
'onMenuShareQZone',//分享接口
'chooseImage',//图片接口
'previewImage',//图片接口
'uploadImage',//图片接口
'startRecord',//录音接口
'stopRecord',//录音接口
'onVoiceRecordEnd',//录音接口
'playVoice',//录音接口
'pauseVoice',//录音接口
'stopVoice',//录音接口
'onVoicePlayEnd',//录音接口
'uploadVoice']
});
wx.ready(function(){
var images = {
localId: '',
serverId: ''
};
var record = {
localId: '',
serverId: ''
}
$('#img').click(function(){//选择图片
wx.chooseImage({
count: 1,
sizeType: ['original'],
sourceType: ['album', 'camera'],
success: function (res) {
$('#imgPreview').html('<img src="'+res.localIds+'" />');
images.localId = res.localIds;
}
});
});
$('#uploadimg').click(function(){//上传图片
if (images.localId.length == 0) {
alert('请先选择图片');
return;
}
console.log(images.localId[0]);
images.serverId = '';
wx.uploadImage({
localId: images.localId[0],
isShowProgressTips: 1,
success: function (res) {
alert('图片上传完成');
images.serverId = res.serverId;
}
});
});
$('#startrecord').click(function(){//开始录音
wx.startRecord();
$('#status').html('录音中...');
});
$('#stoprecord').click(function(){//停止录音
$('#status').html('已完成');
wx.stopRecord({
success: function (res) {
record.localId = res.localId;
}
});
});
$('#playrecord').click(function(){//播放录音
$('#status').html('播放中...');
wx.playVoice({
localId: record.localId
});
});
$('#uploadrecord').click(function(){//上传录音
wx.uploadVoice({
localId: record.localId,
isShowProgressTips: 1,
success: function (res) {
record.serverId = res.serverId;
}
});
})
wx.onVoiceRecordEnd({//录音完成(1分钟限制时间到)
complete: function (res) {
$('#status').html('已完成');
record.localId = res.localId;
}
});
wx.onVoicePlayEnd({//播放录音完成
success: function (res) {
$('#status').html('播放完毕...');
}
});
$('#tj').click(function(){//保存图片的serverId和录音的serverId到服务器
$.post('index.php', {img: images.serverId, voc: record.serverId}, function(res){
alert(res);
})
})
})
})
</script>
服务器上定时根据serverId下载文件,文件保存时间为3天
##download.php
class downloadWeixinFile{
private $access_token;
private $media_id;
function __construct(){
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$redis->auth('redispassword');
$redis->setOption(Redis::OPT_PREFIX, 'WXAPI:');
$appid = "wxappid";
$secret = "wxsecret";
if(!$redis->exists('wx_global_ac_tk')){
$get_token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$secret;
$json_obj = json_decode($this->httpsget($get_token_url), true);
$this->access_token = $json_obj['access_token'];
$redis->setEx('wx_global_ac_tk', $json_obj['expires_in'], $this->access_token);
}else{
$this->access_token = $redis->get('wx_global_ac_tk');
}
$redis->close();
}
public function download($media_id){
$this->media_id = $media_id;
$data = array();
if (is_array($this->media_id)) {
foreach ($this->media_id as $media) {
$url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token={$this->access_token}&media_id={$media}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOBODY, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$package = curl_exec($ch);
$httpinfo = curl_getinfo($ch);
curl_close($ch);
$type = $this->header_byte($httpinfo['content_type']);
$filename = "wxupload_".time().rand(1111,9999).".".$type;
$this->saveWeixinFile("/cache/ynwvote/2016sdds/wxupload/".$filename, $package);
array_push($data, $filename);
}
}else{
$url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token={$this->access_token}&media_id={$this->media_id}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOBODY, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$package = curl_exec($ch);
$httpinfo = curl_getinfo($ch);
curl_close($ch);
$type = $this->header_byte($httpinfo['content_type']);
$filename = "wxupload_".time().rand(1111,9999).".".$type;
$this->saveWeixinFile("/cache/ynwvote/2016sdds/wxupload/".$filename, $package);
array_push($data, $filename);
}
return $data;
}
private function httpsget($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
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, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
private function header_byte($type){
switch ($type){
case 'image/jpeg':
$tp = 'jpg';
break;
case 'image/png':
$tp = 'png';
break;
case 'audio/amr':
$tp = 'amr';
break;
default:
$tp = "notype";
break;
}
return $tp;
}
private function saveWeixinFile($filename, $filecontent){
$local_file = fopen($filename, 'w');
if (false !== $local_file){
if (false !== fwrite($local_file, $filecontent)) {
fclose($local_file);
}
}
}
}
$file = array('imgserverId', 'voiceserverId');
$download = new downloadWeixinFile();
$name = $ddownload->download($file);
调用接口:获取用户信息
根据微信文档,可以使用静默授权获取用户的openid等基本信息,但是需要经过一个跳转
在首页加入js代码,判断是否获取到了token
//index.html
var wx_tk = '';
var cbname = '2016test';
var $_GET = (function(){
var url = window.document.location.href.toString();
var u = url.split("?");
if(typeof(u[1]) == "string"){
u = u[1].split("&");
var get = {};
for(var i in u){
var j = u[i].split("=");
get[j[0]] = j[1];
}
return get;
}else{
return {};
}
})();
var wx_tk= sessionStorage.getItem("wx_tk");
if(wx_tk === null || wx_tk === undefined){
sessionStorage.setItem("wx_tk", 0);
window.location.href='http://localhost/wxcallback/login.php?cbname='+cbname;
}
if(wx_tk == 0){
sessionStorage.setItem("wx_tk", $_GET['wx_tk']);
wx_tk = $_GET['wx_tk'];
}
判断sessionStorage里面有没有wx_tk,没有的话就跳转到login.php,需要新建一个cbname一样的php文件来辅助跳转,主跳转login.php如下:
##login.php
if(isset($_GET['cbname']) && ctype_alnum($_GET['cbname'])) {
$cbname=$_GET['cbname'].'.php';
} else {
$cbname='';
}
$appid = "wxappid";
$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='.$appid.'&redirect_uri=http://webapp.yunnan.cn/wxback/'.$cbname.'&response_type=code&scope=snsapi_base&state=1#wechat_redirect';
header("Location:".$url);
exit();
对应的2016test.php文件如下:
##2016test.php
require_once('global.php');
$url='http://localhost/index.html';
header("Location:".$url.'?c=1&wx_tk='.$wx_tk);
global.php里面使用接口获取用户信息,并存入redis
##global.php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$redis->auth('redispassword');
$redis->select(1);
$redis->setOption(Redis::OPT_PREFIX, 'WXAPI:');
$appid = "wxappid";
$secret = "wxsecret";
$code = $_GET["code"];//跳转获取到的code
$get_token_url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$appid.'&secret='.$secret.'&code='.$code.'&grant_type=authorization_code';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$get_token_url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
$openid = $json_obj['openid'];
if(!$redis->exists('wx_global_ac_tk')){
$get_token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$secret;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$get_token_url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
//根据openid和access_token查询用户信息
$access_token = $json_obj['access_token'];
$redis->set('wx_global_ac_tk', $access_token);
$redis->setTimeout('wx_global_ac_tk', $json_obj['expires_in']-100);
}else{
$access_token = $redis->get('wx_global_ac_tk');
}
$get_user_info_url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token='.$access_token.'&openid='.$openid.'&lang=zh_CN';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$get_user_info_url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$res = curl_exec($ch);
curl_close($ch);
$user_info = json_decode($res,true);
$wx_tk=sha1('VVeIx1n'.$user_info['openid']);
$redis->set($wx_tk, $res);
$redis->setTimeout($wx_tk, 7200);
$redis->close();
经过跳转之后已经将用户的信息存入redis,保存的信息有:用户openid,昵称,是否关注本公众号,头像的url等。
调用接口:微信支付
开通微信支付后需要到公共平台上绑定回调域名,在微信支付后台设置KEY,并下载证书保存到服务器上。
##WxPay_Config.php
class WxPayConfig
{
//=======【基本信息设置】=====================================
//
/**
* TODO: 修改这里配置为您自己申请的商户信息
* 微信公众号信息配置
*
* APPID:绑定支付的APPID(必须配置,开户邮件中可查看)
*
* MCHID:商户号(必须配置,开户邮件中可查看)
*
* KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置)
* 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert
*
* APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置),
* 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
* @var string
*/
const APPID = '';
const MCHID = '';
const KEY = '';
const APPSECRET = '';
//=======【证书路径设置】=====================================
/**
* TODO:设置商户证书路径
* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,
* API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)
* @var path
*/
const SSLCERT_PATH = '../cert/apiclient_cert.pem';
const SSLKEY_PATH = '../cert/apiclient_key.pem';
//=======【curl代理设置】===================================
/**
* TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0
* 本例程通过curl使用HTTP POST方法,此处可修改代理服务器,
* 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置)
* @var unknown_type
*/
const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";
const CURL_PROXY_PORT = 0;//8080;
//=======【上报信息配置】===================================
/**
* TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】,
* 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少
* 开启错误上报。
* 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报
* @var int
*/
const REPORT_LEVENL = 1;
}
##WxRedPackHelper.php
class WxRedPackHelper
{
private $parameters;
function __construct($param)
{
$this->parameters = $param;
}
/**
* 发送单个红包
*/
public function send($url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack')
{
$this->parameters['sign'] = $this->get_sign();
$postXml = $this->arrayToXml($this->parameters);//生成红包接口XML信息
$responseXml = $this->curl_post_ssl($url, $postXml);
$responseObj = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
return $responseXml;
}
/**
* 发送裂变红包
*/
public function send_group()
{
return $this->send('https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack');
}
/**
* 检查生成签名参数
*/
protected function check_sign_parameters()
{
if ($this->parameters["nonce_str"] &&
$this->parameters["mch_billno"] &&
$this->parameters["mch_id"] &&
$this->parameters["wxappid"] &&
$this->parameters["send_name"] &&
$this->parameters["re_openid"] &&
$this->parameters["total_amount"] &&
// $this->parameters["max_value"] &&
// $this->parameters["min_value"] &&
$this->parameters["total_num"] &&
$this->parameters["wishing"] &&
// $this->parameters["client_ip"] &&
$this->parameters["act_name"] &&
$this->parameters["remark"]
) {
return true;
}
return false;
}
/**
* 例如:
* appid: wxd111665abv58f4f
* mch_id: 10000100
* device_info: 1000
* body: test
* nonce_str: ibuaiVcKdpRxkhJA
* 第一步:对参数按照 key=value 的格式,并按照参数名 ASCII 字典序排序如下:
* stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
* 第二步:拼接支付密钥:
* stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
* sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
*/
protected function get_sign()
{
if (!WxPayConfig::KEY) {
die('密钥不能为空');
}
if (!$this->check_sign_parameters()) {
die('生成签名参数缺失');
}
ksort($this->parameters);
$unSignParaString = $this->formatQueryParaMap($this->parameters, false);
return $this->sign($unSignParaString, WxPayConfig::KEY);
}
function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
{
$ch = curl_init();
//超时时间
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//这里设置代理,如果有的话
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//cert 与 key 分别属于两个.pem文件
curl_setopt($ch, CURLOPT_SSLCERT, dirname(__FILE__) . '/cert/apiclient_cert.pem');
curl_setopt($ch, CURLOPT_SSLKEY, dirname(__FILE__) . '/cert/apiclient_key.pem');
curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cert/rootca.pem');
if (count($aHeader) >= 1) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
}
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
//echo($vars);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
function formatQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if ($v && "sign" != $k) {
if ($urlencode) {
$v = urlencode($v);
}
$buff .= "$k=$v&";
}
}
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
function arrayToXml($arr)
{
$xml = '<xml>';
foreach ($arr as $key => $val) {
if (is_numeric($val&&false)) {
$xml .= "<$key>$val</$key>";
} else {
$xml .= "<$key><![CDATA[$val]]></$key>";
}
}
$xml .= '</xml>';
return $xml;
}
protected function sign($content, $key)
{
if (!$content) {
die('签名内容不能为空');
}
$signStr = "$content&key=$key";
return strtoupper(md5($signStr));
}
}
发送红包代码:
##redpack.php
include 'WxPay_Config.php';
include 'WxRedPackHelper.php';
function redpack($money, $openid){
$mch_billno = WxPayConfig::MCHID.time().mt_rand(1000, 100000);
$act = "活动名称";
$wish = "祝福语";
$shares = 1;
$data = array(
'nonce_str' => substr(sha1('rpk'.time().mt_rand(1, 1000)), 32),
'mch_billno' => $mch_billno,
'mch_id' => WxPayConfig::MCHID,
'wxappid' => WxPayConfig::APPID,
'send_name' => '昆明五华发布',
're_openid' => $openid,
'total_amount' => $money,
'total_num' => $shares,
'wishing' => $wish,
'client_ip' => '127.0.0.1',
'act_name' => $act,
'remark' => '返回语'
);
$sender = new WxRedPackHelper($data);
$sender->send();
}
$money = 100;//最低金额为1元,以分为单位
$openid = '';
redpack($money, $openid);
最后:微信公众号里面要配置安全域名,微信支付也是,不然会报错。微信支付的证书路径一定要正确,不然发不出去。