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 isn't the card: it's a reference Stripe accepts on your behalf, for that customer, with no one at the keyboard. That's what both charging actions spend.
The token is stored with the rest of the payment history in Stripe Sync on the Opportunity, in this shape:
{
"stripeCustomerId": "cus_...",
"paymentMethodToken": "pm_...",
"paymentMethodStatus": "active"
}
Charge Payment Token
The action to use for anything recurring. It reads the credential out of a field rather than taking it as an input, which means the token can live in one place and every record that needs it can point at that place.
| 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, capped at 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, each reason a row was skipped, with how many rows it applied to. |
Two paths, and they 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, not an outcome per row. Check the records themselves for those.
Pass one query rather than looping records and calling the action per row. The query is what keeps a large run inside Salesforce's limits, and the action charges in chunks, each chunk 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 skipped row is charged by a later run once the reason clears, as long as 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. A schedule that charges the same card more often than every five days can't be charged by this action.
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 dies 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, recording the card it used, and nothing else. It doesn't set the stage to Closed Won and doesn't mark a payment record paid, because what a recurring charge means for your records is yours to decide. Do it 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
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, so anything you want recorded, record it 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 per Opportunity: 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 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, nothing is emailed; 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 payer, its Contact. Give it a price in one of three ways: a Stripe Price ID; a Product ID, which uses the product's newest recurring price; or 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 an Amount is given. |
| 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, when one was issued. |
| Error Message | Why it didn't start, including a declined card. 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 was one, or 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, where every form field the package doesn't handle itself ends up. 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.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 allowed. |
| Operator | No | equals (the default), not_equals, contains, not_contains, is_blank, is_not_blank. |
| Key Value | No | What to compare against. Not needed for is_blank and is_not_blank. |
The output is Matching IDs, comma-delimited. Comparisons are case-sensitive.