1. 完善易支付API调用全流程

2. 确定支付插件继承基础类和接口规范
3. 引入Yansongda\Pay支付快捷工具
4. 重新整理代码和功能结构
This commit is contained in:
技术老胡
2026-03-12 19:18:21 +08:00
parent 5dae6e7174
commit fa3abdcaff
19 changed files with 1042 additions and 578 deletions

View File

@@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
namespace app\common\base;
use app\common\contracts\PayPluginInterface;
use app\exceptions\PaymentException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use support\Log;
/**
* 支付插件基类(建议所有插件继承)
*
* 目标:把“插件共性”集中在这里,具体渠道差异留给子类实现 `PaymentInterface`。
*
* 生命周期:
* - 服务层会在每次动作前调用 `init($channelConfig)` 注入该通道配置。
* - 子类可在 `init()` 中配置第三方 SDK例如 yansongda/pay或读取必填参数。
*
* 约定:
* - 这里的 `$channelConfig` 来源通常是 `ma_pay_channel.config_json`,属于“通道级配置”。
* - 业务级入参(如订单号、金额、回调地址等)不要混进 `$channelConfig`,应从 `pay()` 的 `$order` 参数获取。
*/
abstract class BasePayment implements PayPluginInterface
{
/**
* 插件元信息(子类必须覆盖)
*
* 常用字段:
* - code/name后台展示与标识
* - pay_types声明支持的支付方式编码如 alipay/wechat
* - config_schema后台配置表单结构fields...
* - 包含code, name, author, link, pay_types, transfer_types, config_schema 等
*
* @var array<string, mixed>
*/
protected array $paymentInfo = [];
/**
* 通道配置(由 init 注入)
*
* 建议是“纯配置”:商户号/密钥/网关地址/产品开关等。
*
* @var array<string, mixed>
*/
protected array $channelConfig = [];
/** HTTP 请求客户端GuzzleHttp */
private ?Client $httpClient = null;
// ==================== 初始化 ====================
/**
* 初始化插件,加载通道配置并创建 HTTP 客户端
*
* @param array<string, mixed> $channelConfig 通道配置(商户号、密钥等)
* @return void
*/
public function init(array $channelConfig): void
{
$this->channelConfig = $channelConfig;
$this->httpClient = new Client([
'timeout' => 10,
'connect_timeout' => 10,
'verify' => true,
'http_errors' => false,
]);
}
/**
* 获取通道配置项
*
* @param string $key 配置键
* @param mixed $default 默认值(键不存在时返回)
* @return mixed
*/
protected function getConfig(string $key, mixed $default = null): mixed
{
return $this->channelConfig[$key] ?? $default;
}
// ==================== 插件元信息 ====================
/** 获取插件代码(唯一标识) */
public function getCode(): string
{
return $this->paymentInfo['code'] ?? '';
}
/** 获取插件名称 */
public function getName(): string
{
return $this->paymentInfo['name'] ?? '';
}
/** 获取作者名称 */
public function getAuthorName(): string
{
return $this->paymentInfo['author'] ?? '';
}
/** 获取作者链接 */
public function getAuthorLink(): string
{
return $this->paymentInfo['link'] ?? '';
}
// ==================== 能力声明 ====================
/**
* 获取插件支持的支付方式列表
*
* @return array<string> 支付方式代码数组,如 ['alipay', 'wechat']
*/
public function getEnabledPayTypes(): array
{
return $this->paymentInfo['pay_types'] ?? [];
}
/**
* 获取插件支持的转账方式列表
*
* @return array<string> 转账方式代码数组
*/
public function getEnabledTransferTypes(): array
{
return $this->paymentInfo['transfer_types'] ?? [];
}
/**
* 获取插件配置表单结构(用于后台配置界面)
*
* @return array<string, mixed> 表单字段定义数组
*/
public function getConfigSchema(): array
{
return $this->paymentInfo['config_schema'] ?? [];
}
// ==================== HTTP 请求 ====================
/**
* 统一 HTTP 请求(对外调用支付渠道 API
*
* @param string $method 请求方法GET/POST/PUT/DELETE 等)
* @param string $url 请求地址
* @param array<string, mixed> $options Guzzle 请求选项headers、json、form_params 等)
* @return ResponseInterface
* @throws PaymentException 未调用 init() 或渠道请求失败时
*/
protected function request(string $method, string $url, array $options = []): ResponseInterface
{
if ($this->httpClient === null) {
throw new PaymentException('支付插件未初始化,请先调用 init()');
}
try {
return $this->httpClient->request($method, $url, $options);
} catch (GuzzleException $e) {
Log::error(sprintf('[BasePayment] HTTP 请求失败: %s %s, error=%s', $method, $url, $e->getMessage()));
throw new PaymentException('渠道请求失败:' . $e->getMessage(), 402, ['method' => $method, 'url' => $url]);
}
}
}