微信支付之前也对接过几次,因为时隔久远,每次对接都要挠头一番,可怜了发际线了,我的宝你在哪里,你再不来我头发掉光了😭
这次决定记录下来,以后对接方便新人也方便自己,说真,其实我是看上官方的拉杆箱了🥰
欲练神功,必先挠头,快快乐乐吃西瓜,清清凉凉一夏天👻
如果你是第一次对接支付:
1.你得申请一个服务号,并通过微信认证
2.获取参数看图:往下看 ⬇️
3.注册微信商户平台并配置API密钥
第一式:随便起个文件名,最好要有特色
//命名为:wwxpay-mini.php 扔到你的微信支付授权目录,就这一个文件已经实现了jsapi支付,无需下载其他sdk
第二式:调试工具
微信公众平台支付接口调试工具:https://pay.weixin.qq.com/wiki/tools/signverify/ (调试错误的时候可以用得上,新手暂时略过)
第三式:上代码
操作方法:将此文件备注位置-【商户参数】/【订单信息】两处的参数,改为你自己的即可,其中订单参数可以为动态获取,如果你只有一个商品那就写死吧
<?php
/*下单参数:
公众账号ID 字段: appid
商户号 字段: mch_id
随机字符串 字段: nonce_str
签名 字段: sign
签名类型 字段: sign_type "MD5"
商品描述 字段: body
商户订单号 字段: out_trade_no
标价金额 字段: total_fee
终端IP 字段: spbill_create_ip
通知地址 字段: notify_url
交易类型 字段: trade_type "JSAPI"
转化为以下格式:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付测试</attach>
<body>JSAPI支付测试</body>
<mch_id>10000100</mch_id>
<detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"苹果手机" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"苹果手机" } ] }]]></detail>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
返回值:
返回状态码 字段: return_code 返回值: SUCCESS/FAIL
返回信息 字段: return_msg 返回值: OK
成功的话,在return_code为SUCCESS的时候有返回:
公众账号ID 字段: appid
商户号 字段: mch_id
签名 字段: sign
业务结果 字段: result_code
错误代码 字段: err_code 返回值: SUCCESS/FAIL(错误的时候返回)
错误码解析:
INVALID_REQUEST 参数错误 参数格式有误或者未按规则上传 订单重入时,要求参数值与原请求一致,请确认参数问题
NOAUTH 商户无此接口权限 商户未开通此接口权限 请商户前往申请此接口权限
NOTENOUGH 余额不足 用户账号余额不足 用户账号余额不足,请用户充值或更换支付卡后再支付
ORDERPAID 商户订单已支付 商户订单已支付,无需重复操作 商户订单已支付,无需更多操作
ORDERCLOSED 订单已关闭 当前订单已关闭,无法支付 当前订单已关闭,请重新下单
SYSTEMERROR 系统错误 系统超时 系统异常,请用相同参数重新调用
APPID_NOT_EXIST APPID不存在 参数中缺少APPID 请检查APPID是否正确
MCHID_NOT_EXIST MCHID不存在 参数中缺少MCHID 请检查MCHID是否正确
APPID_MCHID_NOT_MATCH appid和mch_id不匹配 appid和mch_id不匹配 请确认appid和mch_id是否匹配
LACK_PARAMS 缺少参数 缺少必要的请求参数 请检查参数是否齐全
OUT_TRADE_NO_USED 商户订单号重复 同一笔交易不能多次提交 请核实商户订单号是否重复提交
SIGNERROR 签名错误 参数签名结果不正确 请检查签名参数和方法是否都符合签名算法要求
XML_FORMAT_ERROR XML格式错误 XML格式错误 请检查XML参数格式是否正确
REQUIRE_POST_METHOD 请使用post方法 未使用post传递参数 请检查请求参数是否通过post方法提交
POST_DATA_EMPTY post数据为空 post数据不能为空 请检查post数据是否为空
NOT_UTF8 编码格式错误 未使用指定编码格式 请使用UTF-8编码格式
接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
*/
header('Content-type:text/html; Charset=utf-8');//设置网页编码
ini_set('date.timezone', 'Asia/Shanghai');
//商户参数
$appKey = 'xxxxxxxxxxxxxxxxxx';//公众号的APPKey
$config = array(
'mch_id' => 'xxxxxxxxxx',//微信支付平台商户号
'appid' => 'xxxxxxxxxxxxxxx',//公众号平台的APPID
'key' => 'xxxxxxxxxxxxxxxxxxxxxxx' //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥V2-设置API密钥
);
//订单信息
$unified = array(
'appid' => $config['appid'],//公众账号ID
'attach' => '饭团',//附加数据
'body' => "饭团子",//商品描述
'mch_id' => $config['mch_id'],//商户号
'nonce_str' => NoStr(),//随机字符串
'notify_url' => "http://pay.xxxxxx.com/wxpay/api/returns.php",//通知地址
'openid' => GetOpenid($config),
'out_trade_no' => strval(date("YmdH").time()),//商户订单号
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],//终端IP
'total_fee' => intval(0.01*100), //微信会自己减去两位数*100补回,标价金额
'trade_type' => 'JSAPI',//交易类型
);
$unified['sign'] = getSign($unified, $config['key']);//获取签名
// echo "<pre>";print_r($unified);echo "</pre>";die;
// die(arrayToXml($unified));
$responseXml = curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', arrayToXml($unified));//转换为xml并发送
$unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);//解析XML为对象
if ($unifiedOrder === false) {
die('parse xml error');//解析错误
}
if ($unifiedOrder->return_code != 'SUCCESS') {
die($unifiedOrder->return_msg);//返回成功结果
}
if ($unifiedOrder->result_code != 'SUCCESS') {
die($unifiedOrder->err_code->des);//返回失败结果
}
$arr = array(
"appId" => $config['appid'],//公众账号ID
"timeStamp" => strval(time()),//这里是字符串的时间戳
"nonceStr" => NoStr(),//随机字符串
"package" => "prepay_id=" . $unifiedOrder->prepay_id,//订单详情扩展字符串
"signType" => 'MD5',//声明加密类型
);
$arr['paySign'] = getSign($arr, $config['key']);//获取调起支付的签名
$jsApiParameters=json_encode($arr);
?>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>FT-收银台</title>
<script type="text/javascript">
/*
JSAPI调起支付
公众号id appId
时间戳 timeStamp
随机字符串 nonceStr String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,32位。推荐随机数生成算法
订单详情扩展字符串 package String(128) prepay_id=123456789 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
签名方式 signType MD5 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
签名 paySign 是 String(64) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
*/
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
<?php echo $jsApiParameters; ?>,
function(res){
WeixinJSBridge.log(res.err_msg);
alert(res.err_code+res.err_desc+res.err_msg);
}
);
}
function callpay()
{
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
}
</script>
</head>
<body>
<br/>
<font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">0.01元</span>钱</b></font><br/><br/>
<div align="center">
<button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
</div>
</body>
</html>
<?php
//所有的函数都在这里了
function GetOpenid(array $config){
//通过code获得openid
if (!isset($_GET['code'])){
//触发微信返回code码
$scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://';
$baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);
$url = __CreateOauthUrlForCode($baseUrl,$config);
Header("Location: $url");
exit();
} else {
//获取code码,以获取openid
$code = $_GET['code'];
$openid = GetOpenidFromMp($code,$config);
return $openid;
}
}
function GetOpenidFromMp($code,array $config){
$url = __CreateOauthUrlForOpenid($code,$config);
$res = curlGet($url);
//取出openid
$data = json_decode($res,true);
// $this->data = $data;
$openid = $data['openid'];
return $openid;
}
function __CreateOauthUrlForOpenid($code,array $config){
$urlObj["appid"] = $config['appid'];
$urlObj["secret"] = $GLOBALS['appKey'];
$urlObj["code"] = $code;
$urlObj["grant_type"] = "authorization_code";
$bizString = toparams2($urlObj);
return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
}
/**
* 构造获取code的url连接
* @param string $redirectUrl 微信服务器回跳的url,需要url编码
* @return 返回构造好的url
*/
function __CreateOauthUrlForCode($redirectUrl,array $config){
$urlObj["appid"] = $config['appid'];
$urlObj["redirect_uri"] = "$redirectUrl";
$urlObj["response_type"] = "code";
$urlObj["scope"] = "snsapi_base";
$urlObj["state"] = "STATE"."#wechat_redirect";
$bizString = toparams2($urlObj);
return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
}
function getSign($params, $key){
//生成签名
ksort($params, SORT_STRING);//把值作为字符串来排序
$unSignParaString = toparams1($params, false);
$signStr = strtoupper(md5($unSignParaString . "&key=" . $key));//拼接字符并转换为大写
return $signStr;
}
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;
}
function toparams1($paraMap, $urlEncode = false){
//签名数组排序并拼接
$buff = "";
ksort($paraMap);//对数组进行排序:ABCDEFG……
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v) {
if ($urlEncode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar = false;
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);//移除多余的&
}
return $reqPar;
}
function toparams2($urlObj){
//把数组转成URL参数:appid=123456&sign=xxxxxxxxxxxxxxxxx
$buff = "";
foreach ($urlObj as $k => $v)
{
if($k != "sign") $buff .= $k . "=" . $v . "&";
}
$buff = trim($buff, "&");//移除多余的&
return $buff;
}
function curlGet($url = '', $options = array()){
//get访问
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
function curlPost($url = '', $postData = '', $options = array()){
//post访问
if (is_array($postData)) {
$postData = http_build_query($postData);//把数组转成URL参数:appid=123456&sign=xxxxxxxxxxxxxxxxx
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL超时为30秒
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
function NoStr($length = 16){
//随机字符串默认16位
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
?>
我喜欢面向过程emm,别笑话我🤣
别问为啥这么多备注,因为怕你笨🤣
总结:
1. 申请服务号,注册微信商户平台,获取:Appid Key Api密钥 商户号
2. 创建wxpay-mini.php【名称随意,但是一定放到授权目录】
3.【商户参数】/【订单信息】两处的参数,改为你自己的参数
4.运行支付测试结果
这你还不会的话,来找我,看我怎么揍你🤪
我还没对象,拉杆箱可以不要,官方给我介绍个对象呗,别说给我New一个😭
还没有用过微信云托管的朋友可以看我另一篇教程:https://developers.weixin.qq.com/community/minihome/article/doc/000068648fcae81db5fd0f17e58413
暂时就写这么多,再熬天就要亮了😇
忘了说运行环境了,我的是:Nginx+PHP8.1 如果你们的环境有问题请尝试升级PHP,或留言
采用了的小朋友,反手给我一个赞
向官方反馈一个小问题,修改编辑文章的时候编辑器会自己修改文章内容,应该是哪个过滤机制在捣鬼