{
  "schemaVersion": "1.0",
  "item": {
    "slug": "stripemeter",
    "name": "Stripemeter",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/geminimir/stripemeter",
    "canonicalUrl": "https://clawhub.ai/geminimir/stripemeter",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/stripemeter",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=stripemeter",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "RECONCILIATION.md",
      "SKILL.md",
      "docs/api/ingest.md",
      "docs/api/reconciliation-summary.md",
      "docs/api/usage-history.md",
      "docs/simulator-getting-started.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/stripemeter"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/stripemeter",
    "agentPageUrl": "https://openagent3.xyz/skills/stripemeter/agent",
    "manifestUrl": "https://openagent3.xyz/skills/stripemeter/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/stripemeter/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "StripeMeter",
        "body": "StripeMeter is a Stripe-native usage metering system that ensures correct usage totals for usage-based billing. It dedupes retries, handles late events with watermarks, keeps running counters, and pushes only deltas to Stripe."
      },
      {
        "title": "Quick Start",
        "body": "git clone https://github.com/geminimir/stripemeter && cd stripemeter\ncp .env.example .env && docker compose up -d && pnpm -r build\npnpm db:migrate && pnpm dev"
      },
      {
        "title": "Events (Immutable Ledger)",
        "body": "Every usage event stored with deterministic idempotency key. Events are never deleted or modified."
      },
      {
        "title": "Counters (Materialized Aggregations)",
        "body": "Pre-computed aggregations (sum/max/last) by tenant, metric, customer, and period. Updated in near-real-time."
      },
      {
        "title": "Watermarks (Late Event Handling)",
        "body": "Each counter maintains a watermark timestamp. Events within lateness window (default 48h) trigger re-aggregation."
      },
      {
        "title": "Delta Push (Stripe Synchronization)",
        "body": "Tracks pushed_total per subscription item and only sends delta to Stripe."
      },
      {
        "title": "Ingest Events",
        "body": "curl -X POST http://localhost:3000/v1/events/ingest \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"events\": [{\n      \"tenantId\": \"your-tenant-id\",\n      \"metric\": \"api_calls\",\n      \"customerRef\": \"cus_ABC123\",\n      \"quantity\": 100,\n      \"ts\": \"2025-01-16T14:30:00Z\"\n    }]\n  }'"
      },
      {
        "title": "Get Cost Projection",
        "body": "curl -X POST http://localhost:3000/v1/usage/projection \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"tenantId\": \"your-tenant-id\", \"customerRef\": \"cus_ABC123\"}'"
      },
      {
        "title": "Health & Metrics",
        "body": "Readiness: GET /health/ready\nMetrics: GET /metrics\nEvents: GET /v1/events?tenantId=X&limit=10"
      },
      {
        "title": "Node.js SDK",
        "body": "import { createClient } from '@stripemeter/sdk-node';\n\nconst client = createClient({\n  apiUrl: 'http://localhost:3000',\n  tenantId: 'your-tenant-id',\n  customerId: 'cus_ABC123'\n});\n\n// Track usage\nawait client.track({\n  metric: 'api_calls',\n  customerRef: 'cus_ABC123',\n  quantity: 100,\n  meta: { endpoint: '/v1/search' }\n});\n\n// Get live usage\nconst usage = await client.getUsage('cus_ABC123');\nconst projection = await client.getProjection('cus_ABC123');"
      },
      {
        "title": "Python SDK",
        "body": "from stripemeter import StripeMeterClient\n\nclient = StripeMeterClient(\n    api_url=\"http://localhost:3000\",\n    tenant_id=\"your-tenant-id\",\n    customer_id=\"cus_ABC123\"\n)\n\nclient.track(\n    metric=\"api_calls\",\n    customer_ref=\"cus_ABC123\",\n    quantity=100\n)"
      },
      {
        "title": "Stripe Billing Driver",
        "body": "For direct Stripe integration, use the stripe-driver package:\n\nimport { StripeBillingDriverImpl } from '@stripemeter/stripe-driver';\n\nconst driver = new StripeBillingDriverImpl({\n  liveKey: process.env.STRIPE_SECRET_KEY,\n  testKey: process.env.STRIPE_TEST_SECRET_KEY\n});\n\n// Record usage to Stripe\nawait driver.recordUsage({\n  mode: 'live',\n  stripeAccount: 'default',\n  subscriptionItemId: 'si_xxx',\n  quantity: 100,\n  periodStart: '2025-01-01',\n  idempotencyKey: 'unique-key'\n});\n\n// Get usage summary\nconst summary = await driver.getUsageSummary(\n  'si_xxx',\n  '2025-01-01',\n  'default'\n);"
      },
      {
        "title": "Shadow Mode",
        "body": "Test Stripe usage posting without affecting live invoices:\n\nSet STRIPE_TEST_SECRET_KEY in environment\nMark price mapping with shadow=true\nProvide shadowStripeAccount, shadowPriceId\nLive invoices remain unaffected"
      },
      {
        "title": "Pricing Simulator",
        "body": "import { InvoiceSimulator } from '@stripemeter/pricing-lib';\n\nconst simulator = new InvoiceSimulator();\n\nconst result = simulator.simulate({\n  customerId: 'test',\n  periodStart: '2024-01-01',\n  periodEnd: '2024-02-01',\n  usageItems: [{\n    metric: 'api_calls',\n    quantity: 25000,\n    priceConfig: {\n      model: 'tiered',\n      currency: 'USD',\n      tiers: [\n        { upTo: 10000, unitPrice: 0.01 },\n        { upTo: 50000, unitPrice: 0.008 },\n        { upTo: null, unitPrice: 0.005 }\n      ]\n    }\n  }]\n});"
      },
      {
        "title": "Project Structure",
        "body": "stripemeter/\n├── packages/\n│   ├── core/           # Shared types, schemas\n│   ├── database/       # Drizzle ORM + Redis\n│   ├── pricing-lib/    # Pricing calculator\n│   ├── stripe-driver/  # Direct Stripe API driver\n│   ├── sdk-node/       # Node.js SDK\n│   └── sdk-python/     # Python SDK\n├── apps/\n│   ├── api/            # REST API (Fastify)\n│   ├── workers/        # Background workers (BullMQ)\n│   ├── admin-ui/       # Admin dashboard\n│   └── customer-widget/# Embeddable widget"
      },
      {
        "title": "Environment Variables",
        "body": "STRIPE_SECRET_KEY=sk_live_xxx       # Live Stripe key\nSTRIPE_TEST_SECRET_KEY=sk_test_xxx  # Test Stripe key (for shadow mode)\nDATABASE_URL=postgres://...         # PostgreSQL connection\nREDIS_URL=redis://...               # Redis connection"
      },
      {
        "title": "Verify Idempotency",
        "body": "# Send same event twice - counts once\nTENANT_ID=$(uuidgen) bash examples/api-calls/send.sh\ncurl http://localhost:3000/metrics | grep ingest"
      },
      {
        "title": "Run Reconciliation",
        "body": "Check drift between local totals and Stripe:\n\nDifferences beyond 0.5% epsilon trigger investigation\nSee RECONCILIATION.md for runbook"
      },
      {
        "title": "Replay Late Events",
        "body": "curl -X POST http://localhost:3000/v1/replay \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"tenantId\": \"X\", \"dryRun\": true}'"
      },
      {
        "title": "Additional Resources",
        "body": "API Documentation\nSimulator Guide\nReconciliation Runbook\nAlert Configuration"
      }
    ],
    "body": "StripeMeter\n\nStripeMeter is a Stripe-native usage metering system that ensures correct usage totals for usage-based billing. It dedupes retries, handles late events with watermarks, keeps running counters, and pushes only deltas to Stripe.\n\nQuick Start\ngit clone https://github.com/geminimir/stripemeter && cd stripemeter\ncp .env.example .env && docker compose up -d && pnpm -r build\npnpm db:migrate && pnpm dev\n\nCore Concepts\nEvents (Immutable Ledger)\n\nEvery usage event stored with deterministic idempotency key. Events are never deleted or modified.\n\nCounters (Materialized Aggregations)\n\nPre-computed aggregations (sum/max/last) by tenant, metric, customer, and period. Updated in near-real-time.\n\nWatermarks (Late Event Handling)\n\nEach counter maintains a watermark timestamp. Events within lateness window (default 48h) trigger re-aggregation.\n\nDelta Push (Stripe Synchronization)\n\nTracks pushed_total per subscription item and only sends delta to Stripe.\n\nAPI Endpoints\nIngest Events\ncurl -X POST http://localhost:3000/v1/events/ingest \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"events\": [{\n      \"tenantId\": \"your-tenant-id\",\n      \"metric\": \"api_calls\",\n      \"customerRef\": \"cus_ABC123\",\n      \"quantity\": 100,\n      \"ts\": \"2025-01-16T14:30:00Z\"\n    }]\n  }'\n\nGet Cost Projection\ncurl -X POST http://localhost:3000/v1/usage/projection \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"tenantId\": \"your-tenant-id\", \"customerRef\": \"cus_ABC123\"}'\n\nHealth & Metrics\nReadiness: GET /health/ready\nMetrics: GET /metrics\nEvents: GET /v1/events?tenantId=X&limit=10\nNode.js SDK\nimport { createClient } from '@stripemeter/sdk-node';\n\nconst client = createClient({\n  apiUrl: 'http://localhost:3000',\n  tenantId: 'your-tenant-id',\n  customerId: 'cus_ABC123'\n});\n\n// Track usage\nawait client.track({\n  metric: 'api_calls',\n  customerRef: 'cus_ABC123',\n  quantity: 100,\n  meta: { endpoint: '/v1/search' }\n});\n\n// Get live usage\nconst usage = await client.getUsage('cus_ABC123');\nconst projection = await client.getProjection('cus_ABC123');\n\nPython SDK\nfrom stripemeter import StripeMeterClient\n\nclient = StripeMeterClient(\n    api_url=\"http://localhost:3000\",\n    tenant_id=\"your-tenant-id\",\n    customer_id=\"cus_ABC123\"\n)\n\nclient.track(\n    metric=\"api_calls\",\n    customer_ref=\"cus_ABC123\",\n    quantity=100\n)\n\nStripe Billing Driver\n\nFor direct Stripe integration, use the stripe-driver package:\n\nimport { StripeBillingDriverImpl } from '@stripemeter/stripe-driver';\n\nconst driver = new StripeBillingDriverImpl({\n  liveKey: process.env.STRIPE_SECRET_KEY,\n  testKey: process.env.STRIPE_TEST_SECRET_KEY\n});\n\n// Record usage to Stripe\nawait driver.recordUsage({\n  mode: 'live',\n  stripeAccount: 'default',\n  subscriptionItemId: 'si_xxx',\n  quantity: 100,\n  periodStart: '2025-01-01',\n  idempotencyKey: 'unique-key'\n});\n\n// Get usage summary\nconst summary = await driver.getUsageSummary(\n  'si_xxx',\n  '2025-01-01',\n  'default'\n);\n\nShadow Mode\n\nTest Stripe usage posting without affecting live invoices:\n\nSet STRIPE_TEST_SECRET_KEY in environment\nMark price mapping with shadow=true\nProvide shadowStripeAccount, shadowPriceId\nLive invoices remain unaffected\nPricing Simulator\nimport { InvoiceSimulator } from '@stripemeter/pricing-lib';\n\nconst simulator = new InvoiceSimulator();\n\nconst result = simulator.simulate({\n  customerId: 'test',\n  periodStart: '2024-01-01',\n  periodEnd: '2024-02-01',\n  usageItems: [{\n    metric: 'api_calls',\n    quantity: 25000,\n    priceConfig: {\n      model: 'tiered',\n      currency: 'USD',\n      tiers: [\n        { upTo: 10000, unitPrice: 0.01 },\n        { upTo: 50000, unitPrice: 0.008 },\n        { upTo: null, unitPrice: 0.005 }\n      ]\n    }\n  }]\n});\n\nProject Structure\nstripemeter/\n├── packages/\n│   ├── core/           # Shared types, schemas\n│   ├── database/       # Drizzle ORM + Redis\n│   ├── pricing-lib/    # Pricing calculator\n│   ├── stripe-driver/  # Direct Stripe API driver\n│   ├── sdk-node/       # Node.js SDK\n│   └── sdk-python/     # Python SDK\n├── apps/\n│   ├── api/            # REST API (Fastify)\n│   ├── workers/        # Background workers (BullMQ)\n│   ├── admin-ui/       # Admin dashboard\n│   └── customer-widget/# Embeddable widget\n\nEnvironment Variables\nSTRIPE_SECRET_KEY=sk_live_xxx       # Live Stripe key\nSTRIPE_TEST_SECRET_KEY=sk_test_xxx  # Test Stripe key (for shadow mode)\nDATABASE_URL=postgres://...         # PostgreSQL connection\nREDIS_URL=redis://...               # Redis connection\n\nCommon Tasks\nVerify Idempotency\n# Send same event twice - counts once\nTENANT_ID=$(uuidgen) bash examples/api-calls/send.sh\ncurl http://localhost:3000/metrics | grep ingest\n\nRun Reconciliation\n\nCheck drift between local totals and Stripe:\n\nDifferences beyond 0.5% epsilon trigger investigation\nSee RECONCILIATION.md for runbook\nReplay Late Events\ncurl -X POST http://localhost:3000/v1/replay \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"tenantId\": \"X\", \"dryRun\": true}'\n\nAdditional Resources\nAPI Documentation\nSimulator Guide\nReconciliation Runbook\nAlert Configuration"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/geminimir/stripemeter",
    "publisherUrl": "https://clawhub.ai/geminimir/stripemeter",
    "owner": "geminimir",
    "version": "0.1.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/stripemeter",
    "downloadUrl": "https://openagent3.xyz/downloads/stripemeter",
    "agentUrl": "https://openagent3.xyz/skills/stripemeter/agent",
    "manifestUrl": "https://openagent3.xyz/skills/stripemeter/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/stripemeter/agent.md"
  }
}