基于WordPress的微信小程序支付功能开发

我在2018年的时候总结过一篇微信小程序支付功能开发与踩坑经验总结,当时因为网上相关文档和资源的缺乏,文章获得了很多关注和转载,并且也有很多人指出了其中的不足。主要不足之处就在于那篇文章把所有的签名字串封装都放到了前端,也就是小程序里,通过JS实现,其中还涉及到了商户key这样的敏感字段,因此是不安全的。不过在3年前微信本身对这块也没有做很严格的限制,比如我把对“https://api.mch.weixin.qq.com/pay/unifiedorde”这个接口的请求放在小程序里,那时候照样是能运行的。

近期,把小程序的基础库改成近期版本后,我发现“https://api.mch.weixin.qq.com/pay/unifiedorde”这个接口的请求已经不能放在小程序里了,即使此域名已经加入到request安全域名下也无效,微信那边会自动把你加入的这个域名过滤掉。这就表示一系列的请求必须放到服务器上完成了。因此我重新整理了一下后端的代码,PHP版本的。并且因为我的微信小程序都是基于WordPress做后端的,索性把自定义的接口这块也缝合过来。

当前最新版调试通过:

打包代码如下:

//用于封装各种用得到的字串处理方法
class wx_pay_funcs{
	function postXmlCurl($xml, $url, $useCert = false, $second = 10){
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
		curl_setopt($ch,CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		$data = curl_exec($ch);
		//返回结果
		if($data){
			curl_close($ch);
			return $data;
		} else {
			$error = curl_errno($ch);
			curl_close($ch);
			return $error;
		}
	}
	//微信支付签名字符串串联
	function toUrlParams($data){
		$buff = "";
		foreach ($data as $k => $v) {
			if($k != "sign" && $v != "" && !is_array($v)){
				$buff .= $k . "=" . $v . "&";
			}
		}
		$buff = trim($buff, "&");
		return $buff;
	}
	//数组转XML
	function arrayToXml($arr){
		$xml = "<xml>";
		foreach ($arr as $key=>$val) {
			if (is_numeric($val)){
				$xml.="<".$key.">".$val."</".$key.">";
			}else{
				$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
			}
		}
		$xml.="</xml>";
		return $xml;
	}
	//XML转JSON
	function xml_to_json($xmlstring) {
		//XML转数组
		$xmlarray = simplexml_load_string($xmlstring,'SimpleXMLElement',LIBXML_NOCDATA);
		return json_encode($xmlarray,JSON_UNESCAPED_UNICODE);
	}
}
 
//以下是调起支付请求的接口URL
//例: https://www.abc.com/wp-json/wx_pay/v1/pay/
add_action( 'rest_api_init', function () {
	register_rest_route( 'wx_pay/v1', '/pay/', array(
		'methods' => 'POST',
		'callback' => 'brain1981_wx_pay',
		'show_in_index' => false //注意隐藏接口
	) );
});
function brain1981_wx_pay($request){
	//仅以下4个变量需要自己修改
	$appid="wx..."; //小程序appid
	$appsecret= "avasdfasdf....."; //小程序的secret
	$mch_id="1......"; //商户号id
	$mch_key="abc.........."; //商户号key
 
	$pay_funcs = new wx_pay_funcs;
 
	$total_fee = $request['total_fee']; //支付金额
	$openid = $request['openid']; //用户的Openid
	$order_name = $request['order_name']; //订单名称
	if(empty($total_fee) || empty($openid) || empty($order_name)){
		return "error, loss some parameters.";
	}
 
	$total_fee = $total_fee * 100; //支付金额单位是分
	$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
 
	$data['appid'] = $appid;
	$data['mch_id'] = $mch_id;
	$data['nonce_str'] = md5($mch_id.time());
	$data['openid'] = $openid;
	$data['body'] = $order_name; 
	$data['out_trade_no'] = date("YmdHis").rand(0,1000); //订单号id
	$data['total_fee'] = $total_fee;
	$data['spbill_create_ip'] = '8.8.8.8'; //此参数在小程序中可以随便填个合法的IP地址
	$data['notify_url'] = "https://www.abc.com/wp-json/wx_pay/v1/paid/"; //回调路径稍后也用得到,不过这里其实只是用来填空,并非业务真正的返回
	$data['trade_type'] = "JSAPI";
 
 
	ksort($data); 
	$sign_str = $pay_funcs->toUrlParams($data);
	$sign_str = $sign_str."&key=".$mch_key;
	$data['sign'] = strtoupper(md5($sign_str));
	$xml = $pay_funcs->arrayToXml($data);
	$r = $pay_funcs->postXmlCurl($xml,$url,true);
	$result = json_decode($pay_funcs->xml_to_json($r));
 
	if($result->return_code == 'SUCCESS'){
		$second_data['appId'] = $appid;
		$second_data['timeStamp'] = time();
		$second_data['nonceStr'] = md5(time().rand().rand().$openid);
		$second_data['package'] = "prepay_id=".$result->prepay_id;
		$second_data['signType'] = "MD5";
 
		ksort($second_data);
		$sign_str = $pay_funcs->toUrlParams($second_data);
		$sign_str = $sign_str."&key=".$mch_key;
		$second_data['paySign'] = strtoupper(md5($sign_str));
		//返回二次签名数据到微信小程序,用于最后的支付请求
		return $second_data;
	}else{
		return "签名错误";
	}
}
 
//以下是完成支付请求的接口URL,用于网站数据处理
//例: https://www.abc.com/wp-json/wx_pay/vi/paid/
add_action( 'rest_api_init', function () {
	register_rest_route( 'wx_pay/v1', '/paid/', array(
		'methods' => 'POST',
		'callback' => 'brain1981_api_wx_pay_paid',
		'show_in_index' => false //注意隐藏接口
	) );
});
 
function brain1981_api_wx_pay_paid( $request ){
	if( $request['action']!="paid"){
		return "not a valied request.";
	}
	//此处插入自己的业务代码
	return "success";
}

注:代码中spbill_create_ip和notify_url这两个参数,在H5支付中用得到,但是在小程序中是可以随便填写的,因为小程序不会直接返回通知页面(需在支付完毕后自己写反馈请求到服务器),也无所谓用户的IP地址(反正都是在微信里)。

小程序端的JS代码:

//发起参数请求,开始支付
  payfee: function(){
    var that = this
    var url = "https://www.abc.com/wp-json/wx_pay/v1/pay/"
    var requestData = {
      openid: openid, //此处请先获得用户openid,方法见官方开发文档
      total_fee: 0.01,
      order_name: "XXX费用-订单"+that.data.project_id //自己业务里的订单名称,自由发挥
    }
 
    wx.request({
      url: url,
      data: requestData,
      method: 'POST',
      success: function (res) {
        if (res.statusCode == 200) {
          var param = {
            "timeStamp": res.data.timeStamp.toString(),
            "package": res.data.package,
            "paySign": res.data.paySign,
            "signType": "MD5",
            "nonceStr": res.data.nonceStr
          }
          console.log("param小程序支付接口参数", param);
          that.processPay(param);
 
        } else {
          //请求失败
          wx.showModal({
            title: '请求失败',
            //content: '',
            showCancel:false
          })
        }
      },
    })
  },
 
  /* 小程序支付 */
  processPay: function (param) {
    var that = this
    wx.requestPayment({
      timeStamp: param.timeStamp,
      nonceStr: param.nonceStr,
      package: param.package,
      signType: param.signType,
      paySign: param.paySign,
      success: function (res) {
        // success
        console.log("wx.requestPayment返回信息",res);
        //提交给服务器信息,添加消息
        that.wxPayAfter()
 
        wx.showModal({
          title: '支付成功',
          content: '您将在“微信支付”官方号中收到支付凭证',
          showCancel: false,
          success: function (res) {
            if (res.confirm) {
              //按下确认
            }
          }
        })
      },
      fail: function (res) {
        console.log("支付失败",res);
      },
      complete: function () {
        //console.log("支付完成(成功或失败都为完成)");
      }
    })
  },//processPay()
 
  //支付完成后,使服务器添加一条完成的消息
  wxPayAfter: function(){
    var that = this
    var url = "https://www.abc.com/wp-json/wx_pay/vi/paid/"
    var requestData = {
      project_id: that.data.project_id,//自己业务里的订单号等,此处请自由发挥
      action:"paid"
    }
    wx.request({
      url: url,
      data: requestData,
      method: 'POST'
      success: function (res) {
        console.log("付款状态更新", res)
      },
    })
  },

自此填坑完毕。如果你不是用WordPress也没关系,其中后端部分稍加修改,去掉两个Rest API的接口相关部分,即可使用。

本站所有文章均为原创,欢迎转载,请注明文章出处:https://blog.brain1981.com/2354.html。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。

关注我们的微信公众号-JennyStudio 本站记录了近几年的工作中遇到的一些技术问题和解决过程,“作品集”还收录了本人的大部分作品展示。除了本博客外,我们的工作室网站 – JennyStudio,内有更多作品回顾和展示。
您也可以扫描左边的二维码,关注我们的微信公众号,在微信上查看我们的案例。

2 关于 “基于WordPress的微信小程序支付功能开发” 的评论

  1. 大佬,微信小程序我可以给他设置支付立减吗,就是搞活动,别人扫我的小程序的支付码,然后随机会减几块钱,这种。

    回复
    1. Brain 文章作者

      我没做过这个方面的开发,但这肯定不仅仅要做开发的修改,还要开通相应的服务的。我找了下,你可以看看这里https://blog.csdn.net/sunking001/article/details/79824119

      回复

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注