Setup
How to get Flourish Payments ready to take money in your org after installing it.
Overview
There are five pieces:
- The permission set for the people who work with payments.
- The Flourish Payments Setting record that holds your Stripe keys.
- An Experience Cloud site that hosts the public connector.
- A webhook in Stripe.
- The components on your Opportunity and Product record pages.
Your website's payment form won't reach Salesforce until the first three are in place, and payments won't reconcile onto records until the webhook is.
You need a Stripe account and access to its dashboard. Three values come from there: the publishable key, the secret key, and the webhook signing secret.
Payments needs Flourish Connect, another Flourish package, in the same org. Flourish installs it with Payments and turns on your org's license. If Payments says it needs Flourish Connect or isn't active for this organization, contact Flourish.
Flourish Payments has no tabs and no app of its own. Everything it does happens on the Opportunity and Product record pages, in Flow, or on your own website.
Permission set
One permission set ships with the package.
| Permission set | Give it to |
|---|---|
| Flourish Payments: Admin | People who read or edit payment details on records and people who send products to Stripe. |
It grants read and edit on these fields:
- Opportunity: Stripe Sync, Payment Custom Data, External Ids, Payment Model, Initial Stripe Checkout Datetime, and Subscription Opportunity.
- Contact: Stripe Customer ID.
- Product: Stripe Sync.
It also grants the access the Flourish Payments: Stripe Product component needs. The guest user of your connector site needs a separate grant. The public connector site covers it.
Flourish Payments Setting
All org-wide configuration lives in one custom metadata record.
Go to Setup > Custom Metadata Types > Flourish Payments Setting > Manage Records. The record is named global in lower case. If it's not there, click New and set both the label and the record name to global.
| Field | What it holds |
|---|---|
| Stripe Public Key | Your Stripe publishable key (pk_live_... or pk_test_...). This is the one the browser gets. |
| Stripe Secret Key | Your Stripe secret key (sk_live_... or sk_test_...). It stays in Salesforce and never reaches the browser. |
| Stripe Webhook Secret | The signing secret Stripe gives you when you create the webhook (whsec_...). The package checks every event Stripe sends against it. |
| Opportunity Record Type | Developer name of the record type for Opportunities the package creates. Leave blank for the org default. |
| Default Opportunity Stage | Stage for those Opportunities. Leave blank and the package uses the first of Pledged, Prospecting, Qualification, and New that's an open stage in your org. If none is, it uses another open stage. |
| Pricing Flow Name | API name of an Autolaunched Flow that computes the charge amount. Leave blank unless you need it. See Server-side pricing. |
| Public Connector URL | Optional and only for your own reference. Nothing reads this field. The payment form takes the connector's address from its own configuration. |
| Stripe Account ID | Leave blank. Nothing reads it. |
The package includes the Remote Site Setting for Stripe, so callouts work with no extra configuration.
Keep the secret key and the webhook secret to the people who need them. Anyone who can open this record can read both.
The public connector site
Your payment form runs on your own website. The website can't call Salesforce directly, so the form reaches it through Flourish Payments: Public Connector. That's a component you host on an Experience Cloud page. The form loads that page in a frame and talks to it. The secret key stays in Salesforce. Only the publishable key reaches the browser.
- Create an Experience Cloud site, or use one you already have. A bare LWR site is enough. Nothing on the page is meant to be seen.
- Add the Flourish Payments: Public Connector component to a page on that site.
- Open the site to guests. In Experience Builder, under Settings > General, turn on public access.
- Give the site's guest user two Apex classes:
pay_StripePublicUtilityfor the connector andpay_StripeWebhookfor the webhook. Grant them on the guest profile or on a permission set assigned to the guest user. Nothing else is needed. The connector runs in system mode with no object or field permissions of its own. - Let your website frame the site. List every host your payment form runs on, with and without
www, in three places:- In Experience Builder, under Settings > Security & Privacy, set Clickjack Protection Level to Allow framing of site pages on external domains.
- On the same page, add each host under Trusted Domains for Inline Frames.
- In Setup, under CSP Trusted Sites, add each host.
- Publish the site. Changes don't reach the served pages until you do.
The connector page's URL is what the page author sets as paymentsLwc.siteUrl in the form's configuration. See Payment widget.
The Stripe webhook
Stripe tells Salesforce what happened after the customer pays. Without the webhook, Opportunities created by a checkout stay at their opening stage even when the payment went through, and invoices and subscriptions never update.
1. Build the endpoint URL
The webhook runs through the same Experience Cloud site at its vforcesite address. Its path includes the package's toflourish namespace:
https://YOUR-DOMAIN.my.site.com/SITE-PREFIXvforcesite/services/apexrest/toflourish/payments/webhook
For a site whose URL prefix is payments, that's https://YOUR-DOMAIN.my.site.com/paymentsvforcesite/services/apexrest/toflourish/payments/webhook. Without toflourish in the path, the site answers 404.
2. Add the endpoint in Stripe
In the Stripe dashboard, go to Developers > Webhooks and click Add endpoint:
- Paste the URL.
- Choose an API version of
2025-03-31or later. - Subscribe to these twelve events:
| Event | Needed for |
|---|---|
checkout.session.completed | Every payment form and payment link |
checkout.session.async_payment_succeeded | Bank payments that clear later |
checkout.session.async_payment_failed | Bank payments that fail |
checkout.session.expired | Checkouts the customer abandoned |
charge.refunded | Refunds |
invoice.paid | Invoices and subscription renewals |
invoice.payment_failed | Invoices and subscription renewals |
invoice.voided | Invoices |
invoice.marked_uncollectible | Invoices |
customer.subscription.created | Subscriptions |
customer.subscription.updated | Subscriptions |
customer.subscription.deleted | Subscriptions |
What each one does to a record is on Payments in Salesforce.
A Stripe account you also use for other things is fine. Salesforce acknowledges and ignores events for payments it didn't start. When Salesforce can't match a refund, Stripe retries it for up to a day in case its payment is still being recorded.
3. Save the signing secret
Copy the signing secret Stripe shows for the endpoint and paste it into Stripe Webhook Secret on the global record.
Record pages
Add these in the Lightning App Builder:
| Component | Page | What it's for |
|---|---|---|
| Flourish Payments: Stripe Sync Viewer | Opportunity | The payments, invoices, and subscription on the record. See Payments in Salesforce. |
| Flourish Payments: Custom Data Viewer | Opportunity | What the form collected beyond the standard payment fields. |
| Flourish Payments: Stripe Product | Product | Sending a product to Stripe and managing its prices and payment links. See Products, invoices, and subscriptions. |
Checking it works
- Open the connector page in a browser. The page is meant to be blank. The browser console should log
FL Payments connected. If it warns "Error loading connector init" instead, the reason follows. Most often, the guest user is missing thepay_StripePublicUtilityclass. - Run a test payment with Stripe in test mode and a test card. The form should reach the Stripe payment step. If it stalls before that with a timeout message, check the connector URL, the guest grant, and the three framing settings.
- Open the Opportunity the checkout created and confirm the payment shows as Completed in the Stripe Sync Viewer. A payment still Pending a minute after paying means the webhook isn't arriving. Check its delivery attempts in the Stripe dashboard.
- Read the webhook's responses in Stripe.
- A 404 means the URL is missing
toflourishorvforcesite. - A 401 with "Invalid signature" means Stripe Webhook Secret doesn't match the endpoint.
- A 404 means the URL is missing