Flow actions
How to use the seven actions Flourish Payments adds to Flow: charging a saved card, billing with Stripe invoices and subscriptions, and reading what a payment form collected.
Overview
In Flow Builder, add an Action element and look under the Flourish Payments category.
| Action | What it's for |
|---|---|
| Charge Payment Token | Charge a saved card for recurring payments and back-office charges. |
| Charge Saved Payment Method | The same charge with the card handed to it directly. |
| Send Stripe Invoice | Bill an Opportunity with a Stripe invoice. |
| Start Stripe Subscription | Start a recurring subscription for an Opportunity's payer. |
| Cancel Stripe Subscription | End a subscription now or after the paid period. |
| Get Custom Data Value | Read one value a payment form collected. |
| Filter by Custom Data | Narrow a list of Opportunities by a value the form collected. |
Charging a saved card
When a customer ticks the box to save their card at checkout, the package keeps a token for it. The token stands in for the card. Stripe accepts it for that customer with no one at the keyboard, and both charging actions use it.
The token is stored with the rest of the payment history in Stripe Sync on the Opportunity. It looks like this:
{
"stripeCustomerId": "cus_...",
"paymentMethodToken": "pm_...",
"paymentMethodStatus": "active"
}
Charge Payment Token
Use this action for anything recurring. It reads the credential out of a field rather than taking it as an input. So the token can live in one place, and every record that needs it can point there.
| Input | Required | What it takes |
|---|---|---|
| Credential JSON Field | Yes | The field holding the JSON above. A path through a parent works, so npe03__Recurring_Donation__r.Stripe_Credentials__c reads the token off the parent rather than needing a copy on every child. |
| Amount Field | Yes | The field holding the dollar amount to charge (for example, Amount). |
| Record ID | One of these two | Charge this one record now and wait for the answer. |
| SOQL Query | One of these two | Charge every row this query returns in the background. |
| Currency | No | ISO currency code. Defaults to usd. |
| Description | No | A description sent to Stripe with the charge. |
| Chunk Size | No | How many charges run per transaction on the background path. Defaults to 50. The most is 90. |
| Output | What it tells you |
|---|---|
| Success | The single charge went through. Only meaningful on the Record ID path. |
| Payment Intent ID, Status | The result of that single charge. |
| Enqueued | The background run started. |
| Records Queued | How many rows it will charge. |
| Records Skipped | How many rows it refused to charge. |
| Error Message | Why the charge failed or was skipped. On the SOQL Query path, it lists each reason rows were skipped and how many rows each one applied to. |
The two paths behave differently.
- Record ID: the charge happens while the Flow waits, so a Screen Flow can tell the person at the desk what happened.
- SOQL Query: the action hands the work off and returns straight away. A scheduled Flow that charges a month of installments gets Enqueued, a count, and the skip reasons. It doesn't get an outcome per row, so check the records themselves for those.
Pass one query rather than looping records and calling the action per row. The query keeps a large run inside Salesforce's limits. The action charges in chunks, and each chunk is its own transaction.
Rows it won't charge. The action checks every row before any money moves, whatever your query selected, and skips it with a reason if:
- The credential isn't usable: the field is empty, the JSON has no token or no customer, or
paymentMethodStatusis anything butactive. - The record has already been paid: it holds a completed, processing, or refunded payment. This catches an installment your Flow charged but never closed.
- The card was used in the last five days: at the checkout that saved it, for any other record, or for another row earlier in the same run. This stops an installment dated on the day the customer paid at checkout from charging them twice. It also spaces out several overdue installments for one donor instead of charging them all at once.
A later run charges a skipped row once the reason clears if your query still selects it. So build scheduled queries with a lookback, such as installments due in the last seven days that aren't closed, rather than only those due today. This action can't charge the same card more often than every five days.
Charging twice in one day is guarded against. Each charge carries a key built from the record and the date, and Stripe honors it. If a background run stops partway and is retried the same day, the rows it already charged return their original result instead of charging again.
A successful charge writes a receipt and stops there. It appends the payment to Stripe Sync with the card it used and does nothing else. It doesn't set the stage to Closed Won or mark a payment record paid. What a recurring charge means for your records is yours to decide. Do that in the same Flow after the action.
The query is checked before it runs. It has to be a single SELECT with no semicolon, and it must select the credential field and the amount field you named.
Charge Saved Payment Method
This action makes the same charge with the credentials handed to it directly. Use it when the Flow already has the customer and token in variables or when the token doesn't live in a field on the record.
| Input | Required | What it takes |
|---|---|---|
| Stripe Customer ID | Yes | The cus_... customer. |
| Payment Method ID | Yes | The pm_... token saved at a previous checkout. |
| Amount | Yes | Dollars (for example, 25.00). |
| Output | What it tells you |
|---|---|
| Success | Whether the charge went through. |
| Payment Intent ID | Stripe's ID for the charge. |
| Status | What Stripe reported. |
| Error Message | The decline reason or the error when Success is false. |
This one charges and returns. It has none of Charge Payment Token's checks and writes nothing to the Opportunity. Record anything you want kept in the Flow.
Invoices and subscriptions
How these behave on your records is on Products, invoices, and subscriptions. Each action works on a list of Opportunities in one go and reports on each one. A Stripe refusal for one doesn't stop the others.
Send Stripe Invoice
Bills an Opportunity's payer with a Stripe invoice. The Opportunity's products are the lines. With no products, its Amount is the one line.
| Input | Required | What it takes |
|---|---|---|
| Opportunity ID | Yes | The Opportunity to bill. |
| Days Until Due | No | Days the payer has to pay. Defaults to 30. |
| Email the Invoice | No | When true (the default), Stripe emails the invoice and its reminders to the payer. When false, Stripe emails nothing. Share the Invoice URL yourself. |
| Memo | No | A note shown on the invoice. |
| Output | What it tells you |
|---|---|
| Success | The invoice was created. |
| Invoice ID, Invoice Number | Stripe's identifiers for it. |
| Invoice URL | The page where the payer views and pays the invoice. |
| Error Message | Why it wasn't created. |
Start Stripe Subscription
Starts a subscription for the Opportunity's Contact. Give it a price in one of three ways:
- A Stripe Price ID
- A Product ID alone (the product's newest recurring price)
- A Product ID with an Amount and Billing Interval
| Input | Required | What it takes |
|---|---|---|
| Opportunity ID | Yes | The Opportunity the subscription starts from. Its Contact pays. |
| Stripe Price ID | This or a Product ID | A recurring Stripe price (price_...). |
| Product ID | This or a Stripe Price ID | A Product sent to Stripe. Uses its newest recurring price unless you give an Amount. |
| Amount | No | With a Product ID: dollars charged each interval in place of the product's price. |
| Billing Interval | No | With an Amount: day, week, month (the default), or year. |
| Interval Count | No | With an Amount: intervals between charges (for example, 3 with month for quarterly). Defaults to 1. |
| Quantity | No | Units of the price charged each interval. Defaults to 1. |
| Start Date | No | A future date for the first charge. Leave blank to charge now. |
| Bill by Emailed Invoice | No | When true, Stripe emails an invoice each period instead of charging a card. |
| Days Until Due | No | With Bill by Emailed Invoice: days the payer has to pay each invoice. Defaults to 30. |
| Output | What it tells you |
|---|---|
| Success | The subscription started. |
| Subscription ID | Stripe's ID for it. |
| Status | Stripe's status for the subscription (for example, active or trialing). |
| First Invoice URL | The page for the first invoice if Stripe issued one. |
| Error Message | Why it didn't start (a declined card, for example). A refused first payment creates nothing. |
Without Bill by Emailed Invoice, the payer is charged on the card saved at the Opportunity's checkout. If there's none, it's the default card on their Stripe customer. An Opportunity can have one live subscription at a time. The action refuses a second.
Cancel Stripe Subscription
| Input | Required | What it takes |
|---|---|---|
| Opportunity ID | Yes | The Opportunity the subscription started from or one of its payments. |
| Cancel Immediately | No | When true, ends the subscription now. When false (the default), it ends after the period already paid for. |
| Output | What it tells you |
|---|---|
| Success | The change was made. |
| Status | Stripe's status for the subscription after the change. |
| Error Message | Why it wasn't made. |
Reading what the form collected
Both of these read Payment Custom Data. Every form field the package doesn't handle itself ends up there. See Payments in Salesforce.
Get Custom Data Value
Pulls one value out by name.
| Input | Required | What it takes |
|---|---|---|
| Key Name | Yes | The field name from the form. Dot notation reaches nested values (for example, pricing.total). |
| Opportunity ID | If no JSON String | The Opportunity to read. |
| JSON String | If no Opportunity ID | The JSON itself. In a record-triggered Flow, pass $Record.toflourish__pay_Custom_Data__c and save the query. |
The output is Value.
Filter by Custom Data
Narrows a list of Opportunities to the ones whose custom data matches.
| Input | Required | What it takes |
|---|---|---|
| Opportunity IDs | Yes | Comma-delimited list to test. |
| Key Name | Yes | The field name. Dot notation works. |
| Operator | No | equals (the default), not_equals, contains, not_contains, is_blank, or is_not_blank. |
| Key Value | No | What to compare against. Not needed for is_blank and is_not_blank. |
The output is Matching IDs as a comma-delimited list. Comparisons are case-sensitive.