服务商模式和普通微信支付方式不同点在于,普通支付就是用户支付钱到商户账号,而服务商则是以服务商作为媒介然后付到对应的子商户账号下。(个人理解)
当时前提是已有一个公众号和小程序然后服务商是必须申请公众号的,用户付款是在服务商申请的小程序上,也就是说服务商与公众号和小程序都关联了,其次申请子商户号然后关联服务商和子商户,这相关具体操作请看微信官方文档。
v3接口签名报错排查指引(亲测好用):
https://developers.weixin.qq.com/community/develop/article/doc/000ce8d3ee8b70d868ed6185b5b013
上代码:
/**
* 微信支付
*
* @param type $param
* @return type
*/
public function wxPay($param)
{
$appid = '********';//公众号appid
$mch_id = '******';//服务商商户号
$sub_appid = '*******';//小程序APPID--应用场景appid
$sub_mch_id = $param['mch_id'];//子商户号--对应的商品门店所申请的商户号
$openid = $param['openid'];//这个地方的openid一定要是对应的小程序里面的,因为我是在小程序里面走支付
$body = $param['body']; //这个自己写,微信订单里面显示的是商品名称
$nonce_str = $this->nonce_str();//随机字符串
$notify_url = $param['notify_url'];//回调地址
$out_trade_no = $param['order_sn'];//商户订单号
$spbill_create_ip = '';//服务器IP
$total_fee = $param['total_fee'];//因为充值金额最小是1 而且单位为分 如果是充值1元所以这里需要*100
// $total_fee = 1;
$trade_type = 'JSAPI';//交易类型 默认
// var_dump($total_fee);
//这里是按照顺序的 因为下面的签名是按照顺序 排序错误 肯定出错---请务必按照这个顺序,不然报签名错误
$post['appid'] = $appid;
$post['body'] = $body;
$post['mch_id'] = $mch_id;
$post['nonce_str'] = $nonce_str; //随机字符串
$post['notify_url'] = $notify_url;
$post['out_trade_no'] = $out_trade_no;
$post['spbill_create_ip'] = $spbill_create_ip; //终端的ip
$post['sub_appid'] = $sub_appid;
$post['sub_mch_id'] = $sub_mch_id;
$post['sub_openid'] = $openid;//若sub_openid有传的情况下,sub_appid必填,且sub_appid需与sub_openid对应
$post['total_fee'] = $total_fee; //总金额 最低为一块钱 必须是整数
$post['trade_type'] = $trade_type;
$sign = $this->sign($post); //签名
$post_xml = '<xml>
<appid>'.$appid.'</appid>
<body>'.$body.'</body>
<mch_id>'.$mch_id.'</mch_id>
<nonce_str>'.$nonce_str.'</nonce_str>
<notify_url>'.$notify_url.'</notify_url>
<out_trade_no>'.$out_trade_no.'</out_trade_no>
<spbill_create_ip>'.$spbill_create_ip.'</spbill_create_ip>
<sub_appid>'.$sub_appid.'</sub_appid>
<sub_mch_id>'.$sub_mch_id.'</sub_mch_id>
<sub_openid>'.$openid.'</sub_openid>
<total_fee>'.$total_fee.'</total_fee>
<trade_type>'.$trade_type.'</trade_type>
<sign>'.$sign.'</sign>
</xml> ';
//统一接口prepay_id
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$xml = $this->http_request($url,$post_xml);
// var_dump($xml);
$array = $this->xml($xml);//全要大写
// var_dump($array);
if($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS'){
$time = time();
$tmp = [];//临时数组用于签名
//$tmp['appId'] = $appid;
$tmp['appId'] = $sub_appid;
$tmp['nonceStr'] = $nonce_str;
$tmp['package'] = 'prepay_id='.$array['PREPAY_ID'];
$tmp['signType'] = 'MD5';
$tmp['timeStamp'] = "$time";
$data['state'] = 1;
$data['timeStamp'] = "$time";//时间戳
$data['nonceStr'] = $nonce_str;//随机字符串
$data['signType'] = 'MD5';//签名算法,暂支持 MD5
$data['package'] = 'prepay_id='.$array['PREPAY_ID'];//统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
$data['paySign'] = $this->sign($tmp);//签名,具体签名方案参见微信公众号支付帮助文档;
$data['out_trade_no'] = $out_trade_no;
}else{
$data['state'] = 0;
$data['text'] = "错误";
$data['RETURN_CODE'] = $array['ERR_CODE'];
$data['RETURN_MSG'] = $array['ERR_CODE_DES'];
}
return $data;
}
获取到支付参数后就属于正常支付范围
支付成功之后的回调就和正常回调一样写就好了
把对应的使用到的方法也放到这边
//随机32位字符串
private function nonce_str(){
$result = '';
$str = 'QWERTYUIOPASDFGHJKLZXVBNMqwertyuioplkjhgfdsamnbvcxz';
for ($i=0;$i<32;$i++){
$result .= $str[rand(0,48)];
}
return $result;
}
//签名 $data要先排好顺序
private function sign($data){
$stringA = '';
foreach ($data as $key=>$value){
if(!$value) continue;
if($stringA) $stringA .= '&'.$key."=".$value;
else $stringA = $key."=".$value;
}
$wx_key = '********';//服务商key
$stringSignTemp = $stringA.'&key='.$wx_key;//申请支付后有给予一个商户账号和密码,登陆后自己设置key
//return $stringSignTemp;
return strtoupper(md5($stringSignTemp));
}
//获取xml
private function xml($xml){
$p = xml_parser_create();
xml_parse_into_struct($p, $xml, $vals, $index);
xml_parser_free($p); $data = [];
foreach ($index as $key=>$value){
if($key == 'xml' || $key == 'XML') continue;
$tag = $vals[$value[0]]['tag'];
$value = $vals[$value[0]]['value'];
$data[$tag] = $value;
}
return $data;
}