n8n-nodes-whmcs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +87 -0
- package/dist/credentials/WhmcsApi.credentials.d.ts +9 -0
- package/dist/credentials/WhmcsApi.credentials.js +102 -0
- package/dist/credentials/WhmcsApi.credentials.js.map +1 -0
- package/dist/nodes/Whmcs/GenericFunctions.d.ts +4 -0
- package/dist/nodes/Whmcs/GenericFunctions.js +73 -0
- package/dist/nodes/Whmcs/GenericFunctions.js.map +1 -0
- package/dist/nodes/Whmcs/Whmcs.node.d.ts +5 -0
- package/dist/nodes/Whmcs/Whmcs.node.js +213 -0
- package/dist/nodes/Whmcs/Whmcs.node.js.map +1 -0
- package/dist/nodes/Whmcs/WhmcsTrigger.node.d.ts +12 -0
- package/dist/nodes/Whmcs/WhmcsTrigger.node.js +102 -0
- package/dist/nodes/Whmcs/WhmcsTrigger.node.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/ClientDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/ClientDescription.js +97 -0
- package/dist/nodes/Whmcs/descriptions/ClientDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/CustomDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/CustomDescription.js +43 -0
- package/dist/nodes/Whmcs/descriptions/CustomDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/DomainDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/DomainDescription.js +98 -0
- package/dist/nodes/Whmcs/descriptions/DomainDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/InvoiceDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/InvoiceDescription.js +100 -0
- package/dist/nodes/Whmcs/descriptions/InvoiceDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/OrderDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/OrderDescription.js +79 -0
- package/dist/nodes/Whmcs/descriptions/OrderDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/ProductDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/ProductDescription.js +95 -0
- package/dist/nodes/Whmcs/descriptions/ProductDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/SharedFields.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/SharedFields.js +52 -0
- package/dist/nodes/Whmcs/descriptions/SharedFields.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/SystemDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/SystemDescription.js +61 -0
- package/dist/nodes/Whmcs/descriptions/SystemDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/TicketDescription.d.ts +3 -0
- package/dist/nodes/Whmcs/descriptions/TicketDescription.js +99 -0
- package/dist/nodes/Whmcs/descriptions/TicketDescription.js.map +1 -0
- package/dist/nodes/Whmcs/descriptions/index.d.ts +8 -0
- package/dist/nodes/Whmcs/descriptions/index.js +25 -0
- package/dist/nodes/Whmcs/descriptions/index.js.map +1 -0
- package/dist/nodes/Whmcs/whmcs.svg +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/index.js +3 -0
- package/package.json +63 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anthony Tuberville
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# n8n-nodes-whmcs
|
|
2
|
+
|
|
3
|
+
A community node package that brings the [WHMCS](https://www.whmcs.com/) billing &
|
|
4
|
+
automation API into [n8n](https://n8n.io/) as native nodes.
|
|
5
|
+
|
|
6
|
+
It ships two nodes:
|
|
7
|
+
|
|
8
|
+
- **WHMCS** — an action node covering Clients, Orders, Invoices, Products/Services,
|
|
9
|
+
Tickets, Domains and System operations, plus a *Custom API Call* escape hatch that
|
|
10
|
+
can invoke any of the 200+ documented WHMCS actions.
|
|
11
|
+
- **WHMCS Trigger** — a webhook node that starts workflows from WHMCS hook events
|
|
12
|
+
(new order, invoice paid, ticket opened, …) via a small PHP bridge file.
|
|
13
|
+
|
|
14
|
+
> Full design, endpoint, authentication and workflow documentation lives in
|
|
15
|
+
> [`WHMCS-n8n-Node-Technical-Documentation.md`](./WHMCS-n8n-Node-Technical-Documentation.md)
|
|
16
|
+
> (in the parent folder).
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### Via the n8n UI (recommended)
|
|
21
|
+
1. In n8n go to **Settings → Community Nodes → Install**.
|
|
22
|
+
2. Enter `n8n-nodes-whmcs` and confirm.
|
|
23
|
+
|
|
24
|
+
### Manual / self-hosted
|
|
25
|
+
```bash
|
|
26
|
+
cd ~/.n8n/nodes # or your N8N_CUSTOM_EXTENSIONS path
|
|
27
|
+
npm install n8n-nodes-whmcs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Build from source
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/Falcyn/n8n-nodes-whmcs.git
|
|
33
|
+
cd n8n-nodes-whmcs
|
|
34
|
+
npm install
|
|
35
|
+
npm run build # tsc + copies icons into dist/
|
|
36
|
+
npm run lint # eslint with eslint-plugin-n8n-nodes-base
|
|
37
|
+
```
|
|
38
|
+
The compiled output in `dist/` is what n8n loads (see the `n8n` block in `package.json`).
|
|
39
|
+
|
|
40
|
+
## Credentials
|
|
41
|
+
|
|
42
|
+
Create an API credential in WHMCS first:
|
|
43
|
+
|
|
44
|
+
1. **Configuration → System Settings → Manage API Credentials → Generate New API Credential.**
|
|
45
|
+
2. Pick an admin role that has the **API Access** permission.
|
|
46
|
+
3. Copy the **Identifier** and **Secret** (the secret is shown only once).
|
|
47
|
+
|
|
48
|
+
Then in n8n add a **WHMCS API** credential:
|
|
49
|
+
|
|
50
|
+
| Field | Example | Notes |
|
|
51
|
+
|-------|---------|-------|
|
|
52
|
+
| WHMCS Base URL | `https://billing.example.com` | No trailing slash |
|
|
53
|
+
| API Path | `/includes/api.php` | Default; change only for sub-dir installs |
|
|
54
|
+
| API Identifier | `D4j1dKYE…` | From WHMCS |
|
|
55
|
+
| API Secret | `F1CKGXRI…` | From WHMCS |
|
|
56
|
+
| Access Key | *(optional)* | Matches `$api_access_key` in `configuration.php`; bypasses IP allow-listing |
|
|
57
|
+
| Ignore SSL Issues | off | Enable only for trusted internal hosts |
|
|
58
|
+
|
|
59
|
+
The credential **Test** button calls the harmless `WhmcsDetails` action to confirm the
|
|
60
|
+
identifier/secret and IP/access-key configuration.
|
|
61
|
+
|
|
62
|
+
## Trigger setup (optional)
|
|
63
|
+
|
|
64
|
+
1. Add a **WHMCS Trigger** node and copy its **Production URL**.
|
|
65
|
+
2. Copy `bridge/whmcs-hook-bridge.php` into your WHMCS `includes/hooks/` directory.
|
|
66
|
+
3. Edit the file: set `$n8nWebhookUrl` to the Production URL, optionally set
|
|
67
|
+
`$sharedSecret`, and list the events in `$forwardEvents`.
|
|
68
|
+
4. Set the same secret in the node's **Shared Secret** field.
|
|
69
|
+
|
|
70
|
+
## Quick example
|
|
71
|
+
|
|
72
|
+
*Get every unpaid invoice for client 42:*
|
|
73
|
+
|
|
74
|
+
- Resource **Invoice** → Operation **Get Many**
|
|
75
|
+
- Filters → Client ID `42`, Status `Unpaid`
|
|
76
|
+
|
|
77
|
+
## Disclaimer
|
|
78
|
+
|
|
79
|
+
This is an unofficial, community-maintained project. It is **not affiliated with,
|
|
80
|
+
endorsed by, or sponsored by WHMCS Limited**. "WHMCS" is a trademark of WHMCS Limited
|
|
81
|
+
and is used here only to describe API compatibility. This package contains **no WHMCS
|
|
82
|
+
source code** — it communicates with WHMCS solely through its public, documented API.
|
|
83
|
+
The software is provided "as is", without warranty of any kind (see [LICENSE](LICENSE.md)).
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
[MIT](LICENSE.md) © Anthony Tuberville
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestOptions, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class WhmcsApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions>;
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WhmcsApi = void 0;
|
|
4
|
+
class WhmcsApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'whmcsApi';
|
|
7
|
+
this.displayName = 'WHMCS API';
|
|
8
|
+
this.documentationUrl = 'https://developers.whmcs.com/api/';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'WHMCS Base URL',
|
|
12
|
+
name: 'baseUrl',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: '',
|
|
15
|
+
placeholder: 'https://billing.example.com',
|
|
16
|
+
required: true,
|
|
17
|
+
description: 'Root URL of your WHMCS installation, without a trailing slash. The node appends the API path automatically.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'API Path',
|
|
21
|
+
name: 'apiPath',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: '/includes/api.php',
|
|
24
|
+
required: true,
|
|
25
|
+
description: 'Path to the WHMCS external API endpoint relative to the base URL. Only change this if your installation lives in a sub-directory.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
displayName: 'API Identifier',
|
|
29
|
+
name: 'identifier',
|
|
30
|
+
type: 'string',
|
|
31
|
+
default: '',
|
|
32
|
+
required: true,
|
|
33
|
+
description: 'The API Identifier generated in WHMCS under Configuration > System Settings > Manage API Credentials.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
displayName: 'API Secret',
|
|
37
|
+
name: 'secret',
|
|
38
|
+
type: 'string',
|
|
39
|
+
typeOptions: { password: true },
|
|
40
|
+
default: '',
|
|
41
|
+
required: true,
|
|
42
|
+
description: 'The API Secret paired with the identifier above. Store it securely — WHMCS only shows it once.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
displayName: 'Access Key',
|
|
46
|
+
name: 'accessKey',
|
|
47
|
+
type: 'string',
|
|
48
|
+
typeOptions: { password: true },
|
|
49
|
+
default: '',
|
|
50
|
+
description: 'Optional. The value of $api_access_key from configuration.php. Set this if your n8n host IP is not whitelisted under Setup > General Settings > Security and you want to bypass IP restrictions.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
displayName: 'Ignore SSL Issues',
|
|
54
|
+
name: 'allowUnauthorizedCerts',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
default: false,
|
|
57
|
+
description: 'Whether to connect even if SSL certificate validation fails. Only enable for trusted internal/staging hosts.',
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
this.test = {
|
|
61
|
+
request: {
|
|
62
|
+
baseURL: '={{$credentials.baseUrl.replace(new RegExp("/$"), "")}}',
|
|
63
|
+
url: '={{$credentials.apiPath}}',
|
|
64
|
+
method: 'POST',
|
|
65
|
+
body: {
|
|
66
|
+
action: 'WhmcsDetails',
|
|
67
|
+
responsetype: 'json',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
rules: [
|
|
71
|
+
{
|
|
72
|
+
type: 'responseSuccessBody',
|
|
73
|
+
properties: {
|
|
74
|
+
key: 'result',
|
|
75
|
+
value: 'error',
|
|
76
|
+
message: 'WHMCS rejected the credentials. Check the identifier/secret, the API Access role permission, and IP/access-key restrictions.',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async authenticate(credentials, requestOptions) {
|
|
83
|
+
var _a;
|
|
84
|
+
const body = (_a = requestOptions.body) !== null && _a !== void 0 ? _a : {};
|
|
85
|
+
body.identifier = credentials.identifier;
|
|
86
|
+
body.secret = credentials.secret;
|
|
87
|
+
if (credentials.accessKey) {
|
|
88
|
+
body.accesskey = credentials.accessKey;
|
|
89
|
+
}
|
|
90
|
+
if (!body.responsetype) {
|
|
91
|
+
body.responsetype = 'json';
|
|
92
|
+
}
|
|
93
|
+
requestOptions.body = body;
|
|
94
|
+
requestOptions.headers = {
|
|
95
|
+
...requestOptions.headers,
|
|
96
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
97
|
+
};
|
|
98
|
+
return requestOptions;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.WhmcsApi = WhmcsApi;
|
|
102
|
+
//# sourceMappingURL=WhmcsApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WhmcsApi.credentials.js","sourceRoot":"","sources":["../../credentials/WhmcsApi.credentials.ts"],"names":[],"mappings":";;;AAoBA,MAAa,QAAQ;IAArB;QACC,SAAI,GAAG,UAAU,CAAC;QAElB,gBAAW,GAAG,WAAW,CAAC;QAE1B,qBAAgB,GAAG,mCAAmC,CAAC;QAEvD,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,gBAAgB;gBAC7B,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,6BAA6B;gBAC1C,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,6GAA6G;aAC9G;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,mBAAmB;gBAC5B,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,mIAAmI;aACpI;YACD;gBACC,WAAW,EAAE,gBAAgB;gBAC7B,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,uGAAuG;aACxG;YACD;gBACC,WAAW,EAAE,YAAY;gBACzB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,gGAAgG;aAC7G;YACD;gBACC,WAAW,EAAE,YAAY;gBACzB,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EACV,kMAAkM;aACnM;YACD;gBACC,WAAW,EAAE,mBAAmB;gBAChC,IAAI,EAAE,wBAAwB;gBAC9B,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;gBACd,WAAW,EACV,8GAA8G;aAC/G;SACD,CAAC;QAkCF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,yDAAyD;gBAClE,GAAG,EAAE,2BAA2B;gBAChC,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE;oBACL,MAAM,EAAE,cAAc;oBACtB,YAAY,EAAE,MAAM;iBACpB;aACD;YACD,KAAK,EAAE;gBACN;oBACC,IAAI,EAAE,qBAAqB;oBAC3B,UAAU,EAAE;wBACX,GAAG,EAAE,QAAQ;wBACb,KAAK,EAAE,OAAO;wBACd,OAAO,EACN,8HAA8H;qBAC/H;iBACD;aACD;SACD,CAAC;IACH,CAAC;IAlDA,KAAK,CAAC,YAAY,CACjB,WAA2C,EAC3C,cAAmC;;QAEnC,MAAM,IAAI,GAAG,MAAC,cAAc,CAAC,IAAgC,mCAAI,EAAE,CAAC;QAEpE,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;QACjC,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACxB,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC5B,CAAC;QAED,cAAc,CAAC,IAAI,GAAG,IAAI,CAAC;QAC3B,cAAc,CAAC,OAAO,GAAG;YACxB,GAAG,cAAc,CAAC,OAAO;YACzB,cAAc,EAAE,mCAAmC;SACnD,CAAC;QAEF,OAAO,cAAc,CAAC;IACvB,CAAC;CA4BD;AAtHD,4BAsHC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IDataObject, IExecuteFunctions, IHookFunctions, ILoadOptionsFunctions } from 'n8n-workflow';
|
|
2
|
+
export declare function whmcsApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions, action: string, body?: IDataObject): Promise<IDataObject>;
|
|
3
|
+
export declare function parseCustomParameters(raw: IDataObject | undefined): IDataObject;
|
|
4
|
+
export declare function maybeJsonToObject(this: IExecuteFunctions, value: string | IDataObject, fieldName: string, itemIndex: number): IDataObject;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.whmcsApiRequest = whmcsApiRequest;
|
|
4
|
+
exports.parseCustomParameters = parseCustomParameters;
|
|
5
|
+
exports.maybeJsonToObject = maybeJsonToObject;
|
|
6
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
7
|
+
async function whmcsApiRequest(action, body = {}) {
|
|
8
|
+
var _a;
|
|
9
|
+
const credentials = await this.getCredentials('whmcsApi');
|
|
10
|
+
const baseUrl = credentials.baseUrl.replace(/\/$/, '');
|
|
11
|
+
const apiPath = credentials.apiPath || '/includes/api.php';
|
|
12
|
+
const form = { action, responsetype: 'json' };
|
|
13
|
+
for (const [key, value] of Object.entries(body)) {
|
|
14
|
+
if (value === undefined || value === null || value === '') {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
form[key] = value;
|
|
18
|
+
}
|
|
19
|
+
const options = {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
url: `${baseUrl}${apiPath}`,
|
|
22
|
+
body: form,
|
|
23
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
24
|
+
json: true,
|
|
25
|
+
skipSslCertificateValidation: credentials.allowUnauthorizedCerts === true,
|
|
26
|
+
};
|
|
27
|
+
let response;
|
|
28
|
+
try {
|
|
29
|
+
response = (await this.helpers.httpRequestWithAuthentication.call(this, 'whmcsApi', options));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
33
|
+
}
|
|
34
|
+
if (typeof response === 'string') {
|
|
35
|
+
try {
|
|
36
|
+
response = JSON.parse(response);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `WHMCS returned a non-JSON response for action "${action}". Confirm responsetype=json is permitted.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (response.result === 'error') {
|
|
43
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `WHMCS API error on "${action}": ${(_a = response.message) !== null && _a !== void 0 ? _a : 'Unknown error'}`, { description: 'Check the action parameters and the API role permissions in WHMCS.' });
|
|
44
|
+
}
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
function parseCustomParameters(raw) {
|
|
48
|
+
var _a;
|
|
49
|
+
const out = {};
|
|
50
|
+
if (!raw)
|
|
51
|
+
return out;
|
|
52
|
+
const params = (_a = raw.parameter) !== null && _a !== void 0 ? _a : [];
|
|
53
|
+
for (const p of params) {
|
|
54
|
+
if (p.name !== undefined && p.name !== '') {
|
|
55
|
+
out[p.name] = p.value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function maybeJsonToObject(value, fieldName, itemIndex) {
|
|
61
|
+
if (typeof value !== 'string')
|
|
62
|
+
return value;
|
|
63
|
+
const trimmed = value.trim();
|
|
64
|
+
if (trimmed === '')
|
|
65
|
+
return {};
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(trimmed);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The field "${fieldName}" must contain valid JSON.`, { itemIndex });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=GenericFunctions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenericFunctions.js","sourceRoot":"","sources":["../../../nodes/Whmcs/GenericFunctions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Whmcs = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
6
|
+
const descriptions_1 = require("./descriptions");
|
|
7
|
+
const ACTION_MAP = {
|
|
8
|
+
client: {
|
|
9
|
+
create: 'AddClient',
|
|
10
|
+
get: 'GetClientsDetails',
|
|
11
|
+
getAll: 'GetClients',
|
|
12
|
+
update: 'UpdateClient',
|
|
13
|
+
delete: 'DeleteClient',
|
|
14
|
+
close: 'CloseClient',
|
|
15
|
+
getProducts: 'GetClientsProducts',
|
|
16
|
+
getDomains: 'GetClientsDomains',
|
|
17
|
+
},
|
|
18
|
+
order: {
|
|
19
|
+
create: 'AddOrder',
|
|
20
|
+
getAll: 'GetOrders',
|
|
21
|
+
accept: 'AcceptOrder',
|
|
22
|
+
pending: 'PendingOrder',
|
|
23
|
+
cancel: 'CancelOrder',
|
|
24
|
+
delete: 'DeleteOrder',
|
|
25
|
+
fraud: 'FraudOrder',
|
|
26
|
+
},
|
|
27
|
+
invoice: {
|
|
28
|
+
create: 'CreateInvoice',
|
|
29
|
+
get: 'GetInvoice',
|
|
30
|
+
getAll: 'GetInvoices',
|
|
31
|
+
update: 'UpdateInvoice',
|
|
32
|
+
addPayment: 'AddInvoicePayment',
|
|
33
|
+
applyCredit: 'ApplyCredit',
|
|
34
|
+
generate: 'GenInvoices',
|
|
35
|
+
},
|
|
36
|
+
product: {
|
|
37
|
+
getCatalogue: 'GetProducts',
|
|
38
|
+
getServices: 'GetClientsProducts',
|
|
39
|
+
update: 'UpdateClientProduct',
|
|
40
|
+
upgrade: 'UpgradeProduct',
|
|
41
|
+
suspend: 'ModuleSuspend',
|
|
42
|
+
unsuspend: 'ModuleUnsuspend',
|
|
43
|
+
terminate: 'ModuleTerminate',
|
|
44
|
+
},
|
|
45
|
+
ticket: {
|
|
46
|
+
create: 'OpenTicket',
|
|
47
|
+
get: 'GetTicket',
|
|
48
|
+
getAll: 'GetTickets',
|
|
49
|
+
reply: 'AddTicketReply',
|
|
50
|
+
update: 'UpdateTicket',
|
|
51
|
+
delete: 'DeleteTicket',
|
|
52
|
+
getDepartments: 'GetSupportDepartments',
|
|
53
|
+
},
|
|
54
|
+
domain: {
|
|
55
|
+
getAll: 'GetClientsDomains',
|
|
56
|
+
register: 'DomainRegister',
|
|
57
|
+
renew: 'DomainRenew',
|
|
58
|
+
transfer: 'DomainTransfer',
|
|
59
|
+
getNameservers: 'DomainGetNameservers',
|
|
60
|
+
updateNameservers: 'DomainUpdateNameservers',
|
|
61
|
+
toggleIdProtect: 'DomainToggleIdProtect',
|
|
62
|
+
update: 'UpdateClientDomain',
|
|
63
|
+
},
|
|
64
|
+
system: {
|
|
65
|
+
getStats: 'GetStats',
|
|
66
|
+
details: 'WhmcsDetails',
|
|
67
|
+
sendEmail: 'SendEmail',
|
|
68
|
+
getActivityLog: 'GetActivityLog',
|
|
69
|
+
getCurrencies: 'GetCurrencies',
|
|
70
|
+
getPaymentMethods: 'GetPaymentMethods',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
const FLATTEN_COLLECTIONS = [
|
|
74
|
+
'additionalFields',
|
|
75
|
+
'updateFields',
|
|
76
|
+
'filters',
|
|
77
|
+
'paymentFields',
|
|
78
|
+
'replyOptions',
|
|
79
|
+
'acceptOptions',
|
|
80
|
+
'orderItems',
|
|
81
|
+
'registerFields',
|
|
82
|
+
'upgradeFields',
|
|
83
|
+
'updateNameservers',
|
|
84
|
+
'nameservers',
|
|
85
|
+
'serviceFilters',
|
|
86
|
+
'catalogueFilters',
|
|
87
|
+
'logFilters',
|
|
88
|
+
'emailFields',
|
|
89
|
+
];
|
|
90
|
+
class Whmcs {
|
|
91
|
+
constructor() {
|
|
92
|
+
this.description = {
|
|
93
|
+
displayName: 'WHMCS',
|
|
94
|
+
name: 'whmcs',
|
|
95
|
+
icon: 'file:whmcs.svg',
|
|
96
|
+
group: ['transform'],
|
|
97
|
+
version: 1,
|
|
98
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
99
|
+
description: 'Interact with the WHMCS billing & automation API',
|
|
100
|
+
defaults: { name: 'WHMCS' },
|
|
101
|
+
inputs: ['main'],
|
|
102
|
+
outputs: ['main'],
|
|
103
|
+
credentials: [{ name: 'whmcsApi', required: true }],
|
|
104
|
+
properties: [
|
|
105
|
+
{
|
|
106
|
+
displayName: 'Resource',
|
|
107
|
+
name: 'resource',
|
|
108
|
+
type: 'options',
|
|
109
|
+
noDataExpression: true,
|
|
110
|
+
options: [
|
|
111
|
+
{ name: 'Client', value: 'client' },
|
|
112
|
+
{ name: 'Custom API Call', value: 'custom' },
|
|
113
|
+
{ name: 'Domain', value: 'domain' },
|
|
114
|
+
{ name: 'Invoice', value: 'invoice' },
|
|
115
|
+
{ name: 'Order', value: 'order' },
|
|
116
|
+
{ name: 'Product / Service', value: 'product' },
|
|
117
|
+
{ name: 'System', value: 'system' },
|
|
118
|
+
{ name: 'Ticket', value: 'ticket' },
|
|
119
|
+
],
|
|
120
|
+
default: 'client',
|
|
121
|
+
},
|
|
122
|
+
...descriptions_1.clientOperations,
|
|
123
|
+
...descriptions_1.clientFields,
|
|
124
|
+
...descriptions_1.orderOperations,
|
|
125
|
+
...descriptions_1.orderFields,
|
|
126
|
+
...descriptions_1.invoiceOperations,
|
|
127
|
+
...descriptions_1.invoiceFields,
|
|
128
|
+
...descriptions_1.productOperations,
|
|
129
|
+
...descriptions_1.productFields,
|
|
130
|
+
...descriptions_1.ticketOperations,
|
|
131
|
+
...descriptions_1.ticketFields,
|
|
132
|
+
...descriptions_1.domainOperations,
|
|
133
|
+
...descriptions_1.domainFields,
|
|
134
|
+
...descriptions_1.systemOperations,
|
|
135
|
+
...descriptions_1.systemFields,
|
|
136
|
+
...descriptions_1.customOperations,
|
|
137
|
+
...descriptions_1.customFields,
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async execute() {
|
|
142
|
+
var _a;
|
|
143
|
+
const items = this.getInputData();
|
|
144
|
+
const returnData = [];
|
|
145
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
146
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
147
|
+
for (let i = 0; i < items.length; i++) {
|
|
148
|
+
try {
|
|
149
|
+
let action;
|
|
150
|
+
const body = {};
|
|
151
|
+
if (resource === 'custom') {
|
|
152
|
+
action = this.getNodeParameter('action', i);
|
|
153
|
+
const rawParams = this.getNodeParameter('parameters', i, {});
|
|
154
|
+
const parsed = typeof rawParams === 'string' ? JSON.parse(rawParams || '{}') : rawParams;
|
|
155
|
+
Object.assign(body, parsed);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
action = (_a = ACTION_MAP[resource]) === null || _a === void 0 ? void 0 : _a[operation];
|
|
159
|
+
if (!action) {
|
|
160
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported operation "${operation}" for resource "${resource}".`);
|
|
161
|
+
}
|
|
162
|
+
const SIMPLE_FIELDS = [
|
|
163
|
+
'firstname', 'lastname', 'email', 'password2', 'clientid', 'userid',
|
|
164
|
+
'orderid', 'paymentmethod', 'invoiceid', 'transid', 'amount',
|
|
165
|
+
'serviceid', 'suspendreason', 'deptid', 'subject', 'message', 'ticketid',
|
|
166
|
+
'domainid', 'regperiod', 'eppcode', 'idprotect', 'messagename',
|
|
167
|
+
];
|
|
168
|
+
for (const field of SIMPLE_FIELDS) {
|
|
169
|
+
const value = this.getNodeParameter(field, i, undefined);
|
|
170
|
+
if (value !== undefined && value !== '' && value !== 0) {
|
|
171
|
+
body[field] = value;
|
|
172
|
+
}
|
|
173
|
+
else if (value === 0 && ['clientid', 'invoiceid', 'orderid', 'ticketid', 'domainid', 'serviceid', 'userid', 'deptid'].includes(field)) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
for (const coll of FLATTEN_COLLECTIONS) {
|
|
178
|
+
const value = this.getNodeParameter(coll, i, {});
|
|
179
|
+
if (value && typeof value === 'object') {
|
|
180
|
+
Object.assign(body, value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const returnAll = this.getNodeParameter('returnAll', i, false);
|
|
184
|
+
if (operation === 'getAll') {
|
|
185
|
+
if (!returnAll) {
|
|
186
|
+
body.limitnum = this.getNodeParameter('limit', i, 25);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
body.limitnum = 1000;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const custom = this.getNodeParameter('customParameters', i, {});
|
|
193
|
+
Object.assign(body, (0, GenericFunctions_1.parseCustomParameters)(custom));
|
|
194
|
+
}
|
|
195
|
+
const response = await GenericFunctions_1.whmcsApiRequest.call(this, action, body);
|
|
196
|
+
returnData.push({
|
|
197
|
+
json: response,
|
|
198
|
+
pairedItem: { item: i },
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (this.continueOnFail()) {
|
|
203
|
+
returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return [returnData];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.Whmcs = Whmcs;
|
|
213
|
+
//# sourceMappingURL=Whmcs.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Whmcs.node.js","sourceRoot":"","sources":["../../../nodes/Whmcs/Whmcs.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IHookFunctions, INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
|
|
2
|
+
export declare class WhmcsTrigger implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
webhookMethods: {
|
|
5
|
+
default: {
|
|
6
|
+
checkExists(this: IHookFunctions): Promise<boolean>;
|
|
7
|
+
create(this: IHookFunctions): Promise<boolean>;
|
|
8
|
+
delete(this: IHookFunctions): Promise<boolean>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WhmcsTrigger = void 0;
|
|
4
|
+
class WhmcsTrigger {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.description = {
|
|
7
|
+
displayName: 'WHMCS Trigger',
|
|
8
|
+
name: 'whmcsTrigger',
|
|
9
|
+
icon: 'file:whmcs.svg',
|
|
10
|
+
group: ['trigger'],
|
|
11
|
+
version: 1,
|
|
12
|
+
subtitle: '={{$parameter["events"].join(", ")}}',
|
|
13
|
+
description: 'Starts a workflow when WHMCS fires a hook event (via the PHP hook bridge)',
|
|
14
|
+
defaults: { name: 'WHMCS Trigger' },
|
|
15
|
+
inputs: [],
|
|
16
|
+
outputs: ['main'],
|
|
17
|
+
webhooks: [
|
|
18
|
+
{
|
|
19
|
+
name: 'default',
|
|
20
|
+
httpMethod: 'POST',
|
|
21
|
+
responseMode: 'onReceived',
|
|
22
|
+
path: 'whmcs',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
properties: [
|
|
26
|
+
{
|
|
27
|
+
displayName: 'Install the bundled <code>whmcs-hook-bridge.php</code> file into your WHMCS <code>/includes/hooks/</code> directory and set its <code>$n8nWebhookUrl</code> to the Production URL shown below.',
|
|
28
|
+
name: 'setupNotice',
|
|
29
|
+
type: 'notice',
|
|
30
|
+
default: '',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
displayName: 'Events',
|
|
34
|
+
name: 'events',
|
|
35
|
+
type: 'multiOptions',
|
|
36
|
+
description: 'WHMCS hook point(s) that should trigger this workflow. Must match the events the PHP bridge is configured to forward.',
|
|
37
|
+
options: [
|
|
38
|
+
{ name: 'After Module Create', value: 'AfterModuleCreate' },
|
|
39
|
+
{ name: 'After Module Suspend', value: 'AfterModuleSuspend' },
|
|
40
|
+
{ name: 'After Module Terminate', value: 'AfterModuleTerminate' },
|
|
41
|
+
{ name: 'Any Event', value: '*' },
|
|
42
|
+
{ name: 'Client Add', value: 'ClientAdd' },
|
|
43
|
+
{ name: 'Client Close', value: 'ClientClose' },
|
|
44
|
+
{ name: 'Invoice Created', value: 'InvoiceCreated' },
|
|
45
|
+
{ name: 'Invoice Paid', value: 'InvoicePaid' },
|
|
46
|
+
{ name: 'Order Accepted', value: 'AcceptOrder' },
|
|
47
|
+
{ name: 'Order Pending', value: 'OrderPaid' },
|
|
48
|
+
{ name: 'Ticket Open', value: 'TicketOpen' },
|
|
49
|
+
{ name: 'Ticket Reply', value: 'TicketUserReply' },
|
|
50
|
+
],
|
|
51
|
+
default: ['*'],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Shared Secret',
|
|
55
|
+
name: 'sharedSecret',
|
|
56
|
+
type: 'string',
|
|
57
|
+
typeOptions: { password: true },
|
|
58
|
+
default: '',
|
|
59
|
+
description: 'Optional. If set, the PHP bridge must send the same value in the X-WHMCS-Secret header. Requests with a missing or wrong secret are rejected with 401.',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
this.webhookMethods = {
|
|
64
|
+
default: {
|
|
65
|
+
async checkExists() {
|
|
66
|
+
return true;
|
|
67
|
+
},
|
|
68
|
+
async create() {
|
|
69
|
+
return true;
|
|
70
|
+
},
|
|
71
|
+
async delete() {
|
|
72
|
+
return true;
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async webhook() {
|
|
78
|
+
var _a, _b, _c;
|
|
79
|
+
const req = this.getRequestObject();
|
|
80
|
+
const headers = this.getHeaderData();
|
|
81
|
+
const body = ((_a = req.body) !== null && _a !== void 0 ? _a : {});
|
|
82
|
+
const sharedSecret = this.getNodeParameter('sharedSecret', '');
|
|
83
|
+
if (sharedSecret) {
|
|
84
|
+
const provided = (_b = headers['x-whmcs-secret']) !== null && _b !== void 0 ? _b : '';
|
|
85
|
+
if (provided !== sharedSecret) {
|
|
86
|
+
const res = this.getResponseObject();
|
|
87
|
+
res.status(401).json({ message: 'Invalid WHMCS shared secret' });
|
|
88
|
+
return { noWebhookResponse: true };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const events = this.getNodeParameter('events', []);
|
|
92
|
+
const incomingEvent = (_c = body.event) !== null && _c !== void 0 ? _c : '';
|
|
93
|
+
if (!events.includes('*') && incomingEvent && !events.includes(incomingEvent)) {
|
|
94
|
+
return { webhookResponse: JSON.stringify({ received: true, ignored: incomingEvent }) };
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
workflowData: [this.helpers.returnJsonArray([body])],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.WhmcsTrigger = WhmcsTrigger;
|
|
102
|
+
//# sourceMappingURL=WhmcsTrigger.node.js.map
|