[codex] add credit top-up checkout#118
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b6382520d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const record = await getUserCreditRecord(userId) | ||
| const plan = record?.plan ?? await getUserPlan(userId) | ||
| const total = getPlanCreditCap(plan) | ||
| const total = getPlanCreditCap(plan) + Math.max(0, record?.purchasedCredits || 0) |
There was a problem hiding this comment.
Consume top-up credits instead of adding them to every reset window
For users who buy a one-time credit top-up, this adds purchased_credits_balance to the monthly plan cap, but usage is still measured only within the current reset window and incrementUserCredits never decrements the purchased balance. After the 30-day reset, used returns to 0 while the purchased balance remains, so a $1 top-up permanently increases every future monthly allowance rather than being consumed once.
Useful? React with 👍 / 👎.
| const topupCredits = Number(customData?.topup_credits || 0) | ||
| const topupType = customData?.topup_type | ||
| if (topupType === 'credit_topup' && topupCredits > 0) { | ||
| const applied = await addPurchasedCredits(userId, topupCredits) |
There was a problem hiding this comment.
Handle Lemon Squeezy order_created for credit top-ups
This top-up fulfillment code is only reached from the surrounding switch's order_completed case, but Lemon Squeezy emits successful single-payment checkouts as order_created; for the new credit-pack checkout that means the webhook falls through as unhandled and no purchased credits are applied after payment.
Useful? React with 👍 / 👎.
| const topupCredits = Number(customData?.topup_credits || 0) | ||
| const topupType = customData?.topup_type | ||
| if (topupType === 'credit_topup' && topupCredits > 0) { | ||
| const applied = await addPurchasedCredits(userId, topupCredits) |
There was a problem hiding this comment.
Make credit top-up webhooks idempotent
When Lemon Squeezy retries or a store admin re-sends the same paid top-up webhook, this additive path runs again with no check against the order id or order number, so a single payment can increase purchased_credits_balance multiple times. Store and check a processed order identifier before calling the balance increment.
Useful? React with 👍 / 👎.
This change finishes the credit top-up flow by wiring the new Lemon Squeezy top-up variant into the product-facing billing surfaces.
Users could already see credit limits and some top-up UI, but the checkout path was still modeled around the older variable-price flow. That made the add-credits action incomplete and left the top-up experience inconsistent with the configured Lemon Squeezy product.
The fix does three things:
LEMON_SQUEEZY_CREDIT_TOPUP_VARIANT_IDenv entry to the example config and Cloudflare env typings.The webhook side already handled top-up metadata and credit application, so no backend accounting change was needed beyond widening the Lemon Squeezy custom-data types for TypeScript safety.
Validation:
There are still unrelated pre-existing type errors elsewhere in
app/api/blockchain-lab/*, but they are not part of this credit-topup change.