文件、邮件、支付与外部服务集成
把外部对象和业务状态连接起来,说明完成证据、恢复、测试替代及公开边界。
集成先明确外部完成点
SDK 成功返回可能表示请求已接收、对象已保存或异步工作已建立。服务需要把外部身份和当前状态接到自身事实,并说明后续通知、查询与恢复。接口连通不等于业务全链路完成。
外部依赖的超时、重试、凭据、配额、区域与数据政策进入接线设计。测试替身可检验自身处理分支,真实依赖检查才确认对方实际语义;生产动作遵守任务授权。
文件与图片
服务器生成存储 key,原文件名仅作受控展示。桶默认权限、预签名方法、key、期限和完成确认共同限定访问;文件字节、尺寸、实际格式和解码资源需限制。对象存储的 S3 兼容名称不能保证每个签名、multipart 或权限接口完全相同。
sharp 等图像工具根据输入和变换生成产物。格式、质量、最大尺寸与是否放大按目标决定;归档的 2048 或 q80 是特定推荐,未成为全项目最优值。攻击性尺寸、损坏输入和 EXIF 方向需要处理。
数据库元数据与对象存储写入不在普通共同事务内。可保存 pending、完成后转 ready、失败转可恢复状态,保留对象身份及清理意图。先在内存生成所有图片也无法保证多个对象上传全成功;部分成功需清理或对账。删除同样需要状态、重试和孤儿检查。
邮件
模板把数据生成 HTML 和纯文本,发送器通过 SMTP 或邮件 API 提交。react-email、MJML 或其他模板方式各有渲染和客户端条件;浏览器中的 React 组件能力不能自动搬到邮件。
确认邮件的 URL 按解析后的 origin 和允许路径校验,不能用字符串前缀判可信域。令牌期限、一次性使用和邮件重投需按认证流程定义。请求期间直接 await、平台后台延续和持久队列具有不同延迟与恢复保证,按任务需求选择。
Nodemailer 的发送结果说明 SMTP 对话中哪些收件目标被接受,真实到达、退信和垃圾分类可能更晚。Mailpit 等本地收件器可检查模板和发送接线,未覆盖生产信誉和真实客户端。测试收件人与生产目标明确隔离,避免真实通知。Nodemailer
SMTP 接线可用 Nodemailer,模板输出 HTML 和纯文本分别传入。下面的配置在发送前解析;示例返回接收与拒绝列表,不把它命名为“已送达”:
import nodemailer from 'nodemailer';
import { z } from 'zod';
const mailConfig = z.object({
SMTP_HOST: z.string().min(1),
SMTP_PORT: z.coerce.number().int().min(1).max(65535),
SMTP_USER: z.string().min(1),
SMTP_PASSWORD: z.string().min(1),
MAIL_FROM: z.email(),
}).parse(process.env);
const transport = nodemailer.createTransport({
host: mailConfig.SMTP_HOST,
port: mailConfig.SMTP_PORT,
secure: mailConfig.SMTP_PORT === 465,
requireTLS: mailConfig.SMTP_PORT !== 465,
auth: { user: mailConfig.SMTP_USER, pass: mailConfig.SMTP_PASSWORD },
});
export async function sendNotice(to: string, text: string, html: string) {
const result = await transport.sendMail({
from: mailConfig.MAIL_FROM, to: z.email().parse(to),
subject: 'Notification', text, html,
});
return { messageId: result.messageId, accepted: result.accepted, rejected: result.rejected };
}端口 465 使用连接开始即 TLS;其他端口在此配置要求 STARTTLS,实际提供方需支持。收件目标来自受控业务意图,HTML 来自已转义模板,不能直接拼接不可信内容。队列任务记录提交结果与未知结果,在 SMTP 超时后简单重发仍可能重复邮件。此片段未发送任何真实邮件,认证政策、发信域 DNS、退信与生产信誉需要另行核查。Nodemailer SMTP
支付与通知
服务器根据可信商品、币种和规则计算金额,保存渠道身份。客户端成功跳转提供体验反馈,业务完成由可信渠道通知或查证确认。金额使用渠道规定的最小单位或准确表示,不把所有币种假定为两位小数。
Webhook 验签使用渠道要求的原始请求体,随后检查账户、对象、金额、币种及事件语义。重复与乱序通知需要持久事件身份、业务状态转移和原子提交。先插入去重表后执行效果,会在崩溃时漏处理;二者可在同一事务内完成时才形成相应保证。Stripe Webhooks
创建请求的 idempotency key 由同一逻辑意图产生,相同 key 的参数、期限和结果按渠道契约处理。退款和订阅变化是后续业务流程,不可仅靠一个“已支付”布尔值代表所有状态。Stripe 幂等请求
支付宝、Stripe 和 MoR 产品还涉及市场准入、结算与合同。归档价格、地区支持和许可属于历史调查线索;实际支付选择需取得当时条款,本页不提供当前商业合规或费用结论。
Stripe 对原始请求体验签的接线可以放在 Nest HTTP 入口。启动时启用 rawBody: true,控制器读取原始字节,而非 JSON.stringify(req.body) 后重建:
// main.ts 的创建选项:
const app = await NestFactory.create(AppModule, { rawBody: true });import Stripe from 'stripe';
import type { Request } from 'express';
import {
BadRequestException, Controller, Headers, HttpCode, Inject, Post, Req,
type RawBodyRequest,
} from '@nestjs/common';
export const PAYMENT_EVENTS = Symbol('PAYMENT_EVENTS');
export interface PaymentEvents {
apply(event: Stripe.Event): Promise<void>;
}
@Controller('stripe')
export class StripeWebhook {
constructor(
private readonly stripe: Stripe,
@Inject(PAYMENT_EVENTS) private readonly events: PaymentEvents,
@Inject('STRIPE_WEBHOOK_SECRET') private readonly secret: string,
) {}
@Post('webhook')
@HttpCode(200)
async receive(
@Req() req: RawBodyRequest<Request>,
@Headers('stripe-signature') signature: string | undefined,
) {
if (!req.rawBody || !signature) throw new BadRequestException('Missing signature');
let event: Stripe.Event;
try { event = this.stripe.webhooks.constructEvent(req.rawBody, signature, this.secret); }
catch { throw new BadRequestException('Invalid signature'); }
await this.events.apply(event);
return { received: true };
}
}在模块中把 Stripe token 注册为 SDK 实例,把 webhook secret 注册为已验证的服务器配置,把 PAYMENT_EVENTS 注册为应用服务。该应用服务首先按允许的 event type 和渠道对象核对账户、金额等条件,然后在同一事务中插入唯一事件身份与变更付款状态。发现已提交事件时返回既有结果;依赖失败时抛出,使入口不提前返回 2xx。还要通过对象状态或补查处理乱序事件,不能以任意事件覆盖新状态。
需要迅速应答时,可将验签后的事件完整持久化,再 2xx 返回,由后台恢复处理;持久化之前先返回会留下丢通知窗口。片段提供运输和业务端口,业务状态模型取决于产品,未执行真实 Stripe、数据库或 Nest 服务。Nest 原始请求体、Stripe Webhook 验签
搜索、消息及其他集成
Meilisearch 和向量检索把数据库事实转成派生索引。索引失败应保留可恢复进度,删除与权限变化也要传播;返回候选后仍按服务端权限过滤。搜索可用不表示索引与源立即一致。
短信、浏览器推送和设备推送分别具有地址、订阅、服务商与交付条件。SDK 返回接收确认与用户实际看到消息是不同结果,失效订阅需清理;内容与身份的公开范围需要控制。
特性开关把目标条件映射到行为,故障默认值、稳定分组和旧开关移除都需说明。OpenFeature 提供厂商无关接口,GrowthBook、Unleash 等承担具体决策与管理能力;产品分析 PostHog、Umami 等还需事件定义、身份、同意与数据保留规则。
HTML 解析可用 Cheerio,动态页面可用受控浏览器,规模化采集还需队列、限流和恢复。CLI 使用 Commander、Yargs 或 oclif 解析命令,交互提示由 clack、Inquirer 等组织,非交互运行仍需稳定输入、退出码和输出。库名称提供候选,实际采用按当前接口和边界核查。
最后更新于