diff --git a/package-lock.json b/package-lock.json
index deddad1..c4b76d8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -484,6 +484,37 @@
"win32"
]
},
+ "node_modules/@astrojs/markdown-satteri/node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@astrojs/markdown-satteri/node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@astrojs/markdown-satteri/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@astrojs/markdown-satteri/node_modules/satteri": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/satteri/-/satteri-0.10.5.tgz",
@@ -799,6 +830,37 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@bruits/satteri-win32-arm64-msvc": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz",
@@ -937,20 +999,21 @@
"license": "MIT"
},
"node_modules/@emnapi/core": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
- "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz",
+ "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==",
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
- "@emnapi/wasi-threads": "1.2.2",
+ "@emnapi/wasi-threads": "1.2.3",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
- "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
+ "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -958,11 +1021,12 @@
}
},
"node_modules/@emnapi/wasi-threads": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
- "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz",
+ "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==",
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
@@ -3948,9 +4012,9 @@
}
},
"node_modules/fast-uri": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
- "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz",
+ "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==",
"dev": true,
"funding": [
{
@@ -8032,6 +8096,13 @@
"vscode-uri": "^3.1.0"
}
},
+ "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vscode-html-languageservice": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz",
@@ -8063,9 +8134,9 @@
}
},
"node_modules/vscode-jsonrpc": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
- "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz",
+ "integrity": "sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8086,14 +8157,14 @@
}
},
"node_modules/vscode-languageserver-protocol": {
- "version": "3.17.5",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
- "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+ "version": "3.18.2",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz",
+ "integrity": "sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "vscode-jsonrpc": "8.2.0",
- "vscode-languageserver-types": "3.17.5"
+ "vscode-jsonrpc": "9.0.1",
+ "vscode-languageserver-types": "3.18.0"
}
},
"node_modules/vscode-languageserver-textdocument": {
@@ -8104,6 +8175,34 @@
"license": "MIT"
},
"node_modules/vscode-languageserver-types": {
+ "version": "3.18.0",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz",
+ "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+ "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+ "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-jsonrpc": "8.2.0",
+ "vscode-languageserver-types": "3.17.5"
+ }
+ },
+ "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": {
"version": "3.17.5",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
@@ -8200,9 +8299,9 @@
}
},
"node_modules/yaml": {
- "version": "2.8.3",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
- "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
"devOptional": true,
"license": "ISC",
"bin": {
@@ -8246,6 +8345,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/yaml-language-server/node_modules/yaml": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
"node_modules/yargs": {
"version": "18.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz",
diff --git a/package.json b/package.json
index 0bfb85f..c0b7741 100644
--- a/package.json
+++ b/package.json
@@ -25,5 +25,9 @@
"@markdoc/markdoc": "^0.5.7",
"astro": "^7.0.0",
"sharp": "^0.35.0"
+ },
+ "allowScripts": {
+ "esbuild": true,
+ "fsevents": true
}
}
diff --git a/src/content/docs/extensions-and-development/api.mdoc b/src/content/docs/extensions-and-development/api.mdoc
index 4aeab1e..0aa7176 100644
--- a/src/content/docs/extensions-and-development/api.mdoc
+++ b/src/content/docs/extensions-and-development/api.mdoc
@@ -52,7 +52,7 @@ For browser calls, FOSSBilling accepts the token in any of these places:
- `CSRFToken` in form data or query parameters
- `X-CSRF-Token` request header
-The token must match the `csrf_token` cookie or the session token. The bundled [JavaScript API wrapper](/extensions-and-development/javascript/) reads the cookie and adds the token automatically.
+The token must match the `fossbilling_csrf` cookie or the session token. The bundled [JavaScript API wrapper](/extensions-and-development/javascript/) reads the cookie and adds the token automatically.
Use Twig helpers for theme and module templates. This way, you don't need to manually attach CSRF tokens to the requests:
@@ -66,7 +66,7 @@ Use Twig helpers for theme and module templates. This way, you don't need to man
For raw `fetch()` requests, send the token yourself:
```javascript
-const token = document.cookie.match(/csrf_token=([^;]*)/)?.[1] || '';
+const token = document.cookie.match(/fossbilling_csrf=([^;]*)/)?.[1] || '';
fetch('/api/client/profile/update', {
method: 'POST',
diff --git a/src/content/docs/extensions-and-development/guides/creating-a-theme.mdoc b/src/content/docs/extensions-and-development/guides/creating-a-theme.mdoc
index 1f0231c..7f95118 100644
--- a/src/content/docs/extensions-and-development/guides/creating-a-theme.mdoc
+++ b/src/content/docs/extensions-and-development/guides/creating-a-theme.mdoc
@@ -77,7 +77,7 @@ For links that trigger API actions:
```
-Session-authenticated `client` and `admin` browser API calls require CSRF protection. The [JavaScript API wrapper](/extensions-and-development/javascript/) reads the `csrf_token` cookie and sends the token automatically.
+Session-authenticated `client` and `admin` browser API calls require CSRF protection. The [JavaScript API wrapper](/extensions-and-development/javascript/) reads the `fossbilling_csrf` cookie and sends the token automatically.
Use `role: 'guest'` for public actions such as login, signup, and password reset:
diff --git a/src/content/docs/extensions-and-development/javascript.mdoc b/src/content/docs/extensions-and-development/javascript.mdoc
index 833ecc0..792e3ad 100644
--- a/src/content/docs/extensions-and-development/javascript.mdoc
+++ b/src/content/docs/extensions-and-development/javascript.mdoc
@@ -37,7 +37,7 @@ document.addEventListener('DOMContentLoaded', function() {
## CSRF Tokens
-FOSSBilling uses a double-submit style token for browser API calls. Browser-rendered Twig pages expose `CSRFToken` and set a `csrf_token` cookie.
+FOSSBilling uses a double-submit style token for browser API calls. Browser-rendered Twig pages expose `CSRFToken` and set a `fossbilling_csrf` cookie.
| Call type | CSRF requirement |
|-----------|------------------|
@@ -45,7 +45,7 @@ FOSSBilling uses a double-submit style token for browser API calls. Browser-rend
| `client` or `admin` API calls with the browser session | Required. |
| External `client` or `admin` API calls using an API key | Not required. |
-The JavaScript wrapper reads the `csrf_token` cookie and sends it automatically where applicable. You don't need to manually attach the token to form data when using the wrapper.
+The JavaScript wrapper reads the `fossbilling_csrf` cookie and sends it automatically where applicable. You don't need to manually attach the token to form data when using the wrapper.
## Making Requests
@@ -219,7 +219,7 @@ Use manual `fetch()` only when the wrapper does not fit. For session-authenticat
```javascript
function getCsrfToken() {
- const match = document.cookie.match(/csrf_token=([^;]*)/);
+ const match = document.cookie.match(/fossbilling_csrf=([^;]*)/);
return match ? decodeURIComponent(match[1]) : '';
}
diff --git a/src/content/docs/index.mdoc b/src/content/docs/index.mdoc
index 2f4a5ad..91a160b 100644
--- a/src/content/docs/index.mdoc
+++ b/src/content/docs/index.mdoc
@@ -4,9 +4,9 @@ description: Learn what FOSSBilling is, where to start, and how to get involved
tableOfContents: false
banner:
content: |
- 0.8.5 is here! Check out the
- release notes
- and the 0.7 → 0.8 upgrade guide for details.
+ 0.8.6 is here! Check out the
+ release notes
+ and 0.7 → 0.8 upgrade guide for details.
---
FOSSBilling (*FOSS*: Free and Open Source Software) is a billing and client management solution for hosting providers and digital service businesses.
diff --git a/src/content/docs/maintenance/Updating/0-7-to-0-8.mdoc b/src/content/docs/maintenance/Updating/0-7-to-0-8.mdoc
index 490e8b8..9767ca3 100644
--- a/src/content/docs/maintenance/Updating/0-7-to-0-8.mdoc
+++ b/src/content/docs/maintenance/Updating/0-7-to-0-8.mdoc
@@ -180,10 +180,10 @@ The `DebugBar_renderHead()` Twig function has been renamed to `debug_bar_render_
### CSRF Meta Tag Removed
-The `` tag has been removed from bundled themes. CSRF tokens are now sent via cookie (`csrf_token`) and handled automatically by the JavaScript API wrapper. If your custom theme relies on the meta tag, switch to reading the cookie:
+The `` tag has been removed from bundled themes. CSRF tokens are now sent via cookie (`fossbilling_csrf` since 0.8.6, `csrf_token` before) and handled automatically by the JavaScript API wrapper. If your custom theme relies on the meta tag, switch to reading the cookie:
```javascript
-const token = document.cookie.match(/csrf_token=([^;]*)/)?.[1] || '';
+const token = document.cookie.match(/fossbilling_csrf=([^;]*)/)?.[1] || '';
```
Build output is written to theme/public asset directories such as `src/public/assets`, not to a tracked `frontend/build` directory.
@@ -516,6 +516,31 @@ Custom modules or themes that check permissions directly via `$staff->hasPermiss
Existing staff group assignments are migrated automatically, with legacy non-admin staff from the old default group assigned to the **Migrated staff** group. For untouched legacy groups, `manage_settings` is backfilled on selected core modules where access was already granted to preserve prior behavior. After updating, review staff group assignments and permissions in the admin panel. Use the `has_permission` Twig function for template checks; in PHP, use the staff service's group-aware methods instead of reading the `permissions` field directly.
+## Cookie Standardization & Security Hardening (0.8.6)
+
+### Breaking: Cookie Names
+
+{% aside type="caution" %}
+FOSSBilling cookies now use standardized `fossbilling_*` names. Existing sessions and CSRF, locale, and timezone cookies are migrated transparently, but custom themes or scripts that read the legacy cookie names (`csrf_token`, `BBLANG`, `fb_locale`, `fb_timezone`) directly will need updating.
+{% /aside %}
+
+FOSSBilling migrates existing cookies automatically during browser requests. Update any custom code that reads cookies directly:
+
+```javascript
+// Before (0.8.5 and earlier)
+const token = document.cookie.match(/csrf_token=([^;]*)/)?.[1] || '';
+
+// After (0.8.6)
+const token = document.cookie.match(/fossbilling_csrf=([^;]*)/)?.[1] || '';
+```
+
+The bundled JavaScript API wrapper (`js/api.js`) already handles the new name, so themes using `API.*` or `fb_api_form`/`fb_api_link` require no change. The old names are no longer set after the upgrade.
+
+### Security
+
+- **CSV exports** (clients, invoices, orders) now strip secret columns and require both `view` and `export` permissions. Staff with only `export` will no longer see full records - review group permissions if exports appear empty.
+- **Extension permissions:** `manage_extensions` is now enforced for inactive modules. Staff without that permission can no longer open their configuration pages.
+
## Deprecations & Removals (Summary)
| Component | Status | Notes |
diff --git a/src/content/docs/maintenance/changelog.mdoc b/src/content/docs/maintenance/changelog.mdoc
index 2bf6830..dbc8ea3 100644
--- a/src/content/docs/maintenance/changelog.mdoc
+++ b/src/content/docs/maintenance/changelog.mdoc
@@ -18,6 +18,17 @@ FOSSBilling publishes release notes and tagged versions on GitHub. Use the links
For the latest changes, start with the [most recent release](https://github.com/FOSSBilling/FOSSBilling/releases/latest).
+### Version 0.8.6
+
+| Area | Summary |
+|------|---------|
+| **Breaking: Cookies** | Cookie names standardized to `fossbilling_*` (`csrf_token` → `fossbilling_csrf`, `BBLANG`/`fb_locale`/`fb_timezone` etc.). Migrated transparently, but custom themes or scripts reading legacy names directly must update. |
+| **Security** | CSV exports hardened against secret columns and now require `view` + `export` permissions; cart config injection via downloadable products blocked; `manage_extensions` enforced for inactive modules; update archives verified via SHA-256 before extraction; hosting plan listings scoped to enabled products with order values cross-checked. |
+| **New Features** | Currency format patterns and fraction digits with per-currency manual rate overrides; promo redemption history and duplicate action; `Enable Add Funds` toggle for client top-ups; global Bcc for outgoing mail; ticket priority in email previews; failed invoice-item tracking with attempt counter and Failed Items page; opt-in cron to prune stale never-paid `pending_setup` orders; TLD periods can be restricted to an explicit list; order details show discount and post-discount amount; Update page surfaces preview build. |
+| **Bug Fixes** | PayPal IPN backslash stripping; Namecheap expiration dates; Plesk hosting-plan assignment and invalid webspace `set`; DirectAdmin package value reset; WHM reseller ACL; domain expiry sync not refreshing and sync marker advancing on failure; hosting orders stuck in `pending_setup` with duplicate `createacct`; checkout rolling back entire cart on partial failure; promo setup-fee waiver on non-applicable products; duplicate balance credits; race conditions: invoice-number collisions, credit double-spend, stock oversell, and Stripe webhook double-processing serialized; Stripe non-integer amount validation; cron client emails failing without admin session; session regeneration grace period hardcoded to zero and stale admin identity on client login; password reset confirmation and signup country selector timeout; new-order form crash for products missing period prices; one-time product checkout, TLD handler, and undefined `addons` variable; Cookie Consent crash when unconfigured; currency table missing columns and incomplete custom fields; client group not persisting and group-0 search errors; core-update login lockout and file-swap race; exception handler on non-integer codes; mass mailer preview, order button settings, and Service Management tab fatals; gateway highlight and dark border; inconsistent overdue invoice cleanup now routed through normal deletion. |
+
+[View the full 0.8.6 release notes](https://github.com/FOSSBilling/FOSSBilling/releases/tag/0.8.6) for the complete list of changes.
+
### Version 0.8.5
| Area | Summary |