Stripe marketplace hold
@slashclone/stripe-marketplace-hold
Build a marketplace on Stripe Connect where your platform is the seller and holds each creator's share before paying it out, like eBay: Accounts v2 recipients, subscriptions and one-time sales, thin events, a release job, and refunds that reverse payouts. Writes the code and tests for you to review; never touches live keys.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
How this works
- It is a skill: written instructions Claude reads when your request matches what the skill is for. You do not call it by name; just ask for the task.
- While it works, it reads and changes files in the places it lists, nowhere else.
- Nothing runs until you choose to use it, and you can remove it any time.
SKILL.md
A Stripe Connect marketplace with an eBay-style hold
Goal: buyers pay your platform, your platform keeps its fee, and each creator's share waits in a hold (for example 14 days) before it is transferred to them. Refunds and chargebacks inside the hold come out of money you still have.
Decide first (ask the person, do not guess)
- Who is the seller? This skill assumes the platform is (separate charges and transfers). If creators must be the seller, use direct charges instead and skip the hold; tell them the tax and liability trade-off and stop.
- What is sold: a monthly subscription to a creator, one-time items, tips, or all three. Who sets prices, and the platform's percentage.
- Hold length: a number of days, kept in one setting (an environment variable with a default), between 0 and 90.
- Test mode only until the business, terms, and tax are settled. Add a second flag that must be set before a live key does anything.
Accounts (Stripe Accounts v2)
New Connect platforms cannot create Accounts v1 accounts. Use POST /v2/core/accounts with a pinned Stripe-Version header and a JSON body:
configuration.recipient.capabilities.stripe_balance.stripe_transfers.requested: true(the creator receives transfers and is not the seller);identity.country(required; ask the creator, do not assume);dashboard: "express", which requiresdefaults.responsibilities.fees_collectorandlosses_collectorboth"application". That makes the platform liable for negative balances; the hold is what protects you.
Onboard with POST /v2/core/account_links (use_case.type: "account_onboarding", refresh and return URLs). Do not send configurations in account_onboarding; current versions reject it. A creator is ready when stripe_transfers.status is active; read it fresh with GET /v2/core/accounts/{id}?include=configuration.recipient.
Events
- Account changes arrive as thin events (
v2.core.account.updated,...[configuration.recipient].capability_status_updated,...[requirements].updated) on a destination scoped to "Your account" with the thin payload style. They carry ids only: fetch the account. Also re-read it when a creator returns from onboarding, before any event arrives. - Payments arrive as snapshot events:
checkout.session.completed,invoice.paid(each subscription month),customer.subscription.updatedand.deleted,charge.refunded,charge.dispute.createdand.closed. - Verify the
Stripe-Signatureheader (HMAC-SHA256 oftimestamp.body, 5-minute tolerance) against each destination's secret, and apply each event once by its id. - On newer API versions an invoice may not name its payment intent, and a subscription event may omit the period end. Read them from the API when missing.
Money flow
- Checkout: charge the full amount on the platform. Do not set
transfer_dataor an application fee. Put your record id inmetadata(andsubscription_data.metadatafor subscriptions) and as thetransfer_group. - On payment: record the amount, your fee, and
release_at = paid_at + hold days. Open access only on the webhook, never on the redirect back. - A daily job sends each creator's share once
release_athas passed:POST /v1/transferswithdestination,source_transaction(the charge, when you know it), and an idempotency key per payment, so a retried job never pays twice. Skip creators who are not ready or whom staff froze. - Refund or dispute: mark the payment, close access, and if the share was already transferred, reverse it (
POST /v1/transfers/{id}/reversals). A won dispute restores access and lets the job pay again.
Steps
- Read the existing billing code and data model. List what you will add before writing anything.
- Add tables for creator accounts, purchases (with the version bought, if items are versioned), subscriptions, subscription payments, and tips. Every payment row gets
release_at, a transfer id, and a transferred-at time. - Write the Stripe calls behind one small interface so tests use a fake, and keep the charge shape in one function.
- Write the webhook handling, the release job, and staff refund and freeze actions.
- Write tests against a fake Stripe: the fee split, access only after payment, the hold, one transfer per payment, reversal after a refund, and a frozen creator staying unpaid.
- Document the setup steps for the dashboard: Connect business model, two webhook destinations, which events, and which secrets go where. List secret names only.
Rules
- Never print, log, or ask for a key or webhook secret. Tell the person which environment variable to set.
- Never store card, bank, or identity details. Keep Stripe ids, amounts, and statuses only.
- A live key alone must not enable payments.
- Buyers accept your terms at checkout; record which version of the terms.
README.md
stripe-marketplace-hold
A house kit harvested from building SlashClone. See SKILL.md for what it does and how Claude uses it.
Files
- README.md
- SKILL.md
- kit.json
Did it work?
0worked for them0did not work
Sign in to say whether it worked for you.
Nobody has said yet. Tried it? Tell the next person.

