引言
如果有直接生成二维码的需求,就需要安装下面的依赖
composer require endroid/qr-code
然后导入
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
使用
支付
public static function generatePayQrCode($orderId, $desc = '', $price = 0.01, $notifyUrl = '')
{
// 获取配置
extract(self::getConfig());
//获取32位的随机字符串
$nonce_str = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 32);
//封装参数
$params = [
'appid' => $appid, //应用id
'mch_id' => $machid, //商家号
'nonce_str' => $nonce_str, //随机数
'body' => $desc, //订单描述
'out_trade_no' => $orderId, //订单号
'total_fee' => (int)round($price * 100), // 单位:分,如11元就是1100
'notify_url' => $notifyUrl, //回调地址
'trade_type' => 'NATIVE', // 扫码支付
];
// 签名
$params['sign'] = self::makeSign($params);
// 转 XML
$xml = self::arrayToXml($params);
// 请求微信统一下单接口
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$result = self::postXmlCurl($xml, $url);
//处理返回结果
$xml = simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOENT);
$response = simpleXmlToArray($xml);
if ($response['return_code'] == 'SUCCESS' && $response['result_code'] == 'SUCCESS') {
/**
* 如果不想要base64图片,直接 return $response['code_url']; 就可以了
*/
$code_url = $response['code_url'];
// 生成二维码
$qrCode = new QrCode($code_url);
//创建写入器
$writer = new PngWriter();
//生成二维码
$result = $writer->write($qrCode);
// 转为 base64
$base64Data = base64_encode($result->getString());
//return $base64Data;
return $response['code_url'];
} else {
throw new Exception("微信下单失败: " . $response['return_msg']);
}
}
// 签名
private static function makeSign($params)
{
ksort($params);
$string = '';
foreach ($params as $k => $v) {
if ($v !== '' && $k != 'sign') {
$string .= "{$k}={$v}&";
}
}
// APIV3的秘钥
$aesKey = config('wechat.developer.pay.AesKey');
$string .= "key={$aesKey}";
return strtoupper(md5($string));
}
// 转 XML
private static function arrayToXml($data)
{
$xml = '<xml>';
foreach ($data as $key => $value) {
$xml .= "<{$key}>{$value}</{$key}>";
}
$xml .= '</xml>';
return $xml;
}
//处理返回结果
private static function simpleXmlToArray($xml)
{
if ($xml === null) return null;
$array = (array)$xml;
foreach ($array as $key => $value) {
if (is_object($value) || is_array($value)) {
$array[$key] = simpleXmlToArray($value);
}
}
return $array;
}
//发起请求
private static function postXmlCurl($xml, $url, $timeout = 30)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
回调
//获取数据及解析数据
$xml = file_get_contents('php://input') ?: '';
$data = $this->xmlToArray($xml);
//解析$xml数据
private function xmlToArray($xml)
{
$previous = libxml_use_internal_errors(true);
libxml_clear_errors();
$xml = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_NOCDATA);
$json = json_encode($xml, JSON_UNESCAPED_UNICODE);
$array = json_decode($json, true);
// 清理错误
libxml_clear_errors();
libxml_use_internal_errors($previous);
return $array;
}
回调地址不要使用小程序支付(JSAPI)的回调接口,因为Native的回调数据是 XML 格式,通过原始请求体(raw body)发送,而不是 application/x-www-form-urlencoded表单格式,所以$_POST和 input('post.')压根拿不到数据