Skip to content

feat(cost_ingestion): scope settlement webhooks by merchant - #349

Open
AnkitKmrGupta wants to merge 4 commits into
mainfrom
merchant-scoped-settlement-webhook
Open

feat(cost_ingestion): scope settlement webhooks by merchant#349
AnkitKmrGupta wants to merge 4 commits into
mainfrom
merchant-scoped-settlement-webhook

Conversation

@AnkitKmrGupta

@AnkitKmrGupta AnkitKmrGupta commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

This pull request introduces merchant scoping for cost ingestion credentials and webhook endpoints, enabling multiple merchants to independently ingest from the same connector account without credential conflicts. The changes update both the API and the storage/indexing model to use (merchant_id, connector, account) as the primary key for credentials and ingestion deduplication, and provide improved documentation and tests for this new model.

API & Documentation Updates:

  • The settlement webhook endpoint now includes merchant_id in its path (/webhooks/settlement/:merchant_id/:connector), and the documentation explains why this is necessary for supporting shared connector accounts across merchants. It also describes how multiple merchants can independently ingest from the same connector account. [1] [2]

Credential Storage & Indexing:

  • All credential storage, lookup, and poll index logic is updated to use (merchant_id, connector, account) as the key, ensuring isolation between merchants even if they share connector accounts. This includes changes to config key construction, poll index management, and credential retrieval. [1] [2] [3] [4] [5] [6] [7] [8]

Database Migration:

  • The cost ingestion deduplication constraint is updated to be merchant-scoped, with migrations for both MySQL and PostgreSQL to use (merchant_id, connector, notification_id) as the unique key, and corresponding down migrations to revert if needed. [1] [2] [3] [4]

Testing Improvements:

  • New and updated tests ensure that credentials for shared connector accounts are isolated per merchant, and that poll index entries now include merchant_id for correct operation.

Internal Cleanup:

  • Additional cleanup in the initial PostgreSQL migration file to drop all relevant tables on down migration.

These changes collectively ensure that cost ingestion is robust to shared connector accounts and that merchants' credentials and ingestion jobs remain isolated and secure.

Screenshots

image

Copilot AI lite review requested due to automatic review settings August 11, 2026 09:54
@AnkitKmrGupta AnkitKmrGupta self-assigned this Aug 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the cost-ingestion settlement webhook and credential/deduplication model to be merchant-scoped, allowing multiple Decision Engine merchants to ingest independently even when they share the same connector account.

Changes:

  • Updated settlement webhook route to include merchant_id (/webhooks/settlement/:merchant_id/:connector) and plumbed merchant_id through verification and enqueueing.
  • Updated credential storage and poll-indexing logic to key credentials by (merchant_id, connector, account) and to pass merchant_id through poller/worker flows.
  • Updated cost-ingestion dedupe constraints (MySQL + Postgres) and documentation to match merchant-scoped idempotency.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/routes/settlement_webhook.rs Adds merchant_id to the webhook path and scopes credential lookup/signature verification accordingly.
src/app.rs Wires the new webhook route path in the Axum router.
src/cost_ingestion/creds.rs Changes credential keying to (merchant_id, connector, account) and updates poll index to carry merchant identity.
src/cost_ingestion/poller.rs Passes merchant_id through polling so jobs are enqueued per-merchant.
src/cost_ingestion/worker.rs Loads download credentials using (merchant_id, connector, account) for each job.
src/cost_ingestion/store.rs Makes enqueue idempotency merchant-scoped in the “existing job” check and documentation.
migrations/2026-08-11-000001_cost_ingestion_merchant_scoped_dedupe/up.sql Updates MySQL unique dedupe index to include merchant_id.
migrations/2026-08-11-000001_cost_ingestion_merchant_scoped_dedupe/down.sql Reverts MySQL unique dedupe index back to connector-scoped.
migrations_pg/2026-08-11-000001_cost_ingestion_merchant_scoped_dedupe/up.sql Updates Postgres unique constraint to include merchant_id.
migrations_pg/2026-08-11-000001_cost_ingestion_merchant_scoped_dedupe/down.sql Reverts Postgres unique constraint back to connector-scoped.
migrations_pg/00000000000000_diesel_postgresql_initial_setup/down.sql Expands down-migration cleanup to drop schema tables (per PR description’s “internal cleanup”).
docs/api-refs/cost-ingestion-setup.mdx Updates webhook URL + explains why merchant_id is required and how shared connector accounts work.
Suppressed comments (1)

src/cost_ingestion/creds.rs:446

  • ConnectorCredsStore::get now only looks up the merchant-scoped config key. On upgrade, any credentials previously stored under the legacy key format (e.g. cost_ingest_creds::{connector}::{account}) become unreachable, which can break existing webhooks/polling until everything is re-saved. Consider adding a backwards-compatible fallback lookup (and validating the decrypted blob’s merchant_id matches the requested merchant_id) to keep upgrades smooth.
        let name = config_name(merchant_id, connector, account);
        let stored = service_configuration::find_config_by_name(name)
            .await
            .map_err(|e| IngestError::Storage(e.to_string()))?;
        match stored.and_then(|c| c.value) {

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/cost_ingestion/creds.rs
@AnkitKmrGupta
AnkitKmrGupta force-pushed the merchant-scoped-settlement-webhook branch from 53ca7d5 to 226b715 Compare August 11, 2026 11:38
@AnkitKmrGupta
AnkitKmrGupta force-pushed the merchant-scoped-settlement-webhook branch from bded91f to e5ea141 Compare August 11, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants