Simulators
Connect
Connect lets your platform create v2 accounts and act as them, so you can test onboarding, destination charges and payouts to sellers without a real connected account.
Accounts
/v2/core/accounts creates, reads, updates, lists and closes accounts. Each write sends its v2 events: v2.core.account.created, .updated or .closed; on a create, a part event such as v2.core.account[identity].updated for each of identity, defaults, merchant, recipient and customer it sets, and on an update the merchant part’s; and a capability_status_updated event for each capability status the write moves. GET /v2/core/events lists them, and a thin event destination whose enabled_events names them receives them. Their related_object.url names the part with ?include=. No webhook endpoint receives them, as they have no v1 form.
include=requirements answers {entries, summary}: an onboarded seller owes external_account until it holds a bank account and the identity checks until its activation, then reads entries: [] and summary: null, while future_requirements reads null. A stored identity keeps only each ID number’s type (us_ssn, us_ssn_last_4), never its value, in reads and in v2.core.account[identity].updated, and its address countries read in upper case.
POST /v1/accounts/{account}/external_accounts creates a bank account for a v2 account; it is create only, as GET /v1/accounts/{account}/external_accounts is not served and answers “Unrecognized request URL”. GET and POST /v1/balance_settings hold one setting per account: a connected account reads payments.payouts.status enabled and payments.debit_negative_balances false once its stripe_balance.payouts capability is active, and disabled and true before.
Acting as an account
A request whose Stripe-Account or Stripe-Context header names an account your key created acts as that account: its PaymentIntents, charges, refunds, balance, balance transactions, balance settings, payouts and events are its own. Acting as an account needs a merchant configuration, or a recipient one requesting stripe_transfers, before any parameter is checked. A header naming an account your key closed, or one it never created, answers 403 account_invalid (api_error), and the request creates nothing.
An event a request causes while acting as an account is that account’s: its v1 form carries account and context naming it, and its v2 form carries context. GET /v1/events and GET /v2/core/events list and retrieve it only when read as that account. So is every event a pending refund’s settlement or a subscription’s clock sends for an object made acting as the account. A webhook endpoint created with connect=true takes only connected accounts’ events, and renders application as a stand-in ca_ id where Stripe shows your platform’s own Connect application; every other endpoint takes only the platform’s events. test_helpers.test_clock.ready and checkout.session.expired stay the platform’s even for a test clock or Checkout Session made acting as an account (inferred).
Onboarding and capabilities
A seller the create onboards whole, with an individual identity carrying date of birth, address, ID number and terms-of-service acceptance, and a defaults.profile.business_url, reads card_payments pending until 85 seconds after its created, then active, with or without a bank. Once it holds an external account, its payouts read pending from 18 seconds and active from 85; without one they stay restricted. Each status that moves sends its capability_status_updated event, naming no request, as Stripe’s activation does. Until activation, a charge as the seller answers “Your account cannot currently make charges.” Move past it with POST /_rystic/clock {"advance_seconds": 85}, or run with RYSTIC_PACE=1 so the clock follows real time.
A card_payments capability re-requested after Stripe unfreezes it reads active at once on a seller past its activation; on one frozen before it, card_payments reads pending again, and payouts too while it holds a bank, until the seller’s own activation time.
Destination charges
A PaymentIntent with transfer_data[destination], paid at a create with confirm=true, a confirm or a capture (POST /v1/charges/{charge}/capture of its charge included), is a destination charge. It credits the platform the charge’s net as any card payment does, then transfers a share to the connected account: transfer_data[amount] when set, capped at the captured amount, or else the whole amount. The account gains the share less any application_fee_amount, so with no application fee on a whole-amount transfer the platform loses the card fee. transfer_data[amount] beside application_fee_amount answers 400 parameters_exclusive, as Stripe does. POST /v1/charges takes no Connect parameters.
The charge carries destination, transfer_group (group_ plus the PaymentIntent’s id), transfer (a tr_ id) and, with an application fee, application_fee (a fee_ id) at once, where Stripe names the last two about 6 seconds later; no route reads the transfer or the fee. The platform lists a transfer balance transaction for minus the share, its source the transfer that transfer.created announces, and with a fee an application_fee one for plus the fee, beside the charge row. The account lists a payment balance transaction whose amount is the share, its fee the application fee with one application_fee line in fee_details and its net the credit; its source is the py_ payment, which the account reads with GET /v1/charges/{id} and lists with GET /v1/charges. The py_ payment sends payment.created as the account’s, with request {id: null, idempotency_key: null}: it lists among the account’s events and reaches the platform’s Connect endpoints, never the platform’s own list. The simulator moves both balances and lists the event at the charge, where Stripe shows them seconds later.
Refunding a destination charge
Every refund create route takes reverse_transfer and refund_application_fee on a destination charge. reverse_transfer=true takes the refund’s proportion of the share back from the account: the refund’s transfer_reversal names a trr_ reversal. The platform lists a transfer_refund row, and the account a payment_refund row and a pyr_ refund of its py_ payment, whose amount_refunded grows. refund_application_fee=true also returns the refund’s proportion of the fee, as an application_fee_refund row on the platform and an adjustment row on the account. Each refund’s shares round half up on their own, and the refund that leaves the transfer fully reversed returns the rest of the fee, as Stripe’s do.
Stripe refuses a refund whose reversal rounds to 0 (“You cannot reverse a transfer for amount=0.”) and a fee refund without reverse_transfer; the simulator accepts both. Without refund_application_fee the account keeps paying the fee, so its balance can go negative, as Stripe’s does. Without reverse_transfer the platform alone bears the refund. Stripe’s reserve_transaction rows on such a refund are not modeled.
Payouts to a seller
A connected account’s payout names as destination the bank account its external account create returned, and draws on that account’s own balance (Balance and payouts).
Accounts as customers
An account with the customer configuration pays as customer_account on a PaymentIntent create, update or list, a SetupIntent update and a PaymentMethod attach, which answer it beside the cus_ id of its v1 customer. GET /v1/customers/{account id}, or that cus_ id, answers that customer, and a PaymentIntent created for the cus_ id carries customer_account. An unknown or closed account answers 400 resource_missing wherever customer_account is taken (404 on a subscription create). On those five PaymentIntent, SetupIntent and PaymentMethod operations, an account without the customer configuration answers 400, and on the four writes, customer beside customer_account answers 400.