在之前,寫了一下微信公衆號支付,現在和大家分享一下經驗。
-
首先,在開發之前,看一下官方的開發文檔:https://pay.weixin.qq.com/wiki/doc/api/index.html 點擊選擇JSAPI支付
-
然後,看一下商戶平臺的配置信息,微信商戶平臺地址:
https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
(1)登錄之後,點擊產品中心,點擊公衆號支付,看看是否有公衆號支付(沒有的,請去申請一下)。
(2)接下來,開始配置一些信息。
第一坑:
請確保實際支付時的請求目錄與後臺配置的目錄一致,否則將無法成功喚起微信支付。進行配置支付授權目錄:也就是你的支付頁面所在的目錄,一定是生產環境的,微信不支持 ip +端口 形式的地址 異步通知也不支持,所以測試都需要線上真實環境的域名+支付頁面所在目錄。
圖片…
然後,還需要你登錄微信公衆號平臺配置JS接口安全域名。
登錄網址:https://mp.weixin.qq.com
第二坑
所謂安全域名,就是你線上服務器上的備案域名,記住一定要提前備案,或者找已經備過案的服務器,進行配置域名和上面提到的JSAPI支付支付目錄。
圖片… -
配置完基本信息之後。
接着,我們需要獲取必傳參數:appid,mch_id,加密key
appid: 在微信公衆號平臺,找到基本配置,名字叫開發者ID(AppID)。
mch_id:這個東西是你登錄商戶平臺的商戶號。去商戶平臺的個人信息裏找就行了。
key: 微信商戶平臺(pay.weixin.qq.com)–>賬戶設置–>API安全–>密鑰設置。
圖片… -
再說一下其他的,統一下單必傳參數:
隨機字符串:nonce_str,只要不重複就行。
簽名: sign,微信有專門的簽名算法
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
商品描述:body,主要是說,你開發公衆號支付,是幹什麼的。
商戶訂單號:out_trade_no ,這個,在後臺生成一個唯一的訂單號就行。
如PHP的time(),uniqid()。
標價金額:total_fee,注意這個數字,單位是分。
終端IP:spbill_create_ip,獲取用戶的終端IP
通知地址:notify_url,異步接收微信支付結果通知的回調地址,通知url必須爲外網可訪問的url,不能攜帶參數。
交易類型:trade_type,公衆號支付,選擇——>JSAPI -JSAPI支付
第三坑
用戶標識:openid,“trade_type=JSAPI時(即JSAPI支付),此參數必傳,此參數爲微信用戶在商戶對應appid下的唯一標識”。(我當時的處理是,在用戶剛進入頁面的時候,拿‘code’,向微信發送請求,獲取openid,沒有code,就要先獲取code。我在下邊代碼中會說。)
參考獲取openid的文檔:https://mp.weixin.qq.com/wikit=resource/res_main&id=mp1421140842
現在,所有的必傳參數,我們都有了。
開始上代碼:
後臺:我當時寫了倆文件。一個配置文件(WeiXinPayConfig):主要是一些上面提到的參數。一個是業務邏輯文件,處理用戶下單的操作。
配置文件(WeiXinPayConfig.php):
const APPID = ''; //微信公衆號appid
const SECRET = ''; //AppSecret
const MCH_ID = ''; //微信支付商戶號
const KEY = ''; //自己設置的微信key
const NOTIFY_URL = ''; //異步回調地址,需外網可以訪問
const TOKEN = ''; // Token
//config中的log參數
const LEVEL = '';
const FILE = '';
//config中的oauth的參數
const CALLBACK = ''; //微信公衆號appid
//config中的payment的參數
const CERT_PATH = ''; //證書路徑設置,絕對路徑
const KEY_PATH = ''; //密匙文件,絕對路徑
//attributes中的參數
const BODY = ''; //支付後的支付訂單信息
const TRADE_TYPE = 'JSAPI';//喚醒方式
//wcPayParams中的參數
const SIGN_TYPE = '' //微信簽名方式
上面有些參數你可能比較疑惑,那請你看一下’EasyWeChat’:
https://www.easywechat.com/docs/master/overview
業務邏輯文件(WechatPayController.php):
<?php
namespace App\Http\Controllers\Alipay;
use App\Http\Controllers\Controller;
use App\Model\WeChatPayDatabase;
use config\WeiXinPayConfig;
class WechatPayController extends Controller
{
/**微信 支付 下單付款
* @var null
*/
protected $app = null;
function getPay(Request $request)
{
$total_fee = 1000; //付款金額,單位爲分
$out_trade_no = time(); //平臺內部訂單號
$phone = $request->phone; //學生電話
$pay_ways = $request->pay_ways; //學生的支付方式
$student_id = $request->student_id; //學生學號
$key = WeiXinPayConfig::KEY; //自己設置的微信key
$appid = WeiXinPayConfig::APPID; //微信公衆號appid
$secret = WeiXinPayConfig::SECRET; //AppSecret
$mch_id = WeiXinPayConfig::MCH_ID; //微信支付商戶號
$notify_url = WeiXinPayConfig::NOTIFY_URL; //異步回調地址,需外網可以訪問
$config = [
'debug' => true, // 調試設置爲開啓
'app_id' => $appid, // AppID
'secret' => $secret, // AppSecret
'token' => WeiXinPayConfig::TOKEN, // Token
'aes_key' => '', // EncodingAESKey,安全模式下請一定要填寫!!!
'log' => [ //日誌
'level' => WeiXinPayConfig::LEVEL,
'permission' => 0777,
'file' => WeiXinPayConfig::FILE,
],
'oauth' => [ //網頁授權,獲取用戶信息
'scopes' => ['snsapi_userinfo'],
'callback' => WeiXinPayConfig::CALLBACK,
],
'payment' => [
'merchant_id' => $mch_id, //微信支付商戶號
'key' => $key, //自己設置的微信key
'cert_path' => WeiXinPayConfig::CERT_PATH, //證書路徑設置
'key_path' => WeiXinPayConfig::KEY_PATH, //密匙文件
'notify_url' => $notify_url, //異步回調地址,需外網可以訪問
],
];
$this->app = new Application($config);
$payment = $this->app->payment;
$insert_id = WeChatPayDatabase::insertstuorder($student_id,$phone,$out_trade_no,$pay_ways); //把訂單信息存入數據
if($insert_id){ //如果訂單存入成功
$attributes = [
'body' => WeiXinPayConfig::BODY, //支付後的支付訂單信息
'total_fee' => $total_fee, //支付金額,以'分'爲單位
'notify_url' => $notify_url, //回調函數
'out_trade_no' => $out_trade_no, //訂單號
'trade_type' => WeiXinPayConfig::TRADE_TYPE, //喚醒接口
'openid' => session('openId'), //從session中取出用戶的openid
'spbill_create_ip'=>$this->getIP() //獲取用戶端口號
];
$order = new Order($attributes);
$result = $payment->prepare($order); //使用接口完成訂單的創建
$wcPayParams = [
"appId" => $appid,
"timeStamp" => time(),
"nonceStr" => $result['nonce_str'], //隨機串
"package" => "prepay_id=".$result['prepay_id'], //訂單ID
"signType" => WeiXinPayConfig::SIGN_TYPE //微信簽名方式
];
$paySign = $this->MakeSign($wcPayParams); //生成簽名
$wcPayParams['paySign'] = $paySign; //存簽名
$wcPayParams['payId'] = $insert_id; //存數據庫中的訂單ID
return $this->responseToJson(0,'下單成功',$wcPayParams);
}else{
return $this->responseToJson(1,'下單失敗');
}
}
/**修改訂單狀態
* @param Request $request
*/
function updateOrder(Request $request)
{
WeChatPayDatabase::updateOrders($request->id);
}
/**數據返回JSON格式
* @param int $code
* @param string $msg
* @param null $paras
* @return \Illuminate\Http\JsonResponse
*/
//所有返回前臺的數據,封裝成JSON數據格式
function responseToJson($code = 0, $msg = '', $paras = null) {
$res["code"] = $code;
$res["msg"] = $msg;
$res["result"] = $paras;
return response()->json($res);
}
/**生成微信簽名
* @param $sign
* @return string
*/
//微信支付簽名
public function MakeSign($sign){
//簽名步驟一:按字典序排序參數
ksort($sign);
$string = $this->ToUrlParams($sign);
//簽名步驟二:在string後加入KEY
$string = $string . "&key=你自己的KEY";
//簽名步驟三:MD5加密
$string = md5($string);
//簽名步驟四:所有字符轉爲大寫
$result = strtoupper($string);
return $result;
}
/**解析生成簽名時傳來的JSON數據
* @param $sign
* @return string
*/
//把微信支付簽名,封裝拼接算法
public function ToUrlParams($sign){
$buff = "";
foreach ($sign as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**獲取終端IP
* @return array|false|string
*/
//獲取用戶的終端IP
function getIP() {
if (getenv("HTTP_CLIENT_IP")) //取得用戶的IP代碼;
$ip = getenv("HTTP_CLIENT_IP");
else if(getenv("HTTP_X_FORWARDED_FOR")) //透過代理服務器取得客戶端的真實 IP 地址
$ip = getenv("HTTP_X_FORWARDED_FOR");
else if(getenv("REMOTE_ADDR")) //正在瀏覽當前頁面用戶的IP 地址。
$ip = getenv("REMOTE_ADDR");
else $ip = "Unknow";
return $ip;
}
/**
* @param Request $request
* @return
*/
//目的:獲取用戶的openid,首先獲取用戶的code,然後用code換取openid。
public function index(Request $request){
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) { //如果是微信瀏覽器
if($request->get('code')){ //如果有code參數
$code=$request->get('code');
$get_token_url="https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx2fffc402a50e03a5&secret=956397f1970f6d1b114a8ac835bc0a77&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, 100);
$openid = curl_exec($ch); //拿code換區opeid
$Id=json_decode($openid);
session(['openId' => $Id->openid]); //存session
curl_close($ch);
}else{ //沒有code就先 跳轉 然後回調到這裏 執行上面的if獲取Openid
return redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx2fffc402a50e03a5&redirect_uri=你自己的獲取'openid的路由'&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect");
}
}
return redirect('/');
}
/**支付後回調
* @return mixed
*/
public function wechatNotify() {
$response =$this->app->payment->handleNotify(function($notify, $successful){
$out_no = $notify->out_trade_no;
// 使用通知裏的 "微信支付訂單號" 或者 "商戶訂單號" 去自己的數據庫找到訂單
$order = WeChatPayDatabase::sestuordernum($out_no);
if (!$order) { // 如果訂單不存在
return 'Order not exist.'; // 告訴微信,我已經處理完了,訂單沒找到,別再通知我了
}
if ($successful) { // 用戶是否支付成功
WeChatPayDatabase::updateorstatus($out_no);
return true;
} else { // 用戶支付失敗
return false;
}
return true;
});
return $response;
}
}
前臺:可以參考官方文檔,微信內H5調起支付:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
我當時用的是VUE。
//向後臺發送,進行下單
postpay(){
let self = this;
this.$http.post('wechatpay/getpay',{
student_id : this.form.student_id,
phone : this.form.phone,
pay_ways : this.pay_ways,
}).then(
function (response) {
let data = response.data;
if(data.code == 0){
self.sicallpay(data.result);
}
else {
self.$message({
showClose: true,
message: data.msg,
type: 'error'
});
}
})
},
//
sicallpay(result){
if(typeof WeixinJSBridge == 'undefined'){ //WeixinJSBridge內置對象在其他瀏覽器中無效。
if(document.addEventListener()){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if(document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else {
this.onBridgeReady(result);
}
},
onBridgeReady(result){
let self = this;
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":result.appId, //公衆號名稱,由商戶傳入
"timeStamp":result.timeStamp, //時間戳,自1970年以來的秒數
"nonceStr":result.nonceStr, //隨機串
"package":result.package,
"signType":"MD5", //微信簽名方式:
"paySign":result.paySign
},
function (res) {
/**
JS API的返回結果get_brand_wcpay_request:ok僅在用戶成功完成支付時返回。
*具體的其他返回結果,參考官方文檔
*/
if (res.err_msg == "get_brand_wcpay_request:ok") {
self.updateOrders(result.payId);
} else {
self.$message({
showClose: true,
message: '支付失敗,無法報名',
type: 'error'
});
}
}
);
},
updateOrders(orderid){
/**
* 這裏寫你自己的業務邏輯
*/
},
}
第四坑
前臺喚醒微信支付,需要注意,要兼容IOS系統。上面的前臺寫的,只適用於android。
兼容蘋果系統,我後面會補上。
到這裏,微信公衆號JSAPI支付,基本已經完成了。
喜歡,請留言,點贊!!!!!!