277 lines
9.9 KiB
PHP
277 lines
9.9 KiB
PHP
<?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'; // 默认RSA,v2要传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);
|
||
}
|
||
}
|
||
} |