定义微信配置

protected $sp_appid = ”;//服务商APPID(目前只有公众号)
protected $sp_mchid = ”;//服务商商户号
protected $sub_appid = ”;//小程序APPID
protected $sub_mchid = ”;//商户号
protected $apiV3Key = ”;//支付秘钥(服务商V3级别)
protected $privateKeyPath = ”;//证书物理路径(私钥)
protected $serialNo = ”;//证书序列号
protected $app_secret = ”;//小程序通信秘钥
protected $notify_url = ”;//支付回调地址

初始化加载配置

public function _initialize()
{
parent::_initialize();
$wechatconf = config(‘wechat’);//可以写在配置文件,也可写在数据库
$this->sp_appid = $wechatconf[‘sp_appid’];
$this->sp_mchid = $wechatconf[‘sp_mchid’];
$this->sub_appid = $wechatconf[‘sub_appid’];
$this->sub_mchid = $wechatconf[‘sub_mchid’];
$this->apiV3Key = $wechatconf[‘apiV3Key’];
$this->privateKeyPath = $wechatconf[‘privateKeyPath’];
$this->serialNo = $wechatconf[‘serialNo’];
$this->app_secret = $wechatconf[‘app_secret’];
$this->notify_url = $wechatconf[‘notify_url’];
}

统一下单(注意signType不参与签名,package必须是下面写的prepay_id=)

public function unifyPay(){
$param = $this->request->param();
if(!$param[‘uid’] || !$param[‘orderno’]){
return $this->json_result(400,’缺少参数’);
}
$orderinfo = Db::name(‘service_order’)->where([‘orderno’=>$param[‘orderno’],’uid’=>$param[‘uid’]])->find();
if(empty($orderinfo)){
return $this->json_result(400,’订单不存在’);
}
$userinfo = Db::name(‘user’)->where(‘id’,$param[‘uid’])->find();
if(empty($userinfo)){
return $this->json_result(400,’用户不存在’);
}
;
//这个数组里所有的数据都是必填的
$unifydata = [
‘sp_appid’ => $this->sp_appid,
‘sp_mchid’ => $this->sp_mchid,
‘sub_appid’ => $this->sub_appid,
‘sub_mchid’ => $orderinfo[‘sub_mchid’],
‘description’ => ‘服务’,
‘out_trade_no’ => $param[‘orderno’],//订单号
‘notify_url’ => $this->notify_url,//回调地址
‘settle_info’=>[‘profit_sharing’=>true],
‘amount’=>[‘total’=>(int)bcmul(0.01, ‘100’,0),’currency’=>’CNY’],
‘payer’=>[‘sub_openid’=>$userinfo[‘username’]],
‘scene_info’=>[‘payer_client_ip’=>$_SERVER[“REMOTE_ADDR”]],
];
$orderResult = $this->create_pay_order($unifydata);
$timeStamp = time();
$paydata = [
‘appId’ => $this->sub_appid,
‘timeStamp’ => “$timeStamp”,
‘nonceStr’ => md5(rand(100000,999999)),
‘package’ => ‘prepay_id=’ . $orderResult[‘prepay_id’],
];
$sign = $this->getSign($paydata);//给小程序生成验证签名
$paydata[‘signType’] = ‘RSA’;
$paydata[‘paySign’] = $sign;
return $this->json_result(200,’统一下单成功’,$paydata);
}

调用微信下单接口

public function create_pay_order($inputData){
$inputData = json_encode($inputData);//v3微信传递参数需要json格式
$headers = $this->getV3Sign(‘https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi’,’POST’,$inputData);//将签名放在报头里
$result = $this -> post_data(‘https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi’,$inputData,$headers);//下单
return json_decode($result,true);
}

获取签名

public function getV3Sign($url,$http_method,$body) {
//商户号
$mchid = $this->sp_mchid;
//随机字符串
$nonce = strtoupper($this -> getNoncestr());
//商户序列号
$serialNo = $this->serialNo;
//时间戳
$timestamp = time();
//url
$url_parts = parse_url($url);
//获取绝对路径
$canonical_url = ($url_parts[‘path’] . (!empty($url_parts[‘query’]) ? “?${url_parts[‘query’]}” : “”));
//密钥key
$private_key = $this->getPrivateKey($this->privateKeyPath);
//拼接参数
$message = $http_method.”\n”.
$canonical_url.”\n”.
$timestamp.”\n”.
$nonce.”\n”.
$body.”\n”;
//计算签名值
openssl_sign($message, $raw_sign, $private_key, ‘sha256WithRSAEncryption’);
$sign = base64_encode($raw_sign);
// $token = sprintf(‘mchid=”%s”,nonce_str=”%s”,timestamp=”%d”,serial_no=”%s”,signature=”%s”‘,$mchid, $nonce, $timestamp, $serial_no, $sign);
$token = sprintf(‘WECHATPAY2-SHA256-RSA2048 mchid=”%s”,nonce_str=”%s”,timestamp=”%d”,serial_no=”%s”,signature=”%s”‘,
$mchid, $nonce, $timestamp, $serialNo, $sign);
$headers = [
‘Accept: application/json’,
‘User-Agent: */*’,
‘Content-Type: application/json; charset=utf-8’,
‘Authorization: ‘.$token,
];
return $headers;
}

生成32位随机字符串

public function getNoncestr($length = 32) {
$chars = “abcdefghijklmnopqrstuvwxyz0123456789″;
$str =””;
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}

获取私钥

public function getPrivateKey($filepath) {
return openssl_get_privatekey(file_get_contents($filepath));
}

POST调用API

public function post_data($url,$data=[],$headers=[]){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置header头
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
// POST数据
curl_setopt($ch, CURLOPT_POST, 1);
// 把post的变量加上
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}

给小程序生成验证签名(V3不再用MD5用SHA256)

protected function getSign($data) {
$tmpstr = $data[‘appId’] . “\n” . $data[‘timeStamp’] . “\n” . $data[‘nonceStr’] . “\n” . $data[‘package’] . “\n”;
$privateKey = file_get_contents($this->privateKeyPath);
$binary_signature = “”;
$algo = “SHA256”;
openssl_sign($tmpstr, $binary_signature, $privateKey, $algo);
$sign = base64_encode($binary_signature);
return $sign;
}

支付回调 v3必须大于php7.2

public function notify(){
$result = $this->request->param();
if($result){
$text = base64_decode($result[‘resource’][‘ciphertext’]); //解密
/* =========== 使用V3支付需要PHP7.2.6安装sodium扩展才能进行解密参数 ================ */
$str = sodium_crypto_aead_aes256gcm_decrypt($text, $result[‘resource’][‘associated_data’], $result[‘resource’][‘nonce’], $this->apiV3Key);
$res = json_decode($str, true);
//如果成功返回了
if($res[‘trade_state’] == ‘SUCCESS’){
Db::startTrans();
try {
Db::name(‘service_order’)->where(‘orderno’,$res[‘out_trade_no’])->update([
‘pay_state’=>1,
‘transaction_id’=>$res[‘transaction_id’],
‘pay_time’=>time(),
‘status’=>1
]);
$lid = Db::name(‘service_order’)->where(‘orderno’,$res[‘out_trade_no’])->value(‘lid’);
if($lid){
Db::name(‘service_list’)->where(‘id’,$lid)->setInc(‘sales’,1);
}
Db::commit();
return $this->json_result(200,’支付成功’);
}catch (DbException $e){
return $this->json_result(400,$e->getMessage());
}
}
}
return $this->json_result(400,’支付失败’);
}

原文链接:https://blog.csdn.net/simplexiaobo/article/details/123945995

自由转载,转载请注明: 转载自WEB开发笔记 www.chhua.com

本文链接地址: 微信JSAPI支付V3版本 http://www.chhua.com/web-note5633

随机笔记

更多