Skip to content

Add subscription product admin - #21

Closed
zacjones93 wants to merge 1 commit into
zac/team-purchasingfrom
codex/admin-subscriptions
Closed

Add subscription product admin#21
zacjones93 wants to merge 1 commit into
zac/team-purchasingfrom
codex/admin-subscriptions

Conversation

@zacjones93

@zacjones93 zacjones93 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add an administrator-only subscription product management surface to the builder
  • create and update CourseBuilder and Stripe product/price mappings together
  • restrict commerce writes to local Docker and Stripe test mode while keeping beta read-only and production blocked
  • add contract coverage and document the Phase 0 workflow

Stack

Verification

  • pnpm check

Note

Add subscription product admin at /admin/subscriptions with Stripe sync

  • New admin page and client UI to create and update membership subscription products, syncing records across CourseBuilder and Stripe (product, price, active status).
  • Server actions createSubscriptionProductAction and updateSubscriptionProductAction parse form data via Zod schema, call the workflows in subscription-products.ts, and return structured success/error states.
  • getSubscriptionProductAdminData assembles the admin list with sync status, interval, price, currency, and write capability per product.
  • DB layer now uses createDatabase() factory in index.ts: picks a shared mysql2 pool for local Docker and Planetscale serverless otherwise. closeBuilderDatabase added for graceful shutdown.
  • Writes are hard-blocked outside local Docker + Stripe test mode via assertBuilderCommerceWritesAllowed and assertStripeTestMode.
  • Behavioral Change: DB backend now switches dynamically based on DATABASE_URL; updateSubscriptionProduct rolls back active state on adapter failure.
📊 Macroscope summarized 95722f1. 11 files reviewed, 4 issues evaluated, 0 issues filtered, 3 comments posted

🗂️ Filtered Issues

@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beta-egghead Ready Ready Preview Aug 20, 2026 2:41pm
course-builder-egghead Error Error Aug 20, 2026 2:41pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 49063211-b1e5-4158-a3b2-f373492fd450

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

</div>
<div className="grid gap-4 sm:grid-cols-3">
<div className="grid gap-2">
<FieldLabel htmlFor={`${idPrefix}-price`}>Price (USD)</FieldLabel>

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.

🟠 High subscriptions/subscription-admin-client.tsx:90

Editing an existing non-USD product labels its price as USD, so an administrator can enter the wrong amount for products such as EUR subscriptions. The label is hard-coded even though product.currency contains the Stripe currency preserved by updates; render that currency instead.

Suggested change
<FieldLabel htmlFor={`${idPrefix}-price`}>Price (USD)</FieldLabel>
<FieldLabel htmlFor={`${idPrefix}-price`}>Price ({product?.currency?.toUpperCase() ?? 'USD'})</FieldLabel>
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/builder-egghead/src/app/admin/subscriptions/subscription-admin-client.tsx around line 90:

Editing an existing non-USD product labels its price as USD, so an administrator can enter the wrong amount for products such as EUR subscriptions. The label is hard-coded even though `product.currency` contains the Stripe currency preserved by updates; render that currency instead.

}
throw error
}
if (!updatedProduct?.price) {

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.

🟠 High lib/subscription-products.ts:363

An unsuccessful update after line 332 leaves a previously inactive product active in both the local database and Stripe. The rollback only catches errors from courseBuilderAdapter.updateProduct, so failures from the missing-price check, getProductCommerceRecords, the price update, or final setSubscriptionProductActive skip restoration; wrap the entire post-activation workflow in one rollback path.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/builder-egghead/src/lib/subscription-products.ts around line 363:

An unsuccessful update after line 332 leaves a previously inactive product active in both the local database and Stripe. The rollback only catches errors from `courseBuilderAdapter.updateProduct`, so failures from the missing-`price` check, `getProductCommerceRecords`, the price update, or final `setSubscriptionProductActive` skip restoration; wrap the entire post-activation workflow in one rollback path.

const paymentsAdapter = stripeProvider.options.paymentsAdapter
const status = input.active ? 1 : 0

await paymentsAdapter.updatePrice(input.records.merchantPrice.identifier, {

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.

🟠 High lib/subscription-products.ts:124

prepareProductForAdapterUpdate can leave an inactive product partially activated when any later setSubscriptionProductActive write fails: updatePrice may succeed before updateProduct or a database update throws, leaving Stripe and local statuses inconsistent. Because this call runs before updateSubscriptionProduct's try block, the caller cannot restore the previous state; add compensation for partial Stripe/database writes and include the preparation step in the failure-handling path.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/builder-egghead/src/lib/subscription-products.ts around line 124:

`prepareProductForAdapterUpdate` can leave an inactive product partially activated when any later `setSubscriptionProductActive` write fails: `updatePrice` may succeed before `updateProduct` or a database update throws, leaving Stripe and local statuses inconsistent. Because this call runs before `updateSubscriptionProduct`'s `try` block, the caller cannot restore the previous state; add compensation for partial Stripe/database writes and include the preparation step in the failure-handling path.

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.

1 participant