/**
* 构建微信支付
* @return \Response
*/
public function WxPay()
{
$input = Request::all();
$openId = $input['openId'] ?? null;
//判断金额是否为正整数
$input['total_fee'] = $input['total_fee']*100;
if (!preg_match("/^[1-9][0-9]*$/", $input['total_fee'])) {
return '金额错误';
}
//验证订单
$order_id = intval($input['order_id']);
$order = Order::where('id',$order_id)->first();
if(!$order){
return '订单不存在';
}
$notify_url = "回调地址";
$data = [
'out_trade_no' => $order->order_code,
'total_fee' => $input['total_fee'],
'openid' => $input['openId'],
'body' => $input['body'] ?? null,
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];
$data['notify_url'] = $notify_url;
//调用微信支付统一下单
$result = $this->unifiedOrder($data);
// 请求失败
if (!$result) {
//请求失败
}
if ($result['return_code'] === 'FAIL' || $result['result_code'] === 'FAIL') {
//调用出错
}
//调起支付数据签名字段
$timeStamp = time();
$appid = $result['appid'];
$nonce_pay = str_shuffle($result['nonce_str']);//随机字符串
$package = $result['prepay_id'];
$signType = "MD5";
$key = Common::KEY;
$stringPay = "appId=" . $appid . "&nonceStr=" . $nonce_pay . "&package=prepay_id=" . $package . "&signType=" . $signType . "&timeStamp=" . $timeStamp . "&key=" . $key;
$paySign = strtoupper(md5($stringPay));
//这些参数需要返回给小程序组件使用,弹出支付页面
$pay_data = array(
'nonceStr' => $nonce_pay,
'package' => "prepay_id=" . $package,
'timeStamp' => (string)$timeStamp,
'paySign' => $paySign,
'signType' => $signType
);
return $pay_data;
}
/**
* 小程序 统一下单方法
* @param $data
* array(
* 'out_trade_no' => 商户订单号,
* 'total_fee' => 总金额,
* 'openid' => 用户标识,
* 'body' => 商品描述,
* )
* @return bool|mixed
*/
public function unifiedOrder(array $data)
{
$common = (new Common);
$params['appid'] = $common::SHOP_ID;
$params['mch_id'] = $common::MCH_ID;
$params['nonce_str'] = $common->GenRandomString();//随机字符串
$params['body'] = $data['body'];//商品描述
$params['out_trade_no'] = $data['out_trade_no'];//商户订单号
$params['total_fee'] = $data['total_fee'];//总金额
$params['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];//终端IP
$params['notify_url'] = $data['notify_url'];//通知地址
$params['trade_type'] = 'JSAPI';//交易类型
$params['openid'] = $data['openid'];//用户标识
//$this->params['detail'] = $data['detail'] ?? null;//商品详情
//$this->params['attach'] = $data['attach'] ?? null;//附加数据
ksort($params);
//获取签名数据
$sign = $common->Sign($params);
$params['sign'] = $sign;//签名
$xml = $common->DataToXml($params);
$response = $common->PostXmlCurl($xml, 'https://api.mch.weixin.qq.com/pay/unifiedorder');
if (!$response) {
return false;
}
return $common->XmlToData($response);
}
common.php 文件内容入下:
/**
* 产生一个指定长度的随机字符串,并返回给用户
* @param $len //产生字符串的长度
* @return string 随机字符串
*/
public function GenRandomString($len = 32)
{
$chars = array(
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
"S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
"3", "4", "5", "6", "7", "8", "9"
);
$charsLen = count($chars) - 1;
// 将数组打乱
shuffle($chars);
$output = "";
for ($i = 0; $i < $len; $i++) {
$output .= $chars[mt_rand(0, $charsLen)];
}
return $output;
}
/**
* 签名 $data要先排好顺序
* @param $data
* @return string
*/
public function Sign($data)
{
ksort($data);
$stringA = '';
foreach ($data as $key => $value) {
if (!$value) continue;
if ($stringA) $stringA .= '&' . $key . "=" . $value;
else $stringA = $key . "=" . $value;
}
$wx_key = $this::KEY;//申请支付后有给予一个商户账号和密码,登陆后自己设置的key
$stringSignTemp = $stringA . '&key=' . $wx_key;
return strtoupper(md5($stringSignTemp));
}
/**
* 将xml转为array
* @param string $xml
* return array
* @return bool|mixed
*/
public function XmlToData($xml)
{
if (!$xml) {
return false;
}
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $data;
}
/**
* 输出xml字符
* @param $params //参数名称
* return string 返回组装的xml
*
* @return bool|string
*/
public function DataToXml($params)
{
if (!is_array($params) || count($params) <= 0) {
return false;
}
$xml = "<xml>";
foreach ($params as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
/**
* 以post方式提交xml到对应的接口url
*
* @param string $xml 需要post的xml数据
* @param string $url url
* @param bool $useCert 是否需要证书,默认不需要
* @param int $second url执行超时时间,默认30s
* @return bool|string
* @throws
*/
public function PostXmlCurl($xml, $url, $useCert = false, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch, CURLOPT_URL, $url);
//
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $useCert);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $useCert ? 2 : 0);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
if ($useCert == true) {
//TODO 以下两种方式需选择一种
/*------- --第一种方法,cert 与 key 分别属于两个.pem文件--------------------------------*/
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, '绝对路径');
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, '绝对路径');
/**
* 补充 当找不到ca根证书的时候还需要rootca.pem文件
* TODO 注意,微信给出的压缩包中,有提示信息:
* 由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书,
* 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载
*/
//curl_setopt($ch, CURLOPT_CAINFO,self::APICLIENT_CA);
/*----------第二种方式,两个文件合成一个.pem文件----------------------------------------*/
//curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
}
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
return false;
}
}