financial-compliance-sox について
ECの財務報告に関するSOX内部統制を整備する、財務・エンジニアリングチーム向けのスキルです。統制の文書化、職務分掌、承認記録、取引照合、監査証拠の保全を扱います。
主な機能
- 01財務データフローと統制点の整理
- 02ロールベースの職務分掌設定
- 03高額取引の承認ワークフロー構築
- 04プラットフォームと決済報告の照合
- 05監査可能な証拠とログの記録
- 06アクセスレビューと統制テスト
活用シーン
- 01IPOに向けたICFR統制の準備
- 02監査人からの証拠依頼への対応
- 03特定された統制不備の是正
- 04財務諸表に影響するシステムの統制設計
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill financial-compliance-soxインストール後、Claude Code で/financial-compliance-soxを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/approval-controls-and-audit-trail/criteria.jsontask.mdevals/quarterly-access-review-and-evidence-exp/criteria.jsontask.mdevals/sod-role-enforcement/criteria.jsontask.md詳細
金融コンプライアンス — SOX
概要
SOX(サーベンス・オクスリー法)第302条および第404条は、上場企業に対し、財務報告に係る内部統制(ICFR)を文書化して維持することを義務付けています。Eコマースでは、受注から入金まで、および購買から支払いまでのサイクル全体に統制を導入することを意味します。具体的には、職務分掌(財務取引の開始と承認を同一人物が行えないようにすること)、高額取引の承認ワークフロー、自動照合、改ざんできない監査証跡です。SOX準拠は、主にプロセスと文書化の課題であり、ソフトウェアの課題ではありません。ただし、構築するシステムでは、統制が運用されていることを示す監査可能な証跡を生成する必要があります。
このスキルを使用する場面
- 会社がIPOを準備しており、SOX準拠のICFRを確立する必要がある場合
- 外部監査人から、EコマースプラットフォームのIT全般統制の証跡を求められた場合
- 職務分掌を示す承認ワークフローを構築する場合
- 財務諸表に反映されるシステムのアクセス制御を設計する場合
- 監査人が特定した重大な不備または重要な欠陥を是正する場合
基本的な手順
ステップ 1:財務データの流れと統制ポイントを整理する
設定やコードに着手する前に、どのシステムに財務データが含まれているか、またどのような統制が適用されるかを文書化します。SOX監査人は、以下の文書を確認します。
受注から入金までの統制ポイント:
- 注文入力 → 決済の確定 → 収益計上
- 主な統制:高額注文の承認、不正検知ルール、総勘定元帳への自動記帳、月次照合
購買から支払いまでの統制ポイント:
- 発注書の作成 → 請求書の照合 → 支払いの実行
- 主な統制:基準額を超える発注書の3点照合、多額の支払い処理に対する二重承認
各統制について、以下を文書化します。
- 統制目標(この統制はどのリスクを軽減するか)
- 統制責任者(どの役割がこの統制を実施するか)
- 頻度(継続的、毎日、毎月、取引ごと)
- 証跡(統制が実施されたことを証明する記録は何か)
ステップ 2:プラットフォーム固有のSOX実装
Shopify
ShopifyにはSOX専用のツールはありませんが、プラットフォームの機能やサードパーティ連携を使用して主要な統制を実装できます。
アクセス制御(職務分掌):
- 設定 → ユーザーと権限に移動します
- スタッフアカウントの権限を設定します。Shopifyでは、権限の範囲を細かく指定できます。
- 「返金の発行」「注文の編集」「レポートへのアクセス」を別々に設定します
- スタッフごとに個別のアカウントを作成します(管理者認証情報を共有しないでください)
- より厳格な職務分掌を実施する場合は、Shopify Plusの組織を使用して、複数のストアにまたがる権限を管理します
高額注文の承認ワークフロー: Shopifyには標準の承認ワークフローがありません。Shopify Flow(Shopify/Plus)を使用して、保留と通知のワークフローを作成します。
- Shopify Flowでトリガー 注文が作成されたときを作成します
- 条件を追加します:注文合計 > $10,000
- アクションを追加します:タグ「pending_approval」を追加し、社内メールまたはSlackで通知を送信します
- チームで確認し、タグを手動で削除して注文を履行するか、注文をキャンセルします
- 承認の判断を注文メモとして記録します(監査証跡用)
照合:
- Shopifyの財務 → 決済レポートで、すべての決済確定、返金、入金を確認します
- 月次レポートをダウンロードし、決済処理業者(Stripe、PayPal)の精算レポートと照合します
- 差異があれば説明を添えて記録します。これが照合統制の証跡になります
変更管理の証跡: Shopifyのテーマやアプリの変更については、以下を実施します。
- バージョン管理されたテーマ開発ワークフロー(GitHub)を使用します
- テーマの変更をデプロイする前に、プルリクエストのレビューを必須にします
- アプリのインストールと権限の付与を変更履歴に記録します
WooCommerce
WooCommerce では、役割ベースのアクセス制御やカスタム承認ワークフローをより細かく管理できます。
役割ベースのアクセス制御(職務分掌):
- User Role Editor または Members プラグインをインストールします
- WooCommerce の特定の権限を付与した、個別の WordPress ロールを作成します:
- Order Viewer:注文を閲覧できますが、編集や返金はできません
- Order Processor:注文ステータスを更新できますが、返金はできません
- Finance Manager:返金できますが、注文は作成できません
- Auditor:すべてのレポートを読み取り専用で閲覧できますが、変更はできません
- スタッフにロールを割り当てます。「注文の作成」と「返金」の両方の権限を同じ人に付与してはいけません
高額注文の承認ワークフロー: WooCommerce Order Approval(無料)または YITH WooCommerce Order Approval(年間約70ドル)をインストールします:
- 承認しきい値を設定します(例:10,000ドルを超える注文は手動承認が必要)
- プラグインにより、注文は「承認待ち」ステータスに保留されます
- 指定された承認者にメール通知が届き、WooCommerce 管理画面で承認または却下できます
- 承認の判断と承認者名が注文メモに記録されます(監査証跡)
監査ログ: Simple History(無料プラグイン)をインストールし、ユーザー情報、タイムスタンプ、変更前後の値とともに、すべての WooCommerce 管理操作を記録します。
月次照合:
- 月ごとの WooCommerce 注文合計をエクスポートします(WooCommerce → Reports → Orders)
- 同じ期間の Stripe/PayPal の決済精算レポートをダウンロードします
- 合計を照合し、差異を記録して、証跡を共有コンプライアンスフォルダーに保存します
BigCommerce
ユーザーアクセス制御:
- Account Settings → Users → Add User に移動します
- BigCommerce は、きめ細かな権限を設定できる役割ベースのアクセス制御に対応しています
- 職務分掌の要件に沿ったロールを作成します。注文処理、返金承認、レポート閲覧者をそれぞれ分けてください
承認ワークフロー: BigCommerce には標準の承認ワークフローがありません。次の方法があります:
- BigCommerce Webhooks を使い、高額注文の作成時に外部ワークフローツール(Zapier、Monday.com、カスタムアプリ)で承認プロセスを開始します
- カスタム注文ステータスと標準業務手順を使って、高額注文を手動で保留します
照合: BigCommerce Analytics → Revenue report を使い、決済処理業者の精算内容と比較します。両方を CSV としてエクスポートし、毎月比較結果を記録してください。
カスタム/ヘッドレス
カスタムストアフロントでは、制御をプログラムで実装します。SOX 監査で最も重要な3つの制御は、職務分掌の徹底、証跡を取得する承認ワークフロー、自動照合です。
職務分掌 — ロールモデル:
enum FinancialRole {
ORDER_ENTRY = 'order_entry',
ORDER_APPROVER = 'order_approver',
CASH_RECEIPTS = 'cash_receipts',
PO_REQUESTER = 'po_requester',
PO_APPROVER = 'po_approver',
PAYMENT_INITIATOR = 'payment_initiator',
PAYMENT_APPROVER = 'payment_approver',
AUDITOR = 'auditor', // Read-only — zero transaction capability
}
// SOD conflicts — these role combinations are prohibited
const SOD_CONFLICTS: [FinancialRole, FinancialRole][] = [
[FinancialRole.PO_REQUESTER, FinancialRole.PO_APPROVER],
[FinancialRole.PAYMENT_INITIATOR, FinancialRole.PAYMENT_APPROVER],
[FinancialRole.PO_REQUESTER, FinancialRole.PAYMENT_APPROVER],
[FinancialRole.ORDER_ENTRY, FinancialRole.ORDER_APPROVER],
];
function hasSodConflict(roles: FinancialRole[]): { conflict: boolean; pairs: [FinancialRole, FinancialRole][] } {
const conflicts = SOD_CONFLICTS.filter(([a, b]) => roles.includes(a) && roles.includes(b));
return { conflict: conflicts.length > 0, pairs: conflicts };
}
// Enforce SOD when assigning roles
async function assignRoles(userId: string, newRoles: FinancialRole[], assignedBy: string) {
const { conflict, pairs } = hasSodConflict(newRoles);
if (conflict) {
throw new Error(`SOD conflict: ${pairs.map(([a, b]) => `${a} + ${b}`).join(', ')}`);
}
await db.userRoles.setRoles(userId, newRoles);
// Log to immutable audit trail
await auditLog.record({ eventType: 'user_roles_changed', actorId: assignedBy, aggregateId: userId,
afterState: { roles: newRoles }, controlRef: 'SOX-ITGC-AC-001' });
}
高額注文の承認制御:
const HIGH_VALUE_THRESHOLD_CENTS = 1_000_000; // $10,000
async function processOrderApproval(orderId: string, approverId: string) {
const order = await db.orders.findById(orderId);
const approver = await db.users.findById(approverId);
if (order.total_cents >= HIGH_VALUE_THRESHOLD_CENTS) {
const hasRole = approver.roles.includes(FinancialRole.ORDER_APPROVER);
await auditLog.record({
eventType: 'high_value_order_approval_check',
aggregateId: orderId,
actorId: approverId,
afterState: { orderTotal: order.total_cents, hasApprovalRole: hasRole, outcome: hasRole ? 'pass' : 'fail' },
controlRef: 'SOX-OTC-001',
});
if (!hasRole) throw new Error('ORDER_APPROVER role required for orders above $10,000');
}
}
月次照合の自動化:
async function runMonthlyReconciliation(month: Date) {
const [ordersRevenue, processorSettlements] = await Promise.all([
db.orders.sumRevenue(month),
paymentProcessor.getSettlementsForMonth(month),
]);
const varianceCents = ordersRevenue.totalCents - processorSettlements.totalCents;
const outcome = Math.abs(varianceCents) <= 100 ? 'pass' : 'exception'; // $1 tolerance
await auditLog.record({
eventType: 'monthly_revenue_reconciliation',
aggregateId: month.toISOString().slice(0, 7),
actorId: 'system_recon_job',
afterState: { ordersRevenue: ordersRevenue.totalCents, processorSettlements: processorSettlements.totalCents, varianceCents, outcome },
controlRef: 'SOX-OTC-RECON-001',
});
if (outcome === 'exception') {
await alertFinanceTeam(`Reconciliation exception: ${varianceCents / 100} variance for ${month.toISOString().slice(0, 7)}`);
}
}
四半期ごとのユーザーアクセスレビュー:
async function generateQuarterlyAccessReview(quarter: string) {
const financialUsers = await db.users.findWithFinancialRoles();
const report = {
quarter,
generatedAt: new Date().toISOString(),
users: financialUsers.map(u => ({
userId: u.id, email: u.email,
roles: u.financialRoles,
lastLogin: u.lastLoginAt,
dormant: !u.lastLoginAt || u.lastLoginAt < new Date(Date.now() - 90 * 86400000),
sodConflict: hasSodConflict(u.financialRoles).conflict,
})),
};
report.exceptions = report.users.filter(u => u.dormant || u.sodConflict);
// Route to manager for sign-off with 30-day deadline
await routeForManagerApproval(report);
await auditLog.record({ eventType: 'quarterly_access_review_generated', aggregateId: quarter,
actorId: 'system', afterState: { exceptionCount: report.exceptions.length }, controlRef: 'SOX-ITGC-AC-002' });
return report;
}
ベストプラクティス
- 自動化する前に制御を文書化する — コードを作成する前に、制御の説明(目的、軽減するリスク、担当者、頻度、証跡)を1ページにまとめます。監査人はまず文書を確認します
- 制御の失敗は警告ログではなく例外を発生させる — 失敗をログに記録しながら処理を続行する SOX 制御は、制御がない場合よりも悪いものです。予防的制御では処理をブロックする必要があります
- 改ざんできない監査ログストレージを使用する — アプリケーションロールには監査テーブルへの INSERT のみを許可し、UPDATE と DELETE を取り消します。二次ストアとして追記専用のログサービスを使用します
- すべての監査イベントに制御参照 ID を記録する — 監査人から Control SOX-P2P-002 の証跡を求められた場合、
control_refで絞り込む単一のクエリですべての証跡を取得できるようにします - 四半期ごとのアクセスレビューを自動化する — 手動レビューは、最もよくある制御不備です。レポートの生成を自動化し、期限を設定して管理者に送付します
- 四半期ごとにステージング環境で制御をテストする — 模擬ウォークスルーを実施します。各制御にサンプルトランザクションを通し、監査人が本番環境でテストする前に証跡が取得されることを確認します
よくある落とし穴
| 問題 | 解決策 |
|---|---|
| ロールの適用がユーザー登録後に追加されたため、職務分掌(SOD)の競合が存在します | 既存のすべてのユーザーとロールの割り当てを対象に、SOD スキャンを一度実行します。例外チケットを作成し、監査期間の開始前に是正します |
| 監査ログが変更可能であり、DBA が行を削除できます | すべてのデータベースロールから DELETE 権限を取り消します。改ざんを検知できる二次コピーとして、別のログ集約サービス(CloudWatch Logs、Datadog)を使用します |
| 週末や休日の管理策の証跡がありません | 金融システムが取引を処理する日は、毎日管理策を実施する必要があります。照合ジョブは週7日実行する必要があります |
| 直接 API を呼び出すことで承認管理策が回避されます | すべての金融取引の変更エンドポイントで、UI だけでなくサービスレイヤーでも管理策を確認する必要があります。管理策の関数はサービスコードから呼び出し、コントローラーからのみ呼び出すことがないようにします |
| ホットフィックスの変更管理の証跡がありません | ホットフィックスを含むすべての本番環境への変更は、コードレビューを経る必要があります。main と同じレビュー要件を持つ hotfix ブランチタイプを作成します |
関連スキル
- @financial-audit-trail
- @pci-dss-compliance
- @account-security
- @data-retention-policies