Skip to content

第三方集成开发指南

概述

DSPlatform 采用驱动管理器模式集成各种第三方服务,包括支付、短信、文件存储、LBS、打印机等。通过统一的接口和配置管理,实现了第三方服务的可插拔式集成。

技术架构

核心组件

  • BaseDriverManager: 驱动管理器基类
  • Manager 类: 各种第三方服务的管理器
  • Provider 类: 具体的第三方服务实现
  • Base Provider: 各服务的基础实现类

目录结构

app/deshang/third_party/
├── base/
│   └── BaseDriverManager.php        # 驱动管理器基类
├── trade/                           # 支付服务
│   ├── TradeManager.php
│   └── providers/
│       ├── BaseTrade.php
│       ├── Wechat.php
│       └── Alipay.php
├── sms/                             # 短信服务
│   ├── SmsManager.php
│   └── providers/
│       ├── BaseSms.php
│       ├── Aliyun.php
│       └── Tencent.php
├── upload/                          # 文件上传服务
│   ├── UploadManager.php
│   └── providers/
│       ├── BaseUpload.php
│       ├── Aliyun.php
│       └── Local.php
├── lbs/                             # 位置服务
│   ├── LbsManager.php
│   └── providers/
│       ├── BaseLbs.php
│       ├── Baidu.php
│       ├── Gaode.php
│       ├── Tencent.php
│       └── Tianditu.php
├── printer/                         # 打印机服务
│   ├── PrinterManager.php
│   └── providers/
│       ├── BasePrinter.php
│       ├── Feie.php
│       ├── Jiabo.php
│       ├── Xinye.php
│       └── Yilian.php
├── express/                         # 快递服务
│   ├── ExpressManager.php
│   └── providers/
│       ├── BaseExpress.php
│       ├── Kuaidi100.php
│       └── Kuaidiniao.php
└── waybill/                         # 运单服务
    └── README.md

驱动管理器基类

BaseDriverManager

php
// app/deshang/base/BaseDriverManager.php
abstract class BaseDriverManager
{
    protected $namespace;     // 驱动类命名空间
    protected $driver;        // 当前使用的驱动实例
    protected $driverName;     // 当前使用的驱动名称
    protected $config;         // 配置信息
    protected $drivers = [];   // 缓存的驱动实例

    /**
     * 构造函数
     * @param string $driverName 驱动名称
     * @param array $config 驱动配置
     */
    public function __construct(string $driverName, array $config = [])
    {
        $this->driverName = ucfirst(strtolower($driverName)) ?: $this->getDefaultDriverName();
        $this->config = $config;
        $this->loadDriver();
    }

    /**
     * 获取默认驱动名称
     * @return string 默认驱动名称
     */
    abstract protected function getDefaultDriverName(): string;

    /**
     * 加载驱动
     */
    protected function loadDriver()
    {
        if (isset($this->drivers[$this->driverName])) {
            $this->driver = $this->drivers[$this->driverName];
            return;
        }

        if (empty($this->namespace)) {
            throw new Exception("命名空间未定义");
        }

        $driverClass = $this->namespace . '\\' . $this->driverName;

        if (!class_exists($driverClass)) {
            throw new Exception("驱动类 {$driverClass} 不存在");
        }
        
        $this->driver = new $driverClass($this->config);
        $this->drivers[$this->driverName] = $this->driver;
    }

    /**
     * 动态调用驱动方法
     * @param string $method 方法名
     * @param array $arguments 参数
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        if (!method_exists($this->driver, $method)) {
            throw new Exception("方法 {$method} 不存在于驱动 {$this->driverName}");
        }
        return $this->driver->$method(...$arguments);
    }

    /**
     * 获取当前使用的驱动名称
     * @return string 当前使用的驱动名称
     */
    public function getDriverName(): string
    {
        return $this->driverName;
    }
}

第三方服务集成

1. 支付服务 (Trade)

TradeManager

php
// app/deshang/third_party/trade/TradeManager.php
class TradeManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\trade\providers';

    protected function getDefaultDriverName(): string
    {
        return config('trade.default', 'Yansongda');
    }
}

微信支付实现

php
// app/deshang/third_party/trade/providers/Wechat.php
class Wechat extends BaseTrade
{
    public function __construct(array $config)
    {
        $wechat_config = [
            'wechat' => [
                'default' => [
                    'mch_id' => $config['mch_id'],
                    'mch_secret_key' => $config['mch_secret_key'],
                    'mch_secret_cert' => $config['mch_secret_cert_path'],
                    'mch_public_cert_path' => $config['mch_public_cert_path'],
                    'notify_url' => $config['notify_url'],
                    'wechat_public_cert_path' => [
                        $config['wechat_public_cert_id'] => $config['wechat_public_cert_path'],
                    ],
                    'mode' => Pay::MODE_NORMAL,
                ]
            ],
        ];

        // 根据支付场景设置不同的 app_id
        if ($config['trade_scene'] == TradePaymentConfigEnum::SCENE_WECHAT_OFFICIAL) {
            $wechat_config['wechat']['default']['mp_app_id'] = $config['app_id'];
        }
        if ($config['trade_scene'] == TradePaymentConfigEnum::SCENE_WECHAT_MINI) {
            $wechat_config['wechat']['default']['mini_app_id'] = $config['app_id'];
        }
        if ($config['trade_scene'] == TradePaymentConfigEnum::SCENE_APP) {
            $wechat_config['wechat']['default']['app_id'] = $config['app_id'];
        }

        Pay::config($wechat_config);
    }

    // 小程序支付
    public function mini(array $data)
    {
        $order = [
            'out_trade_no' => $data['out_trade_no'],
            'description' => $data['subject'],
            'amount' => [
                'total' => intval($data['total_amount'] * 100),
                'currency' => 'CNY',
            ],
            'payer' => [
                'openid' => $data['openid'],
            ],
        ];

        $result = Pay::wechat()->mini($order);
        return $this->parseResponse($result);
    }

    // 扫码支付
    public function scan(array $data)
    {
        $order = [
            'out_trade_no' => $data['out_trade_no'],
            'description' => $data['subject'],
            'amount' => [
                'total' => intval($data['total_amount'] * 100),
            ],
        ];

        $result = Pay::wechat()->scan($order);
        return $this->parseResponse($result);
    }

    // 退款
    public function refund(array $data): bool
    {
        $order = [
            'out_trade_no' => $data['out_trade_no'],
            'out_refund_no' => $data['out_refund_no'],
            'amount' => [
                'refund' => intval($data['refund_amount'] * 100),
                'total' => intval($data['total_amount'] * 100),
                'currency' => 'CNY',
            ],
        ];
        
        $result = $this->parseResponse(Pay::wechat()->refund($order));
        return $result['status'] == 'SUCCESS' || $result['status'] == 'PROCESSING' ? true : false;
    }

    // 支付回调
    public function callback()
    {
        $result = Pay::wechat()->callback();
        $result = $this->parseResponse($result);
        $ciphertext = $result['resource']['ciphertext'];

        return [
            'status' => 'success',
            'buyer_id' => $ciphertext['payer']['openid'],
            'seller_id' => $ciphertext['mchid'],
            'trade_no' => $ciphertext['transaction_id'],
            'out_trade_no' => $ciphertext['out_trade_no'],
            'total_amount' => $ciphertext['amount']['total'] / 100,
        ];
    }

    // 确认回调
    public function confirm()
    {
        return Pay::wechat()->success();
    }
}

使用示例

php
// 创建支付管理器
$tradeManager = new TradeManager('Wechat', $config);

// 小程序支付
$result = $tradeManager->mini([
    'out_trade_no' => 'order_123456',
    'subject' => '商品购买',
    'total_amount' => 100.00,
    'openid' => 'user_openid'
]);

// 扫码支付
$result = $tradeManager->scan([
    'out_trade_no' => 'order_123456',
    'subject' => '商品购买',
    'total_amount' => 100.00
]);

// 退款
$result = $tradeManager->refund([
    'out_trade_no' => 'order_123456',
    'out_refund_no' => 'refund_123456',
    'refund_amount' => 50.00,
    'total_amount' => 100.00
]);

2. 短信服务 (SMS)

SmsManager

php
// app/deshang/third_party/sms/SmsManager.php
class SmsManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\sms\providers';

    protected function getDefaultDriverName(): string
    {
        return 'Tencent';
    }
}

阿里云短信实现

php
// app/deshang/third_party/sms/providers/Aliyun.php
class Aliyun extends BaseSms
{
    protected $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    /**
     * 初始化阿里云客户端
     */
    protected function initClient()
    {
        if (empty($this->config['access_key_id']) || empty($this->config['access_key_secret'])) {
            throw new \Exception('阿里云短信配置不完整,缺少AccessKey');
        }

        $config = new Config([
            'type' => 'access_key',
            'accessKeyId' => $this->config['access_key_id'],
            'accessKeySecret' => $this->config['access_key_secret'],
        ]);
        
        $config->regionId = isset($this->config['region_id']) ? $this->config['region_id'] : 'cn-hangzhou';
        $config->endpoint = 'dysmsapi.aliyuncs.com';
        
        return new Dysmsapi($config);
    }

    /**
     * 发送短信
     */
    public function send(string $to, string $templateCode, array|string $templateParam = []): bool
    {
        $client = $this->initClient();
        
        try {
            if (empty($this->config['sign_name'])) {
                throw new \Exception('阿里云短信配置不完整,缺少短信签名');
            }
            
            $sendSmsRequest = new SendSmsRequest([
                'phoneNumbers' => $to,
                'signName' => $this->config['sign_name'],
                'templateCode' => $templateCode,
            ]);

            if (!empty($templateParam)) {
                if (is_array($templateParam)) {
                    $sendSmsRequest->templateParam = json_encode($templateParam, JSON_UNESCAPED_UNICODE);
                } else {
                    $sendSmsRequest->templateParam = $templateParam;
                }
            }
            
            $response = $client->sendSms($sendSmsRequest);
            
            if (isset($response->body->Code) && $response->body->Code == 'OK') {
                return true;
            } else {
                $error_msg = isset($response->body->Message) ? $response->body->Message : '未知错误';
                return '阿里云短信发送失败:' . $error_msg;
            }
            
        } catch (TeaError $e) {
            return '阿里云短信发送错误:' . $e->getMessage();
        } catch (\Exception $e) {
            throw $e;
        }
    }
}

使用示例

php
// 创建短信管理器
$smsManager = new SmsManager('Aliyun', $config);

// 发送短信
$result = $smsManager->send(
    '13800138000',
    'SMS_123456789',
    ['code' => '123456']
);

3. 文件上传服务 (Upload)

UploadManager

php
// app/deshang/third_party/upload/UploadManager.php
class UploadManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\upload\providers';

    protected function getDefaultDriverName(): string
    {
        return config('upload.default', 'Aliyun');
    }
}

阿里云 OSS 实现

php
// app/deshang/third_party/upload/providers/Aliyun.php
class Aliyun extends BaseUpload
{
    protected $config;
    protected $ossClient;
    protected $bucket;

    public function __construct(array $config)
    {
        parent::__construct();
        $this->config = $config;
        $this->initClient();
    }

    /**
     * 初始化阿里云 OSS 客户端
     */
    protected function initClient()
    {
        if (empty($this->config['access_key_id']) || empty($this->config['access_key_secret'])) {
            throw new \Exception('阿里云上传配置不完整,缺少AccessKey');
        }
        if (empty($this->config['endpoint']) || empty($this->config['bucket'])) {
            throw new \Exception('阿里云上传配置不完整,缺少Endpoint或Bucket');
        }

        $this->ossClient = new OssClient(
            $this->config['access_key_id'], 
            $this->config['access_key_secret'], 
            $this->config['endpoint']
        );
        $this->bucket = $this->config['bucket'];
    }

    /**
     * 上传文件到阿里云 OSS
     */
    public function upload($dir)
    {
        if (!$this->file) {
            throw new Exception("文件未上传");
        }

        $this->randomSaveName();
        $object = $dir . '/' . $this->save_name;

        try {
            $this->ossClient->uploadFile($this->bucket, $object, $this->fileInfo['realPath']);
            return true;
        } catch (OssException $e) {
            throw new Exception("文件上传失败: " . $e->getMessage());
        }
    }

    /**
     * 删除阿里云 OSS 上的文件
     */
    public function delete($path_list)
    {
        foreach ($path_list as $path) {
            try {
                $this->ossClient->deleteObject($this->bucket, $path);
            } catch (OssException $e) {
                throw new Exception("文件删除失败: " . $e->getMessage());
            }
        }
        return true;
    }
}

使用示例

php
// 创建上传管理器
$uploadManager = new UploadManager('Aliyun', $config);

// 上传文件
$result = $uploadManager->upload('images');

// 删除文件
$result = $uploadManager->delete(['images/file1.jpg', 'images/file2.jpg']);

4. LBS 位置服务

LbsManager

php
// app/deshang/third_party/lbs/LbsManager.php
class LbsManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\lbs\providers';

    protected function getDefaultDriverName(): string
    {
        return 'Tencent';
    }
}

5. 打印机服务

PrinterManager

php
// app/deshang/third_party/printer/PrinterManager.php
class PrinterManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\printer\providers';

    protected function getDefaultDriverName(): string
    {
        return 'Feie';
    }
}

6. 快递服务

ExpressManager

php
// app/deshang/third_party/express/ExpressManager.php
class ExpressManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\express\providers';

    protected function getDefaultDriverName(): string
    {
        return 'Kuaidi100';
    }
}

依赖库

Composer 依赖

json
{
    "require": {
        "yansongda/pay": "~3.7.0",
        "alibabacloud/dysmsapi-20170525": "3.1.2",
        "aliyuncs/oss-sdk-php": "^2.7",
        "w7corp/easywechat": "6.17",
        "endroid/qr-code": "^4.8"
    }
}

主要第三方服务

  1. 支付服务

    • 微信支付 (yansongda/pay)
    • 支付宝支付 (yansongda/pay)
  2. 短信服务

    • 阿里云短信 (alibabacloud/dysmsapi-20170525)
    • 腾讯云短信
  3. 文件存储

    • 阿里云 OSS (aliyuncs/oss-sdk-php)
    • 本地存储
  4. 微信服务

    • 微信公众号 (w7corp/easywechat)
    • 微信小程序 (w7corp/easywechat)
  5. 二维码生成

    • QR Code (endroid/qr-code)

配置管理

环境变量配置

env
# 支付配置
WECHAT_MCH_ID=your_mch_id
WECHAT_MCH_SECRET_KEY=your_secret_key
WECHAT_NOTIFY_URL=https://your-domain.com/pay/notify

# 短信配置
ALIYUN_SMS_ACCESS_KEY_ID=your_access_key_id
ALIYUN_SMS_ACCESS_KEY_SECRET=your_access_key_secret
ALIYUN_SMS_SIGN_NAME=your_sign_name

# 文件存储配置
ALIYUN_OSS_ACCESS_KEY_ID=your_access_key_id
ALIYUN_OSS_ACCESS_KEY_SECRET=your_access_key_secret
ALIYUN_OSS_ENDPOINT=your_endpoint
ALIYUN_OSS_BUCKET=your_bucket

配置文件结构

php
// config/trade.php
return [
    'default' => 'Wechat',
    'wechat' => [
        'mch_id' => env('WECHAT_MCH_ID'),
        'mch_secret_key' => env('WECHAT_MCH_SECRET_KEY'),
        'notify_url' => env('WECHAT_NOTIFY_URL'),
    ],
];

// config/sms.php
return [
    'default' => 'Aliyun',
    'aliyun' => [
        'access_key_id' => env('ALIYUN_SMS_ACCESS_KEY_ID'),
        'access_key_secret' => env('ALIYUN_SMS_ACCESS_KEY_SECRET'),
        'sign_name' => env('ALIYUN_SMS_SIGN_NAME'),
    ],
];

// config/upload.php
return [
    'default' => 'Aliyun',
    'aliyun' => [
        'access_key_id' => env('ALIYUN_OSS_ACCESS_KEY_ID'),
        'access_key_secret' => env('ALIYUN_OSS_ACCESS_KEY_SECRET'),
        'endpoint' => env('ALIYUN_OSS_ENDPOINT'),
        'bucket' => env('ALIYUN_OSS_BUCKET'),
    ],
];

使用示例

支付服务使用

php
use app\deshang\third_party\trade\TradeManager;

// 创建支付管理器
$config = [
    'mch_id' => 'your_mch_id',
    'mch_secret_key' => 'your_secret_key',
    'mch_secret_cert_path' => '/path/to/cert.pem',
    'mch_public_cert_path' => '/path/to/public_cert.pem',
    'wechat_public_cert_id' => 'your_cert_id',
    'wechat_public_cert_path' => '/path/to/wechat_cert.pem',
    'notify_url' => 'https://your-domain.com/pay/notify',
    'app_id' => 'your_app_id',
    'trade_scene' => TradePaymentConfigEnum::SCENE_WECHAT_MINI,
];

$tradeManager = new TradeManager('Wechat', $config);

// 小程序支付
$result = $tradeManager->mini([
    'out_trade_no' => 'order_' . time(),
    'subject' => '商品购买',
    'total_amount' => 100.00,
    'openid' => 'user_openid'
]);

短信服务使用

php
use app\deshang\third_party\sms\SmsManager;

// 创建短信管理器
$config = [
    'access_key_id' => 'your_access_key_id',
    'access_key_secret' => 'your_access_key_secret',
    'sign_name' => 'your_sign_name',
];

$smsManager = new SmsManager('Aliyun', $config);

// 发送验证码短信
$result = $smsManager->send(
    '13800138000',
    'SMS_123456789',
    ['code' => '123456']
);

文件上传使用

php
use app\deshang\third_party\upload\UploadManager;

// 创建上传管理器
$config = [
    'access_key_id' => 'your_access_key_id',
    'access_key_secret' => 'your_access_key_secret',
    'endpoint' => 'your_endpoint',
    'bucket' => 'your_bucket',
];

$uploadManager = new UploadManager('Aliyun', $config);

// 上传文件
$result = $uploadManager->upload('images');

扩展开发

添加新的第三方服务

  1. 创建管理器类
php
// app/deshang/third_party/newservice/NewServiceManager.php
class NewServiceManager extends BaseDriverManager
{
    protected $namespace = 'app\deshang\third_party\newservice\providers';

    protected function getDefaultDriverName(): string
    {
        return 'DefaultProvider';
    }
}
  1. 创建基础提供者类
php
// app/deshang/third_party/newservice/providers/BaseNewService.php
abstract class BaseNewService
{
    protected $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    abstract public function method1();
    abstract public function method2();
}
  1. 创建具体实现
php
// app/deshang/third_party/newservice/providers/DefaultProvider.php
class DefaultProvider extends BaseNewService
{
    public function method1()
    {
        // 具体实现
    }

    public function method2()
    {
        // 具体实现
    }
}

添加新的驱动

  1. 在现有管理器中添加新驱动
php
// app/deshang/third_party/sms/providers/NewSmsProvider.php
class NewSmsProvider extends BaseSms
{
    public function send(string $to, string $templateCode, array|string $templateParam = []): bool
    {
        // 新短信服务商的具体实现
    }
}
  1. 更新配置文件
php
// config/sms.php
return [
    'default' => 'NewSmsProvider',
    'new_sms_provider' => [
        'api_key' => env('NEW_SMS_API_KEY'),
        'api_secret' => env('NEW_SMS_API_SECRET'),
    ],
];

错误处理

异常处理机制

php
try {
    $result = $tradeManager->mini($data);
} catch (\Exception $e) {
    // 记录错误日志
    writeSysAccessLog([
        'error' => $e->getMessage(),
        'data' => $data,
        'driver' => $tradeManager->getDriverName()
    ]);
    
    // 返回错误信息
    return ds_json_error('支付失败:' . $e->getMessage());
}

常见错误类型

  1. 配置错误

    • 缺少必要的配置参数
    • 配置格式不正确
  2. 网络错误

    • 第三方服务不可用
    • 网络超时
  3. 业务错误

    • 参数验证失败
    • 业务逻辑错误
  4. 权限错误

    • API 密钥无效
    • 权限不足

监控和日志

日志记录

php
// 记录第三方服务调用日志
writeSysAccessLog([
    'service' => 'trade',
    'driver' => 'Wechat',
    'method' => 'mini',
    'request' => $data,
    'response' => $result,
    'duration' => $duration,
    'status' => 'success'
]);

性能监控

php
$start_time = microtime(true);

$result = $tradeManager->mini($data);

$end_time = microtime(true);
$duration = round(($end_time - $start_time) * 1000, 2);

// 记录性能数据
if ($duration > 5000) { // 超过5秒
    writeSysAccessLog([
        'warning' => 'slow_response',
        'service' => 'trade',
        'duration' => $duration
    ]);
}

最佳实践

1. 配置管理

  • 使用环境变量存储敏感信息
  • 不同环境使用不同配置
  • 定期轮换 API 密钥

2. 错误处理

  • 实现统一的错误处理机制
  • 记录详细的错误日志
  • 提供友好的错误提示

3. 性能优化

  • 使用连接池
  • 实现请求缓存
  • 监控响应时间

4. 安全考虑

  • 验证回调签名
  • 使用 HTTPS 传输
  • 限制 API 调用频率

5. 测试策略

  • 单元测试各个驱动
  • 集成测试第三方服务
  • 模拟测试环境

最后更新:2024-01-20
维护者:DSPlatform技术团队