> ## 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 Testing: Production and Sandbox

> Configure and test Stripe payments safely in production and sandbox fusionCore orgs, using separate Payment Provider Accounts, test keys, and per-environment webhooks.

This page is a unified guide and test plan for configuring and testing Stripe in both production and sandbox fusionCore orgs. Use it as the authoritative reference when you set up a new org, test before go-live, or validate after a sandbox refresh.

A few terms you will see throughout, defined for newcomers:

* **Stripe** is the payment processor that actually charges the card, bank account (ACH), Alipay, or WeChat Pay.
* **Salesforce** is the platform fusionCore runs inside. A **sandbox** is a copy of your production org used for testing.
* A **Payment Provider Account (PPA)** is the fusionCore record (`FCORE_PAY__Payment_Provider_Account__c`) that holds the Stripe connection settings.
* A **webhook** is a URL Stripe calls to push events (payment succeeded, refunded, customer created) back into your org.
* **Custom Metadata** records are configuration rows you edit in **Setup**; the Stripe API keys live there, not on the PPA.

<Note>
  For a newcomer, Stripe is "the thing that actually charges the card when you take a payment on an order." It plugs into the everyday order flow: Opportunity to Order conversion, order entry, invoice, then order payments and refunds.
</Note>

## Quick FAQ

<AccordionGroup>
  <Accordion title="Is the External Id the same in test and live?">
    Yes. The same Stripe account ID (`FCORE_PAY__External_Id__c`) is used in both modes. What differs are your API keys (test vs live) and the webhook configuration (test vs live).
  </Accordion>

  <Accordion title="How many PPAs should I have in production for testing?">
    Two. Create one live PPA and one separate test PPA for controlled test charges in production.
  </Accordion>

  <Accordion title="Can I flip my existing live PPA into test mode?">
    No. Never switch a live PPA into test mode. Create a separate test PPA instead, so you do not break the live integration.
  </Accordion>

  <Accordion title="What is the difference between Is Test and Force Test Mode?">
    `Is Test` labels a PPA as a test configuration. `Force Test Mode` (`FCORE_PAY__Force_Test_Mode__c`) enforces test behavior: when it is on, fusionCore serves the sandbox keys and treats Stripe events with `livemode=false` as valid, even in production. Use both on a dedicated test PPA; keep `Force Test Mode` off on the live PPA.
  </Accordion>
</AccordionGroup>

## Stripe Account Models in fusionCore

fusionCore supports two ways of integrating with Stripe. Confirm which one applies before you configure anything.

| Model                        | Description                                                                                                                                                                                                                                      |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Connected (platform) account | The default model. fusionCore connects through fusionSpan's platform Stripe account; your org is a Stripe **connected account**. fusionSpan manages the underlying Stripe API keys. Calls send a `Stripe-Account` header and an application fee. |
| Direct Stripe account        | Your org connects to your own standalone Stripe account using keys configured in your org. You manage the keys. No connected-account header and no application fee are sent. Enabled by a feature parameter.                                     |

For Direct-account setup, see [Stripe direct accounts](/end-user-guides/stripe/stripe-direct-accounts/index).

<Warning>
  **Direct vs Connected accounts and keys.** Most safety warnings on this page about "do not use live keys in sandbox" or "replace production keys after refresh" apply mainly to **Direct** accounts, where you hold the keys. For **Connected** accounts, fusionSpan manages the Stripe keys; clients do not rotate them, and sandbox-vs-production safety is enforced through PPA flags, environment separation, and webhooks rather than by replacing keys.
</Warning>

## Core PPA Fields for Stripe

Stripe configuration lives on the Payment Provider Account record. These are the fields you set most often.

| Field (API name)                                                    | Purpose                                                                                                                                                | Production notes                                                  |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `FCORE_PAY__Force_Test_Mode__c` (Force Test Mode)                   | When on, fusionCore uses Stripe **test keys** (`Sandbox_Value__c`) and accepts `livemode=false` webhooks even in production.                           | Off for the live PPA. On for a dedicated test PPA in production.  |
| `FCORE_PAY__External_Id__c` (External Id)                           | The Stripe account ID this PPA links to. The **same** account ID is used for both test and live; modes differ by keys and webhook, not by External Id. | Must be unique across PPAs (see pitfalls).                        |
| `FCORE_PAY__Is_Active__c` (Is Active)                               | Whether this PPA is available for processing.                                                                                                          | Turn on only when you are ready to process or test.               |
| `FCORE_PAY__Request_Signature_Secret__c` (Request Signature Secret) | The webhook signing secret fusionCore uses to verify inbound Stripe webhooks.                                                                          | Must match the webhook configured in Stripe for this environment. |
| `FCORE_PAY__Validation_Status__c` (Validation Status)               | Set by the **Validate** action: `Pending`, `Failed`, or `Succeeded`.                                                                                   | Editing key fields resets this (see pitfalls).                    |

## Webhook Endpoint Overview

Stripe webhooks let fusionCore receive event notifications for payments, refunds, customers, and saved payment methods.

The fusionCore webhook endpoint path is:

```
/services/apexrest/FCORE_PSTRIPE/Stripe
```

Append this path to the base Salesforce domain of the org (production or sandbox). Always point the Stripe webhook at the domain that matches the environment you are testing or running live.

Subscribe the endpoint to these event types:

| Group          | Events                                                                                                                                                |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Payment intent | `payment_intent.canceled`, `payment_intent.payment_failed`, `payment_intent.processing`, `payment_intent.requires_action`, `payment_intent.succeeded` |
| Refund         | `charge.refund.updated`                                                                                                                               |
| Customer       | `customer.created`, `customer.updated`                                                                                                                |
| Setup intent   | `setup_intent.canceled`, `setup_intent.requires_action`, `setup_intent.setup_failed`, `setup_intent.succeeded`                                        |
| Payment method | `payment_method.updated`, `payment_method.automatically_updated`, `payment_method.detached`                                                           |

For full webhook setup, see [Connected account webhook set up](/end-user-guides/stripe/connected-account-webhook-set-up).

## Production Configuration and Testing

### Prerequisites

Before configuring Stripe in your live org, confirm:

* You have administrator access in fusionCore with permission to manage Payment Provider Accounts and payment settings.
* You have administrator access to the production Stripe account or platform (Direct or Connected) to view keys and configure webhooks.
* You know your Stripe account model (Direct vs Connected) and any platform-specific instructions from your implementation team.
* You have a documented test plan listing the payment scenarios, users, and records you will use for testing.

### Configure the Payment Provider Account in Production

The core setup (credentials, PPA fields, webhook, validation) is the same for both account models. Direct-only steps are called out in [Stripe direct accounts](/end-user-guides/stripe/stripe-direct-accounts/index).

<Steps>
  <Step title="Open the Payment Provider Account object">
    In your **production** org, open the **Payment Provider Account** object from the **App Launcher**.
  </Step>

  <Step title="Create two PPAs">
    As a best practice, create **two** Stripe PPAs: one **live** PPA for real customer payments, and one **test** PPA for controlled test charges in production. If you already have a live PPA, create a separate test PPA rather than reusing or flipping the live one.
  </Step>

  <Step title="Set the core fields on each PPA">
    * `FCORE_PAY__Payment_Provider_Name__c`: set to `Stripe`.
    * `FCORE_PAY__Is_Active__c`: turn on only when the PPA is ready to be used.
    * `FCORE_PAY__External_Id__c`: the Stripe (connected or direct) account ID. The **same** External Id is used for both the live and test PPAs, because both connect to the same Stripe account.
    * `FCORE_PAY__Force_Test_Mode__c`: leave **off** on the live PPA; turn **on** on the test PPA so it uses Stripe test keys.
    * `FCORE_PAY__Request_Signature_Secret__c`: set after webhook configuration (next step).
  </Step>

  <Step title="Configure the production webhook">
    In the Stripe **production (Live mode)** dashboard, go to **Developers → Webhooks** and create an endpoint using your production Salesforce domain plus the endpoint path:

    ```
    https://acme-association.my.salesforce.com/services/apexrest/FCORE_PSTRIPE/Stripe
    ```

    Subscribe it to the events listed in [Webhook endpoint overview](#webhook-endpoint-overview). Save the endpoint, copy the **signing secret**, and paste it into `FCORE_PAY__Request_Signature_Secret__c` on the PPA.
  </Step>

  <Step title="Validate the configuration">
    Add the **Validate** quick action to the Payment Provider Account page layout if it is not already there, then click **Validate** on the PPA. Validation checks the External Id, Request Signature Secret, secret key, public key, and payment types, then creates or updates a Stripe "test customer."

    Validation is **two-stage and asynchronous**. Clicking **Validate** does not set `Succeeded` on its own. It writes the Stripe customer, then waits for the inbound `customer.created` or `customer.updated` webhook to flip `FCORE_PAY__Validation_Status__c` to `Succeeded`. If the webhook is misconfigured, validation never completes even with correct keys. Validation errors appear in `FCORE_PAY__Validation_Message__c`.
  </Step>
</Steps>

<Tip>
  A Secret/Public-key validation error in production often means `FCORE_PAY__Force_Test_Mode__c` must be on for that PPA, so fusionCore serves the sandbox (test) keys.
</Tip>

### Safe Production Testing Before Go-Live

Use a **dedicated test PPA in production** that is marked test-only (`Is Test` on, `FCORE_PAY__Force_Test_Mode__c` on) and never used for real traffic. It should use Stripe test-mode keys and a test-mode webhook, and it shares the **same External Id** as your live PPA.

<Steps>
  <Step title="Plan and communicate">
    Pick a small set of internal testers and low-dollar test records. Tell finance and support that controlled test transactions will run and may appear in Stripe.
  </Step>

  <Step title="Confirm safe configuration">
    Before any payment attempt, confirm you are using the test PPA (not the live PPA), that the intended Stripe mode is active, and that `FCORE_PAY__Is_Active__c` is on only once you are ready to run the tests and have validated successfully.
  </Step>

  <Step title="Run test scenarios">
    Run a single low-dollar one-time payment with a Stripe test card, then refund it from fusionCore. If you use recurring billing, create one recurring payment and confirm the initial charge and schedule. For each, confirm fusionCore shows the expected status and the Stripe payment intent or charge ID, and confirm the matching transaction in Stripe.
  </Step>

  <Step title="Verify webhook processing">
    In Stripe, confirm events for your test transactions were delivered without errors. In fusionCore, confirm status updates (succeeded, refunded) are reflected. If webhooks are failing, pause testing until the endpoint is corrected.
  </Step>
</Steps>

### Validate After a Production Upgrade

When fusionCore is upgraded in a live org, confirm Stripe still works without impacting real customers.

* Confirm the upgrade did not change your Stripe keys, webhook endpoints, or PPA fields. Compare `FCORE_PAY__Force_Test_Mode__c`, `FCORE_PAY__External_Id__c`, and `FCORE_PAY__Is_Active__c` against your pre-upgrade notes.
* Review the release notes for Stripe-related changes that need configuration updates.
* Run a small smoke test: one small live or test-mode charge, confirming the payment is created in both fusionCore and Stripe and that webhooks are received and processed.

## Sandbox Configuration and Testing

In a Salesforce sandbox, fusionCore automatically uses the sandbox (test) keys and only accepts webhooks with `livemode=false`. Your job is to make sure no live keys or production webhooks remain after a refresh.

### Post-sandbox-refresh Checklist

A refresh copies Stripe configuration from production, but it must not process real payments. Immediately after each refresh:

1. Review every Payment Provider Account in the sandbox and identify those that reference Stripe.
2. For each Stripe PPA, update:
   * `FCORE_PAY__Is_Active__c`: turn **off** for any PPA referencing live credentials or that should not run in the sandbox.
   * `FCORE_PAY__Force_Test_Mode__c`: turn **on** for any sandbox PPA used for testing, so only test-mode behavior is possible.
   * `FCORE_PAY__External_Id__c`: leave as the **same** Stripe account ID. Sandbox safety comes from test keys and a test-mode webhook, not from changing the External Id.
3. Confirm no integration or scheduled job can auto-charge real payment methods using live keys copied from production. Disable or reconfigure such jobs.

<Info>
  **External Id after refresh.** It is normal for the sandbox PPA to keep the same External Id as production. What must change are the **keys** (replace any live keys with test keys) and the **webhook endpoint** (repoint to the sandbox domain in Stripe test mode).
</Info>

### Configure the Sandbox PPA and Webhook

<Steps>
  <Step title="Obtain test-mode credentials">
    In the Stripe dashboard, switch to **Test mode** and locate the **test** publishable and secret keys. For Direct accounts, never use live keys in sandbox. For Connected accounts, fusionSpan-managed keys are used and are not visible to the client.
  </Step>

  <Step title="Configure the sandbox PPA">
    Use a sandbox-dedicated PPA. Turn `FCORE_PAY__Force_Test_Mode__c` on, set `FCORE_PAY__External_Id__c` to the expected Stripe account ID, and turn `FCORE_PAY__Is_Active__c` on only after confirming the PPA is test-only.
  </Step>

  <Step title="Configure the sandbox webhook">
    In Stripe **Test mode**, create a webhook endpoint using your sandbox Salesforce domain plus the endpoint path:

    ```
    https://acme-association--qa.sandbox.my.salesforce.com/services/apexrest/FCORE_PSTRIPE/Stripe
    ```

    Subscribe it to the same events as production, in test mode only. Copy the test-mode signing secret into `FCORE_PAY__Request_Signature_Secret__c` on the sandbox PPA. Never reuse the production endpoint or signing secret in sandbox.
  </Step>

  <Step title="Validate the sandbox configuration">
    Click **Validate** on the sandbox PPA and resolve any errors in `FCORE_PAY__Validation_Message__c` until validation succeeds. As in production, **Validate** focuses on connectivity and credential checks and does not run real charges.
  </Step>
</Steps>

### Ensure No Real Payments in Sandbox

* Use only Stripe test-mode keys in sandbox.
* Turn on `FCORE_PAY__Force_Test_Mode__c` for every sandbox PPA used for testing.
* Mark any PPA that contains copied live keys as inactive immediately after refresh, and replace the keys with test keys.
* Restrict access to sandbox payment pages and flows to internal testers.
* Treat the External Id as a stable account reference. Safety comes from test keys, test-mode webhooks, and the PPA flags, not from changing the External Id.

### Run Test Scenarios in Sandbox

* Successful one-time payments using Stripe test cards from the Stripe documentation.
* Failed payments using decline test cards, to validate error handling.
* Refunds triggered from fusionCore and confirmed in Stripe test mode.
* Webhook delivery, by simulating refund or dispute events in Stripe test mode and confirming fusionCore processes them.

## Version-specific Considerations

Use this to decide what to re-test when upgrading an org that processes Stripe payments.

### V1.4.0

No Stripe-related changes.

### V1.5.0 — Webhook Reliability, Traceability, New Events

* **Direct link to Stripe transaction.** New field `FCORE_PAY__Transaction_URL__c` on Payment Provider Event stores a one-click link to the matching Stripe transaction for reconciliation.
* **Org-aware webhook handling.** fusionCore stamps Stripe-side records it creates with the Salesforce Org Id, and webhook processing only handles events for this org. A webhook event not relevant to the current org returns HTTP **204** (instead of an error).
* **Skipped-event signal.** When fusionCore cannot identify a webhook event as relevant, it fires the `SkippedStripeWebhookEvent` Service Layer event, useful for monitoring and for distinguishing "ignored but expected" from genuine failures.
* **Separate transactions.** Webhook-driven updates are processed in separate transactions under the Automated Process user, isolating webhook failures from user-initiated work.

When upgrading to 1.5.0 or higher, add to your test plan: confirm events for this org are processed and unrelated events are cleanly skipped (204), confirm `FCORE_PAY__Transaction_URL__c` opens the correct Stripe transaction, and confirm orphaned/missing Payment Provider Events are detected and cancelled during payment validation.

### V1.6.0 — Direct Account Support and Saved Payment Methods

* **Direct connection.** First release to support a direct connection to a Stripe account, alongside the connected-account model.
* **Improved saved payment method handling.** Better consistency when replacing, expiring, or updating saved payment methods (Wallet Items) backed by Stripe.
* **New field.** `FCORE_PSTRIPE__Stripe_MDT_Prefix__c` (Stripe MDT Prefix, type Text) on `FCORE_PAY__Payment_Provider_Account__c`, used to tie a PPA to the correct Stripe custom metadata configuration for direct accounts.

When upgrading to 1.6.0 and using Stripe: re-test one-time payments, refunds, and recurring/auto-charge flows on the direct account; confirm the webhook endpoint and domain per environment; and test editing, replacing, and removing Wallet Items in both back-office and portal, confirming Stripe stays in sync and no orphaned Stripe payment methods remain.

## Testing Safety Reminders

What **not** to do:

* Do not use live keys in sandbox.
* Do not leave `FCORE_PAY__Force_Test_Mode__c` on in production when your goal is real payments.
* Do not reuse webhook endpoints or signing secrets between sandbox and production.
* Do not run large-volume or load testing in production; use sandbox.
* Do not flip an existing live PPA into test mode. Create a separate test PPA instead.

Safety practices:

* Restrict access to Stripe configuration and PPA records to a few trusted administrators.
* Keep a simple log of when keys, webhook endpoints, or PPA flags change, by whom, and why.
* After any major change or upgrade, re-run **Validate** and one or two key test scenarios before resuming normal operations.
* If you detect unexpected charges, pause testing, clear `FCORE_PAY__Is_Active__c` on the affected PPA, review Stripe and fusionCore logs, and coordinate with support before re-enabling.

## Limitations and Common Pitfalls

* **Validation is asynchronous and two-stage.** Clicking **Validate** does not set `Succeeded` by itself. It writes the Stripe customer and waits for the inbound `customer.created`/`customer.updated` webhook to flip `FCORE_PAY__Validation_Status__c`. If the webhook is misconfigured, validation never completes even with correct keys. The customer event is also ignored unless its validation timestamp is within about 10 seconds of now.
* **Editing key fields silently resets validation.** Changing `FCORE_PAY__External_Id__c`, `FCORE_PAY__Request_Signature_Secret__c`, or `FCORE_PSTRIPE__Stripe_MDT_Prefix__c` nulls `FCORE_PAY__Validation_Status__c`. You must re-run **Validate**.
* **External Id must be unique.** Two PPAs sharing the same `FCORE_PAY__External_Id__c` cause "Multiple Payment Provider Accounts were found with the same External Id." A missing or unknown External Id causes "Payment Provider Secret Not Found."
* **Webhook signature and age guards return a silent 204.** Inbound requests are rejected if the `Stripe-Signature` header is missing or malformed, the signature does not match any PPA's signing secret, or the request is older than 300 seconds. On rejection the endpoint returns **204** and fires `SkippedStripeWebhookEvent`. Stripe sees a 2xx and will **not retry**, so a misconfigured secret can silently drop events.
* **Org-id and livemode gating drop events silently.** A webhook whose Stripe metadata org id differs from this org returns 204. A production-vs-sandbox `livemode` mismatch (with `Force_Test_Mode` off) causes the event to return early and do nothing.
* **Subscribe only to the listed event types.** An event type outside the handled set produces an error response and noise in the `FCORE_PSTRIPE__Stripe_API_Log__c` log. Subscribe only to the events in [Webhook endpoint overview](#webhook-endpoint-overview).
* **Alipay and WeChat Pay require Direct accounts.** These methods are Direct-only. On a connected-account setup, saving a payment type for them is blocked with "This payment method is not available with current Stripe setup."
* **Charge timeouts auto-cancel pending payments.** Payments stuck at `requires_action` are auto-cancelled past per-method cutoffs: Card after 60 minutes, ACH after 7 days, Alipay and WeChat Pay after 5 minutes. Long member delays in completing 3-D Secure or microdeposit verification will be cancelled.
* **Large saved-wallet batches can partially fail.** Charging saved payment methods runs in-line and bails at the callout or time limit, marking remaining events **Failed** with "Callout limit exceeded." Keep batches modest.
* **Do not toggle the Direct feature on a live account.** Switching the Direct-account feature changes routing, fees, and which secret records are used. Re-key before enabling it on an account already in use.

## Common Questions

<AccordionGroup>
  <Accordion title="Is External Id different between test and live?">
    No. The same External Id (Stripe account ID) is used in both modes; only API keys and webhook endpoints differ.
  </Accordion>

  <Accordion title="How many PPAs should I have in production?">
    Two: one live PPA and one separate test PPA for safe validation in production.
  </Accordion>

  <Accordion title="Is sandbox safe after a refresh from production?">
    Yes, if you replace any live keys with test keys, repoint webhooks to the sandbox domain in test mode, and turn on the test-only flags. External Id can stay the same.
  </Accordion>

  <Accordion title="How should I validate configuration before go-live?">
    Use the Validate action, run small-amount test scenarios through a dedicated test PPA, and confirm webhook delivery and statuses in both systems.
  </Accordion>

  <Accordion title="When should I use Direct vs Connected?">
    Direct when you want full control of your own Stripe account and keys; Connected when using a platform that manages onboarding, keys, and routing.
  </Accordion>
</AccordionGroup>

For common failure patterns and fixes, see [Common issues and troubleshooting](/end-user-guides/stripe/troubleshooting).
