微信支付v3接入流程 php版

发布时间 2023-09-01 14:24:55作者: 42530

laravel版微信支付怎么接入?话不多说,直接上干货。

第一步,公众号及支付平台获取相关数据

appid
appsecret
mchid
证书
证书序列号
v3key

第二步,下载SDK

composer require wechatpay/wechatpay

第三步,生成证书

composer exec CertificateDownloader.php -- -k QJOm2R36vRotFOH1In9lmPVvOyMefDuy -m 1651531118 -f /filepath/apiclient_key.pem -s 114FE505154F026C63212D4ACD70CA2F72C204EC -o /filepath
如果ssl有问题把vendor\guzzlehttp\guzzle\src\Client.php 里面verify设为false

第四步,开始下单 

1.基础信息
function wepay()
{
$openid = '123456797654333333456-wc';//用户openid,数据库查询
$appid = $this->appid;//微信小程序appid
$mchid= $this->mchid;//商户id
$xlid = $this->serialNumber;//证书序列号
$apiclient_key = $this->privateKeyPath;//证书签名,官网下载,存放于服务器本地,注意路径
$time = time(); //时间戳
$orderid = 'orderid_1234567890abcdefghijklmn';//订单编号
$noncestr = md5($orderid.$time.rand());//随机字符串,可以将订单编号存于此处
$ordertotal = 1;//支付宝以元为单位,微信以分为单位
$url = $this->url;//生成预支序号所提交路径
$urlarr = parse_url($url); //路径拆解为:[scheme=>https,host=>api.mch.weixin.qq.com,path=>/v3/pay/transactions/jsapi]
//2.格式化信息
$data = array();
$data['appid'] = $appid;
$data['mchid'] = $mchid;
$data['description'] = '我的商品大又壮';//商品描述
$data['out_trade_no'] = $orderid;//订单编号
$data['notify_url'] = $this->notify_url;//回调接口,可以为空
$data['amount']['total'] = (integer)$ordertotal;//金额 单位 分
$data['scene_info']['payer_client_ip'] = '0.0.0.0';//场景:ip
$data['payer']['openid'] =$openid;//openid
$jsonData = json_encode($data); //变为json格式
//3.签名一:后端获取prepay_id时所需的参数,通过header提交
//包含了微信指定地址、时间戳、随机字符串和具体内容
$str = "POST"."\n".$urlarr['path']."\n".$time."\n".$noncestr."\n".$jsonData."\n";
$signHead = $this->getSign($str);
$token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"',$mchid,$xlid,$noncestr,$time,$signHead);
$header = array('Content-Type:application/json; charset=UTF-8','Accept:application/json','User-Agent:*/*','Authorization:WECHATPAY2-SHA256-RSA2048 '.$token
);
//4.下单
//向微信接口地址提交json格式的$data和header的头部信息,得到预支编号
$res = $this->httpRequest($url,$jsonData,$header);
//取出prepay_id
$data = json_decode($res,true);
$prepayID = $data['prepay_id'];
//5、签名二:前端支付时所需的参数
//包含了小程序appId + 时间戳 + 随机字符串 + 订单详情扩展字符串(预支序号)
//注意:格式为prepay_id=aabbcc
$prepay = 'prepay_id='.$prepayID;
$str = $appid."\n".$time."\n".$noncestr."\n".$prepay."\n";
$signPay = $this->getSign($str);
//6.支付
//生成返回值提供给前端
return view('demo.wechat',['appid'=>$this->appid,'paySign' => $signPay, 'nonceStr' => $noncestr,'timeStamp' => $time, 'package' => $prepay]);
}
public function getSign($content)
{
$binary_signature = "";
$privateKey = file_get_contents($this->api_key); //证书
$algo = "SHA256"; //将上传内容与api证书结合生成签名
openssl_sign($content, $binary_signature, $privateKey, $algo);
return base64_encode($binary_signature);
}
public function httpRequest($url='',$data='',$header='')
{
$curl = curl_init(); // 启动一个CURL会话
curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在,如果出错则修改为0,默认为1
curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
if(!empty($data)){curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
}
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
if(!empty($header)){
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);//$header以array格式
}
$response = curl_exec($curl); // 执行操作
if (curl_errno($curl)){
echo 'Error:'.curl_error($curl);//捕抓异常
}
curl_close($curl); // 关闭CURL会话
return $response; // 返回数据,json格式
}
 
 
第五步,回调解码
public function paynotify()
{
// 解密后的消息$res,可以参考下面
$postStr = file_get_contents('php://input');
$postData = json_decode($postStr, true);
if ($postData['resource']) {
$data = $this->decryptToString($postData['resource']['associated_data'], $postData['resource']['nonce'], $postData['resource']['ciphertext']);
$data = json_decode($data, true);
return is_array($data) ? $data : false;
}
return false;
//处理订单业务逻辑
}

public function decryptToString($associatedData, $nonceStr, $ciphertext)
{
$ciphertext = base64_decode($ciphertext);
if (strlen($ciphertext) <= 16) {
return false;
}

// ext-sodium (default installed on >= PHP 7.2)
if (function_exists('sodium_crypto_aead_aes256gcm_is_available') &&
sodium_crypto_aead_aes256gcm_is_available()) {
return sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->v3key);
}

// ext-libsodium (need install libsodium-php 1.x via pecl)
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
\Sodium\crypto_aead_aes256gcm_is_available()) {
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->v3key);
}
// openssl (PHP >= 7.1 support AEAD)
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -16);
$authTag = substr($ciphertext, -16);
return openssl_decrypt($ctext, 'aes-256-gcm', $this->apiv3key, OPENSSL_RAW_DATA, $nonceStr,$authTag, $associatedData);
}

exit('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}