mcpField
F
financial-audit-trail

financial-audit-trail

github.com/finsilabs安全60133 次安装6 天前更新
代码仓库

关于 financial-audit-trail

面向财务与工程团队,介绍如何记录可追溯的财务变更。内容包括补充电商平台日志,或构建不可变审计记录并导出供审计使用。

主要功能

  1. 01检查平台审计日志
  2. 02追踪财务变更操作者
  3. 03记录变更前后状态
  4. 04设计不可变日志
  5. 05导出 JSON 和 CSV 审计数据

适用场景

  1. 01准备外部审计材料
  2. 02调查订单或付款差异
  3. 03追踪人工折扣、退款和发票审批
  4. 04满足合规日志记录需求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill financial-audit-trail

安装后在 Claude Code 中输入/financial-audit-trail确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

tile.json
evals/append-only-schema-with-immutability-enf/
criteria.json
task.md
evals/audit-logger-service-with-hash-chaining/
criteria.json
task.md
evals/compliance-export-and-chain-integrity-ve/
criteria.json
task.md

详细介绍

财务审计跟踪

概述

财务审计跟踪会记录对财务数据所做的每项更改——订单、付款、退款、手动价格调整和发票审批——包括由谁更改、何时更改、来自哪个 IP,以及更改前后的记录内容。这类基础设施是满足 PCI-DSS 日志合规要求、SOX ITGC 证据要求、GDPR 删除审计跟踪要求以及财务报表审计要求的必要条件。每个主要的电商平台都会生成某种审计历史记录;通常不足之处在于记录不完整、无法追溯手动更改的责任人,以及面向审计人员的导出格式。

何时使用此技能

  • 当财务团队无法还原是谁更改了订单总额、应用了手动折扣或作废了发票时
  • 当准备外部审计,需要提供特定时间段内完整且可搜索的交易历史记录时
  • 当构建需要提供控制措施运行证据的 SOX 合规财务系统时
  • 当实施 PCI-DSS 日志要求,以记录对持卡人数据环境的访问时
  • 当 GDPR 删除请求要求证明客户的财务数据已被匿名化时
  • 当调查电商收入与支付处理商结算报告之间的差异时

核心说明

步骤 1:确定商家的平台以及已记录的内容
平台内置审计能力需要添加的内容
Shopify订单时间线记录所有事件(创建、收款、退款、编辑);对管理员操作的归属记录有限通过 Admin API 导出时间线数据;对于管理员更改,在 Settings 中启用 Staff activity logging
WooCommerce订单备注显示客户可见的操作;系统备注显示部分状态更改安装 WooCommerce Admin Audit Log 或 Simple History 插件,以完整跟踪管理员操作
BigCommerceAdvanced Settings → Store Logs 中的商店日志记录系统事件;可通过 API 获取订单历史记录要追溯管理员操作的责任人,请通过 API 导出并与服务器访问日志合并
Custom / Headless无内置功能必须自行构建——请参阅 Custom 部分
步骤 2:提取并补充平台审计数据

Shopify

启用和访问审计数据:

  1. 前往 Settings → Activity(Shopify 管理后台)查看近期员工活动
  2. 如需更细粒度的数据,请安装 Shopify Audit 或使用 Admin API:
    • GET /admin/api/2024-04/events.json 会返回所有商店事件
    • 按 verb(confirmed、placed、edited、refunded 等)和 subject_type(Order、Refund、Customer)筛选
  3. 每个订单都包含一个 Timeline 部分,显示所有状态更改及其时间戳

为审计人员导出:

  1. 前往 Orders → Export,将订单数据下载为 CSV
  2. 如需详细的退款记录:Orders → [specific order] → Timeline 会显示每项操作
  3. 使用 Shopify Admin API 提取完整事件流:
// Fetch all financial events for a date range
const events = await shopify.event.list({
  verb: 'confirmed,placed,refunded,voided',
  created_at_min: '2026-01-01T00:00:00Z',
  created_at_max: '2026-12-31T23:59:59Z',
  limit: 250,
});

员工操作跟踪(Shopify Plus):

  • 前往 Settings → Users and permissions → Staff activity log
  • 其中会显示管理员操作,包括订单编辑、价格调整和退款,并注明操作用户

WooCommerce

WooCommerce 订单备注会提供部分审计历史记录,但不会记录进行更改的管理员用户。请安装专用审计插件。

Simple History 插件(免费,推荐):

  1. 从 WordPress 插件目录安装 Simple History
  2. 它会自动记录:
    • 订单状态变更及对应的用户
    • 商品价格变更
    • WooCommerce 设置变更
    • 用户登录/登出事件
  3. 前往 Dashboard → Simple History 查看日志
  4. 通过 Simple History → Settings → Export 导出为 CSV

WooCommerce Admin Audit Log(高级版,每年约 50 美元):

  1. 从 WooCommerce.com 或第三方市场安装
  2. 提供更精细的日志记录,包括:
    • 手动修改订单总额
    • 应用折扣及对应的员工用户
    • 退款金额及批准用户
  3. 导出为 CSV 或 PDF,供审计人员查阅

手动跟踪订单编辑: 对于任何手动财务变更(应用折扣、调整总额),请添加订单备注(内部备注,客户不可见),记录:

  • 更改了什么
  • 更改原因
  • 批准人

未启用自动日志记录时,这是审计人员可接受的最低证据要求。


BigCommerce

访问商店日志:

  1. 前往 Advanced Settings → Store Logs
  2. 按日志类型筛选:Order(财务变更)、User(管理员活动)、System
  3. 导出为 CSV

通过 API 查看订单历史记录: 使用 BigCommerce Orders API 提取包含状态变更的完整订单历史记录:

// Get all orders modified in a date range
const orders = await bigcommerce.get('/v2/orders', {
  min_date_modified: '2026-01-01T00:00:00+00:00',
  max_date_modified: '2026-12-31T23:59:59+00:00',
  status_id: '',  // all statuses
  limit: 250,
});

结合 Order Transactions API(/v2/orders/{id}/transactions)获取付款扣款、退款和撤销记录。


Custom / Headless

对于自定义店面,请实现仅追加的审计日志,记录每次财务变更。关键设计要求:不可变(应用角色没有 UPDATE 或 DELETE 权限)、包含变更前后的状态,并支持篡改检测。

CREATE TABLE financial_audit_events (
  id             UUID        NOT NULL DEFAULT gen_random_uuid(),
  seq            BIGSERIAL   NOT NULL,          -- Monotonic — gap detection
  occurred_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  event_type     VARCHAR(64) NOT NULL,           -- 'order.total_changed', 'refund.issued'
  aggregate_type VARCHAR(32) NOT NULL,           -- 'order', 'payment', 'refund'
  aggregate_id   VARCHAR(128) NOT NULL,
  actor_id       VARCHAR(128) NOT NULL,          -- user ID or 'system'
  actor_role     VARCHAR(64),
  actor_ip       INET,
  before_state   JSONB,                          -- Snapshot before change
  after_state    JSONB,                          -- Snapshot after change
  delta          JSONB,                          -- Only changed fields
  correlation_id UUID,                           -- Tie events in same request
  hash           VARCHAR(64),                    -- SHA-256 for tamper detection
  PRIMARY KEY (id)
);

-- Grant INSERT and SELECT only — never UPDATE or DELETE
GRANT INSERT, SELECT ON financial_audit_events TO app_role;
REVOKE UPDATE, DELETE ON financial_audit_events FROM app_role;

CREATE INDEX idx_fae_aggregate ON financial_audit_events (aggregate_type, aggregate_id, occurred_at DESC);
CREATE INDEX idx_fae_actor ON financial_audit_events (actor_id, occurred_at DESC);
CREATE INDEX idx_fae_seq ON financial_audit_events (seq);

带篡改检测哈希的审计日志记录器:

import { createHash } from 'crypto';

async function recordAuditEvent(input: {
  eventType: string;
  aggregateType: string;
  aggregateId: string;
  actorId: string;
  actorRole?: string;
  actorIp?: string;
  beforeState?: object | null;
  afterState?: object | null;
  correlationId?: string;
}): Promise<void> {
  const prevEvent = await db.financialAuditEvents.findFirst({
    where: { aggregate_type: input.aggregateType, aggregate_id: input.aggregateId },
    orderBy: { seq: 'desc' },
    select: { seq: true, hash: true },
  });

  const occurredAt = new Date().toISOString();
  const hashInput = [
    String(prevEvent?.seq ?? 0),
    occurredAt,
    input.eventType,
    input.aggregateId,
    input.actorId,
    JSON.stringify(input.afterState ?? null),
  ].join('|');

  const hash = createHash('sha256').update(hashInput).digest('hex');

  await db.financialAuditEvents.insert({
    occurred_at: occurredAt,
    event_type: input.eventType,
    aggregate_type: input.aggregateType,
    aggregate_id: input.aggregateId,
    actor_id: input.actorId,
    actor_role: input.actorRole ?? null,
    actor_ip: input.actorIp ?? null,
    before_state: input.beforeState ?? null,
    after_state: input.afterState ?? null,
    correlation_id: input.correlationId ?? null,
    hash,
    prev_hash: prevEvent?.hash ?? null,
  });
}

供审计人员查阅的导出内容:

async function exportAuditTrail(from: Date, to: Date, format: 'json' | 'csv'): Promise<Buffer> {
  const events = await db.financialAuditEvents.findAll({
    where: { occurred_at: { gte: from, lte: to } },
    orderBy: { seq: 'asc' },
  });

  if (format === 'json') return Buffer.from(JSON.stringify(events, null, 2));

  // CSV for auditor delivery
  const rows = events.map(e => ({
    'Date/Time': e.occurred_at,
    'Event Type': e.event_type,
    'Record Type': e.aggregate_type,
    'Record ID': e.aggregate_id,
    'Actor': e.actor_id,
    'Actor Role': e.actor_role ?? '',
    'IP Address': e.actor_ip ?? '',
    'Before': e.before_state ? JSON.stringify(e.before_state) : '',
    'After': e.after_state ? JSON.stringify(e.after_state) : '',
    'Delta': e.delta ? JSON.stringify(e.delta) : '',
  }));
  return buildCsv(rows);
}

最佳实践

  • 在每次变更中包含 before_state 和 after_state — 仅存储差异不足以满足合规要求;审计人员需要能够还原记录在任意时间点的完整状态
  • 在数据库层撤销 UPDATE 和 DELETE 权限 — 应用层检查可能被绕过;唯一可靠的不可变性保障,是应用角色不具备的数据库权限
  • 同时记录执行者的 IP 地址和用户 ID — 调查欺诈行为时,IP 地址往往更有用;两者都要记录
  • 记录 HTTP 请求中的 correlation_id — 如果单个 API 请求创建了多个审计事件,共享的 correlation_id 可帮助还原完整的因果链
  • 每月导出并验证一个样本 — 每月 1 日生成合规导出文件并验证哈希链;这样在审计人员索取前,你就能准备好经过测试的证据包
  • 将审计事件存储在单独的数据库架构中 — 防止应用错误或 DBA 操作失误意外影响生产数据中的审计记录

常见陷阱

问题解决方案
开发人员直接调用 db.update(),导致审计事件缺失必须在服务层调用审计日志记录器,而不是在控制器中调用;在代码审查清单中添加一项,检查是否直接对财务表调用 db.update
开发人员只在变更后捕获状态,导致 before_state 为 null在同一个数据库事务中,于变更前获取并记录该记录的快照
审计表增长到数亿行,导致查询变慢按 occurred_at 对表进行分区(按月分区);在热存储中保留 12 个月的数据,并将更早的分区归档到 S3 + Athena
攻击者入侵应用角色后可以删除审计行撤销所有角色的 DELETE 权限;考虑将日志以只写方式同步到外部服务(CloudWatch Logs、Datadog)
生成合规性导出需要数小时为常见的审计报告模式预先构建索引视图;确保范围查询使用 occurred_at 索引

相关技能

  • @financial-compliance-sox
  • @pci-dss-compliance
  • @data-retention-policies
  • @gdpr-ecommerce

评论

支持 Markdown