在之前,写了一下微信公众号支付,现在和大家分享一下经验。
-
首先,在开发之前,看一下官方的开发文档: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支付,基本已经完成了。
喜欢,请留言,点赞!!!!!!