Skip to content

feat(web): GitHub Marketplace plan-change webhook + admin panel - #162

Merged
ralyodio merged 1 commit into
masterfrom
worktree-tc-marketplace-webhook
Aug 21, 2026
Merged

feat(web): GitHub Marketplace plan-change webhook + admin panel#162
ralyodio merged 1 commit into
masterfrom
worktree-tc-marketplace-webhook

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The Marketplace listing can now post marketplace_purchase events to /api/webhooks/github/marketplace, and /admin shows what arrived.

Signature: X-Hub-Signature-256 over the RAW body, compared with timingSafeEqual. A missing secret returns 503 rather than 200, so an unconfigured deploy shows red in GitHub's delivery log instead of silently dropping purchases.

Two tables. github_marketplace_purchases is the current subscription per GitHub account; github_marketplace_events is every raw delivery. GitHub does not resend failed deliveries, so the raw log is the only way to replay. RLS on with no policies: service role only.

Two ordering details worth keeping:

  • pending_change carries the FUTURE plan at the top level and the current one in previous_marketplace_purchase. Reading the wrong one downgrades a customer the moment they schedule a downgrade instead of at the end of their billing cycle.
  • an event whose effective_date predates what we already applied is recorded and skipped, not applied.

JSON is the configured content type; the urlencoded form is accepted too so a mis-set content type degrades to working rather than to a silent parse failure.

32 tests. The migration still has to be applied by hand.

The Marketplace listing can now post marketplace_purchase events to
/api/webhooks/github/marketplace, and /admin shows what arrived.

Signature: X-Hub-Signature-256 over the RAW body, compared with
timingSafeEqual. A missing secret returns 503 rather than 200, so an
unconfigured deploy shows red in GitHub's delivery log instead of
silently dropping purchases.

Two tables. github_marketplace_purchases is the current subscription per
GitHub account; github_marketplace_events is every raw delivery. GitHub
does not resend failed deliveries, so the raw log is the only way to
replay. RLS on with no policies: service role only.

Two ordering details worth keeping:

- pending_change carries the FUTURE plan at the top level and the current
  one in previous_marketplace_purchase. Reading the wrong one downgrades
  a customer the moment they schedule a downgrade instead of at the end
  of their billing cycle.
- an event whose effective_date predates what we already applied is
  recorded and skipped, not applied.

JSON is the configured content type; the urlencoded form is accepted too
so a mis-set content type degrades to working rather than to a silent
parse failure.

32 tests. The migration still has to be applied by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ralyodio
ralyodio merged commit 69e7ce7 into master Aug 21, 2026
7 of 8 checks passed
@ralyodio
ralyodio deleted the worktree-tc-marketplace-webhook branch August 21, 2026 18:12
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

11 finding(s)

HIGH/CRITICAL: 1 | MEDIUM: 5 | LOW: 5

Severity Rule Location
HIGH secret-aws-access-key prd/0003-detect-hardcoded-secrets-before-they-are-committed-or-served.md:126
MEDIUM js-open-redirect apps/web/src/app/auth/login/page.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/hire/page.tsx:96
MEDIUM js-unescaped-html-sink apps/web/src/app/hire/page.tsx:100
MEDIUM js-open-redirect apps/web/src/components/funding/FundingClient.tsx:97
MEDIUM js-uninitialized-buffer packages/scan/src/node-rules.ts:456
LOW secret-generic-credential PRD.md:268
LOW tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:121
LOW tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:122
LOW sh-remote-script-execution scripts/smoke-test.sh:47
LOW secret-aws-access-key scripts/smoke-test.sh:112

Snippets are redacted; ThreatCrush never prints matched credential material.


const action = payload.action;
if (!isMarketplaceAction(action)) {
console.warn(`[gh marketplace] unknown action: ${logSafe(action)}`);

if (existing && isStaleEvent(existing.effective_date as string | null, row.effective_date)) {
console.warn(
`[gh marketplace] ignoring out-of-order ${logSafe(action)} for ${logSafe(row.github_account_login)}`,
await finish(true);

console.log(
`[gh marketplace] ${logSafe(action)} ${logSafe(row.github_account_login)} -> ${logSafe(row.plan_name)}`,
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