2024-06-26 15:07:49 +08:00

277 lines
9.9 KiB
PHP
Raw 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
namespace Extend\Wechat;
use App\Constants\HttpEnumCode;
use App\Exception\BusinessException;
use App\Utils\Log;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\OfficialAccount\Message;
use EasyWeChat\Pay\Application;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\SimpleCache\CacheInterface;
/**
* 微信支付类
*/
class WechatPay
{
protected array $config;// 系统配置
protected array $pay_config;// 支付系统配置
protected string $pay_notify_url;// 支付回调地址
protected string $refund_notify_url;// 退款回调地址
protected string $domain_name;// 退款网址
/**
* @param string $user_type 用户类型1患者端 2专家端 3药师端
* @param int $order_type 订单类型1:问诊订单 2:药品订单 3:检测订单 4:健康包订单 5:随访包订单)
*/
public function __construct(string $user_type,int $order_type)
{
if ($user_type == 1){
$this->config = config("we_chat.applets.patient");
}elseif ($user_type == 2){
$this->config = config("we_chat.applets.doctor");
}elseif ($user_type == 3){
$this->config = config("we_chat.applets.pharmacist");
}
if (empty($this->config)){
throw new BusinessException("系统配置错误", HttpEnumCode::SERVER_ERROR);
}
if (!in_array($order_type,[1,2,3,4,5])){
throw new BusinessException("订单类型错误", HttpEnumCode::SERVER_ERROR);
}
if ($order_type == 1){
$this->pay_notify_url = $this->config['inquiry_pay_notify_url'];
$this->refund_notify_url = $this->config['inquiry_refund_notify_url'];
$this->pay_config = config("we_chat.applets.pay.1659662936");
}
if ($order_type == 2){
$this->pay_notify_url = $this->config['product_pay_notify_url'];
$this->refund_notify_url = $this->config['product_refund_notify_url'];
$this->pay_config = config("we_chat.applets.pay.1636644248");
}
if ($order_type == 3){
$this->pay_notify_url = $this->config['detection_pay_notify_url'];
$this->refund_notify_url = $this->config['detection_refund_notify_url'];
$this->pay_config = config("we_chat.applets.pay.1636644248");
}
if ($order_type == 4){
$this->pay_notify_url = $this->config['service_pay_notify_url'];
$this->refund_notify_url = $this->config['service_refund_notify_url'];
$this->pay_config = config("we_chat.applets.pay.1659662936");
}
if ($order_type == 5){
$this->pay_notify_url = $this->config['service_pay_notify_url'];
$this->refund_notify_url = $this->config['service_refund_notify_url'];
$this->pay_config = config("we_chat.applets.pay.1659662936");
}
$app_env = config('app_env','dev');
if ($app_env == "prod"){
$this->domain_name = env('DOMAIN_NAME_PROD','https://prod.hospital.applets.igandanyiyuan.com/');
}else{
$this->domain_name = env('DOMAIN_NAME_DEV','https://dev.hospital.applets.igandanyiyuan.com/');
}
}
/**
* 创建工厂类
* @return Application
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function createApp(): Application
{
$config = [
'mch_id' => $this->pay_config['mch_id'],
// 商户证书
'private_key' => __DIR__ . '/' . $this->pay_config['private_key'],
'certificate' => __DIR__ . '/' . $this->pay_config['certificate'],
// v3 API 秘钥
'secret_key' => $this->pay_config['v3_api_secret'],
// v2 API 秘钥
'v2_secret_key' => '',
// 平台证书:微信支付 APIv3 平台证书,需要使用工具下载
// 下载工具https://github.com/wechatpay-apiv3/CertificateDownloader
'platform_certs' => [
// 请使用绝对路径
__DIR__ . '/certs/' . $this->pay_config['mch_id'] . '/' . $this->pay_config['platform_certs'],
],
/**
* 接口请求相关配置,超时时间等,具体可用参数请参考:
* https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php
*/
'http' => [
'throw' => false, // 状态码非 200、300 时是否抛出异常,默认为开启
'timeout' => 5.0,
// 'base_uri' => 'https://api.mch.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
],
];
try {
$app = new Application($config);
$request = \Hyperf\Context\ApplicationContext::getContainer()->get(RequestInterface::class);
$app->setRequest($request);
return $app;
} catch (InvalidArgumentException $e) {
throw new BusinessException('实例化EasyWeChat类失败:' . $e->getMessage(), HttpEnumCode::SERVER_ERROR);
}
}
/**
* 获取jsapi预支付交易会话标识
* @param string $out_trade_no 商户系统内部订单号
* @param int $total 支付金额实际金额x100
* @param string $openid
* @param string $description 备注
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getJsapiPrepayId(string $out_trade_no,int $total,string $openid,string $description): array
{
$app = $this->createApp();
$options = [
"mchid" => $this->pay_config['mch_id'], // <---- 商户号
"out_trade_no" => $out_trade_no, // 商户系统内部订单号
"appid" => $this->config['app_id'],
"description" => $description,
"notify_url" => $this->domain_name . $this->pay_notify_url,
"amount" => [
"total" => $total,//订单总金额,单位为分。
"currency" => "CNY"
],
"payer" => [
"openid" => $openid // 下单用户的 openid
]
];
Log::getInstance()->info(json_encode($options,JSON_UNESCAPED_UNICODE));
try {
$response = $app->getClient()->postJson("v3/pay/transactions/jsapi", $options);
if ($response->isFailed()) {
// 出错了,处理异常
$result = $response->toArray(false);
if(empty($result)){
// 返回值为空
throw new BusinessException("发起支付失败");
}
if (!empty($result['code'])){
throw new BusinessException($result['message']);
}
throw new BusinessException("发起支付失败");
}
return $response->toArray(false);
} catch (\Exception $e) {
throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
}
}
/**
* 获取小程序支付配置
* @param string $prepay_id 预支付交易会话标识
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getAppletsPayConfig(string $prepay_id): array
{
try {
$app = $this->createApp();
$utils = $app->getUtils();
$appId = $this->config['app_id'];
$signType = 'RSA'; // 默认RSAv2要传MD5
$config = $utils->buildMiniAppConfig($prepay_id, $appId, $signType);
if (empty($config)){
throw new BusinessException("发起支付失败");
}
return $config;
} catch (\Exception $e) {
throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
}
}
/**
* 退款接口
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function refund(array $options)
{
try {
$app = $this->createApp();
$options['notify_url'] = $this->domain_name . $this->refund_notify_url;
$response = $app->getClient()->postJson("v3/refund/domestic/refunds", $options);
if ($response->isFailed()) {
// 出错了,处理异常
$result = $response->toArray(false);
if(empty($result)){
// 返回值为空
throw new BusinessException("发起退款失败");
}
if (!empty($result['code'])){
throw new BusinessException($result['message']);
}
throw new BusinessException("发起退款失败");
}
return $response->toArray(false);
} catch (\Exception $e) {
throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
}
}
/**
* 关闭订单
* @param string $out_trade_no 商户系统内部订单号
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function closeOrder(string $out_trade_no): void
{
$app = $this->createApp();
$options = [
"mchid" => $this->pay_config['mch_id'], // <---- 商户号
];
Log::getInstance()->info(json_encode($options,JSON_UNESCAPED_UNICODE));
try {
$url = "v3/pay/transactions/out-trade-no/" . $out_trade_no . "/close";
$app->getClient()->postJson($url, $options);
} catch (\Throwable $e) {
throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
}
}
}