微信APP支付,后端返回预支付信息,前端APP拉起支付,死活不能唤醒微信支付界面。我们的前端,对着包名一遍又一遍的对应,我这边对着签名一遍又一遍的生成校对。
网上说的各种坑,我们都踩过了,唉,最后却是意外之坑啊。返回前端的数据时json,里面有status状态值,和预支付信息,谁曾想,前端直接把结果全部丢给了微信支付的接口,累死也掉不起来。
吃一堑,长一智,希望下次都注重数据结构,不要想当然。
话不多,源码线上:
<?php
namespace app\wxpay\controller;
class WxappController extends HomeBaseController
{
public function paywx()
{
$app_id="00000000000000000";
$mch_id='000000000';
$app_key='000000000000000000000000000000';
$payTime = time();
$oid=input('oid'); // 订单号
$order = Db::name('jjr_morder')->where('id',$oid)->find();
$orderIds = $order['order_num'];
$prepay_id = $this->generatePrepayId($app_id, $mch_id, $app_key,$orderIds,$order['paymount'],$order['title']);
$response = array(
'appid' => $app_id,
'noncestr' => $this->generateNonce(),
'partnerid' => $mch_id,
'package' => 'Sign=WXPay',
'prepayid' => $prepay_id,
'timestamp' =>$payTime,
);
$response['sign'] = $this->calculateSign($response, $app_key);
return json_encode($response);
}
public static function curlPost($url = '', $postData = '', $options = array())
{
if (is_array($postData)) {
$postData = http_build_query($postData);
}
$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允许执行的最长秒数
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;
}
/**
* 随机数
*
*/
private function generateNonce($length = 16) {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* a-z拼接 md5生成signval
*
*/
function calculateSign($arr, $key){
ksort($arr);
$buff = "";
foreach ($arr as $k => $v) {
if ($k != "sign" && $k != "key" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return strtoupper(md5($buff . "&key=" . $key));
}
/**
* Get xml from array
*/
function getXMLFromArray($arr){
$xml = "<xml>";
foreach ($arr as $key => $val) {
if (is_numeric($val)) {
$xml .= sprintf("<%s>%s</%s>", $key, $val, $key);
} else {
$xml .= sprintf("<%s><![CDATA[%s]]></%s>", $key, $val, $key);
}
}
$xml .= "</xml>";
return $xml;
}
/**
* Generate a prepay id
*
*/
public static function getSign($params, $key)
{
ksort($params, SORT_STRING);
$unSignParaString = self::formatQueryParaMap($params, false);
$signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
return $signStr;
}
public static function formatQueryParaMap($paraMap, $urlEncode = false)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v) {
if ($urlEncode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
function generatePrepayId($appId, $mchId, $appKey, $orderIds,$order_amount,$attach)
{
$params = array(
'appid' => $appId,
'mch_id' => $mchId,
'nonce_str' => $this->generateNonce(),
'body' => "经纪人大学-".$attach,
'out_trade_no' => $orderIds,
'total_fee' => intval($order_amount * 100),
'spbill_create_ip' => $this->getIP(),
'notify_url' => 'http://www.test.net/wx/notify/index', // 回调地址,不能带特殊符号
'trade_type' => 'APP'
);
$params['sign'] = $this->calculateSign($params, $appKey);
$xml = $this->getXMLFromArray($params);
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => "https://api.mch.weixin.qq.com/pay/unifiedorder",
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_POSTFIELDS => $xml,
));
$result = curl_exec($ch);
curl_close($ch);
$xml = simplexml_load_string($result);
$unifiedOrder = simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA);
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);
}
// dump($unifiedOrder);
return (string)$xml->prepay_id;
}
function getIP() {
if (getenv('HTTP_CLIENT_IP')) {
$ip = getenv('HTTP_CLIENT_IP');
}
elseif (getenv('HTTP_X_FORWARDED_FOR')) {
$ip = getenv('HTTP_X_FORWARDED_FOR');
}
elseif (getenv('HTTP_X_FORWARDED')) {
$ip = getenv('HTTP_X_FORWARDED');
}
elseif (getenv('HTTP_FORWARDED_FOR')) {
$ip = getenv('HTTP_FORWARDED_FOR');
}
elseif (getenv('HTTP_FORWARDED')) {
$ip = getenv('HTTP_FORWARDED');
}
else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
}