APP支付
1 组装请求参数
文档地址:pay.weixin.qq.com/wiki/doc/api/app...
由支付文档可知必填的参数有哪些,以下是组装好的请求参数:
/**
* 组装统一下单请求数据
*/
public function getOrderData()
{
$postData = [
'appid' => '这里是填申请到的appid', // 应用APPID
'mch_id' => '这里是填收款的商户号', // 商户号
'nonce_str' => (string)time(), // 随机字符串
'body' => "商品描述", // 商品描述
'out_trade_no' => time(), // 订单号
'total_fee' => 100, // 订单总金额,单位为分
'spbill_create_ip' => $this->getClientRealIp(), // 终端IP
'notify_url' => 'https://your.domain.name/notify', // 支付回调通知地址
'trade_type' => 'APP', // 交易类型
];
$postData['sign'] = $this->generateWxPaySign($postData, '这里是填商户的密钥'); // 使用商户密钥签名
return $this->arrayToXml($postData);
}
getClientRealIp
:获取客户端IP,具体代码见 1.1。generateWxPaySign()
:对请求参数使用商户密钥进行签名,具体代码见 1.2。arrayToXml()
:将 array 转换为 xml 格式,具体代码见 1.3。
1.1 获取客户端IP
/**
* 获取客户端IP
*/
public function getClientRealIp()
{
if (isset($_SERVER['HTTP_CDN_SRC_IP']) && $_SERVER['HTTP_CDN_SRC_IP'] != 'unknown') {
$realip = $_SERVER["HTTP_CDN_SRC_IP"];
} elseif (getenv('HTTP_X_FORWARDED_FOR')) {
$realip = getenv('HTTP_X_FORWARDED_FOR');
} elseif (getenv('HTTP_CLIENT_IP')) {
$realip = getenv('HTTP_CLIENT_IP');
} else {
$realip = getenv('REMOTE_ADDR');
}
$realip = explode(',', $realip);
if ($realip[0] === '::1') return '127.0.0.1';
return $realip[0];
}
1.2 对请求参数签名
/**
* 使用商户平台密钥对数据进行签名
*
* @param $data array 需要计算签名的数据
* @param $privateKey string 商户平台密钥
* @return string
*/
public function generateWxPaySign($data, $privateKey)
{
ksort($data);
$signStr = http_build_query($data).'&key='.$privateKey;
return strtoupper(md5(urldecode($signStr)));
}
1.3 array 转为 xml 格式
/**
* 数组转为XML格式
*
* @param array $postData 需要转换的数组
* @return string
*/
public function arrayToXml($postData)
{
$xml = "<xml>";
$xml .= $this->toXml($postData);
$xml .= "</xml>";
return $xml;
}
/**
* 数组拼接为XML字符
*
* @param mixed $data 数据
* @param string $item 数字索引时的节点名称
* @param string $id 数字索引key转换为的属性名
* @return string
*/
public function toXml($data, $item = 'item', $id = 'id')
{
$xml = $attr = '';
foreach ($data as $key => $val) {
if (is_numeric($key)) {
$id && $attr = " {$id}=\"{$key}\"";
$key = $item;
}
$xml .= "<{$key}{$attr}>";
$xml .= (is_array($val) || is_object($val)) ? $this->toXml($val, $item, $id) : $val;
$xml .= "</{$key}>";
}
return $xml;
}
2 请求统一下单
/**
* 请求统一下单
*
* @param $xmlPostData array 请求参数
* @return bool|string
*/
public function curlUnifiedOrder($xmlPostData)
{
$curl = curl_init('https://api.mch.weixin.qq.com/pay/unifiedorder');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlPostData);
$output = curl_exec($curl);
return $output;
}
如果请求参数没错的话,会返回如下结果:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<mch_id><![CDATA[这里是商户号]]></mch_id>
<appid><![CDATA[这里是appid]]></appid>
<nonce_str><![CDATA[bwWvf4Azm9JCh8c1]]></nonce_str>
<sign><![CDATA[FDD250EC6929B547089F40968BCD7B8C]]></sign>
<prepay_id><![CDATA[wx041514252452088160473a494188b21111]]></prepay_id>
<trade_type><![CDATA[APP]]></trade_type>
</xml>
3 获取预支付交易会话标识
从步骤2返回的结果中获取prepay_id
。
/**
* 获取prepay_id
*
* @param $output string 请求结果,xml格式
* @return false|mixed
*/
public function getPrepayId($output)
{
$output = $this->xmlToArray($output);
$conditions = is_array($output)
&& isset($output['return_code'])
&& $output['return_code'] == 'SUCCESS'
&& isset($output['result_code'])
&& $output['result_code'] == 'SUCCESS';
return $conditions ? $output['prepay_id'] : false;
}
xmlToArray()
:xml 格式转换为 array,具体代码见 3.1。
3.1 xml 转为 array 格式
/**
* XML转为Array格式
*
* @param $data string Xml格式的数据
* @return mixed
*/
public function xmlToArray($data)
{
$loadXml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
return json_decode(json_encode($loadXml), true)
}
4 调起支付
文档地址:pay.weixin.qq.com/wiki/doc/api/app...
组装前端调起微信支付需要的数据,如下所示:
/**
* 组装APP端调起支付需要的数据
*
* @param $prepayid string 预支付交易会话标识
* @return array
*/
public function getPayData($prepayid)
{
$payData = [
'appid' => '这里是填申请到的appid', // 应用APPID
'partnerid' => '这里是填收款的商户号', // 商户号
'package' => 'Sign=WXPay', // 扩展字段
'prepayid' => $prepayid, // 预支付交易会话标识
'noncestr' => time(), // 随机字符串
'timestamp' => time() // 时间戳
];
$payData['sign'] = $this->generateWxPaySign($payData, '这里是填商户的密钥'); // 使用商户密钥签名
return $payData;
}
generateWxPaySign()
:对请求参数使用商户密钥进行签名,具体代码见 1.2。
5 支付回调
文档地址:pay.weixin.qq.com/wiki/doc/api/app...
用户支付完成后,微信会通知商户,商户需要在接受数据、处理数据后,返回应答。
回调地址是在步骤1中的notify_url
字段填写的。
public function notify()
{
$notifyXml = file_get_contents('php://input');
$notifyArray = $this->xmlToArray($notifyXml);
// 检查回调参数是否正确
$checkParams = isset($notifyArray['sign'])
&& isset($notifyArray['result_code'])
&& $notifyArray['result_code'] === 'SUCCESS'
&& isset($notifyArray['return_code'])
&& $notifyArray['return_code'] === 'SUCCESS';
if (!$checkParams) return false;
// 验签
$sign = $this->generateWxPaySign($notifyArray, '这里是填商户的密钥');
if ($sign != $notifyArray['sign']) return false;
// TODO:按照业务更新订单支付状态等等
}
xmlToArray()
:xml 格式转换为 array,具体代码见 3.1。generateWxPaySign()
:对请求参数使用商户密钥进行签名,具体代码见 1.2。
如果文章有帮到你的话,别忘了点赞收藏噢 :smile: