canyin-project/ybcy/models/common/WeChatPay.php

334 lines
15 KiB
PHP
Raw Permalink Normal View History

2024-11-01 16:07:54 +08:00
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2021/4/26 0026
* Time: 14:20
*/
namespace app\models\common;
use function EasyWeChat\Kernel\Support\generate_sign;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use WechatPay\GuzzleMiddleware\Auth\CertificateVerifier;
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Validator;
use WechatPay\GuzzleMiddleware\Util\AesUtil;
use WechatPay\GuzzleMiddleware\Validator;
use Yii;
use yii\db\ActiveRecord;
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use app\models\common\Config;
class NoopValidator implements Validator
{
public function validate(\Psr\Http\Message\ResponseInterface $response)
{
return true;
}
}
class WeChatPay extends ActiveRecord
{
public static function getPayConfig($uniacid)
{
try{
$weChatPaySerialNo = (new \yii\db\Query())
->from('{{%ybwm_core_system}}')
->where(['ident' => 'weChatServicePaySerialNo', 'uniacid' => $uniacid])
->one();
$payData=Config::getSystemSet('payConfig',$uniacid);
// 商户相关配置
// $merchantId = '1556224401'; // 商户号
// $merchantSerialNumber = '7816CD8F2C3D62D6B243C2C4905770AEF62DA9A7'; // 商户API证书序列号
$merchantSerialNumber = $payData['servicePayNo']; // 商户API证书序列号
$merchantId = $payData['serviceMchId']; // 商户号
$merchantPrivateKey = PemUtil::loadPrivateKey(Yii::$app->basePath."/payment/" . 'service_apiclient_key_' . $uniacid . '.pem'); // 商户私钥
// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate(Yii::$app->basePath."/payment/" . 'wechatpay_'.$weChatPaySerialNo['data'].'.pem'); // 微信支付平台证书
// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey)// 传入商户相关配置
->withWechatPay([$wechatpayCertificate])// 可传入多个微信支付平台证书参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时将HandlerStack传入
return new Client(['handler' => $stack]);
}catch (\Exception $exception){
return [];
}
}
public static function test($uniacid, $origin, $outTradeNo, $money, $notifyUrl, $note, $module, $openId, $storeId, $profit_sharing)
{
$client = self::getPayConfig($uniacid);
if (Yii::$app->params['isDev'] == true) {
$url = Yii::$app->request->hostInfo . '/addons/' . $module . '/index.php/' . $notifyUrl;
} else {
$url = Yii::$app->request->hostInfo . '/index.php/' . $notifyUrl;
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', //请求URL
[
// JSON请求体
'json' => [
"mchid" => "1556224401",
"appid" => "wx2f926923ad50c5e5",
"payer" => [
"openid" => $openId,
],
"amount" => [
"total" => $money * 100,
],
"description" => $note,
"notify_url" => $url,
"out_trade_no" => $outTradeNo,
],
'headers' => ['Accept' => 'application/json']
]
);//
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理
$prepay_id = json_decode($resp->getBody()->getContents(), true)['prepay_id'];
$params = [
'appId' => 'wx2f926923ad50c5e5',
'timeStamp' => strval(time()),
'nonceStr' => uniqid(),
'package' => "prepay_id=$prepay_id",
];
$var = '';
foreach ($params as $key => $value) {
$var .= $value . PHP_EOL;
}
$mch_private_key = PemUtil::loadPrivateKey(Yii::$app->basePath."/payment/" . 'apiclient_key_' . $uniacid . '.pem');
openssl_sign($var, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
// echo base64_encode($raw_sign);die;
$params['signType'] = 'RSA';
$params['paySign'] = base64_encode($raw_sign);
return $params;
} else if ($statusCode == 204) { //处理成功无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage() . "\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
// /www/server/php/72/bin/php tool/CertificateDownloader.php -k admin888admin888admin888admin888 -m 1541578721 -f /www/wwwroot/bkycms.com/addons/yb_wm/payment/service_apiclient_key_39.pem -s 1E6373075BB556DEB62105E3300E51A0725C95D0 -o /www/wwwroot/bkycms.com/addons/yb_wm/payment/
}
public static function downloadCert($opts){
try {
// 构造一个WechatPayMiddleware
$builder = WechatPayMiddleware::builder()
->withMerchant($opts['mchid'], $opts['serialno'], PemUtil::loadPrivateKey($opts['privatekey'])); // 传入商户相关配置
if (isset($opts['wechatpay-cert'])) {
$builder->withValidator(new NoopValidator()); // 临时"跳过”应答签名的验证
// $builder->withWechatPay([ PemUtil::loadCertificate($opts['wechatpay-cert']) ]); // 使用平台证书验证
}
else {
$builder->withValidator(new NoopValidator()); // 临时"跳过”应答签名的验证
}
$wechatpayMiddleware = $builder->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时将HandlerStack传入
$client = new Client(['handler' => $stack]);
// 接下来正常使用Guzzle发起API请求WechatPayMiddleware会自动地处理签名和验签
$resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/certificates', [
'headers' => [ 'Accept' => 'application/json' ]
]);
if ($resp->getStatusCode() < 200 || $resp->getStatusCode() > 299) {
return "download failed, code={$resp->getStatusCode()}, body=[{$resp->getBody()}]";
}
$list = json_decode($resp->getBody(), true);
$plainCerts = [];
$x509Certs = [];
$decrypter = new AesUtil($opts['key']);
foreach ($list['data'] as $item) {
$encCert = $item['encrypt_certificate'];
$plain = $decrypter->decryptToString($encCert['associated_data'],
$encCert['nonce'], $encCert['ciphertext']);
if (!$plain) {
return "加密证书解密失败!";
}
// 通过加载对证书进行简单合法性检验
$cert = \openssl_x509_read($plain); // 从字符串中加载证书
if (!$cert) {
return "下载证书检查失败";
}
$plainCerts[] = $plain;
$x509Certs[] = $cert;
}
$validator = new WechatPay2Validator(new CertificateVerifier($x509Certs));
if (!$validator->validate($resp)) {
return "使用下载的证书验证响应失败!";
}
$effective_time=0;
$wechatpayCertificate=[];//平台证书
// 输出证书信息,并保存到文件
foreach ($list['data'] as $index => $item) {
if((new \DateTime($item['effective_time']))->getTimestamp()>$effective_time){
$effective_time=(new \DateTime($item['effective_time']))->getTimestamp();
$wechatpayCertificate=[
'serial_no'=>$item['serial_no'],
'text'=> $plainCerts[$index],
'effective_time'=> (new \DateTime($item['effective_time']))->getTimestamp(),
'expire_time'=> (new \DateTime($item['expire_time']))->getTimestamp(),
];
}
}
$info = (new \yii\db\Query())
->from('{{%ybwm_core_system}}')
->where(['ident' => 'weChatServicePaySerialNo', 'uniacid' => $opts['uniacid']])
->one();
if($info){
$data['changeAt']=time();
$data['data']=$wechatpayCertificate['serial_no'];
Yii::$app->db->createCommand()->update('{{%ybwm_core_system}}',$data,['id'=>$info['id']] )->execute();
}else{
$data['data']=$wechatpayCertificate['serial_no'];
$data['createdAt']=time();
$data['ident']='weChatServicePaySerialNo';
$data['name']='微信支付平台证书序列号';
$data['uniacid']=$opts['uniacid'];
Yii::$app->db->createCommand()->insert('{{%ybwm_core_system}}', $data)->execute();
}
$outpath = $opts['output'].'wechatpay_'.$wechatpayCertificate['serial_no'].'.pem';
$res=file_put_contents($outpath, $wechatpayCertificate['text']);
if($res){
return true;
}else{
return '证书生成失败';
}
}
catch (RequestException $e) {
echo "download failed, message=[{$e->getMessage()}] ";
if ($e->hasResponse()) {
return "code={$e->getResponse()->getStatusCode()}, body=[{$e->getResponse()->getBody()}]\n";
}
}
catch (\Exception $e) {
return "download failed, message=[{$e->getMessage()}]\n";
}
return '证书生成失败';
}
/**
* @param $orderId
* @param string $status
* @param int $type[1.外卖2店内3快餐]
* @return bool|string
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public static function reporting($orderId,$status='CREATE_DEAL',$type=1){
if($type==1){
$order = (new \yii\db\Query())
->from('{{%ybwm_takeout_order}}')
->where('id=:id', [':id' => $orderId])->one();
$originMoney=bcadd(bcadd($order['originMoney'],$order['deliveryMoney'],2),$order['boxMoney'],2);
$discount_amount=bcsub($order['money'],$originMoney,2);
}else{
$order = (new \yii\db\Query())
->from('{{%ybwm_instore_order}}')
->where('id=:id', [':id' => $orderId])->one();
$originMoney=bcadd($order['originMoney'],$order['tablewareMoney'],2);
$discount_amount=bcsub($order['money'],$originMoney,2);
}
$payData=Config::getSystemSet('payConfig',$order['uniacid']);
if($payData['smOpen']!=1){
return false;
}
$client = self::getPayConfig($order['uniacid']);
if(!$client){
return false;
}
$config=Config::getSystemSet('miniConfig',$order['uniacid']);
$payConfig=Config::getSystemSet('payConfig',$order['uniacid']);//服务商配置
$storeSet=Config::getStoreSet('serviceCharge',$order['storeId']);//商户子商户号设置
$subMchId=$storeSet['subMchId']?:$payConfig['sonMchId'];
$goods=(new \yii\db\Query())
->from('{{%ybwm_order_goods}}')
->where('orderId=:orderId AND item='.$type, [':orderId' => $orderId])->all();
$goodsArr=[];
for($i=0;$i<count($goods);$i++){
$goodsArr[]=[
'out_dish_no'=>$goods[$i]['goodsId'],
'name'=>$goods[$i]['name'],
'price'=>$goods[$i]['money']*100,
'unit'=>'BY_SHARE',
'count'=>floatval($goods[$i]['num']),
];
}
$user=(new \yii\db\Query())
->from('{{%ybwm_member}}')
->where('id=:id', [':id' => $order['userId']])->one();
$data=[
'sp_appid'=>$payConfig['serviceAppId'],
'sp_mchid'=>$payConfig['serviceMchId'],
'sub_mchid'=>$subMchId,
'sub_appid'=>$config['appId'],
'out_shop_no'=>$order['storeId'],
'sub_openid'=>$user['openId'],
'login_token'=>$user['login_token'],
'order_entry'=>'yb_wm/index/goods',
'total_amount'=>$originMoney*100,
'discount_amount'=>$discount_amount*100,
'user_amount'=>$order['money']*100,
'status'=>$status,
'action_time'=>date(DATE_RFC3339),
'out_trade_no'=>$order['outTradeNo'],
'out_order_no'=>$order['outTradeNo'],
'dish_list'=>$goodsArr,
];
if($status=='PAY_SUCCESS'){
$data['pay_time']=date(DATE_RFC3339);
$data['transaction_id']=$order['transaction_id'];
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/catering/orders/sync-status', //请求URL
[
// JSON请求体
'json' => $data,
'headers' => ['Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
// $res = json_decode($resp->getBody()->getContents(), true);
return true;
} else if ($statusCode == 204) { //处理成功无返回Body
return true;
}
} catch (RequestException $e) {
// 进行错误处理
if ($e->hasResponse()) {
return "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
}
return false;
}
}