微信 JSAPI 支付
程序员文章站
2022-06-22 08:42:23
一、申请微信公众号、开通微信支付,通过【APPID】将两者关联,具体操作步骤参考:点击查看 二、在公众号管理后台设置【接收微信支付异步回调通知域名】, 三、在微信支付管理后台设置【支付授权域名】及【KEY】,支付授权域名与接收回调通知域名最好为同域名, 四、生成支付需要的配置文件 五、首次访问网站时 ......
一、申请微信公众号、开通微信支付,通过【appid】将两者关联,具体操作步骤参考:点击查看
二、在设置【接收微信支付异步回调通知域名】,
三、在设置【支付授权域名】及【key】,支付授权域名与接收回调通知域名最好为同域名,
四、生成支付需要的配置文件
五、首次访问网站时静默获取用户 openid
六、用户点击支付时,调用微信【】,获取 prepay_id
七、生成 jsapi 支付需要的参数
八、用户输完支付密码,前台轮询订单状态,后台在 notify_url 中处理订单
九、php demo如下:
<?php return $config = array( 'site_url' => 'http://www.gentsir.com/', 'weixinpay_config' => array( 'appid' => 'wxf96fa703d64967cc', // 公众号后台获取 'appsecret' => 'e2e87179cfe614dfa0ca16146b0cdfe3', // 公众号后台获取,用于获取用户openid 'mchid' => '1582427110', // 微信支付后台获取, 'pay_key' => 'a5f5764bc7905be3075c79d1ce216014', // 微信支付后台设置,用于参数签名 'notify_url' => 'http://www.gentsir.com/home/wxpay_sync_notice/', // 异步接收微信支付结果地址,不能有任何鉴权逻辑,能在浏览器中访问 'trade_type' => 'jsapi', // 支付类型 ), ); ?> <?php class homecontroller extends controller { public function __construct() { parent::__construct(); } /** * 首页 * */ public function panel() { # code here... } /** * 引导页 * */ public function index() { // 微信静默授权 if (empty($_session['wx_openid'])) { $appid = $config['weixinpay_config']['appid']; $jump_url = $config['site_url'] . 'home/index/wxoauth2/'; $oauth2_url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $appid; $oauth2_url .= '&redirect_uri=' . urlencode($jump_url); $oauth2_url .= '&response_type=code'; $oauth2_url .= '&scope=snsapi_base'; $oauth2_url .= '&state=state#wechat_redirect'; redirect($oauth2_url); } else { redirect('/home/panel/'); } } /** * 不弹出询问获取用户openid * */ public function wxoauth2() { $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $config['weixinpay_config']['appid']; $url .= '&secret=' . $config['weixinpay_config']['appsecret']; $url .= '&code=' . trim($_get['code']); $url .= '&grant_type=authorization_code'; $ch = curl_init(); curl_setopt($ch, curlopt_url, $url); curl_setopt($ch, curlopt_ssl_verifypeer, false); curl_setopt($ch, curlopt_ssl_verifyhost, false); curl_setopt($ch, curlopt_returntransfer, true); $output = curl_exec($ch); if (curl_error($ch)) { redirect('/home/index'); } curl_close($ch); $result = json_decode($output, true); if (!empty($result['openid'])) { $_session('wx_openid', $result['openid']); redirect('/home/panel/'); } redirect('/home/index'); } /** * 用户发请ajax支付请求 * * 请自先定义 api_error(), api_success(), array2xml(), xml2array(), write_log() * */ public function wxpay() { is_weixin() or exit(api_error('请在微信中打开...')); if (empty($_session['user_id'])) { redirect($config['site_url']); } if (empty($_post['total_fee'])) { exit(api_error('支付金额为0')); } if (empty($_post['goods_id'])) { exit(api_error('待支付商品不存在')); } $nonce_str = md5(uniqid(null, true) . mt_rand()); $out_trade_no = crc32($nonce_str); // 调用统一下单接口获取prepay_id $unifiedorder_params = array( 'appid' => $config['weixinpay_config']['appid'], 'mch_id' => $config['weixinpay_config']['mchid'], 'trade_type' => $config['weixinpay_config']['trade_type'], 'notify_url' => $config['weixinpay_config']['notify_url'], 'openid' => $_session['wx_openid'], 'nonce_str' => $nonce_str, 'spbill_create_ip' => $_server['remote_addr'], 'out_trade_no' => $out_trade_no, 'body' => '微信支付后台商家名称-商品类目名' . rand(1, 100), 'total_fee' => $_post['total_fee'] * 100, 'product_id' => $_post['goods_id'], ); ksort($unifiedorder_params); $tmp_str = http_build_query($unifiedorder_params); $tmp_str .= '&key=' . $config['weixinpay_config']['pay_key']; $sign = md5($tmp_str); $unifiedorder_params['sign'] = strtoupper($sign); $ch = curl_init(); curl_setopt($ch, curlopt_url, 'https://api.mch.weixin.qq.com/pay/unifiedorder'); curl_setopt($ch, curlopt_ssl_verifypeer, false); curl_setopt($ch, curlopt_ssl_verifyhost, false); curl_setopt($ch, curlopt_returntransfer, true); curl_setopt($ch, curlopt_post, true); curl_setopt($ch, curlopt_httpheader, array('content-type: text/xml')); curl_setopt($ch, curlopt_postfields, array2xml($unifiedorder_params)); $output = curl_exec($ch); if ($errmsg = curl_error($ch)) { exit(api_error($errmsg)); } curl_close($ch); $unifiedorder = xml2array($output); if (empty($unifiedorder['prepay_id'])) { exit(api_error('微信预支付订单生成失败')); } write_log($unifiedorder); // 生成jsapi参数 $jsapi_params = array( 'appid' => $config['weixinpay_config']['appid'], 'timestamp' => time(), 'noncestr' => $nonce_str, 'package' => 'prepay_id=' . $unifiedorder['prepay_id'], 'signtype' => 'md5', ); ksort($jsapi_params); $tmp_str = http_build_query($jsapi_params); $tmp_str .= '&key=' . $config['weixinpay_config']['pay_key']; $sign = md5($tmp_str); $jsapi_params['paysign'] = strtoupper($sign); $jsapi_params['order_no'] = $out_trade_no; // 用于前台轮询订单状态 write_log($jsapi_params); // 商户订单入库 $order = array( 'pay_state' => 0, // 0待支付 1支付成功 2支付失败 'pay_price' => $_post['total_fee'], 'pay_type' => $config['weixinpay_config']['trade_type'], 'pay_order' => $out_trade_no, 'user_id' => $_session['user_id'], 'goods_id' => $_post['goods_id'], 'create_time' => time(), ); if (!(m('t_order')->add($order))) { $order['errmsg'] = '商户订单入库失败'; write_log($order); exit(api_error('支付失败,请重新发起支付请求')); } exit(api_success($jsapi_params)); } /** * 微信支付异步通知 * */ public function wxpay_sync_notice() { write_log('微信异步通知--start--'); // 获取微信通知 $xml = file_get_contents('php://input', 'r'); $notify = xml2array($xml); if (!isset($notify['return_code'], $notify['result_code']) || $notify['return_code'] !== 'success' || $notify['result_code'] !== 'success' ) { $log = array( 'errmsg' => '微信未返回return_code、result_code为success', 'wx_sync_notice' => $notify, ); write_log($log); $pay_fail = '<xml><return_code><![cdata[fail]]></return_code><return_msg><![cdata[签名失败]]></return_msg></xml>'; exit($pay_fail); } if (empty($notify['sign']) || $notify['out_trade_no']) { $log = array( 'errmsg' => '微信未返回签名或订单号', 'wx_sync_notice' => $notify, ); write_log($log); $pay_fail = '<xml><return_code><![cdata[fail]]></return_code><return_msg><![cdata[签名失败]]></return_msg></xml>'; exit($pay_fail); } // 验证签名 $wx_sign = $notify['sign']; unset($notify['sign']); ksort($notify); $tmp_str = http_build_query($notify); $tmp_str .= '&key=' . $config['weixinpay_config']['pay_key']; $valid_sign = strtoupper(md5($tmp_str)); if ($wx_sign !== $valid_sign) { $log = array( 'errmsg' => '微信返回的签名未通过验证', 'wx_sync_notice' => $notify, 'valid_sign' => $valid_sign, ); write_log($log); $pay_fail = '<xml><return_code><![cdata[fail]]></return_code><return_msg><![cdata[签名失败]]></return_msg></xml>'; exit($pay_fail); } // 验证订单金额及状态 $where = "order_no = " . $notify['out_trade_no']; $order = m('t_order')->where($where)->find(); if (empty($order) || $order['pay_price'] != $notify['total_fee'] / 100) { $log = array( 'errmsg' => '商户订单不存在或微信返回的订单金额与商户订单金额不一致', 'wx_sync_notice' => $notify, 'order_info' => $order, ); write_log($log); $pay_fail = '<xml><return_code><![cdata[fail]]></return_code><return_msg><![cdata[签名失败]]></return_msg></xml>'; exit($pay_fail); } if ($order['pay_state'] == 1) { $log = array( 'errmsg' => '订单已被标记为‘支付成功’(重复异步通知)', 'wx_sync_notice' => $notify, 'order_info' => $order, ); write_log($log); $pay_success = '<xml><return_code><![cdata[success]]></return_code><return_msg><![cdata[ok]]></return_msg></xml>'; exit($pay_success); } // 更新订单 $data = array( 'pay_state' => 1, 'pay_time' => time(), ); $update = m('t_order')->where($where)->save($data); if ($update === false) { $log = array( 'errmsg' => '商户更新订单状态为‘成功’时失败', 'wx_sync_notice' => $notify, 'order_info' => $order, 'update_order' => $data, ); write_log($log); $pay_fail = '<xml><return_code><![cdata[fail]]></return_code><return_msg><![cdata[签名失败]]></return_msg></xml>'; exit($pay_fail); } // 销量+1 库存-1 // code here... write_log("支付成功.\n微信异步通知--end--\n\n"); $pay_success = '<xml><return_code><![cdata[success]]></return_code><return_msg><![cdata[ok]]></return_msg></xml>'; exit($pay_success); } /** * 检查订单支付状态 * */ public function order_state() { $order_no = $_post['order_no']; if (empty($order_no)) { exit('fail'); } $map['pay_order'] = $order_no; $map['pay_state'] = 1; $order = m('t_order')->where($map)->find(); if (empty($order)) { exit('fail'); } exit('success'); } // end all } ?>
<!doctype html> <html> <head> <meta charset="utf-8"> <title>微信支付</title> <style type="text/css"> input#pay_btn.disabled { pointer-events: none; background: #ccc; } input#pay_btn:link, input#pay_btn:visited, input#pay_btn:hover, input#pay_btn:active { outline: none; box-shadow: none; } </style> </head> <body> <form id="pay_form"> <input type="text" name="total_fee" value="5.9"> <input type="hidden" name="goods_id" value="100256"> <input type="button" name="" value="点击支付" id="pay_btn" class=""> </form> </body> <script src="//layer-v3.0.3/layer/layer.js"></script> <script type="text/javascript"> function check_order_state(order_no) { intvl = setinterval(function () { $.ajax({ url: '<?= $config['site_url'] . 'home/order_state/' ?>', type: 'post', data: {order_no: order_no} }) .done(function (msg) { if (msg === 'success') { clearinterval(intvl); alert('支付成功'); } else { console.log('pay fail...'); } }); }, 1000); } function onbridgeready(data) { weixinjsbridge.invoke('getbrandwcpayrequest', data, function (res) { layer.closeall(); if (res.err_msg == "get_brand_wcpay_request:ok") { // 使用以上方式判断前端返回,微信团队郑重提示: //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。 check_order_state(data.order_no); } else if (res.err_msg == "get_brand_wcpay_request:cancel") { alert('已取消支付'); } else { alert('支付失败'); } return false; }); } $('#pay_btn').click(function () { $.ajax({ url: '<?= $config['site_url'] . 'home/wxpay/' ?>', type: 'post', data: $('#pay_form').serialize(), datatype: 'json', beforesend: function () { $('#pay_btn').addclass('disabled'); layer.msg('支付中,请稍候...', {icon: 16, shade: 0.3}); }, }) .done(function (data) { settimeout(function () { layer.closeall(); $('#pay_btn').removeclass('disabled'); if (data.errmsg) { layer.msg(data.errmsg); return false; } // 调起微信支付 onbridgeready(data.data); }, 2000); }) .fail(function () { layer.closeall(); $('#pay_btn').removeclass('disabled'); layer.msg('支付失败,请重新点击支付!'); }); }); </script> </html>