Skip to content
LazyFrog

Taking payments (the user's own shop)

How to take payments in an app on LazyFrog with your own Stripe account: customers pay you directly and LazyFrog never touches the money.

  • Customers pay the user's own payment account (Stripe; or PayPal, Paddle, Lemon Squeezy): the user is the seller, and LazyFrog never holds their money. That has nothing to do with LazyFrog billing (Account › Billing is the user's LazyFrog plan): say so if they mix them up. These providers are reachable on every plan.
  • Where the business is: Stripe serves about 46 countries (the UK, the EU, the US and more), but not South Africa: there, use Paystack (owned by Stripe; Checkout-like hosted payments, its own webhooks signed with x-paystack-signature), or PayFast, Yoco or Peach. Ask where the business is registered before choosing.
  • Marketplaces (a commission on payments between users): Stripe Connect with destination charges (application_fee_amount, transfer_data.destination), Express onboarding with account.updated, bookable only once the seller's payouts_enabled is true, a payout schedule, a webhook for connected accounts' events, and refunds that reverse the fee. Who is the merchant of record, and tax, are the user's decisions: ask. Stripe Connect doesn't serve every country (South Africa: Paystack split payments, PayFast, Yoco, Peach; reachable on every plan).
  • A SaaS whose customers are the sellers (a ticketing app for promoters, a booking app for salons): each customer connects their own Stripe account, so they are the merchant and the money never passes through the user. Stripe Connect with Standard accounts and direct charges (the stripeAccount option on each call), Account Links for onboarding, sales open only once account.updated says charges_enabled, and each event's account checked against the tenant it's for. The user's own platform key is the one payments connect takes (a restricted key with Write on Connected accounts, Account links, Checkout Sessions and Refunds); payments webhook with connected_accounts: true makes the endpoint for their customers' accounts (its secret is STRIPE_CONNECT_WEBHOOK_SECRET).
  • Selling limited places (tickets, seats, classes): hold the places in the database with the Checkout session (row locks or a constraint, so two buyers never get the last one). A session's expires_at is at least 30 minutes away, so a hold lasts at least that long; release holds on checkout.session.expired and from a cron (missed events), and when a payment arrives after its places went to someone else, refund it at once and say so.
  • Subscriptions (a SaaS's plans): Checkout in subscription mode, with trial_period_days (or a trial without a card, ended by the app); handle checkout.session.completed, customer.subscription.created/updated/deleted and invoice.payment_failed, and tie each to the tenant through client_reference_id or metadata; send people to Stripe's Billing Portal to change cards or cancel; decide what a lapsed trial does (read-only, a banner) and run it from a cron. Test mode works against the live address before launch.
  • Use a hosted checkout (Stripe Checkout) so card details never touch the app; Payment Links suit a handful of products with no code. Prices come from the database, never from the browser.
  • Stripe through the payments tool: payments connect (env preview) gives the user a link to paste a restricted key in test mode (rk_test_…), and says what to tick at Stripe; the key never passes through you. payments status checks it with Stripe (test or live, restricted or full, the account's country) and warns about a live key on preview or a test key on production. When the app has its webhook route, payments webhook makes the endpoint on their Stripe account for that environment's address and keeps its signing secret as STRIPE_WEBHOOK_SECRET. For the live site, the same with env production and a live key (rk_live_…) when they're ready to sell. Until a key is set, the app says "payments aren't set up yet" instead of failing. Paystack and others: secrets request for their keys, and their dashboard for the webhook.
  • The webhook route: read the raw body, check Stripe-Signature with STRIPE_WEBHOOK_SECRET, keep the event ids so a repeat does nothing, mark orders paid only from it, and answer 2xx quickly (a sleeping site wakes for it; Stripe retries for 3 days). A preview password blocks webhooks to the preview: test without one, or on the live site. The preview and the live site each have their own endpoint and signing secret (payments webhook for each env), and rotate_preview_url changes the preview's address: run payments webhook again after it.
  • Tell them in plain words what to do at Stripe: make an account (testing needs no bank details), stay in Test mode, create the restricted key payments connect describes and paste it into the link (never into the chat), test with card 4242 4242 4242 4242, and switch to live mode to sell.
  • A shop needs terms of sale, refunds and privacy pages: placeholders the owner fills in.

For your AI: this page as Markdown · llms.txt