Skip to content

Feat/auto create products#85

Open
SteakFisher wants to merge 2 commits into
mainfrom
feat/auto-create-products
Open

Feat/auto create products#85
SteakFisher wants to merge 2 commits into
mainfrom
feat/auto-create-products

Conversation

@SteakFisher

Copy link
Copy Markdown
Member

No description provided.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR removes the manual Dodo product-ID fields from the onboarding flow and replaces them with automatic product creation at setup time, using Promise.all to parallelize webhook and product registration for both live and test environments. The currency field is simultaneously tightened to an explicit enum with an uppercase transform.

  • Auto-create products: two products.create calls (live + test) are added to the existing Promise.all alongside the webhook registrations; the resulting product IDs replace the previously user-supplied values in upsertMetadata.
  • Schema cleanup: dodoLiveProductId and dodoTestProductId are removed from onboardingSchema, and currency is narrowed from a free-form string to z.enum([\"usd\",\"eur\",\"gbp\",\"inr\",\"jpy\"]) with an automatic uppercase transform.
  • Error messaging: Sentry context and the 400-error message are updated to reflect the broader scope of the operation.

Confidence Score: 3/5

Safe to merge if the orphan-on-retry behavior is acceptable, but the failure window between Promise.all and retrieveSecret now silently pollutes Dodo with double the resources compared to before.

The onboarding handler gains product-creation calls inside an atomic-looking try/catch that is actually not atomic: Promise.all can partially succeed before retrieveSecret throws, leaving 2 webhooks and 2 products orphaned in Dodo with no cleanup path. Repeated failed attempts accumulate dangling objects. Additionally, the companion documentation in scrawndotdev/documentation still tells users to manually supply Product IDs that no longer exist in the form, making the setup guide actively wrong.

src/routes/http/api/onboarding.ts — the resource-cleanup gap between the Promise.all block and the retrieveSecret calls warrants a closer look; src/zod/internals.ts is straightforward.

Important Files Changed

Filename Overview
src/routes/http/api/onboarding.ts Refactored to auto-create Dodo products alongside webhooks via Promise.all; removed product ID inputs from the request. A partial-failure path (Promise.all succeeds but retrieveSecret throws) leaves up to 4 orphaned Dodo resources with no cleanup or de-duplication on retry.
src/zod/internals.ts Removed dodoLiveProductId and dodoTestProductId from onboardingSchema; tightened currency to a strict enum with uppercase transform via currencyMap. Changes are clean and consistent.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Onboarding
    participant DodoLive
    participant DodoTest
    participant DB

    Client->>Onboarding: POST /onboarding (apiKeys + currency + redirectUrl)
    Onboarding->>Onboarding: validate schema (no product IDs)

    par Promise.all
        Onboarding->>DodoLive: webhooks.create(live)
        Onboarding->>DodoTest: webhooks.create(test)
        Onboarding->>DodoLive: products.create(live)
        Onboarding->>DodoTest: products.create(test)
    end

    Onboarding->>DodoLive: webhooks.retrieveSecret(liveWebhook.id)
    Onboarding->>DodoTest: webhooks.retrieveSecret(testWebhook.id)

    Note over Onboarding,DodoTest: If either retrieveSecret fails here,
    Note over Onboarding,DodoTest: all 4 already-created resources are orphaned

    Onboarding->>DB: upsertMetadata(keys + auto-generated productIds + secrets)
    Onboarding-->>Client: 201 OK
Loading

Reviews (1): Last reviewed commit: "feat: auto-create Dodo products during o..." | Re-trigger Greptile

Comment on lines 99 to +104
liveSecret = (await liveClient.webhooks.retrieveSecret(liveWebhook.id))
.secret;

const testWebhook = await testClient.webhooks.create({
url: `${appUrl}/webhooks/payment/createdCheckout?mode=test`,
description: "Scrawn test payment webhook",
filter_types: ["payment.succeeded", "payment.failed"],
});
testSecret = (await testClient.webhooks.retrieveSecret(testWebhook.id))
.secret;
liveProductId = liveProduct.product_id;
testProductId = testProduct.product_id;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Orphaned Dodo resources on retrieveSecret failure

If Promise.all completes successfully (creating 2 webhooks + 2 products) but either retrieveSecret call then throws, the error handler returns a 400 with no cleanup — leaving all four resources orphaned in Dodo. A subsequent retry will create a fresh set of four resources without removing the previous ones. Previously, only webhook objects were at risk; this PR doubles the surface area by adding product creation inside the same unguarded transaction boundary.

Consider using separate try/catch scopes for the retrieveSecret calls, or deleting already-created resources in the catch block, so at least partial-failure retries don't accumulate dangling objects in Dodo.

Comment on lines +73 to +96
liveClient.products.create({
name: "Scrawn Billing",
price: {
type: "one_time_price",
currency: validated.currency,
price: 0,
pay_what_you_want: true,
purchasing_power_parity: false,
discount: 0,
},
tax_category: "saas",
}),
testClient.products.create({
name: "Scrawn Billing",
price: {
type: "one_time_price",
currency: validated.currency,
price: 0,
pay_what_you_want: true,
purchasing_power_parity: false,
discount: 0,
},
tax_category: "saas",
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Documentation in related repo is now stale

The scrawndotdev/documentation repo's dashboard-setup.mdx still walks users through manually creating Dodo products and pasting their Product IDs into the onboarding form. With this PR, product creation is automatic and the dodoLiveProductId/dodoTestProductId fields have been removed from the schema. Any user following the current documentation will look for input fields that no longer exist, making the setup guide actively misleading.

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