npm.io
1.29.12 • Published 2d ago

@blocklet/payment-vendor

Licence
Apache-2.0
Version
1.29.12
Deps
6
Size
104 kB
Vulns
0
Weekly
0

@blocklet/payment-vendor

Deprecation (express→hono migration, decision D2)

src/middleware.ts exports Express middleware (ensureVendorAuth / ensureBrokerAuth / healthCheck / errorHandler / rateLimiter). did-pay's core + example blocklets are now express-free (migrated to hono), but this package keeps its Express middleware surface unchanged as a backward-compatible contract for existing vendor integrations. It is not migrated to hono and is slated for removal in a future major version. The framework-agnostic pieces (VendorAuth signing/verification, WhitelistManager, types) are unaffected — only the Express middleware wrappers are deprecated. New integrations should call VendorAuth directly from their own framework.

Payment Kit 供应商集成软件开发工具包,为供应商提供标准化的认证、签名验证和安全通信解决方案。

概述

本 SDK 旨在简化供应商与 Payment Kit 的集成过程,提供标准化的接口和安全机制。主要解决供应商在接收、验证和处理来自中介请求时面临的技术挑战。

核心功能模块

认证与安全模块
  • 签名验证:基于 Ed25519 算法的请求签名验证
  • 白名单管理:中介访问控制和权限管理
  • 时间戳验证:防重放攻击机制
Express 中间件
  • 认证中间件:自动验证请求合法性
  • 日志中间件:记录请求和响应信息
  • 错误处理中间件:统一的错误响应格式
  • 限流中间件:防止接口滥用
  • 健康检查中间件:系统状态监控

安装

npm install @blocklet/payment-vendor

快速开始

本 SDK 支持两种使用场景,请根据你的角色选择对应的集成方式:

角色对比
角色 身份 主要职责 使用的 SDK 功能 典型场景
Vendor
(供应商)
商品/服务提供方 • 接收发货请求
• 处理用户订单
• 提供服务实例
• 中间件验证
• 白名单管理
• 请求解析
Blocklet 开发者、
SaaS 服务商、
API 提供商
Broker
(中介)
Payment Kit 等平台 • 发送发货请求
• 管理支付流程
• 协调供应商
• 请求签名
• 认证生成
• 通信协议
Payment Kit、
电商平台、
分发平台
供应商端集成(Vendor Side)

适用场景: 你是商品/服务的提供方,需要接收并处理来自 Payment Kit 的发货请求。

核心能力:

  • 请求验证:自动验证来自 Payment Kit 的签名请求
  • 白名单管理:控制哪些中介可以访问你的服务
  • 安全防护:防重放攻击、限流保护、错误处理
  • 监控运维:健康检查、请求日志、系统状态
基础集成(5分钟快速上手)
import VendorSDK from '@blocklet/payment-vendor';
import express from 'express';

const app = express();
app.use(express.json());

// 1. 配置信任的中介白名单(Payment Kit 等)
VendorSDK.setBrokers([
  {
    id: 'payment-kit',
    name: 'Payment Kit Service',
    publicKey: process.env.PAYMENT_KIT_PUBLIC_KEY, // Payment Kit 提供给你的公钥
    status: 'active',
    rateLimit: 100 // 每分钟最多100次请求
  }
]);

// 2. 应用安全中间件(自动验证所有请求)
app.use('/api/vendor', VendorSDK.middleware.ensureAuth());

// 3. 实现发货端点(这是你的核心业务逻辑)
app.post('/api/vendor/deliveries', async (req, res) => {
  const { userInfo, deliveryParams, description } = req.body;
  
  try {
    // 你的发货逻辑:创建实例、分配资源、发送邮件等
    const result = await createInstanceForUser(userInfo, deliveryParams);
    
    res.json({
      success: true,
      status: 'completed',
      message: '发货成功',
      data: {
        instanceId: result.instanceId,
        serviceUrl: result.serviceUrl,
        installUrl: result.installUrl
      }
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      status: 'failed',
      message: error.message
    });
  }
});

// 4. 健康检查端点(供 Payment Kit 监控)
app.get('/health', VendorSDK.middleware.healthCheck({
  service: 'my-vendor-service',
  version: '1.0.0'
}));

// 你的业务逻辑实现
async function createInstanceForUser(userInfo, deliveryParams) {
  console.log(`为用户 ${userInfo.userDid} 创建实例: ${deliveryParams.instanceName}`);
  
  // 这里实现你的具体业务逻辑
  // 例如:调用云服务API、创建数据库记录、发送通知等
  
  return {
    instanceId: `inst_${Date.now()}`,
    serviceUrl: `https://my-service.com/${deliveryParams.instanceName}`,
    installUrl: `https://my-service.com/install/${deliveryParams.instanceName}`
  };
}

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`🏪 供应商服务运行在端口 ${port}`);
});
中介端集成(Broker Side - Payment Kit)

适用场景: 你是 Payment Kit 或其他中介服务,需要向供应商发送发货请求。

核心能力:

  • 请求签名:使用 Ed25519 算法为发送给供应商的请求签名
  • 安全通信:确保请求的完整性和来源可信性
  • 协议标准化:统一的请求格式和响应处理
  • 错误处理:完善的异常情况处理机制
基础发货请求
import { VendorAuth } from '@blocklet/payment-vendor';

// 1. 准备发货请求数据(符合 DeliveryRequest 接口)
const deliveryRequest = {
  path: '/api/vendor/deliveries',
  method: 'POST',
  timestamp: Date.now(),
  userInfo: {
    userDid: 'z1muQ3xqHQK2uiACHyZ7G5S1scgbZiEdB',
    email: 'user@example.com',
    description: '用户购买了 Blocklet 实例'
  },
  deliveryParams: {
    instanceName: 'my-awesome-app',
    productId: 'blocklet_001',
    blockletMetaUrl: 'https://registry.blocklet.io/blocklets/my-app',
    customParams: {
      region: 'us-west-2',
      tier: 'standard'
    }
  },
  description: '为用户创建 Blocklet 实例'
};

// 2. 生成签名请求(推荐方式:使用 Header 签名)
const { headers, body } = VendorAuth.signRequestWithHeaders(
  deliveryRequest,
);

// 3. 发送到供应商
const response = await fetch('https://vendor.example.com/api/vendor/deliveries', {
  method: 'POST',
  headers, // 自动包含 x-broker-vendor-sig 和 x-broker-did
  body     // JSON 字符串
});

const result = await response.json();
if (result.success) {
  console.log('✅ 发货成功:', result.data);
  // 保存 instanceId 和 serviceUrl 供后续使用
} else {
  console.error('❌ 发货失败:', result.message);
}
完整的中介服务示例
import { VendorAuth } from '@blocklet/payment-vendor';
import express from 'express';

const app = express();
app.use(express.json());

// 发货管理端点(Payment Kit 内部使用)
app.post('/api/internal/fulfill-order', async (req, res) => {
  try {
    const { orderId, userId, productId, vendorEndpoint } = req.body;
    
    // 构建发货请求
    const deliveryRequest = {
      path: '/api/vendor/deliveries',
      method: 'POST',
      timestamp: Date.now(),
      userInfo: {
        userDid: userId,
        email: req.body.userEmail,
        description: `订单 ${orderId} 的发货请求`
      },
      deliveryParams: {
        instanceName: `${productId}-${userId.slice(-8)}`,
        productId,
        customParams: req.body.customParams || {}
      },
      description: `处理订单 ${orderId} 的发货`
    };

    // 签名并发送请求
    const { headers, body } = VendorAuth.signRequestWithHeaders(
      deliveryRequest,
    );

    const vendorResponse = await fetch(`${vendorEndpoint}/api/vendor/deliveries`, {
      method: 'POST',
      headers,
      body
    });

    const result = await vendorResponse.json();
    
    // 返回处理结果
    res.json({
      orderId,
      fulfilled: result.success,
      vendorResponse: result,
      timestamp: new Date().toISOString()
    });

  } catch (error: any) {
    console.error('发货请求失败:', error);
    res.status(500).json({
      error: '发货处理失败',
      message: error.message
    });
  }
});

完整集成示例

供应商端完整示例
import VendorSDK from '@blocklet/payment-vendor';
import express from 'express';

const app = express();

// 基础中间件
app.use(express.json());

// 配置信任的中介白名单(动态配置)
VendorSDK.setBrokers(() => {
  const configSource = process.env.VENDOR_DISTRIBUTORS || '[]';
  return JSON.parse(configSource);
});

// 应用安全中间件
app.use('/api/vendor', VendorSDK.middleware.rateLimiter({
  windowMs: 60000,
  defaultLimit: 100
}));
app.use('/api/vendor', VendorSDK.middleware.ensureAuth({
  skipTimestamp: false,
  maxAge: 300000
}));

// 核心发货端点
app.post('/api/vendor/deliveries', async (req, res) => {
  try {
    const { userInfo, deliveryParams, description } = req.body;
    
    // 处理发货逻辑
    const result = await processFulfillment(userInfo, deliveryParams, description);
    res.json({
      success: result.success,
      status: result.success ? 'completed' : 'failed',
      data: result.data,
      message: result.message
    });
  } catch (error) {
    console.error('处理请求失败:', error);
    res.status(500).json({
      success: false,
      error: '内部服务器错误',
      message: error.message
    });
  }
});

// 管理接口:查看中介状态
app.get('/api/admin/brokers', async (req, res) => {
  try {
    const brokers = await VendorSDK.getWhitelist();
    res.json({
      total: brokers.length,
      active: brokers.filter(d => d.status === 'active').length,
      brokers: brokers.map(d => ({
        id: d.id,
        name: d.name,
        status: d.status,
        rateLimit: d.rateLimit
      }))
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 错误处理中间件
app.use(VendorSDK.middleware.errorHandler);

// 业务逻辑函数(供应商实现)
async function processFulfillment(userInfo: any, deliveryParams: any, description: string) {
  console.log(`处理发货请求 - 用户: ${userInfo.userDid}, 实例: ${deliveryParams.instanceName}`);
  
  try {
    // 你的具体业务逻辑:
    // 1. 创建服务实例
    // 2. 分配资源
    // 3. 发送通知邮件
    // 4. 更新数据库等
    
    const instanceId = `inst_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    return {
      success: true,
      data: { 
        instanceId,
        serviceUrl: `https://my-service.com/service/${deliveryParams.instanceName}`,
        installUrl: `https://my-service.com/install/${instanceId}`,
        expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
      },
      message: '发货成功'
    };
  } catch (error: any) {
    return {
      success: false,
      data: null,
      message: `发货失败: ${error.message}`
    };
  }
}

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`🏪 供应商服务运行在端口 ${port}`);
});
中介端完整示例
import { VendorAuth } from '@blocklet/payment-vendor';
import express from 'express';

const app = express();
app.use(express.json());

// Payment Kit 内部的发货管理服务
class VendorFulfillmentService {
  private brokerSecretKey: string;
  private brokerPublicKey: string;
  
  constructor() {
    this.brokerSecretKey = process.env.BROKER_SECRET_KEY!;
    this.brokerPublicKey = process.env.BROKER_PUBLIC_KEY!;
  }

  // 向供应商发送发货请求
  async fulfillOrder(orderData: {
    orderId: string;
    userId: string;
    userEmail: string;
    productId: string;
    instanceName: string;
    vendorEndpoint: string;
    customParams?: Record<string, any>;
  }) {
    try {
      // 构建标准化的发货请求
      const deliveryRequest = {
        path: '/api/vendor/deliveries',
        method: 'POST',
        timestamp: Date.now(),
        userInfo: {
          userDid: orderData.userId,
          email: orderData.userEmail,
          description: `订单 ${orderData.orderId} 的发货请求`
        },
        deliveryParams: {
          instanceName: orderData.instanceName,
          productId: orderData.productId,
          customParams: orderData.customParams || {}
        },
        description: `处理订单 ${orderData.orderId} 的发货`
      };

      // 签名请求
      const { headers, body } = VendorAuth.signRequestWithHeaders(
        deliveryRequest,
      );

      // 发送到供应商
      const response = await fetch(`${orderData.vendorEndpoint}/api/vendor/deliveries`, {
        method: 'POST',
        headers,
        body,
        timeout: 30000 // 30秒超时
      });

      if (!response.ok) {
        throw new Error(`Vendor responded with status: ${response.status}`);
      }

      const result = await response.json();
      
      return {
        orderId: orderData.orderId,
        fulfilled: result.success,
        vendorResponse: result,
        timestamp: new Date().toISOString()
      };

    } catch (error: any) {
      console.error(`发货请求失败 - 订单: ${orderData.orderId}`, error);
      throw new Error(`Vendor fulfillment failed: ${error.message}`);
    }
  }

  // 查询发货状态
  async checkFulfillmentStatus(instanceId: string, vendorEndpoint: string) {
    try {
      const statusRequest = {
        path: '/api/vendor/status',
        method: 'GET',
        timestamp: Date.now(),
        params: { instanceId }
      };

      const { headers, body } = VendorAuth.signRequestWithHeaders(
        statusRequest,
      );

      const response = await fetch(`${vendorEndpoint}/api/vendor/status`, {
        method: 'POST',
        headers,
        body
      });

      return await response.json();
    } catch (error: any) {
      throw new Error(`Status check failed: ${error.message}`);
    }
  }
}

// Payment Kit 使用示例
const fulfillmentService = new VendorFulfillmentService();

app.post('/api/internal/fulfill-order', async (req, res) => {
  try {
    const result = await fulfillmentService.fulfillOrder(req.body);
    res.json(result);
  } catch (error: any) {
    res.status(500).json({
      error: '发货处理失败',
      message: error.message
    });
  }
});

app.listen(3000, () => {
  console.log('🔄 Payment Kit 中介服务运行在端口 3000');
});
快速对比:两种集成方式
对比项 供应商端 中介端
主要用途 接收并处理发货请求 发送发货请求给供应商
核心导入 import VendorSDK from '@blocklet/payment-vendor' import { VendorAuth } from '@blocklet/payment-vendor'
关键配置 VendorSDK.setBrokers([...]) 设置信任的中介 process.env.BROKER_SECRET_KEY 配置自己的私钥
核心功能 使用 middleware.ensureAuth() 验证请求 使用 VendorAuth.signRequestWithHeaders() 签名请求
请求方向 被动接收来自 Payment Kit 的请求 主动向供应商发送请求
典型端点 POST /api/vendor/deliveries (接收) 调用供应商的 /api/vendor/deliveries
安全职责 验证请求签名,防止未授权访问 生成请求签名,证明身份合法

配置管理

中介配置

SDK 支持两种配置模式:

静态配置模式

适用于中介列表相对稳定的场景:

VendorSDK.setBrokers([
  {
    id: 'payment-kit',
    name: 'Payment Kit Service',
    publicKey: '0x680688a033b86da179f39018e074410439ae82a97c4cb8e1ad16c26eabcc1795',
    status: 'active',
    rateLimit: 100
  }
]);
动态配置模式

适用于需要实时更新中介配置的场景:

VendorSDK.setBrokers(() => {
  // 从环境变量、配置文件或数据库读取
  const configSource = process.env.VENDOR_DISTRIBUTORS || '[]';
  const brokers = JSON.parse(configSource);
  
  return brokers.map(item => ({
    id: item.brokerId,
    name: item.name,
    publicKey: item.publicKey,
    status: item.enabled ? 'active' : 'inactive',
    rateLimit: item.rateLimit || 100
  }));
});
环境变量配置
# 中介配置
export VENDOR_DISTRIBUTORS='[{
  "brokerId": "payment-kit",
  "name": "Payment Kit Service",
  "publicKey": "0x680688a033b86da179f39018e074410439ae82a97c4cb8e1ad16c26eabcc1795",
  "enabled": true,
  "rateLimit": 100
}]'

# Payment Kit 公钥(用于签名验证)
export PAYMENT_KIT_PUBLIC_KEY="0x680688a033b86da179f39018e074410439ae82a97c4cb8e1ad16c26eabcc1795"

# 服务端口
export PORT=3000

API 文档

核心 API
VendorSDK.setBrokers(brokersOrGetter)

设置中介白名单配置。

参数:

  • brokersOrGetter: BrokerConfig[] | () => BrokerConfig[] | Promise<BrokerConfig[]>

BrokerConfig 接口:

字段 类型 必需 描述
id string 中介唯一标识符
name string 中介显示名称
publicKey string 用于验证签名的公钥
status 'active' | 'inactive' 中介状态,仅 active 状态可通过验证
rateLimit number 可选的请求频率限制
VendorSDK.getWhitelist()

返回当前配置的中介白名单。

返回: Promise<BrokerConfig[]>

中间件 API
VendorSDK.middleware.ensureAuth(options?)

Express 认证中间件,验证请求签名和中介权限。

可选参数:

  • skipTimestamp: boolean - 是否跳过时间戳验证
  • maxAge: number - 时间戳最大有效期(毫秒)
// 基础用法
app.use('/api/vendor', VendorSDK.middleware.ensureAuth());

// 自定义配置
app.use('/api/vendor', VendorSDK.middleware.ensureAuth({
  skipTimestamp: false,
  maxAge: 300000 // 5分钟
}));
VendorSDK.middleware.rateLimiter(options?)

请求频率限制中间件。

可选参数:

  • windowMs: number - 时间窗口(毫秒)
  • defaultLimit: number - 默认最大请求次数
  • skipSuccessfulRequests: boolean - 是否跳过成功请求计数
// 使用默认配置
app.use('/api/vendor', VendorSDK.middleware.rateLimiter());

// 自定义配置
app.use('/api/vendor', VendorSDK.middleware.rateLimiter({
  windowMs: 60000, // 1分钟
  defaultLimit: 100, // 默认最多100次请求
  skipSuccessfulRequests: false
}));
VendorSDK.middleware.healthCheck(options?)

健康检查中间件。

可选参数:

  • service: string - 服务名称
  • version: string - 版本信息
  • includeDetails: boolean - 是否包含详细系统信息
// 基础用法
app.get('/health', VendorSDK.middleware.healthCheck());

// 自定义配置
app.get('/health', VendorSDK.middleware.healthCheck({
  service: 'launcher-vendor',
  version: '1.0.0',
  includeDetails: true
}));
VendorSDK.middleware.errorHandler

统一错误处理中间件。

// 应该放在所有路由的最后
app.use(VendorSDK.middleware.errorHandler);
签名相关 API
VendorAuth.signRequestWithHeaders(request)

生成包含签名头部的完整请求对象,适用于 header 签名验证场景。

参数:

  • request: Record<string, any> - 请求数据对象
  • brokerSk: string - 中介私钥
  • brokerDid: string - 中介公钥/DID

返回: { headers: Record<string, string>, body: string }

import { VendorAuth } from '@blocklet/payment-vendor';

const requestData = {
  path: '/api/vendor/deliveries',
  method: 'POST',
  timestamp: Date.now(),
  userInfo: {
    userDid: 'user_did_123',
    email: 'user@example.com'
  },
  body: { appName: 'test-app' }
};

const { headers, body } = VendorAuth.signRequestWithHeaders(
  requestData, 
);

// 发送请求
const response = await fetch('https://vendor.example.com/api/deliveries', {
  method: 'POST',
  headers, // 包含 x-broker-vendor-sig 和 x-broker-vendor-did
  body     // JSON 字符串
});
VendorAuth.signAndSerialize(data, secretKey)

签名并序列化请求数据,用于 body 签名验证场景。

参数:

  • data: VendorRequest - 请求数据对象
  • secretKey: string - 请求方私钥

返回: string - 序列化的签名请求

const signedBody = VendorAuth.signAndSerialize(requestData, secretKey);

const response = await fetch('https://vendor.example.com/api/deliveries', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: signedBody
});

错误处理

常见错误及解决方案
"Brokers must be an array"

原因: 传入 setBrokers 的参数类型不正确。

解决方案:

// ❌ 错误用法
VendorSDK.setBrokers(undefined);
VendorSDK.setBrokers("invalid");

// ✅ 正确用法
VendorSDK.setBrokers([]);  // 空数组
VendorSDK.setBrokers([{ 
  id: 'test', 
  name: 'Test', 
  publicKey: 'xxx', 
  status: 'active' 
}]);
VendorSDK.setBrokers(() => parseConfigFromEnv());  // 函数
"Missing signature header"

原因: 使用了不正确的签名方式。

解决方案:

// ❌ 错误用法(缺少签名头部)
const signedBody = VendorAuth.signAndSerialize(data, secretKey);
fetch(url, { body: signedBody });

// ✅ 正确用法(包含签名头部)
const { headers, body } = VendorAuth.signRequestWithHeaders(data);
fetch(url, { method: 'POST', headers, body });
"Broker not in whitelist"

原因: 请求的中介不在白名单中或状态为 inactive。

解决方案:

  1. 检查中介 ID 是否正确
  2. 确认中介状态为 'active'
  3. 验证白名单配置是否正确加载
"Request timestamp too old"

原因: 请求时间戳过期(超过5分钟)。

解决方案:

// 确保时间戳是当前时间
const requestData = {
  // ...其他字段
  timestamp: Date.now() // 使用当前时间戳
};

调试和监控

启用详细日志
// 添加请求日志中间件
app.use('/api/vendor', VendorSDK.middleware.ensureAuth());
手动验证签名
import { VendorAuth } from '@blocklet/payment-vendor';

async function debugSignature(requestBody, expectedBrokerId) {
  try {
    const brokers = await VendorSDK.getWhitelist();
    const broker = brokers.find(d => d.id === expectedBrokerId);
    
    if (!broker) {
      console.log('中介不存在:', expectedBrokerId);
      return false;
    }
    
    const isValid = await VendorAuth.verifyRequest(requestBody, broker);
    console.log('签名验证结果:', isValid);
    return isValid;
  } catch (error) {
    console.error('签名验证失败:', error);
    return false;
  }
}
监控端点
// 健康检查
app.get('/health', VendorSDK.middleware.healthCheck());

// 中介状态查询
app.get('/api/admin/brokers', async (req, res) => {
  try {
    const brokers = await VendorSDK.getWhitelist();
    res.json({
      total: brokers.length,
      active: brokers.filter(d => d.status === 'active').length,
      brokers: brokers.map(d => ({
        id: d.id,
        name: d.name,
        status: d.status,
        rateLimit: d.rateLimit
      }))
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 系统信息
app.get('/api/admin/info', (req, res) => {
  res.json({
    version: require('../package.json').version,
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    nodeVersion: process.version
  });
});

最佳实践

生产环境配置
  1. 使用环境变量管理敏感信息

    export PAYMENT_KIT_PUBLIC_KEY="your_public_key_here"
    export VENDOR_DISTRIBUTORS='[...]'
  2. 配置适当的限流和超时设置

    app.use('/api/vendor', VendorSDK.middleware.rateLimiter({
      windowMs: 60000,
      max: 1000 // 根据实际需要调整
    }));
  3. 定期轮换密钥和证书

    • 建议每季度更新一次密钥对
    • 使用配置热重载功能平滑切换
安全建议
  1. 验证所有入站请求的签名

    app.use('/api/vendor', VendorSDK.middleware.ensureAuth());
  2. 使用 HTTPS 传输敏感数据

    // 强制 HTTPS
    app.use((req, res, next) => {
      if (req.header('x-forwarded-proto') !== 'https') {
        res.redirect(`https://${req.header('host')}${req.url}`);
      } else {
        next();
      }
    });
  3. 实施适当的错误处理

    app.use(VendorSDK.middleware.errorHandler);
  4. 定期审查和更新中介列表

    • 使用动态配置模式便于管理
    • 定期检查并移除不活跃的中介
性能优化
  1. 使用缓存减少重复计算
  2. 合理设置限流参数
  3. 定期清理日志文件
  4. 监控内存使用情况

示例代码

完整的集成示例请参考 examples/usage-example.ts 文件。

技术支持

如需技术支持或报告问题,请访问项目的 GitHub 仓库提交 Issue。

许可证

本项目采用 Apache-2.0 许可证。详细信息请参阅 LICENSE 文件。

Keywords