# Finance Settings

Stripe API key configuration, webhook secret setup, and QuickBooks integration credentials.

Last updated: 2026-03-31
Canonical: https://nphelper.com/docs/settings-finance

Finance Settings holds the credentials for Stripe payment processing and QuickBooks accounting sync. This page is only visible to users with the **super-admin** role or the **manage_financial_settings** permission (assigned to the Treasurer role).

---

## Stripe keys

Three separate values are required to connect Stripe. All three come from the Stripe Dashboard.

### Publishable Key

Starts with `pk_live_` (or `pk_test_` in test mode). This is a public key — safe to store in plaintext and expose in browser JavaScript. It is saved with the **Save** button at the bottom of the page.

**Where to find it:** Stripe Dashboard → Developers → API Keys → Publishable key.

### Secret Key

A restricted API key starting with `rk_live_`. This key is encrypted at rest and never displayed after it is saved. Use a restricted key, not the full `sk_live_` key. This key authorises the application to create Checkout sessions and manage Stripe Price objects.

**Where to find it:** Stripe Dashboard → Developers → API Keys → Create restricted key.

**Required permissions on the restricted key:**

| Section | Resource | Permission |
|---|---|---|
| Core | Charges and Refunds | Read |
| Core | Customers | Write |
| Core | Payment Intents | Read |
| Core | Products | Write |
| Billing | Invoices | Read |
| Billing | Prices | Write |
| Billing | Subscriptions | Write |
| Checkout | Checkout Sessions | Write |

All other permissions must be set to **None**. In particular, the key must have no write access to Payouts, Refunds, Transfers, or Balance — the application is deposit-only and must never be able to move money out of the account.

### Webhook Secret

Starts with `whsec_`. This value is generated by Stripe when you create a webhook endpoint and is used to verify that incoming webhook requests are genuinely from Stripe. It is encrypted at rest and never displayed after it is saved.

**Where to find it:** Stripe Dashboard → Developers → Webhooks → your endpoint → Signing secret.

**Webhook endpoint URL:** `https://yourdomain.com/webhooks/stripe`

**Events to enable on the webhook endpoint:**

| Event | Purpose |
|---|---|
| `checkout.session.completed` | Records successful payments for product purchases, event registrations, and memberships |
| `payment_intent.payment_failed` | Records failed payment attempts |
| `refund.created` | Records refunds issued through the Stripe dashboard |
| `invoice.payment_succeeded` | Records successful recurring donation charges |
| `invoice.payment_failed` | Records failed recurring donation charges |
| `customer.subscription.deleted` | Records subscription cancellations |

---

## Setting and changing keys

Secret keys and the webhook secret use a gated entry flow:

- **Not yet configured:** a **Set Key** button opens a modal with a one-time entry field. The value is encrypted before storage and cannot be retrieved by anyone, including administrators — record it in a password manager before saving.
- **Already configured:** a **Change Key** (danger) button opens a rotation modal that requires a confirmation checkbox acknowledging the system impact before the new value is accepted. Key changes are recorded in the activity log.

The publishable key is set and changed through the standard form — enter the value and click **Save**.

---

## Payment Methods

Controls which payment methods are offered to donors and customers at checkout. Only enabled methods appear in Stripe Checkout.

### Available methods

| Method | Label | Recurring donations? |
|---|---|---|
| Card | Visa, Mastercard, Amex, etc. | Yes |
| ACH Direct Debit | US bank account transfers | Yes |
| Link | Stripe's one-click checkout | Yes |
| Cash App Pay | Cash App mobile payments | No — one-time only |
| Amazon Pay | Amazon account payments | No — one-time only |

**Card payments are always enabled** and cannot be turned off.

### How recurring donations are handled

When a donor selects a recurring donation, the checkout page automatically shows only the methods that Stripe supports for subscriptions (Card, ACH, and Link). Methods that only support one-time payments (Cash App Pay, Amazon Pay) are filtered out automatically — no configuration is needed.

### Changing payment methods

Select or deselect methods in the checkbox list and click **Save**. Changes take effect immediately for all new checkouts. In-progress checkout sessions are not affected.

---

## QuickBooks

QuickBooks Online is used for one-way transaction sync — completed payments and refunds recorded in this system are automatically pushed to QuickBooks as Sales Receipts (for payments) and Refund Receipts (for refunds). Sync is automatic once connected and configured.

### Obtaining OAuth credentials

1. Go to the [Intuit Developer Portal](https://developer.intuit.com/) and sign in with your QuickBooks account.
2. Create a new app (or open an existing one) and select **QuickBooks Online Accounting** as the platform.
3. Under **Keys & credentials**, note the **Client ID** and **Client Secret**.
4. Add your callback URL to the **Redirect URIs** list: `https://yourdomain.com/admin/quickbooks/callback`.

### Setting credentials

Enter the Client ID and Client Secret using the **Set Key** buttons in the QuickBooks sections on this page. Like Stripe keys, these values are encrypted at rest and cannot be retrieved after saving — record them in a password manager before proceeding.

### Connecting

Once both the Client ID and Client Secret are configured, a **Connect to QuickBooks** button appears. Click it to be redirected to Intuit's authorization page, where you sign in and grant access. After authorization, you are returned to Finance Settings with a success message.

### Connected state

When connected, the page shows:

- A green **Connected** badge
- The **Company ID (Realm)** — this identifies which QuickBooks company is linked
- The **Token expiry** — tokens are refreshed automatically; this is shown for diagnostic purposes

### Disconnecting

Click **Disconnect** (danger button) and confirm. This removes all stored QuickBooks tokens. You will need to re-authorize to reconnect. Disconnecting does not affect data already synced to QuickBooks.

### Transaction Sync

Once connected, a **QuickBooks — Transaction Sync** section appears below the connection panel.

**Income Account:** select the QuickBooks income account where all synced transactions will be posted. This dropdown is populated from your QuickBooks Chart of Accounts (income-type accounts only). The list is cached for one hour — click **Refresh Accounts** to reload it.

Sync is disabled until an income account is selected. Once configured, sync is fully automatic — no manual action is needed for normal operation.

**How sync works:**

- When a payment completes (donation, product purchase, or recurring invoice), a Sales Receipt is created in QuickBooks.
- When a refund is processed through Stripe, a Refund Receipt is created in QuickBooks.
- Each transaction is synced exactly once. A transaction that has already been synced is never sent again.

**Sync statuses** (visible on the Transactions list):

| Status | Meaning |
|---|---|
| **Synced** | Successfully pushed to QuickBooks (hover for timestamp) |
| **Error** | Sync failed (hover for error message) |
| **Pending** | Not yet synced and no error — may be awaiting the queue worker |
| **N/A** | QuickBooks is not connected |

**Manual re-sync:** if a transaction shows an error, users with the **manage_financial_settings** permission can click the **Sync to QB** action on the Transactions list to retry.

### Important notes

- QuickBooks OAuth credentials (Client ID and Secret) are encrypted at rest, like Stripe keys. They cannot be viewed after saving.
- The Connect button only appears after both the Client ID and Client Secret have been set.
- Sync only applies to new transactions going forward. Pre-existing transactions are not retroactively synced.

---

## Important notes

- This application **never initiates refunds, payouts, or balance transfers** through Stripe. All financial reversals are handled directly in the Stripe dashboard.
- Stripe-originated transactions appear automatically in the Transactions ledger via webhook. Do not enter Stripe payments manually.
- If the webhook secret is incorrect, incoming Stripe events will be rejected with a 400 response. Stripe will retry failed events — check the webhook log in the Stripe dashboard if transactions are not appearing.
