Skip to content

How-to: entity sync and reconciliation - #12

Merged
cb-srinaths merged 1 commit into
devfrom
howto/entity-sync
Aug 31, 2026
Merged

cb-srinaths merged 1 commit into
devfrom
howto/entity-sync

Conversation

@cb-srinaths

@cb-srinaths cb-srinaths commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Added a guide for syncing and reconciling Chargebee entities, covering webhook-driven local mirrors, idempotent version-ordered updates, reconciliation and backfills, cold-start bootstrapping, catalog handling, DLQ recovery, caching, and go-live correctness checks.

@snyk-io

snyk-io Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues
✅ Secrets 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fe10fce8-80e2-4996-9f5c-0643a1fc65b7

📥 Commits

Reviewing files that changed from the base of the PR and between d985c18 and 5efc840.

📒 Files selected for processing (1)
  • how-to/syncing-and-reconciling-chargebee-entities.md

Walkthrough

Adds a guide for syncing Chargebee entities into a local mirror through durable webhooks, reconciling missed events, applying resource_version ordering, bootstrapping cold starts, and routing authoritative mutations through Chargebee.

Changes

Chargebee entity synchronization

Layer / File(s) Summary
Webhook sync and reconciliation
how-to/syncing-and-reconciling-chargebee-entities.md
Documents durable webhook ingestion, worker upserts, event deduplication, replay and backfill strategies, gap recovery, and cache invalidation.
Version ordering and cold-start bootstrap
how-to/syncing-and-reconciling-chargebee-entities.md
Defines per-resource resource_version comparisons, atomic cursor updates, cold-start sequencing, and Product Catalog 2.0 listing guidance.
Mutation ownership and launch validation
how-to/syncing-and-reconciling-chargebee-entities.md
Describes Chargebee-first mutations, split-brain prevention, compare-and-swap reconciliation, DLQ handling, and go-live checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.cursor/skills/authoring-how-to/scripts/check_how_to.py:
- Around line 344-351: Update the external-link checking flow associated with
the link collection around the regex branch and the logic at lines 374-382 to
prevent SSRF: before each request, including every redirect, validate the target
against an approved-host policy or resolve its hostname and reject loopback,
private, link-local, metadata, and other reserved addresses. Preserve reporting
for allowed external URLs while ensuring disallowed targets are never fetched.

In @.cursor/skills/authoring-how-to/SKILL.md:
- Around line 29-31: Update the validation command in SKILL.md to reference the
created authoring-how-to directory instead of author-ref-arch-how-to, while
preserving the existing check_how_to.py invocation and file argument.

In `@how-to/implementing-usage-based-billing.md`:
- Around line 28-33: Update the usage-based billing flow diagram around the raw
event enqueue and database insert to represent an atomic transactional outbox or
equivalent queue/store operation, preventing either side from succeeding
independently. Show caller acknowledgment only after the local transaction
commits, while preserving retry or buffering behavior for failed
persistence/publication.

In `@how-to/syncing-and-reconciling-chargebee-entities.md`:
- Around line 166-168: Update the “Pull runtime state” guidance to prohibit
automatic customer-to-user linking by email. Require an immutable, app-issued
identifier stored in Chargebee metadata for synchronization, and state that
email matching is allowed only through an explicit manual-reconciliation flow.
- Around line 83-84: Update the reconciliation guidance around per-resource
high-water marks to prohibit using occurred_at alone as the replay cursor.
Require an overlap window with event-ID deduplication, or a cursor that combines
occurred_at with event identity, so same-second events are not skipped.
- Around line 58-60: Update the webhook flow description to explicitly require
Chargebee HTTP Basic Auth or an equivalent endpoint-secret check before durable
enqueueing, replacing the vague “validates” wording; retain event.id and
resource_version solely for deduplication and ordering, not sender
authentication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ff5488f-1688-4849-8f5d-c8ee204e7d01

📥 Commits

Reviewing files that changed from the base of the PR and between 6b31449 and d985c18.

📒 Files selected for processing (9)
  • .cursor/skills/authoring-how-to/SKILL.md
  • .cursor/skills/authoring-how-to/assets/how-to-template.md
  • .cursor/skills/authoring-how-to/references/chargebee-sources.md
  • .cursor/skills/authoring-how-to/references/diagrams.md
  • .cursor/skills/authoring-how-to/references/language.md
  • .cursor/skills/authoring-how-to/scripts/check_how_to.py
  • .cursorignore
  • how-to/implementing-usage-based-billing.md
  • how-to/syncing-and-reconciling-chargebee-entities.md

Comment on lines +344 to +351
if re.match(r"^(https?:|mailto:)", lowered):
if not text.strip():
report.warn(line_number, f"link with empty text: {target}")
elif text.strip().lower() in ("here", "click here", "this", "link"):
report.warn(line_number, f"uninformative link text: {text!r}")
if lowered.startswith("http"):
external.append((line_number, target))
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Prevent SSRF when checking external links.

A PR can add a link such as http://127.0.0.1/... or a cloud metadata address; --external will fetch it, including redirects. Restrict targets to approved hosts, or resolve and reject loopback/private/reserved addresses on every redirect before requesting them.

As per path instructions, “Restrict feedback to errors, security risks, or functionality-breaking problems.”

Also applies to: 374-382

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/skills/authoring-how-to/scripts/check_how_to.py around lines 344 -
351, Update the external-link checking flow associated with the link collection
around the regex branch and the logic at lines 374-382 to prevent SSRF: before
each request, including every redirect, validate the target against an
approved-host policy or resolve its hostname and reject loopback, private,
link-local, metadata, and other reserved addresses. Preserve reporting for
allowed external URLs while ensuring disallowed targets are never fetched.

Sources: Path instructions, Linters/SAST tools

Comment on lines +29 to +31
```bash
python3 .cursor/skills/author-ref-arch-how-to/scripts/check_how_to.py how-to/<your-file>.md
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the validator path.

Line 30 uses author-ref-arch-how-to, but this PR creates authoring-how-to; the required validation command always fails.

- python3 .cursor/skills/author-ref-arch-how-to/scripts/check_how_to.py how-to/<your-file>.md
+ python3 .cursor/skills/authoring-how-to/scripts/check_how_to.py how-to/<your-file>.md

As per path instructions, “Restrict feedback to errors, security risks, or functionality-breaking problems.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
python3 .cursor/skills/author-ref-arch-how-to/scripts/check_how_to.py how-to/<your-file>.md
```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/skills/authoring-how-to/SKILL.md around lines 29 - 31, Update the
validation command in SKILL.md to reference the created authoring-how-to
directory instead of author-ref-arch-how-to, while preserving the existing
check_how_to.py invocation and file argument.

Source: Path instructions

Comment on lines +28 to +33
app->>queue: Enqueue raw event
app->>db: Insert raw event<br>(dedupe key unique)
alt Store failed
app->>svc: Reject or buffer<br>(caller retries)
else Stored
app->>svc: Accept request

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make queue publication and local persistence atomic.

The diagram enqueues the event before inserting the local reconciliation record. If the queue succeeds but the database write fails, Chargebee can receive a billable event that has no local dedupe/reconciliation row; if the database succeeds but queue publication fails, the event can be stranded. Use a transactional outbox or an atomic queue/store, and acknowledge the caller only after the local transaction commits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@how-to/implementing-usage-based-billing.md` around lines 28 - 33, Update the
usage-based billing flow diagram around the raw event enqueue and database
insert to represent an atomic transactional outbox or equivalent queue/store
operation, preventing either side from succeeding independently. Show caller
acknowledgment only after the local transaction commits, while preserving retry
or buffering behavior for failed persistence/publication.

Comment on lines +58 to +60
Chargebee owns every billing fact — customers, subscriptions, invoices, payments, and the product catalog. Your app keeps a **mirror**: enough relational state to authorize requests, render dashboards, and enforce entitlements without calling Chargebee on every page view. Webhooks are the steady-state sync path: the HTTP handler validates and durably enqueues, a worker applies idempotent upserts keyed on `resource_version`, and only then acknowledges the message.

Reconciliation is the safety net. A scheduled or manually triggered job compares local rows against Chargebee — either by replaying events from the [List events API](https://apidocs.chargebee.com/docs/api/events/list-events) or by listing resources directly — and repairs gaps that webhooks missed. When a local mutation must happen (sign-up provisioning, admin override), route it through the Chargebee API first; the webhook that follows carries the authoritative `resource_version` and wins any race with optimistic local writes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target doc and nearby references.
git ls-files 'how-to/syncing-and-reconciling-chargebee-entities.md' 'how-to/*chargebee*' | sed -n '1,120p'

echo '--- file excerpt ---'
nl -ba how-to/syncing-and-reconciling-chargebee-entities.md | sed -n '1,140p'

echo '--- searches ---'
rg -n "webhook|basic auth|secret|authentication|authenticate|validate" how-to docs . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' || true

Repository: chargebee/reference-architecture

Length of output: 325


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check official Chargebee docs text for webhook authentication guidance.
python3 - <<'PY'
import urllib.request, ssl, re

urls = [
    "https://apidocs.chargebee.com/docs/api/events",
    "https://apidocs.chargebee.com/docs/api/events/list-events",
]
ctx = ssl.create_default_context()
for url in urls:
    try:
        with urllib.request.urlopen(url, context=ctx, timeout=20) as r:
            data = r.read().decode('utf-8', 'ignore')
        print(f"=== {url} ===")
        for pat in [
            r'webhook.{0,120}auth',
            r'authentication.{0,120}webhook',
            r'basic auth.{0,120}webhook',
            r'authorization.{0,120}webhook',
            r'endpoint secret.{0,120}webhook',
            r'signature.{0,160}webhook',
            r'verify.{0,140}payload',
        ]:
            m = re.search(pat, data, re.I | re.S)
            if m:
                s = m.start()
                print(data[max(0,s-180):min(len(data), s+500)].replace('\n',' ')[:700])
                break
        else:
            print("No obvious auth-related match in fetched HTML.")
    except Exception as e:
        print(f"=== {url} ===")
        print("ERROR:", e)
PY

Repository: chargebee/reference-architecture

Length of output: 574


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for Chargebee-specific webhook/auth docs in repo text that may support or contradict the comment.
rg -n "Chargebee|webhook|basic auth|endpoint secret|signature|resource_version|event id|verify" . \
  -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' -g '!**/.git/**' || true

Repository: chargebee/reference-architecture

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target markdown section around the commented lines for exact wording and context.
nl -ba how-to/syncing-and-reconciling-chargebee-entities.md | sed -n '48,72p'

Repository: chargebee/reference-architecture

Length of output: 210


🌐 Web query:

Chargebee webhook authentication basic auth endpoint secret official docs

💡 Result:

Chargebee webhooks use HTTP Basic Authentication as their primary security mechanism rather than HMAC signing [1]. To secure your webhook endpoint, you must configure a username and password in the Chargebee webhook settings [2][3]. Configuration and Usage: 1. In the Chargebee dashboard, go to Settings > Configure Chargebee > Webhooks [3][4]. 2. Enable the option to protect your webhook URL with basic authentication [2][3]. 3. Enter your chosen username and password [2][5][6]. 4. When Chargebee sends a webhook, it will include an Authorization header containing these credentials, which your server must then validate [7]. Alternatively, you can include a secret key directly as part of your webhook URL (e.g., https://yourdomain.com/webhook?key=your_random_key), though this is considered less secure than using formal basic authentication [2][3]. Important Notes: - Chargebee does not support HMAC-based webhook verification [1]. - To ensure data integrity, it is recommended to treat webhooks as notifications and use the retrieved event ID to fetch the full event details via the Chargebee Retrieve an Event API [4]. - Always use HTTPS to protect the credentials in transit [2]. - You can manage these credentials programmatically via the Chargebee API using the create/update webhook endpoint operations, which accept basic_auth_username and basic_auth_password fields [5][6].

Citations:


Spell out the webhook auth check. Replace “validates” with an explicit Chargebee HTTP Basic Auth check (or equivalent endpoint secret) before enqueueing; event.id and resource_version only dedupe/order messages and do not authenticate the sender.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@how-to/syncing-and-reconciling-chargebee-entities.md` around lines 58 - 60,
Update the webhook flow description to explicitly require Chargebee HTTP Basic
Auth or an equivalent endpoint-secret check before durable enqueueing, replacing
the vague “validates” wording; retain event.id and resource_version solely for
deduplication and ordering, not sender authentication.

Source: Path instructions

Comment thread how-to/syncing-and-reconciling-chargebee-entities.md
Comment on lines +166 to +168
1. **Push catalog to Chargebee** (greenfield) — create item families, features, items, item prices, and entitlements via API so the site matches your product definition. Catalog objects change infrequently; many teams treat the repo as canonical and run an idempotent bootstrap script on deploy rather than mirroring catalog rows from webhooks into Postgres.
2. **Pull runtime state** — paginate all customers and subscriptions (and any other mirrored resources) from Chargebee; upsert each with its `resource_version`. Link customers to app users by email or explicit metadata you set at creation time.
3. **Seed version cursors** — populate the `resource_version` store from the API bodies so the first live webhook does not replay history you already ingested.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not automatically link customers by email.

Email is not a safe billing identity key: it can change, be unverified, or be shared. An incorrect match can attach subscriptions and entitlements to the wrong user or organization. Require an immutable app-issued ID in Chargebee metadata; restrict email matching to an explicit manual-reconciliation flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@how-to/syncing-and-reconciling-chargebee-entities.md` around lines 166 - 168,
Update the “Pull runtime state” guidance to prohibit automatic customer-to-user
linking by email. Require an immutable, app-issued identifier stored in
Chargebee metadata for synchronization, and state that email matching is allowed
only through an explicit manual-reconciliation flow.

@cb-srinaths cb-srinaths added the generated LLM generated label Jul 27, 2026
@cb-srinaths
cb-srinaths changed the base branch from main to dev August 31, 2026 10:26
@cb-srinaths
cb-srinaths marked this pull request as ready for review August 31, 2026 10:26
@cb-srinaths
cb-srinaths merged commit 942ebf3 into dev Aug 31, 2026
8 checks passed
@cb-srinaths
cb-srinaths deleted the howto/entity-sync branch August 31, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

generated LLM generated

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant