# Payment collection

Accept one-time payments from respondents via Stripe.

## Payment collection

Collect one-time payments inside your form. Connect Stripe, insert a Payment field, set a price, and respondents pay through Stripe Checkout.

<h2 id="connect-stripe">Connect Stripe</h2>

<p>
  A Stripe connection belongs to one form: connect it from the form you want to collect money on, and repeat for the next form. Card details
  never pass through formbase.
</p>

<p>The badge on the Payment block tells you where the connection stands:</p>

> ⚠️ **Publishing is blocked until payments are complete**
> <p>
>     A form with a Payment field will not publish until every payment block has a supported currency and an amount at or above that
>     currency's minimum, and the form has a connected Stripe account with charges enabled.
>   </p>

<h2 id="how-it-works">How the Stripe connection works</h2>

<p>
  formbase uses{' '}
  <a href="https://docs.stripe.com/connect" target="_blank" rel="noopener">
    Stripe Connect
  </a>{' '}
  to link your Stripe account to your form. The charge is created on your account, not ours. In practice:
</p>

<p>
  formbase can see your Stripe account status (whether charges are enabled and onboarding is done) and basic transaction info (payment
  status, amount, receipt link). It cannot read your Stripe balance, your customer list, or anything outside payments made through your
  forms.
</p>

<h2 id="set-the-price">Set the price</h2>

<p>
  Pick a currency and type an amount on the Payment block. The amount must be at least Stripe's minimum charge for that currency — $0.50 for
  USD and EUR, £0.30 for GBP, ¥50 for JPY, 3 kr for SEK and NOK. Stripe caps a single charge just under 1,000,000, so keep amounts below
  that.
</p>

<p>Supported currencies: USD, EUR, GBP, JPY, CAD, AUD, CHF, SEK, NOK, DKK, NZD, SGD, HKD, AED, TRY.</p>

<p>The Payment field takes one-time charges only. Recurring subscriptions are not part of it.</p>

<h2 id="dynamic-pricing">Dynamic pricing</h2>

<p>
  You can vary the amount from the respondent's answers. Add a <strong>Set payment amount</strong> action to a logic rule above your Payment
  field and point it at the block.
</p>

<p>
  Each action picks how it changes the price — <strong>set to</strong>, <strong>increase by</strong>, or <strong>decrease by</strong>:
</p>
<ul>
  <li>
    <strong>Set to</strong> — replace the running price with an absolute amount.
  </li>
  <li>
    <strong>Increase by</strong> / <strong>decrease by</strong> — adjust the running price. Stack several for add-ons, surcharges, or
    discounts; each builds on the amount so far, and the result never drops below zero.
  </li>
</ul>
<p>The value itself can be:</p>
<ul>
  <li>
    <strong>A fixed number</strong> — a literal amount.
  </li>
  <li>
    <strong>A field reference</strong> — pick a <a href="/building-forms/calculated-fields">calculated field</a> so the amount follows the
    answers.
  </li>
</ul>

<p>
  Rules run in the order they appear in the form, starting from the price on the block. A later <strong>set to</strong> overwrites whatever
  earlier rules produced, so put your most specific rule last.
</p>

<p>
  Example: a ticket form has a Dropdown ("Standard" or "VIP") and a Number field for quantity. A calculated field multiplies the base price
  by quantity. A logic rule targets the Payment field: if the Dropdown is "VIP", <strong>set</strong> the payment amount to the calculated
  field.
</p>

<p>
  Add-on example: a $50 price on the block, a "Rush delivery" switch that <strong>increases</strong> the amount by 15, and a "Member" switch
  that <strong>decreases</strong> it by 5. With both on, the respondent pays $60.
</p>

> 💡 **The block price is the fallback**
> <p>
>     The price you set on the Payment block is what respondents see before any rule fires. Make it your most common price so the form never
>     shows a placeholder amount.
>   </p>

<h2 id="respondent-experience">Respondent experience</h2>

<p>
  The respondent clicks <strong>Complete Payment</strong> on the card and Stripe Checkout opens, hosted by Stripe. After paying they return
  to the form to finish and submit. The amount charged is recomputed on the server from your own rules — a tampered client cannot change the
  price.
</p>

<p>If the payment fails (expired card, insufficient funds), the card shows the error and a Retry payment button that reopens Checkout.</p>

<h2 id="after-payment">After payment</h2>

<p>
  Once a respondent has paid, formbase locks the questions that fed the amount — the conditions and values of any rule that set it, the
  fields behind a referenced calculated field, and anything that controls whether the Payment block is visible. Locked questions show their
  answers read-only with "Locked because this answer was used for a completed payment." The rest of the form stays editable.
</p>

> ⚠️ **A payment makes the submission final**
> <p>
>     Any form version containing a Payment, Signature, or Schedule appointment field turns off{' '}
>     <a href="/submissions-analytics/edit-after-submit">edit after submit</a> for submissions made against it.
>   </p>

<h2 id="viewing-payments">Viewing payments</h2>

<p>Payments show up with the response in the Submissions tab:</p>

<p>
  The payment also goes out with the submission: notification emails, the PDF, exports, webhooks, Zapier, n8n, Make, request callbacks and
  the API all carry its amount, currency, status and receipt link under the Payment question. See{' '}
  <a href="/developers/webhooks-reference#bookings-and-payments">Bookings and payments</a> for the shape.
</p>

<h2 id="refunds-and-disputes">Refunds and disputes</h2>

<p>
  Payments land in your Stripe account, so you handle refunds and disputes yourself. formbase reserves the right to issue refunds on your
  behalf in cases of abuse or policy violations. When Stripe reports a refund or a dispute, the payment on the submission changes to{' '}
  <strong>Refunded</strong>, <strong>Partially refunded</strong> or <strong>Disputed</strong>; integrations are not sent again.
</p>

<p>Stripe's own documentation covers both in depth:</p>
<ul>
  <li>
    <a href="https://docs.stripe.com/refunds" target="_blank" rel="noopener">
      Refunds
    </a>
  </li>
  <li>
    <a href="https://docs.stripe.com/disputes" target="_blank" rel="noopener">
      Disputes
    </a>
  </li>
</ul>

<h2 id="logic-conditions">Payment logic conditions</h2>

<p>
  A Payment field can also be read by logic: <strong>amount equals</strong>, <strong>amount is greater than</strong>,{' '}
  <strong>amount is less than</strong>, <strong>is completed</strong>, and <strong>is not completed</strong>. See{' '}
  <a href="/building-forms/conditional-logic">Conditional logic</a> for the full operator reference.
</p>

<h2 id="next-steps">Next steps</h2>
<div class="not-prose grid gap-3 sm:grid-cols-2">
  - [Bot protection](/building-forms/captcha-bot-protection) — Block fraudulent submissions on payment forms
  - [Submission inbox](/submissions-analytics/submission-inbox) — View payment details alongside responses
  - [Share links](/sharing-publishing/sharing-embedding) — Create and manage share link URLs
  - [Calculated fields](/building-forms/calculated-fields) — Compute dynamic prices from respondent answers
</div>
