大纲:
- 支付流程图
- 开发前期准备
开发过程
一、支付流程图
微信支付业务流程时序图
我理解的微信支付流程
二、开发前期准备
1、服务号配置
2、开发需要的资源
- 微信服务号(已认证) appId、appScert
- 微信商户号的 mch_id、mch_key
- 服务器
- 域名
三、开发过程
1、假设服务器资源认证完成, OAuth 2.0 验证已经完成,js 接口安全域名正确,微信支付目录配置正确。
2、后台统一下单接口的调用
我选择的有以下参数
参数 | 说明 |
---|---|
appid | 微信服务号 appId |
mch_id | 微信商户号 mch_id |
nonce_str | 随机字符串 |
attach | 备注 |
body | 信息 |
notify_url | 微信支付结果通知 |
openid | 微信用户在该公众号的 openid |
trade_type | 交易类型 |
out_trade_no | 商户系统订单号 |
total_fee | 支付金额 单位为分 |
spbill_create_ip | 支付设备 ip |
参数代码
$paramArray = array(
"appid" => C("APPID"),
"mch_id" => C("MACH_ID"),
"nonce_str" => $this->weixinService->getRandChar(32),
"attach" => "购买支付",
"body" => "谢谢惠顾",
"notify_url" => C("WEIXIN_PAY_REPLY"),
"openid" => session(C("SESSION_OPEN_ID")),
"trade_type" => "JSAPI",
"out_trade_no" => $outTradeNo , //商户系统的订单号
"total_fee" => 1,
"spbill_create_ip" => $_SERVER['REMOTE_ADDR'],
);
随机字符串代码
/**
* 获取随机串
* @param $length 要随机的长度
* @return null|string
*/
static function getRandChar($length){
$str = null;
$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($strPol)-1;
for($i=0;$i<$length;$i++){
$str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
}
return $str;
}
订单号生成方法
/**
* 生成订单号
*/
public function create_guid(){
$charid = strtoupper(md5(uniqid(mt_rand(), true)));
return $charid;
}
参数排序,加上商户号 mch_key,如果要生成签名,可以使用 strtoupper(md5(sortArrayByKey($paramArray )));
/**
* 根据键值进行排序,并拼接成字符串返回
* @param $array 参数列表
* @param $mch_secret 是否要生成支付签名
* @return string
*/
static function sortArrayByKey($array,$mch_secret = false){
ksort($array);
$paramArray = array();
foreach($array as $key => $val){
array_push($paramArray,"$key=$val");
}
if($mch_secret){
array_push($paramArray,"key=".C("MACH_SECRET"));
}
return join("&",$paramArray);
}
由于统一下单接口所需要传递参数为 xml 格式,所以还需要将 array 转换成 xml 格式:
// 将 key => value 转为 XML 格式
public function toXML($paramArray){
$xml = "<xml>";
foreach ($paramArray as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
最后调用统一下单接口(在这里统一下单部分就完成了):
/**
* xmlParam 为转成xml了后的参数
*/
public function unifiedorder($xmlParam){
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$responeArray = self::post_CurlMethodXml($url,$xmlParam);
// var_dump($responeArray);
return $responeArray;
}
post_CurlMethodXml 是我编写的封装所有微信 请求方式为post 请求 参数格式为 xml 的接口请求方法:
/**
* $url 为请求接口地址
* $xmlParam 为请求接口所需要的参数
* 返回值为 array 键值对
*/
public function post_CurlMethodXml($url,$xmlParam){
$header[] = "Content-type: text/xml"; //定义content-type为xml,注意是数组
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0);//严格校验
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlParam);
$data = curl_exec($ch);
/* if(curl_errno($ch)){
print curl_error($ch);
}*/
$error = curl_error($ch);
curl_close($ch);
if($error) {
throw new Exception('请求发生错误:' . $error);
}
libxml_disable_entity_loader(true);
//xml 转 array
$dataTmp = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $dataTmp;
}
以下未经过 xml 转 array 的统一下单接口的返回信息:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
<mch_id><![CDATA[10000100]]></mch_id>
<nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>
最后在完成统一下单后需要为前端提供 h5 调起支付 Api 提供参数:
//$resultUnifiedorder(统一下单接口返回信息xml 转 array 后)
$returnWxH5PayParam = array();
if($resultUnifiedorder["return_code"] == "SUCCESS"){
$returnWxH5PayParam["appId"] = $resultUnifiedorder["appid"];
$returnWxH5PayParam["nonceStr"] = $resultUnifiedorder["nonce_str"];
$time = time();
$returnWxH5PayParam["timeStamp"] = "$time";
$returnWxH5PayParam["package"] = "prepay_id=".$resultUnifiedorder["prepay_id"];
$returnWxH5PayParam["signType"] = "MD5";
$paySign = $this->weixinService->sortArrayByKey($returnWxH5PayParam,true); // 未进行 md5 加密的签名
$returnWxH5PayParam["paySign"] = strtoupper(md5($paySign));
$result["status"] = 0;
$result["data"] = $returnWxH5PayParam;
}else{
$result["status"] = 1;
}
$this->ajaxReturn($result);
3、h5 调起支付 Api
找不到代码。。。参考微信的吧
前端接收支付返回可以做点处理
大体流程就这样了。