微信支付一共分为7种,分为是:付款码支付、JSAPI支付、Native支付、APP支付、H5支付、小程序支付、人脸支付。
此次业务中使用到的是微信JSAPI支付:用户通过微信扫码、关注公众号等方式进入商家H5页面,并在微信内调用 JSSDK完成支付
文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
SDK:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=11_1
JSAPI支付需要在微信中的浏览器打开才能唤起微信支付,效果如下图

附上界面代码
代码中使用了模板引擎
html:
<div class="am-modal-bd">
<img src="{$competition['avatar']}" alt=""><br>
<span style="font-size: 13px;color: #72c6ef">{$competition['username']}</span><br>
<span id="tips" style="font-size: 13px"></span>
<ul class="ul_box">
<li>
<div></div>
<div class="label_box">
<label>
<input type="radio" name="price" value="{$prices[0]}" checked="">
<div class="active"><span class="am-icon-diamond"></span> {$prices[0]}钻</div>
</label>
<label>
<input type="radio" name="price" value="{$prices[1]}">
<div><span class="am-icon-diamond"></span> {$prices[1]}钻</div>
</label>
<label>
<input type="radio" name="price" value="{$prices[2]}">
<div><span class="am-icon-diamond"></span> {$prices[2]}钻</div>
</label>
</div>
</li>
<li>
<div></div>
<div class="label_box">
<label>
<input type="radio" name="price" value="{$prices[3]}">
<div><span class="am-icon-diamond"></span> {$prices[3]}钻</div>
</label>
<label>
<input type="radio" name="price" value="{$prices[4]}">
<div><span class="am-icon-diamond"></span> {$prices[4]}钻</div>
</label>
<label>
<input type="number" name="price" id="price" class="input" placeholder="自定义">
</label>
</div>
</li>
</ul>
<span style="font-size: 13px">注:1钻=1元,1钻={$activity['offset']}票</span><br>
<button type="button" class="am-btn am-btn-primary am-radius" onclick="callpay()" style="margin-top: 5px;">立即微信支付</button>
</div>
JavaScript:
// 投票
var offset = {$activity['offset']};
$('#tips').html('正在给{$competition['code']}号赠送{$prices[0]}钻='+({$prices[0]}*offset)+'票');
$('#vote').click(function(){
$.post('/index/index/detailed.html?cid={$cid}&aid={$aid}',{
formhash :'{FORMHASH}',
submit:'1',
type:1,
openid:'{$_G['member']['openid']}'
},function(res){
alert(res.msg);
if (res.code == 0) {
$('.box-1 span').text(res.data.all);
$('.box-2 span').text(res.data.rank);
$('.box-3 span').text(res.data.up + '票');
}
});
})
/* jQuery对象级别插件扩展 */
$.fn.extend({
/* 单选框 */
hlRadio:function () {
var radioEl=$(this);
radioEl.click(function () {
var price = 0;
price = $('input:radio:checked').val();
$('#price').val('');
$('#tips').html('正在给{$competition['code']}号赠送'+price+'钻='+(price*offset)+'票');
radioEl.siblings("div").removeClass("active");
$(this).siblings("div").addClass("active");
});
},
});
$("input[name='price']").hlRadio();
$('#price').bind('input propertychange', function(){
var price = 0;
price = $('#price').val();
$('#tips').html('正在给{$competition['code']}号赠送'+price+'钻='+(price*offset)+'票');
})
CSS部分
.ul_box {
margin:0 auto;
padding:0;
list-style:none;
width: 344px;
}
.ul_box>li {
padding:10px 10px 0 10px;
overflow:hidden;
border-bottom:#e5e5e5 solid 1px;
}
.ul_box>li:last-child {
border-bottom:none;
}
.ul_box>li>div {
float:left;
}
.ul_box>li>div:nth-child(1) {
width:100px;
}
.ul_box>li>div:nth-child(2) {
width:480px;
overflow:hidden;
}
.label_box>label {
display:block;
float:left;
margin:0 10px 10px 0;
position:relative;
overflow:hidden;
}
.label_box>label>input {
position:absolute;
top:0;
left:-20px;
}
.label_box>label>div {
width:100px;
text-align:center;
border:#dddddd solid 1px;
height:40px;
line-height:40px;
color:#666666;
user-select:none;
overflow:hidden;
position:relative;
height: 75px;
}
.label_box>label>div.active{
border:#d51917 solid 1px;
background-color: #fff9f8;
color:#d51917;
}
.label_box>label>div.active:after {
content:'';
display:block;
width:20px;
height:20px;
background-color:#d51917;
transform:skewY(-45deg);
position:absolute;
bottom:-10px;
right:0;
z-index:1;
}
.label_box>label>div.active:before {
content:'';
display:block;
width:3px;
height:8px;
border-right:#ffffff solid 2px;
border-bottom:#ffffff solid 2px;
transform:rotate(35deg);
position:absolute;
bottom:2px;
right:4px;
z-index:2;
}
.input{
height: 75px!important;
border: 1px solid #DDD!important;
position: initial!important;
width: 100px!important;
text-align: center!important;
padding: 5px!important;
font-size: 19px!important;
}
.am-modal-bd{
border-bottom: none;
margin-top: 20px;
}
.am-modal-bd img{
width: 50px;
border-radius: 50px;
}
.am-icon-diamond{
font-size: 20px;
display: block;
text-align: center;
margin-bottom: -7px;
}
唤起微信支付,此处的js代码仅在微信手机浏览器中生效//调用微信JS api 支付
function jsApiCall(appId,timeStamp,nonceStr,package,signType,paySign)
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',{
"appId":appId, //公众号名称,由商户传入
"timeStamp":timeStamp, //时间戳,自1970年以来的秒数
"nonceStr":nonceStr, //随机串
"package":package,
"signType":signType, //微信签名方式:
"paySign":paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
WeixinJSBridge.log(res.err_msg);
alert('投票成功');
// window.history.go(-1);
var url = '/index/index/detailed.html?cid={$cid}&aid={$aid}';
window.location.href=url;
// location.reload();
return false;
}
alert('投票失败');
}
);
}
// 支付
function callpay()
{
var price = '';
if ($('#price').val()) {
price = $('#price').val();
}else if($('input:radio:checked').val()){
price = $('input:radio:checked').val();
}else{
alert('请输入购买数量');
}
if (Number(price) <= 0) {
alert('请输入一个大于0的正数');
$('#price').val('');
return false
}
$.post('/index/index/weixin.html?cid={$cid}&aid={$aid}',{
formhash :'{FORMHASH}',
submit:'1',
type:1,
pay:1,
price: price,
openid:'{$_G['member']['openid']}'
},function(res){
if (res.code ==0) {
jsApiCall(
res.data.appId,
res.data.timeStamp,
res.data.nonceStr,
res.data.package,
res.data.signType,
res.data.paySign,
);
}else{
alert(res.msg);
}
});
// 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{
// }
}
界面基于妹子UI,效果如下图
PHP后端,创建本地订单
//①、获取用户openid
$tools = new JsApiPay();
$openid = $tools->Getopenid(); //如果已经有了,可以直接赋值
//②、统一下单
$input = new WxPayUnifiedOrder();
$input->SetBody("购买钻石票");
$input->SetAttach(json_encode([ //附加参数
'order' => $order_id,
'cid' => $cid,
'uid' => $_G['uid'],
'aid' => $aid,
'md5' => $md5
]));
$input->SetOut_trade_no(WxPayConfig::MCHID.date("YmdHis"));
$input->SetTotal_fee($price); //价格,单位分,“元记得*100”
$input->SetTime_start(date("YmdHis"));
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag("test");
$input->SetNotify_url(WX_NOTIFY_URL);
$input->SetTrade_type("JSAPI");
$input->Setopenid($openid);
$order = WxPayApi::unifiedOrder($input);
$jsApiParameters = $tools->GetJsApiParameters($order);
//保存本地订单
DB::insert('vote_order', [
'order' => $order_id,
'time' => time(),
'price' => intval($_GET['price']),
'uid' => $_G['uid'],
'openid' => $openid,
'ip' => $ip
]);
Json(['code' => 0, 'msg' => '成功','data' => json_decode($jsApiParameters,true)]);
//获取共享收货地址js函数参数
// $editAddress = $tools->GetEditAddressParameters();
// debug($jsApiParameters);
//③、在支持成功回调通知中处理成功之后的事宜,见 notify.php
/**
* 注意:
* 1、当你的回调地址不可访问的时候,回调通知会失败,可以通过查询订单来确认支付是否成功
* 2、jsapi支付时需要填入用户openid,WxPay.JsApiPay.php中有获取openid流程 (文档可以参考微信公众平台“网页授权接口”,
* 参考http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html)
*/
<?php
/**
*
* 回调基础类
* @author widyhu
*
*/
class WxPayNotify extends WxPayNotifyReply {
/**
*
* 回调入口
* @param bool $needSign 是否需要签名输出
*/
final public function Handle($needSign = true) {
$msg = "OK";
//当返回false的时候,表示notify中调用NotifyCallBack回调失败获取签名校验失败,此时直接回复失败
$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
// 这是一个调试技巧
// file_put_contents('a.txt',print_r($msg,true));
if ($result == false) {
$this->SetReturn_code("FAIL");
$this->SetReturn_msg($msg);
$this->ReplyNotify(false);
return;
} else {
//该分支在成功回调到NotifyCallBack方法,处理完成之后流程
$attach = json_decode($msg['attach'], true);
// file_put_contents('aaaa.txt',print_r($attach,true));
$cid = $attach['cid'];
$aid = $attach['aid'];
$uid = $attach['uid'];
$order = $attach['order'];
$md5 = $attach['md5'];
$vote_order = DB::fetch_first("SELECT * FROM " . DB::table('vote_order') . " WHERE `order`='{$order}' and status = 0");
if (!$vote_order) {
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
}
$sign_md5 = md5($vote_order['openid'] . $vote_order['order'] . $vote_order['uid'] );
if ($sign_md5 == $md5) {
// 所有活动信息
$activity = DB::fetch_first("SELECT * FROM " . DB::table('vote_activity') . " WHERE aid={$aid} ");
$ip = $vote_order['ip'];
$jewel_vote = $vote_order['price'] * $activity['offset'];
// 投票
DB::query("UPDATE `pre_vote_competition` SET `jewel`=jewel+{$vote_order['price']},jewel_vote=jewel_vote+{$jewel_vote} WHERE (`cid`='{$cid}')");
// 增加投票记录
DB::insert('vote_record',[
'time' => TIMESTAMP,
'openid' => $vote_order['openid'],
'type' => 1,
'aid' => $aid,
'cid' => $cid,
'ip' => $ip,
'price' => $vote_order['price'],
'jewel_vote' => $jewel_vote,
'uid' => $uid,
'order' => $vote_order['order']
]);
// 更新订单状态
DB::query("UPDATE `pre_vote_order` SET `status` = 1 WHERE `order`={$order}");
// 更新活动总收益
DB::query("UPDATE `pre_vote_activity` SET `profit`=profit+{$vote_order['price']} WHERE `aid`={$aid}");
}
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
}
$this->ReplyNotify($needSign);
}
/**
*
* 回调方法入口,子类可重写该方法
* 注意:
* 1、微信回调超时时间为2s,建议用户使用异步处理流程,确认成功之后立刻回复微信服务器
* 2、微信服务器在调用失败或者接到回包为非确认包的时候,会发起重试,需确保你的回调是可以重入
* @param array $data 回调解释出的参数
* @param string $msg 如果回调处理失败,可以将错误信息输出到该方法
* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
*/
public function NotifyProcess($data, &$msg) {
//TODO 用户基础该类之后需要重写该方法,成功的时候返回true,失败返回false
return true;
}
/**
*
* notify回调方法,该方法中需要赋值需要输出的参数,不可重写
* @param array $data
* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
*/
final public function NotifyCallBack($data) {
$msg = "OK";
$result = $this->NotifyProcess($data, $msg);
if ($result == true) {
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
} else {
$this->SetReturn_code("FAIL");
$this->SetReturn_msg($msg);
}
return $result;
}
/**
*
* 回复通知
* @param bool $needSign 是否需要签名输出
*/
final private function ReplyNotify($needSign = true) {
//如果需要签名
if ($needSign == true &&
$this->GetReturn_code($return_code) == "SUCCESS") {
$this->SetSign();
}
WxpayApi::replyNotify($this->ToXml());
}
}
回调类调用
<?php
if(!defined('IN_DISCUZ')) {
exit('Access Denied');
}
require_once "./source/plugin/vote/class/WxpayAPI_php_v3/lib/WxPay.Api.php";
require_once "./source/plugin/vote/class/WxpayAPI_php_v3/lib/WxPay.Notify.php";
require_once "./source/plugin/vote/class/WxpayAPI_php_v3/example/WxPay.JsApiPay.php";
require_once './source/plugin/vote/class/WxpayAPI_php_v3/example/log.php';
//初始化日志
// $logHandler= new CLogFileHandler("../logs/".date('Y-m-d').'.log');
// $log = Log::Init($logHandler, 15);
// Log::DEBUG("begin notify");
$notify = new WxPayNotify();
$notify->Handle(false);
注意:微信支付需要在后台配置一个微信支付授权目录,其中的APPID等参数在微信支付后台可获取到。微信公众号信息配置
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




