> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fusioncore.us/llms.txt
> Use this file to discover all available pages before exploring further.

# Stripe Direct Accounts Overview

> Connect a customer's own Stripe account directly to fusionCore instead of routing through fusionSpan's platform Stripe account.

A **Stripe Direct Account** connects a customer's own Stripe account directly to fusionCore, instead of routing payments through fusionSpan's platform Stripe account (the "connected-account" model).

In Salesforce, the connection is stored on a **Payment Provider Account** record (`FCORE_PAY__Payment_Provider_Account__c`, abbreviated PPA below). A Direct Account differs from a connected account in three ways the code enforces:

* API calls go straight to the customer's Stripe account, with no `Stripe-Account` header and no platform application fee.
* The secret keys are looked up by a per-account prefix you set on the PPA.
* The Direct model must be turned on through a feature parameter (a packaged on/off switch).

<Note>
  For newcomers: **Stripe** is the payment processor that actually charges the card when you take a payment on an order. A **webhook** is a URL that Stripe calls to push events (payment succeeded, refunded, customer created) back into your org.
</Note>

## Before You Begin

* Both the Commerce package (`FCORE_PAY`) and the Stripe addon (`FCORE_PSTRIPE`) must be installed in the org.
* The `FCORE_PSTRIPE__EnableDirectStripeInstance` feature parameter must be enabled for production. This is a packaged Boolean switch; contact fusionSpan support to have it turned on before you install or configure a live Direct Account. (Sandbox uses a different mechanism — see [Step 4](#step-4-enable-and-test-the-direct-account-connection).)
* You will need the customer's Stripe **publishable key** (begins with `pk_`) and **secret key** (begins with `sk_`).

<Tip>
  Ask the customer to create [restricted keys](https://docs.stripe.com/keys-best-practices#limit-access) rather than full-access keys. The restricted key must include the scope of the webhook events listed in step 7 of the [Connected Account Webhook Set Up guide](/end-user-guides/stripe/connected-account-webhook-set-up). Request and store any keys through a secure channel.
</Tip>

## Configure a Stripe Direct Account in fusionCore

### Step 1: Set the Prefix on the Payment Provider Account

The prefix tells fusionCore which secret records to read for this account.

<Steps>
  <Step title="Open the Payment Provider Account">
    Open the PPA record for the Stripe Direct Account you are configuring.
  </Step>

  <Step title="Set the Stripe MDT Prefix">
    Set the **Stripe MDT Prefix** field (`FCORE_PSTRIPE__Stripe_MDT_Prefix__c`) to a short value, for example `FS`.

    This field is not on the page layout by default. Add it to the layout, or set it with a Salesforce data tool, in order to populate it.
  </Step>
</Steps>

<Frame caption="Setting the Stripe MDT Prefix on the Payment Provider Account">
  <img src="https://mintcdn.com/fusionspan/PQhCyVfBbt1uWfmh/images/end-user-guides/4902027265/image-20260112-182258.png?fit=max&auto=format&n=PQhCyVfBbt1uWfmh&q=85&s=15c875519f0101a5b3bba0da7ade2255" alt="Payment Provider Account record showing the Stripe MDT Prefix field" width="943" height="711" data-path="images/end-user-guides/4902027265/image-20260112-182258.png" />
</Frame>

### Step 2: Create the Prefixed Secret Records

The customer's Stripe keys are stored in protected Custom Metadata, not on the PPA record. Create one record for each key, named with the prefix you set in Step 1.

<Steps>
  <Step title="Open Payment Provider Secret">
    In Salesforce, go to **Setup → Custom Metadata Types → Payment Provider Secret** and click **Manage Records**.
  </Step>

  <Step title="Create the public key record">
    Click **New**. Name it `[Prefix]_Stripe_Public_Key` (for example, `FS_Stripe_Public_Key`) and paste the customer's publishable key (begins with `pk_`).
  </Step>

  <Step title="Create the secret key record">
    Click **New** again. Name it `[Prefix]_Stripe_Secret_Key` (for example, `FS_Stripe_Secret_Key`) and paste the customer's secret key (begins with `sk_`).
  </Step>
</Steps>

| Record     | Required name format         | Source in the Stripe Dashboard       |
| ---------- | ---------------------------- | ------------------------------------ |
| Public key | `[Prefix]_Stripe_Public_Key` | **Developers → Overview → API keys** |
| Secret key | `[Prefix]_Stripe_Secret_Key` | **Developers → Overview → API keys** |

<Info>
  Pull both keys from the Stripe account that is connecting directly (**Developers → Overview → API keys** in the Stripe Dashboard). Request the keys from the customer and store them securely.
</Info>

<Frame caption="Creating the prefixed Payment Provider Secret records">
  <img src="https://mintcdn.com/fusionspan/PQhCyVfBbt1uWfmh/images/end-user-guides/4902027265/image-20260112-181828.png?fit=max&auto=format&n=PQhCyVfBbt1uWfmh&q=85&s=ba3495e9528a0420f56fdf33fb765ec8" alt="Payment Provider Secret Custom Metadata records named with a prefix for the public and secret keys" width="1900" height="682" data-path="images/end-user-guides/4902027265/image-20260112-181828.png" />
</Frame>

<Frame caption="Stripe API keys in the Developers tab">
  <img src="https://mintcdn.com/fusionspan/PQhCyVfBbt1uWfmh/images/end-user-guides/4902027265/image-20260112-182046.png?fit=max&auto=format&n=PQhCyVfBbt1uWfmh&q=85&s=66f5fd6eb161305a097a38f331ee4385" alt="Stripe Dashboard Developers tab showing the publishable and secret API keys" width="1260" height="753" data-path="images/end-user-guides/4902027265/image-20260112-182046.png" />
</Frame>

### Step 3: Assign Permission Sets

Assign Stripe permission sets to the users who need them:

* `FC_Stripe_Manage` — admins and configuration users who manage Stripe settings.
* `FC_Stripe_Access` — staff who process payments.
* `FC_Stripe_Portal` — portal users who pay through the portal.
* `FC_Stripe_Integration_User` — any integration or automation user.

### Step 4: Enable and Test the Direct Account Connection

How you turn on the Direct model depends on the environment.

**Production:** the `FCORE_PSTRIPE__EnableDirectStripeInstance` feature parameter controls Direct mode. Confirm it is enabled (see [Before you begin](#before-you-begin)). The `FS_Enable_Direct_Stripe_Instance` constant described below is **ignored in production**.

**Sandbox:** Salesforce does not reliably propagate feature parameters to sandboxes, so Direct mode is controlled by a constant instead.

<Steps>
  <Step title="Open Constants">
    Go to **Setup → Custom Metadata Types → Constant** and click **Manage Records**.
  </Step>

  <Step title="Find or create the constant">
    Search for **Enable Direct Stripe Instance** (`FS_Enable_Direct_Stripe_Instance`). If it does not exist, click **New** and create it with that label and API name.
  </Step>

  <Step title="Set it to true">
    Set the checkbox value to **True** and click **Save**.
  </Step>
</Steps>

<Frame caption="The FS_Enable_Direct_Stripe_Instance constant set to true">
  <img src="https://mintcdn.com/fusionspan/PQhCyVfBbt1uWfmh/images/end-user-guides/4902027265/image-20260204-165749.png?fit=max&auto=format&n=PQhCyVfBbt1uWfmh&q=85&s=24b5713c341cbe41c091fe65518668b1" alt="Constant Custom Metadata record FS_Enable_Direct_Stripe_Instance with its checkbox set to true" width="1651" height="534" data-path="images/end-user-guides/4902027265/image-20260204-165749.png" />
</Frame>

Once enabled, you test a Stripe Direct Account the same way you would a connected account. Set up the webhook (see [Set up your webhook](#set-up-your-webhook-for-a-direct-account)), then validate the PPA. Validation is asynchronous: clicking **Validate** sets the status to **Pending**, and the status flips to **Succeeded** only after Stripe's `customer.created` or `customer.updated` webhook reaches the org. If validation fails, the reason appears in the **Validation Message** field (`FCORE_PAY__Validation_Message__c`).

<Note>
  **Environment notes**

  * **Sandbox:** when `FS_Enable_Direct_Stripe_Instance` is true, the org uses a direct connection even if the customer never requested it, because Salesforce does not propagate feature parameters to sandboxes. Stripe's guide to creating a sandbox is [here](https://docs.stripe.com/sandboxes/dashboard/manage).
  * **Production:** the `FS_Enable_Direct_Stripe_Instance` constant is ignored; the feature parameter governs Direct mode instead.
  * **Refreshes:** keep the Stripe keys stored in production so they are retained after a sandbox refresh.
</Note>

## Set Up Your Webhook for a Direct Account

The webhook endpoint and the events to subscribe to are the same as for a connected account, but you log into the customer's own Stripe account. Follow [Setting up webhooks for your Salesforce org Stripe Direct Account](/end-user-guides/stripe/stripe-direct-accounts/setting-up-webhooks-for-your-salesforce-org-stripe-direct-account).

The endpoint URL is your Salesforce Site URL with `/services/apexrest/FCORE_PSTRIPE/Stripe` appended, and must contain no extra slashes or spaces — for example:

```
https://example.my.salesforce-sites.com/services/apexrest/FCORE_PSTRIPE/Stripe
```

<Tip>
  When you add the events, Stripe may automatically add `payment_method.card_automatically_updated`, so you can end up with 16 events instead of 15. This is expected.
</Tip>

After the webhook is in place, copy its **signing secret** (Stripe Dashboard: **Developers → Webhooks → \[your webhook]**) into the **Request Signature Secret** field (`FCORE_PAY__Request_Signature_Secret__c`) on the PPA, then run **Validate**.

## Supported Payment Types

Stripe Direct Accounts support these payment types. See [Enabling payment methods in Stripe](/end-user-guides/stripe/setting-up-a-stripe-account/enabling-payment-methods-in-stripe) for how to turn them on.

* Card
* ACH
* WeChat Pay
* Alipay

<Note>
  Alipay and WeChat Pay are available **only** on Direct Accounts. On a connected-account setup, saving a payment type for these methods is blocked.
</Note>

## Pending Payments and Auto-Cancellation

When a member starts a Stripe payment, Stripe sometimes can't finish charging immediately and puts the payment in a state called `requires_action` — it is waiting on the member to complete one more step:

* **Card** → **3-D Secure** (the bank's "verify it's you" challenge or one-time code).
* **ACH** → **microdeposit verification** (confirming the two small test deposits in the bank account).
* **Alipay / WeChat Pay** → an external **redirect or approval** in the wallet app.

In your org, that Stripe state shows up as a **Payment Provider Event** (`FCORE_PAY__Payment_Provider_Event__c` — the fusionCore record that mirrors one Stripe payment attempt) with status **Action Required**. If the member walks away and never finishes, the payment would sit in this status indefinitely.

To keep abandoned attempts from lingering, the scheduled **FC: Payment Provider Event Validation** batch re-checks every payment still in `requires_action` and cancels it in Stripe once a method-specific timeout passes. When it cancels the payment (with the reason `abandoned`), the Payment Provider Event status becomes **Cancelled**.

| Method     | Timeout before cancellation | Constant record                           |
| ---------- | --------------------------- | ----------------------------------------- |
| Card       | 60 minutes                  | `Stripe_Card_Cancel_Timeout_In_Minutes`   |
| ACH        | 7 days                      | `Stripe_ACH_Cancel_Timeout_In_Days`       |
| Alipay     | 5 minutes                   | `Stripe_Alipay_Cancel_Timeout_In_Minutes` |
| WeChat Pay | 5 minutes                   | `Stripe_WeChat_Cancel_Timeout_In_Minutes` |

These thresholds are `FCORE_BASE__Constant__mdt` Custom Metadata records. You can change them without code in **Setup → Custom Metadata Types → Constant → Manage Records**.

<Note>
  Cancellation happens on the next batch run after the timeout is exceeded, so the real delay also depends on how the batch is scheduled (daily by default). Revalidating the order from the pay screen can trigger the check sooner. See [Scheduled Jobs in fusionCore](/end-user-guides/platform-setup/scheduled-jobs-in-fusioncore) for this batch's schedule and how to manage it.
</Note>

## Limitations and Common Pitfalls

* **A prefix is required.** With Direct mode on, leaving **Stripe MDT Prefix** blank throws "Stripe MDT Prefix is blank on Payment Provider Account." The matching `[Prefix]_Stripe_Public_Key` and `[Prefix]_Stripe_Secret_Key` records must exist, or callouts fail with "Payment Provider Secret Not Found" / "...Is Blank."
* **Validation completes only after the webhook fires.** Clicking **Validate** does not by itself set **Succeeded** — it waits for Stripe's inbound `customer.created` / `customer.updated` webhook. If the webhook is misconfigured, validation never completes even when the keys are correct. The customer event is also ignored unless it arrives within about 10 seconds of clicking **Validate**.
* **Editing key fields resets validation.** Changing **External Id** (`FCORE_PAY__External_Id__c`), **Request Signature Secret** (`FCORE_PAY__Request_Signature_Secret__c`), or **Stripe MDT Prefix** clears the validation status. You must re-run **Validate**.
* **External Id must be unique.** Two PPAs sharing the same `FCORE_PAY__External_Id__c` throws "Multiple Payment Provider Accounts were found with the same External Id."
* **Do not toggle Direct mode on a live account.** Switching the `EnableDirectStripeInstance` feature parameter changes call routing, application fees, and which secret records are read. Re-key and re-validate before relying on a flipped account.
* **Failed webhooks are easy to miss.** If the `Stripe-Signature` header is missing or its HMAC-SHA256 signature does not match, or the request is older than 300 seconds, or the event's `sf_org_id` does not match this org, the endpoint returns HTTP 204 and skips the event. Stripe sees a success response and does not retry. Check the `Stripe_API_Log__c` records if events seem to be dropped.
* **Pending payments auto-cancel.** Payments a member never finishes are cancelled after a method-specific timeout. See [Pending Payments and Auto-Cancellation](#pending-payments-and-auto-cancellation) above.

## Related Features

<CardGroup cols={2}>
  <Card title="Set up webhooks for a Direct Account" icon="plug" href="/end-user-guides/stripe/stripe-direct-accounts/setting-up-webhooks-for-your-salesforce-org-stripe-direct-account" arrow="true">
    Point the customer's own Stripe account back at the Salesforce org.
  </Card>

  <Card title="Stripe Direct accounts email template" icon="envelope" href="/end-user-guides/stripe/stripe-direct-accounts/stripe-direct-accounts-email-template" arrow="true">
    The template for requesting the customer's keys and webhook details.
  </Card>

  <Card title="Enabling payment methods in Stripe" icon="toggle-on" href="/end-user-guides/stripe/setting-up-a-stripe-account/enabling-payment-methods-in-stripe" arrow="true">
    Turn on Card, ACH, Alipay, and WeChat Pay on a Direct account.
  </Card>

  <Card title="Stripe" icon="stripe-s" href="/end-user-guides/stripe/index" arrow="true">
    How the Direct model compares to the default connected-account model.
  </Card>
</CardGroup>
