1、申請微信公衆號、開通微信支付,經過【APPID】將二者關聯,具體操做步驟參考:點擊查看javascript
2、在公衆號管理後臺設置【接收微信支付異步回調通知域名】,php
3、在微信支付管理後臺設置【支付受權域名】及【KEY】,支付受權域名與接收回調通知域名最好爲同域名,css
4、生成支付須要的配置文件html
5、首次訪問網站時靜默獲取用戶 OPENID前端
6、用戶點擊支付時,調用微信【統一下單接口】,獲取 PREPAY_IDjava
7、生成 JSAPI 支付須要的參數ajax
8、用戶輸完支付密碼,前臺輪詢訂單狀態,後臺在 NOTIFY_URL 中處理訂單json
9、PHP demo以下:api
<?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>