canyin-project/ybcy/models/common/WeChatPay.php
2024-11-01 16:07:54 +08:00

334 lines
15 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}