Skip to main content

Stripe

How to Build a Lovable App and Verify Integrations with FetchSandbox

Build a Lovable gym or bookstore, connect FetchSandbox MCP with a key from /keys, then inject webhook retries, 429s, and 401s before live keys.

2026-09-21FetchSandbox Engineering

You've shipped a Lovable app. The UI looks done. Paddle or Stripe is wired. Resend is supposed to send the invoice.

You still do not know whether a duplicate webhook grants membership twice, or whether a Resend 401 drops the receipt with no log.

This walkthrough builds a gym membership app in Lovable, connects FetchSandbox through Lovable's MCP connector, points the app at twins (API-compatible hosts, not a tap on live Paddle), and injects the failures that show up on a normal week.

Short answer

  1. Prompt Lovable to build the app on its stack (React, TypeScript, Lovable Cloud). Do not ask it for Next.js or a Python backend.
  2. Create a key at fetchsandbox.com/keys. Add MCP server https://fetchsandbox.com/mcp/v1 with Bearer token, not None.
  3. Ask the agent to boot Paddle and Resend twins, put sandbox_base_url and sandbox_api_key in secrets, and run checkout.
  4. Arm rate_limited, auth_failure, and (on Stripe) webhook_retries. Check the membership row, not the 200.

Lovable and RapidDev still stop at test cards and a green webhook delivery. That is the happy path.

Step 1: Build the sample app in Lovable

Open lovable.dev and start a project. Use a prompt that names the providers and the failures:

Build a gym membership app with Paddle subscriptions, Resend for
emailing customer invoices, and resilient handling for duplicate
webhooks, auth failures, rate limits, and payment invariants,
tested against FetchSandbox twins. Build on Lovable's stack
(React, TypeScript, Lovable Cloud) without Next.js or a Python backend.

Lovable prompt for Ironworks: gym membership on Paddle and Resend, tested against FetchSandbox twins, Lovable Cloud stack only.

That run produced Ironworks — a membership landing page with Sign in / Join now, not a Next.js app.

Ironworks preview in Lovable. Strength first. Everything else follows. Start your membership / See plans.

Bookstore variant if you want Stripe + SMS:

Build a bookstore where users browse, add to cart, and check out
with Stripe. Send an order SMS via Twilio and a receipt via Resend.
Handle duplicate webhooks, 429s, and auth failures. Lovable Cloud
stack only — no Next.js, no Python.

You need a checkout path and a place the UI reads for "member" or "order confirmed." Stubbed buttons are not enough.

Step 2: Get a FetchSandbox API key

Sign in at fetchsandbox.com/keys. Create another key. Copy the secret once. The list later only shows fsk_… prefixes.

Do not paste it into Lovable chat.

Step 3: Add the MCP connector

Open lovable.dev/dashboard?connectors+MCP server.

FieldValue
Server nameFetchSandbox
Server URLhttps://fetchsandbox.com/mcp/v1
AuthenticationBearer token or API key (not OAuth, not None)
Tokenthe key from /keys

If the form still shows a connection type, pick the HTTP / direct option. This endpoint is streamable HTTP, not SSE.

Add server. If MCP server is greyed out, a workspace admin disabled custom servers.

Chat connectors are personal. They are not in the published app. Screenshots of the keys page and the form: Test Your Lovable Integration With a Custom MCP Connector.

Step 4: Boot twins and point the app at them

In Lovable chat:

Use FetchSandbox to spin up a Paddle twin and a Resend twin.
Give me sandbox_base_url and sandbox_api_key.
Put those in secrets. Point the Paddle and Resend clients at
the twin hosts, not api.paddle.com or api.resend.com.

For the bookstore prompt, boot Stripe and Twilio twins the same way.

FetchSandbox does not sit in front of live Paddle and "capture" production traffic. The Edge Function calls a twin that speaks the same paths and error envelope. Same idea as the Cozy Closet Stripe/Resend preview checkout.

Then a sanity run:

Run a membership checkout against the Paddle twin.
Show the subscription (or transaction) id, then SELECT the
columns the Join now / member gate uses for this user.

If Paddle says paid and the gate still says visitor, stop. That join is paid without unlock — same bug on Stripe.

Resend:

Send the invoice email on the Resend twin after a successful
checkout. Show me the POST /emails status. Do not claim
delivered unless email.delivered exists on the twin.

Twilio (bookstore path):

Send the order SMS on the Twilio twin. Show the message sid
and status. queued is not delivered.

Step 5: Inject the messy week

Ask what the sandbox can actually inject, then arm named scenarios. Do not invent provider errors the twin does not have.

What failure scenarios can this Paddle sandbox inject?
Arm rate_limited and run checkout again.
Then arm auth_failure and run it again.
Prompt in Lovable chatTwin scenarioWhat you check
Duplicate membership / double invoiceStripe: webhook_retries. Paddle: ask for duplicate-notification / retryGate row and invoice count stay 1
SMS or API 429Twilio or Paddle rate_limitedApp retries or surfaces an error; it does not drop the send with a 200 UI
Email 401Resend auth_failureNo silent success; no "receipt emailed" until 2xx
Payment failedStripe payment_declinedNo member row
Slow webhookStripe slow_networkHandler still 2xx or you see the retry; no double grant
BounceResend email_bouncedYou do not treat bounce as delivered
Arm webhook_retries on the Stripe twin and check out again.
Did event.id create one order or two?
Arm Twilio rate_limited and trigger the order SMS.
Does the UI lie that it sent?

A handler 500 on a webhook is useful only if you then watch the retry with the same event id. That is webhook_retries, not a one-shot 500.

If Stripe's POST never reaches Deno, you have a JWT 401 after redeploy, not an idempotency bug.

Step 6: Fix, then run the same failure again

Membership is granted twice on a retried Paddle/Stripe notification.
Deduplicate on the stable event id before you upsert the member row.
Then arm the retry scenario again and show me the gate row.
On Resend 401, do not flip receipt emailed. Surface the error.
Arm auth_failure again.

The second run is the proof. "I asked Lovable to fix it" is not.

What this is not

  • Not live Paddle or Stripe. Swap real keys after the twin join holds.
  • Not Gmail. Twin POST /emails 200 ≠ inbox.
  • Not Stripe.js inside Lovable's preview iframe. Twins are called from Edge Functions.
  • Not an intercepting proxy. If the app still talks to api.paddle.com, the twin never sees the call.

Broader loop: integration testing for AI-built apps.

Questions about this Lovable tutorial

Do I need FetchSandbox to build the Lovable UI?

No. Lovable builds the UI. FetchSandbox is how you prove Paddle/Stripe/Resend/Twilio behavior before live keys.

Can I pick No authentication on the MCP form?

No. Use a bearer token from fetchsandbox.com/keys. None 401s.

Why Paddle in the screenshots and Stripe in other posts?

Ironworks was prompted with Paddle + Resend. Cozy Closet used Stripe + Resend. The connector and the twin host override are the same. Failure scenario names differ per spec — ask the sandbox, then arm what it lists.

Is a webhook 200 enough?

No. 200 means the function returned 200. Membership means the gate columns for auth.uid() changed once.