@toneflix/paystack-cli 0.1.5 → 0.1.7
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/README.md +4 -3
- package/bin/cli.cjs +301 -92
- package/bin/cli.js +295 -88
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Paystack CLI
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@toneflix/paystack-cli)
|
|
4
|
-
[](https://www.npmjs.com/package/@toneflix/paystack-cli)
|
|
4
|
+
[](https://www.npmjs.com/package/@toneflix/paystack-cli)
|
|
5
|
+
[](https://github.com/toneflix/paystack-cli/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/toneflix/paystack-cli/actions/workflows/ci.yml)
|
|
6
7
|
[](https://github.com/toneflix/paystack-cli/actions/workflows/deploy-docs.yml)
|
|
7
8
|
|
|
8
9
|
The Paystack CLI helps you build, test, and manage your Paystack integration right from the terminal. Interact with the Paystack API, test webhooks locally, and manage your integration settings without leaving your command line.
|
package/bin/cli.cjs
CHANGED
|
@@ -22,51 +22,80 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
|
|
24
24
|
//#endregion
|
|
25
|
-
let path = require("path");
|
|
26
|
-
path = __toESM(path);
|
|
27
25
|
let better_sqlite3 = require("better-sqlite3");
|
|
28
26
|
better_sqlite3 = __toESM(better_sqlite3);
|
|
29
|
-
let
|
|
30
|
-
|
|
27
|
+
let os = require("os");
|
|
28
|
+
os = __toESM(os);
|
|
31
29
|
let fs = require("fs");
|
|
32
30
|
fs = __toESM(fs);
|
|
31
|
+
let path = require("path");
|
|
32
|
+
path = __toESM(path);
|
|
33
33
|
let __h3ravel_shared = require("@h3ravel/shared");
|
|
34
34
|
__h3ravel_shared = __toESM(__h3ravel_shared);
|
|
35
|
+
let cli_table3 = require("cli-table3");
|
|
36
|
+
cli_table3 = __toESM(cli_table3);
|
|
35
37
|
let axios = require("axios");
|
|
36
38
|
axios = __toESM(axios);
|
|
39
|
+
let url = require("url");
|
|
40
|
+
url = __toESM(url);
|
|
37
41
|
let __h3ravel_musket = require("@h3ravel/musket");
|
|
38
42
|
__h3ravel_musket = __toESM(__h3ravel_musket);
|
|
39
|
-
let
|
|
40
|
-
|
|
43
|
+
let h3 = require("h3");
|
|
44
|
+
h3 = __toESM(h3);
|
|
45
|
+
let detect_port = require("detect-port");
|
|
46
|
+
detect_port = __toESM(detect_port);
|
|
41
47
|
let module$1 = require("module");
|
|
42
48
|
module$1 = __toESM(module$1);
|
|
43
|
-
let os = require("os");
|
|
44
|
-
os = __toESM(os);
|
|
45
49
|
let crypto = require("crypto");
|
|
46
50
|
crypto = __toESM(crypto);
|
|
47
51
|
let __ngrok_ngrok = require("@ngrok/ngrok");
|
|
48
52
|
__ngrok_ngrok = __toESM(__ngrok_ngrok);
|
|
49
53
|
|
|
54
|
+
//#region src/utils/global.ts
|
|
55
|
+
String.prototype.toKebabCase = function() {
|
|
56
|
+
return this.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
57
|
+
};
|
|
58
|
+
String.prototype.toCamelCase = function() {
|
|
59
|
+
return this.replace(/[-_ ]+([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()).replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
60
|
+
};
|
|
61
|
+
String.prototype.toPascalCase = function() {
|
|
62
|
+
return this.replace(/(^\w|[-_ ]+\w)/g, (match) => match.replace(/[-_ ]+/, "").toUpperCase());
|
|
63
|
+
};
|
|
64
|
+
String.prototype.toSnakeCase = function() {
|
|
65
|
+
return this.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
66
|
+
};
|
|
67
|
+
String.prototype.toTitleCase = function() {
|
|
68
|
+
return this.toLowerCase().replace(/(^|\s)\w/g, (match) => match.toUpperCase());
|
|
69
|
+
};
|
|
70
|
+
String.prototype.toCleanCase = function() {
|
|
71
|
+
return this.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ").trim().replace(/\b\w{1,3}\b/g, (match) => match.toUpperCase());
|
|
72
|
+
};
|
|
73
|
+
String.prototype.truncate = function(n, suffix = "…") {
|
|
74
|
+
return this.length > n ? this.slice(0, n - 1) + suffix : this.toString();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
50
78
|
//#region src/db.ts
|
|
51
79
|
let db;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
(
|
|
55
|
-
|
|
80
|
+
let dbPath = path.default.join((0, os.homedir)(), ".paystackcli");
|
|
81
|
+
(0, fs.mkdirSync)(dbPath, { recursive: true });
|
|
82
|
+
const useDbPath = () => [dbPath, (path$3) => {
|
|
83
|
+
dbPath = path$3;
|
|
84
|
+
}];
|
|
56
85
|
/**
|
|
57
86
|
* Hook to get or set the database instance.
|
|
58
87
|
*
|
|
59
88
|
* @returns
|
|
60
89
|
*/
|
|
61
90
|
const useDb = () => {
|
|
62
|
-
return [() => db, (
|
|
63
|
-
db =
|
|
91
|
+
return [() => db, (filename) => {
|
|
92
|
+
db = new better_sqlite3.default(path.default.join(dbPath, filename));
|
|
64
93
|
const [{ journal_mode }] = db.pragma("journal_mode");
|
|
65
94
|
if (journal_mode !== "wal") db.pragma("journal_mode = WAL");
|
|
66
95
|
}];
|
|
67
96
|
};
|
|
68
97
|
const [getDatabase, setDatabase] = useDb();
|
|
69
|
-
setDatabase(
|
|
98
|
+
setDatabase("app.db");
|
|
70
99
|
/**
|
|
71
100
|
* Initialize the database
|
|
72
101
|
*
|
|
@@ -114,7 +143,7 @@ function remove(key) {
|
|
|
114
143
|
* @param key
|
|
115
144
|
* @returns
|
|
116
145
|
*/
|
|
117
|
-
function read(key) {
|
|
146
|
+
function read(key, defaultValue) {
|
|
118
147
|
const db$1 = getDatabase();
|
|
119
148
|
try {
|
|
120
149
|
const row = db$1.prepare("SELECT * FROM json_store WHERE key = ?").get(key);
|
|
@@ -124,7 +153,7 @@ function read(key) {
|
|
|
124
153
|
return row.value;
|
|
125
154
|
}
|
|
126
155
|
} catch {}
|
|
127
|
-
return null;
|
|
156
|
+
return defaultValue ?? null;
|
|
128
157
|
}
|
|
129
158
|
|
|
130
159
|
//#endregion
|
|
@@ -398,6 +427,15 @@ const findCLIPackageJson = (startDir = __dirname$1) => {
|
|
|
398
427
|
}
|
|
399
428
|
return null;
|
|
400
429
|
};
|
|
430
|
+
const objectToTable = (obj, titleKeys = false) => {
|
|
431
|
+
const table = new cli_table3.default();
|
|
432
|
+
for (const rawKey in obj) {
|
|
433
|
+
if (typeof obj[rawKey] === "object") continue;
|
|
434
|
+
const key = logger((titleKeys ? rawKey.toCleanCase() : rawKey).truncate(30), ["bold"]);
|
|
435
|
+
table.push({ [key]: String(obj[rawKey]).truncate(40) });
|
|
436
|
+
}
|
|
437
|
+
return table.toString();
|
|
438
|
+
};
|
|
401
439
|
|
|
402
440
|
//#endregion
|
|
403
441
|
//#region src/paystack/apis.ts
|
|
@@ -2565,7 +2603,7 @@ const buildSignature = (param, cmd) => {
|
|
|
2565
2603
|
};
|
|
2566
2604
|
|
|
2567
2605
|
//#endregion
|
|
2568
|
-
//#region src/utils/
|
|
2606
|
+
//#region src/utils/builders.ts
|
|
2569
2607
|
/**
|
|
2570
2608
|
* We will recursively map through the result data and log each key value pair
|
|
2571
2609
|
* as we apply coloring based on the value type.
|
|
@@ -2580,7 +2618,7 @@ const dataRenderer = (data) => {
|
|
|
2580
2618
|
for (const key in obj) {
|
|
2581
2619
|
const value = obj[key];
|
|
2582
2620
|
if (typeof value === "object" && value !== null) {
|
|
2583
|
-
console.log(`${indentation}${
|
|
2621
|
+
console.log(`${indentation}${key.toCleanCase()}:`);
|
|
2584
2622
|
render(value, indent + 2);
|
|
2585
2623
|
} else {
|
|
2586
2624
|
let coloredValue;
|
|
@@ -2596,24 +2634,33 @@ const dataRenderer = (data) => {
|
|
|
2596
2634
|
break;
|
|
2597
2635
|
default: coloredValue = value;
|
|
2598
2636
|
}
|
|
2599
|
-
console.log(`${indentation}${
|
|
2637
|
+
console.log(`${indentation}${key.toCleanCase()}: ${coloredValue}`);
|
|
2600
2638
|
}
|
|
2601
2639
|
}
|
|
2602
2640
|
};
|
|
2603
2641
|
render(data);
|
|
2604
2642
|
};
|
|
2605
2643
|
/**
|
|
2606
|
-
*
|
|
2607
|
-
* capitalizing the first letter of every word,
|
|
2608
|
-
* converting camelCase to spaced words,
|
|
2609
|
-
* and trimming any leading or trailing spaces.
|
|
2610
|
-
* If a sentence is only two letters long we will make it uppercase.
|
|
2644
|
+
* Starts a mini HTTP server on the specified port to listen for incoming webhook requests.
|
|
2611
2645
|
*
|
|
2612
|
-
* @param
|
|
2646
|
+
* @param port
|
|
2613
2647
|
* @returns
|
|
2614
2648
|
*/
|
|
2615
|
-
const
|
|
2616
|
-
|
|
2649
|
+
const miniServer = async (port = 3e3) => {
|
|
2650
|
+
const route = async (event) => {
|
|
2651
|
+
console.log("Incoming Webhook Request [", event.req.method, "]");
|
|
2652
|
+
const payload = JSON.parse(await event.req.text() || "{}");
|
|
2653
|
+
return Object.assign({}, { signature: event.req.headers.get("x-paystack-signature") ?? "N/A" }, payload);
|
|
2654
|
+
};
|
|
2655
|
+
const app = new h3.H3().get("/webhook", route).post("/webhook", route);
|
|
2656
|
+
port = await (0, detect_port.detect)(port);
|
|
2657
|
+
const server = (0, h3.serve)(app, {
|
|
2658
|
+
port,
|
|
2659
|
+
silent: true
|
|
2660
|
+
});
|
|
2661
|
+
const url$1 = `http://localhost:${port}/webhook`;
|
|
2662
|
+
__h3ravel_shared.Logger.log([["🚀 Mini server is running at:", "green"], [url$1, "cyan"]], " ");
|
|
2663
|
+
return Object.assign({}, server, { url: url$1 });
|
|
2617
2664
|
};
|
|
2618
2665
|
|
|
2619
2666
|
//#endregion
|
|
@@ -2638,14 +2685,14 @@ var Commands_default = () => {
|
|
|
2638
2685
|
signature = `${key} \n${args}`;
|
|
2639
2686
|
description = schema.description || "No description available.";
|
|
2640
2687
|
handle = async () => {
|
|
2641
|
-
const [
|
|
2688
|
+
const [command$1, setCommand] = useCommand();
|
|
2642
2689
|
setCommand(this);
|
|
2643
2690
|
for (const param of schema.params) if (param.required && !this.argument(param.parameter)) return void this.newLine().error(`Missing required argument: ${param.parameter}`).newLine();
|
|
2644
2691
|
const selected_integration = read("selected_integration")?.id;
|
|
2645
2692
|
const user = read("user")?.id;
|
|
2646
2693
|
if (!selected_integration || !user) return void this.error("ERROR: You're not signed in, please run the [login] command before you begin").newLine();
|
|
2647
2694
|
this.newLine();
|
|
2648
|
-
const spinner = (
|
|
2695
|
+
const spinner = command$1().spinner("Loading...\n").start();
|
|
2649
2696
|
const [err, result] = await promiseWrapper(executeSchema(schema, {
|
|
2650
2697
|
...this.options(),
|
|
2651
2698
|
...this.arguments()
|
|
@@ -2746,31 +2793,32 @@ var InfoCommand = class extends __h3ravel_musket.Command {
|
|
|
2746
2793
|
version: "unknown",
|
|
2747
2794
|
dependencies: {}
|
|
2748
2795
|
};
|
|
2796
|
+
const user = read("user");
|
|
2749
2797
|
const pkgPath = findCLIPackageJson();
|
|
2750
2798
|
const require$1 = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
2751
|
-
const [
|
|
2752
|
-
const [dbPath] = useDbPath();
|
|
2799
|
+
const [, setCommand] = useCommand();
|
|
2800
|
+
const [dbPath$1] = useDbPath();
|
|
2753
2801
|
setCommand(this);
|
|
2754
2802
|
init();
|
|
2755
|
-
const spinner =
|
|
2803
|
+
const spinner = this.spinner("Gathering application information...\n").start();
|
|
2756
2804
|
if (pkgPath) try {
|
|
2757
2805
|
pkg = require$1(pkgPath);
|
|
2758
2806
|
} catch {}
|
|
2759
2807
|
wait(500, () => {
|
|
2760
|
-
spinner.
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
username: os.default.userInfo().username,
|
|
2771
|
-
database: dbPath + "/app.db",
|
|
2772
|
-
dependencies: Object.keys(pkg.dependencies).join(", ")
|
|
2808
|
+
spinner.succeed("Application Information Loaded.\n");
|
|
2809
|
+
const out = objectToTable({
|
|
2810
|
+
"App Version": pkg.version,
|
|
2811
|
+
"Platform": `${os.default.platform()} ${os.default.arch()} (${os.default.release()})`,
|
|
2812
|
+
"CPUs": os.default.cpus().length,
|
|
2813
|
+
"Host": `${os.default.userInfo().username}@${os.default.hostname()}`,
|
|
2814
|
+
"Memory": `${(os.default.freemem() / 1024 ** 3).toFixed(2)} GB / ${(os.default.totalmem() / 1024 ** 3).toFixed(2)} GB`,
|
|
2815
|
+
"Database Path": dbPath$1 + "/app.db",
|
|
2816
|
+
"Paystack User": user ? `${user.first_name} ${user.last_name} (ID: ${user.id})` : "Not logged in",
|
|
2817
|
+
"Default Integration": read("selected_integration")?.business_name || "Not set"
|
|
2773
2818
|
});
|
|
2819
|
+
console.log(out.toString());
|
|
2820
|
+
logger("\nDependencies:", "yellow");
|
|
2821
|
+
logger(Object.keys(pkg.dependencies).map((dep) => `${dep}`).join(", "), "green");
|
|
2774
2822
|
this.newLine();
|
|
2775
2823
|
});
|
|
2776
2824
|
}
|
|
@@ -2789,6 +2837,21 @@ var InitCommand = class extends __h3ravel_musket.Command {
|
|
|
2789
2837
|
}
|
|
2790
2838
|
};
|
|
2791
2839
|
|
|
2840
|
+
//#endregion
|
|
2841
|
+
//#region src/Commands/IntegrationInfoCommand.ts
|
|
2842
|
+
var IntegrationInfoCommand = class extends __h3ravel_musket.Command {
|
|
2843
|
+
signature = "integration:info";
|
|
2844
|
+
description = "Get information about the currently selected integration.";
|
|
2845
|
+
async handle() {
|
|
2846
|
+
const [_, setCommand] = useCommand();
|
|
2847
|
+
setCommand(this);
|
|
2848
|
+
const selected_integration = read("selected_integration");
|
|
2849
|
+
if (!selected_integration) return void this.error(`ERROR: No integration selected, please run the ${logger("integration:set", ["grey", "italic"])} command to select an integration before proceeding.`);
|
|
2850
|
+
console.log(objectToTable(selected_integration, true));
|
|
2851
|
+
this.newLine();
|
|
2852
|
+
}
|
|
2853
|
+
};
|
|
2854
|
+
|
|
2792
2855
|
//#endregion
|
|
2793
2856
|
//#region src/paystack/webhooks.ts
|
|
2794
2857
|
const webhook = {
|
|
@@ -2796,7 +2859,7 @@ const webhook = {
|
|
|
2796
2859
|
event: "charge.success",
|
|
2797
2860
|
data: {
|
|
2798
2861
|
id: 302961,
|
|
2799
|
-
domain: "
|
|
2862
|
+
domain: "test",
|
|
2800
2863
|
status: "success",
|
|
2801
2864
|
reference: "qTPrJoy9Bx",
|
|
2802
2865
|
amount: 1e4,
|
|
@@ -2863,14 +2926,14 @@ const webhook = {
|
|
|
2863
2926
|
"transfer.success": {
|
|
2864
2927
|
event: "transfer.success",
|
|
2865
2928
|
data: {
|
|
2866
|
-
domain: "
|
|
2929
|
+
domain: "test",
|
|
2867
2930
|
amount: 1e4,
|
|
2868
2931
|
currency: "NGN",
|
|
2869
2932
|
source: "balance",
|
|
2870
2933
|
source_details: null,
|
|
2871
2934
|
reason: "Bless you",
|
|
2872
2935
|
recipient: {
|
|
2873
|
-
domain: "
|
|
2936
|
+
domain: "test",
|
|
2874
2937
|
type: "nuban",
|
|
2875
2938
|
currency: "NGN",
|
|
2876
2939
|
name: "Someone",
|
|
@@ -2945,7 +3008,7 @@ const webhook = {
|
|
|
2945
3008
|
source_details: null,
|
|
2946
3009
|
reason: "Test",
|
|
2947
3010
|
recipient: {
|
|
2948
|
-
domain: "
|
|
3011
|
+
domain: "test",
|
|
2949
3012
|
type: "nuban",
|
|
2950
3013
|
currency: "NGN",
|
|
2951
3014
|
name: "Test account",
|
|
@@ -2965,6 +3028,95 @@ const webhook = {
|
|
|
2965
3028
|
transferred_at: null,
|
|
2966
3029
|
created_at: "2017-12-01T08:51:37.000Z"
|
|
2967
3030
|
}
|
|
3031
|
+
},
|
|
3032
|
+
"customeridentification.failed": {
|
|
3033
|
+
"event": "customeridentification.failed",
|
|
3034
|
+
"data": {
|
|
3035
|
+
"customer_id": 82796315,
|
|
3036
|
+
"customer_code": "CUS_XXXXXXXXXXXXXXX",
|
|
3037
|
+
"email": "email@email.com",
|
|
3038
|
+
"identification": {
|
|
3039
|
+
"country": "NG",
|
|
3040
|
+
"type": "bank_account",
|
|
3041
|
+
"bvn": "123*****456",
|
|
3042
|
+
"account_number": "012****345",
|
|
3043
|
+
"bank_code": "999991"
|
|
3044
|
+
},
|
|
3045
|
+
"reason": "Account number or BVN is incorrect"
|
|
3046
|
+
}
|
|
3047
|
+
},
|
|
3048
|
+
"customeridentification.success": {
|
|
3049
|
+
"event": "customeridentification.success",
|
|
3050
|
+
"data": {
|
|
3051
|
+
"customer_id": "9387490384",
|
|
3052
|
+
"customer_code": "CUS_xnxdt6s1zg1f4nx",
|
|
3053
|
+
"email": "bojack@horsinaround.com",
|
|
3054
|
+
"identification": {
|
|
3055
|
+
"country": "NG",
|
|
3056
|
+
"type": "bvn",
|
|
3057
|
+
"value": "200*****677"
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
},
|
|
3061
|
+
"dedicatedaccount.assign.failed": {
|
|
3062
|
+
"event": "dedicatedaccount.assign.failed",
|
|
3063
|
+
"data": {
|
|
3064
|
+
"customer": {
|
|
3065
|
+
"id": 100110,
|
|
3066
|
+
"first_name": "John",
|
|
3067
|
+
"last_name": "Doe",
|
|
3068
|
+
"email": "johndoe@test.com",
|
|
3069
|
+
"customer_code": "CUS_hcekca0j0bbg2m4",
|
|
3070
|
+
"phone": "+2348100000000",
|
|
3071
|
+
"metadata": {},
|
|
3072
|
+
"risk_action": "default",
|
|
3073
|
+
"international_format_phone": "+2348100000000"
|
|
3074
|
+
},
|
|
3075
|
+
"dedicated_account": null,
|
|
3076
|
+
"identification": { "status": "failed" }
|
|
3077
|
+
}
|
|
3078
|
+
},
|
|
3079
|
+
"dedicatedaccount.assign.success": {
|
|
3080
|
+
"event": "dedicatedaccount.assign.success",
|
|
3081
|
+
"data": {
|
|
3082
|
+
"customer": {
|
|
3083
|
+
"id": 100110,
|
|
3084
|
+
"first_name": "John",
|
|
3085
|
+
"last_name": "Doe",
|
|
3086
|
+
"email": "johndoe@test.com",
|
|
3087
|
+
"customer_code": "CUS_hp05n9khsqcesz2",
|
|
3088
|
+
"phone": "+2348100000000",
|
|
3089
|
+
"metadata": {},
|
|
3090
|
+
"risk_action": "default",
|
|
3091
|
+
"international_format_phone": "+2348100000000"
|
|
3092
|
+
},
|
|
3093
|
+
"dedicated_account": {
|
|
3094
|
+
"bank": {
|
|
3095
|
+
"name": "Test Bank",
|
|
3096
|
+
"id": 20,
|
|
3097
|
+
"slug": "test-bank"
|
|
3098
|
+
},
|
|
3099
|
+
"account_name": "PAYSTACK/John Doe",
|
|
3100
|
+
"account_number": "1234567890",
|
|
3101
|
+
"assigned": true,
|
|
3102
|
+
"currency": "NGN",
|
|
3103
|
+
"metadata": null,
|
|
3104
|
+
"active": true,
|
|
3105
|
+
"id": 987654,
|
|
3106
|
+
"created_at": "2022-06-21T17:12:40.000Z",
|
|
3107
|
+
"updated_at": "2022-08-12T14:02:51.000Z",
|
|
3108
|
+
"assignment": {
|
|
3109
|
+
"integration": 100123,
|
|
3110
|
+
"assignee_id": 100110,
|
|
3111
|
+
"assignee_type": "Customer",
|
|
3112
|
+
"expired": false,
|
|
3113
|
+
"account_type": "PAY-WITH-TRANSFER-RECURRING",
|
|
3114
|
+
"assigned_at": "2022-08-12T14:02:51.614Z",
|
|
3115
|
+
"expired_at": null
|
|
3116
|
+
}
|
|
3117
|
+
},
|
|
3118
|
+
"identification": { "status": "success" }
|
|
3119
|
+
}
|
|
2968
3120
|
}
|
|
2969
3121
|
};
|
|
2970
3122
|
var webhooks_default = webhook;
|
|
@@ -3101,6 +3253,7 @@ function getKeys(token, type = "secret", domain = "test") {
|
|
|
3101
3253
|
*/
|
|
3102
3254
|
async function pingWebhook(options, event = "charge.success") {
|
|
3103
3255
|
const [command] = useCommand();
|
|
3256
|
+
const cmd = command();
|
|
3104
3257
|
let canProceed = false;
|
|
3105
3258
|
try {
|
|
3106
3259
|
canProceed = await refreshIntegration();
|
|
@@ -3111,33 +3264,35 @@ async function pingWebhook(options, event = "charge.success") {
|
|
|
3111
3264
|
if (options.domain) domain = options.domain;
|
|
3112
3265
|
if (options.event) event = options.event;
|
|
3113
3266
|
const key = await getKeys(read("token"), "secret", domain);
|
|
3114
|
-
return
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
if (eventObject) {
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
reject();
|
|
3267
|
+
if (!canProceed) return void cmd.error("ERROR: Unable to ping webhook URL");
|
|
3268
|
+
const eventObject = webhooks_default[event];
|
|
3269
|
+
if (eventObject) {
|
|
3270
|
+
if (options.mod) for (const [key$1, val] of Object.entries(eventObject.data)) if (["string", "number"].includes(typeof val)) eventObject.data[key$1] = await cmd.ask(`Enter new value for '${key$1}':`, String(val));
|
|
3271
|
+
else if (typeof val === "boolean") eventObject.data[key$1] = await cmd.choice(`Enter new value for '${key$1}':`, ["true", "false"], val ? 0 : 1) === "true";
|
|
3272
|
+
else continue;
|
|
3273
|
+
const hash = crypto.default.createHmac("sha512", key).update(JSON.stringify(eventObject)).digest("hex");
|
|
3274
|
+
const uri = read("selected_integration")[domain + "_webhook_endpoint"];
|
|
3275
|
+
const spinner = cmd.spinner(`Sending sample ${event} event payload to ${uri}`).start();
|
|
3276
|
+
try {
|
|
3277
|
+
const response = await axios_default.post(uri, eventObject, { headers: { "x-paystack-signature": hash } });
|
|
3278
|
+
spinner.succeed(`Sample ${event} event payload sent to ${uri}`);
|
|
3279
|
+
return {
|
|
3280
|
+
code: response.status,
|
|
3281
|
+
text: response.statusText,
|
|
3282
|
+
data: response.data
|
|
3283
|
+
};
|
|
3284
|
+
} catch (e) {
|
|
3285
|
+
spinner.fail(`Failed to send sample ${event} event payload to ${uri}`);
|
|
3286
|
+
return {
|
|
3287
|
+
code: e.response?.status ?? 0,
|
|
3288
|
+
text: e.response?.statusText || "No response",
|
|
3289
|
+
data: typeof e.response?.data === "string" && e.response?.data?.includes("<html") ? { response: "HTML Response" } : e.response?.data || "No response data"
|
|
3290
|
+
};
|
|
3139
3291
|
}
|
|
3140
|
-
}
|
|
3292
|
+
} else {
|
|
3293
|
+
cmd.error("ERROR: Invalid event type - " + event);
|
|
3294
|
+
throw new Error("Invalid event type: " + event);
|
|
3295
|
+
}
|
|
3141
3296
|
}
|
|
3142
3297
|
/**
|
|
3143
3298
|
* Get integration
|
|
@@ -3148,7 +3303,7 @@ async function pingWebhook(options, event = "charge.success") {
|
|
|
3148
3303
|
*/
|
|
3149
3304
|
function getIntegration(id, token) {
|
|
3150
3305
|
const [command] = useCommand();
|
|
3151
|
-
const spinner = (
|
|
3306
|
+
const spinner = command().spinner("getting integration").start();
|
|
3152
3307
|
return new Promise((resolve, reject) => {
|
|
3153
3308
|
axios_default.get("/integration/" + id, { headers: {
|
|
3154
3309
|
Authorization: "Bearer " + token,
|
|
@@ -3172,7 +3327,7 @@ function getIntegration(id, token) {
|
|
|
3172
3327
|
*/
|
|
3173
3328
|
async function signIn(email, password) {
|
|
3174
3329
|
const [command] = useCommand();
|
|
3175
|
-
const spinner = (
|
|
3330
|
+
const spinner = command().spinner("Logging in...").start();
|
|
3176
3331
|
try {
|
|
3177
3332
|
const { data: response } = await axios_default.post("/login", {
|
|
3178
3333
|
email,
|
|
@@ -3292,7 +3447,7 @@ var LogoutCommand = class extends __h3ravel_musket.Command {
|
|
|
3292
3447
|
const [_, setCommand] = useCommand();
|
|
3293
3448
|
setCommand(this);
|
|
3294
3449
|
this.newLine();
|
|
3295
|
-
const spinner =
|
|
3450
|
+
const spinner = this.spinner("Logging out...").start();
|
|
3296
3451
|
try {
|
|
3297
3452
|
await wait(1e3, () => clearAuth());
|
|
3298
3453
|
spinner.succeed("Logged out successfully");
|
|
@@ -3304,15 +3459,47 @@ var LogoutCommand = class extends __h3ravel_musket.Command {
|
|
|
3304
3459
|
}
|
|
3305
3460
|
};
|
|
3306
3461
|
|
|
3462
|
+
//#endregion
|
|
3463
|
+
//#region src/Commands/SetIntegrationCommand.ts
|
|
3464
|
+
var SetIntegrationCommand = class extends __h3ravel_musket.Command {
|
|
3465
|
+
signature = "integration:set";
|
|
3466
|
+
description = "Set the active integration for Paystack CLI usage.";
|
|
3467
|
+
async handle() {
|
|
3468
|
+
const [_, setCommand] = useCommand();
|
|
3469
|
+
setCommand(this);
|
|
3470
|
+
const user = read("user");
|
|
3471
|
+
const token = read("token");
|
|
3472
|
+
if (!token || !user) return void this.error(`ERROR: You're not signed in, please run the ${logger("login", ["grey", "italic"])} command before you begin`);
|
|
3473
|
+
const [err, integration] = await promiseWrapper(selectIntegration(user.integrations, token));
|
|
3474
|
+
if (err || !integration) this.error("ERROR: " + (err ?? "Integration selection failed")).newLine();
|
|
3475
|
+
else {
|
|
3476
|
+
write("selected_integration", integration);
|
|
3477
|
+
const user_role = read("selected_integration").logged_in_user_role;
|
|
3478
|
+
const [err$1, integrationData] = await promiseWrapper(getIntegration(integration.id, token));
|
|
3479
|
+
if (err$1 || !integrationData) return void this.error("ERROR: " + (err$1 ?? "Failed to fetch integration data")).newLine();
|
|
3480
|
+
integrationData.logged_in_user_role = user_role;
|
|
3481
|
+
write("selected_integration", integrationData);
|
|
3482
|
+
__h3ravel_shared.Logger.log([
|
|
3483
|
+
["Switched to", "white"],
|
|
3484
|
+
[integration.business_name, "green"],
|
|
3485
|
+
["(" + integration.id + ")", "white"]
|
|
3486
|
+
], " ");
|
|
3487
|
+
this.newLine();
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
};
|
|
3491
|
+
|
|
3307
3492
|
//#endregion
|
|
3308
3493
|
//#region src/Commands/WebhookCommand.ts
|
|
3309
3494
|
var WebhookCommand = class extends __h3ravel_musket.Command {
|
|
3310
3495
|
signature = `webhook
|
|
3311
3496
|
{command=listen : The command to run to listen for webhooks locally : [listen, ping]}
|
|
3312
|
-
{
|
|
3497
|
+
{url? : Specify the url to listen on for webhooks (only for listen command, should be an accessible local url)}
|
|
3313
3498
|
{--D|domain=test : Specify the domain to ping the webhook : [test, live]}
|
|
3314
3499
|
{--F|forward? : Specify a URL to forward the webhook to instead of the saved webhook URL}
|
|
3315
|
-
{--E|event? : Specify the event type to simulate : [charge.success,transfer.success,transfer.failed,subscription.create]}
|
|
3500
|
+
{--E|event? : Specify the event type to simulate (leave empty to prompt with more options) : [charge.success,transfer.success,transfer.failed,subscription.create]}
|
|
3501
|
+
{--S|serve : Start a local server to receive webhooks (only for listen command, ignored if url is provided)}
|
|
3502
|
+
{--M|mod : Show options to modify the webhook payload before sending (only for ping command)}
|
|
3316
3503
|
`;
|
|
3317
3504
|
description = "Listen for webhook events locally, runs a webhook endpoint health check and listens for incoming webhooks, and ping your webhook URL from the CLI.";
|
|
3318
3505
|
async handle() {
|
|
@@ -3322,12 +3509,15 @@ var WebhookCommand = class extends __h3ravel_musket.Command {
|
|
|
3322
3509
|
this.newLine();
|
|
3323
3510
|
const config = getConfig();
|
|
3324
3511
|
let event = this.option("event");
|
|
3325
|
-
let
|
|
3512
|
+
let server = null;
|
|
3513
|
+
let localUrl = this.argument("url");
|
|
3326
3514
|
const selected_integration = read("selected_integration")?.id;
|
|
3327
3515
|
const user = read("user")?.id;
|
|
3328
|
-
if (!selected_integration || !user) return void this.error(
|
|
3329
|
-
if (this.argument("command") == "listen" && !
|
|
3330
|
-
|
|
3516
|
+
if (!selected_integration || !user) return void this.error(`ERROR: You're not signed in, please run the ${logger("login", ["grey", "italic"])} command before you begin`);
|
|
3517
|
+
if (this.argument("command") == "listen" && !localUrl) {
|
|
3518
|
+
if (this.option("serve")) server = await miniServer(3e3);
|
|
3519
|
+
localUrl = server?.url ?? await this.ask("Enter the url to listen on for webhooks: ", "http://localhost:8080/webhook");
|
|
3520
|
+
} else if (this.argument("command") == "ping" && !event) event = await this.choice("Select event to simulate", [
|
|
3331
3521
|
{
|
|
3332
3522
|
name: "Charge Success",
|
|
3333
3523
|
value: "charge.success"
|
|
@@ -3343,6 +3533,22 @@ var WebhookCommand = class extends __h3ravel_musket.Command {
|
|
|
3343
3533
|
{
|
|
3344
3534
|
name: "Subscription Create",
|
|
3345
3535
|
value: "subscription.create"
|
|
3536
|
+
},
|
|
3537
|
+
{
|
|
3538
|
+
name: "Customer Identification Failed",
|
|
3539
|
+
value: "customeridentification.failed"
|
|
3540
|
+
},
|
|
3541
|
+
{
|
|
3542
|
+
name: "Customer Identification Success",
|
|
3543
|
+
value: "customeridentification.success"
|
|
3544
|
+
},
|
|
3545
|
+
{
|
|
3546
|
+
name: "DVA Assign Failed",
|
|
3547
|
+
value: "dedicatedaccount.assign.failed"
|
|
3548
|
+
},
|
|
3549
|
+
{
|
|
3550
|
+
name: "DVA Assign Success",
|
|
3551
|
+
value: "dedicatedaccount.assign.success"
|
|
3346
3552
|
}
|
|
3347
3553
|
], 0);
|
|
3348
3554
|
const domain = this.option("domain", "test");
|
|
@@ -3353,7 +3559,7 @@ var WebhookCommand = class extends __h3ravel_musket.Command {
|
|
|
3353
3559
|
this.error("ERROR: Your session has expired. Please run the `login` command to sign in again.");
|
|
3354
3560
|
return;
|
|
3355
3561
|
}
|
|
3356
|
-
const url$1 = parseURL(
|
|
3562
|
+
const url$1 = parseURL(localUrl);
|
|
3357
3563
|
if (!url$1.port) url$1.port = "8000";
|
|
3358
3564
|
if (!url$1.search || url$1.search == "?") url$1.search = "";
|
|
3359
3565
|
try {
|
|
@@ -3361,17 +3567,18 @@ var WebhookCommand = class extends __h3ravel_musket.Command {
|
|
|
3361
3567
|
} catch {
|
|
3362
3568
|
this.debug("No existing ngrok process found to kill.");
|
|
3363
3569
|
}
|
|
3364
|
-
const
|
|
3570
|
+
const listener = await __ngrok_ngrok.default.forward({
|
|
3365
3571
|
addr: url$1.port,
|
|
3366
3572
|
authtoken: config.ngrokAuthToken || process.env.NGROK_AUTH_TOKEN,
|
|
3367
3573
|
domain: process.env.NGROK_DOMAIN
|
|
3368
|
-
})
|
|
3574
|
+
});
|
|
3575
|
+
const webhookUrl = new URL(listener.url() + url$1.pathname + url$1.search);
|
|
3369
3576
|
const domain$1 = this.option("domain", "test");
|
|
3370
|
-
const spinner =
|
|
3371
|
-
const [err, result] = await promiseWrapper(setWebhook(
|
|
3577
|
+
const spinner = this.spinner("Tunelling webhook events to " + logger(localUrl)).start();
|
|
3578
|
+
const [err, result] = await promiseWrapper(setWebhook(webhookUrl.toString(), token, read("selected_integration").id));
|
|
3372
3579
|
if (err || !result) return void this.error("ERROR: " + (err ?? "Failed to set webhook URL")).newLine();
|
|
3373
|
-
spinner.succeed(
|
|
3374
|
-
this.newLine().success(`INFO:
|
|
3580
|
+
spinner.succeed();
|
|
3581
|
+
this.newLine().success(`INFO: Listening for incoming webhook events at: ${logger(localUrl)}`).success(`INFO: Webhook URL set to: ${logger(webhookUrl.toString())} for ${domain$1} domain`).success(`INFO: Press ${logger("Ctrl+C", ["grey", "italic"])} to stop listening for webhook events.`).newLine();
|
|
3375
3582
|
process.stdin.resume();
|
|
3376
3583
|
} else if (this.argument("command") == "ping") {
|
|
3377
3584
|
await promiseWrapper(refreshIntegration());
|
|
@@ -3415,6 +3622,8 @@ __h3ravel_musket.Kernel.init(new Application(), {
|
|
|
3415
3622
|
LogoutCommand,
|
|
3416
3623
|
ConfigCommand,
|
|
3417
3624
|
WebhookCommand,
|
|
3625
|
+
SetIntegrationCommand,
|
|
3626
|
+
IntegrationInfoCommand,
|
|
3418
3627
|
...Commands_default()
|
|
3419
3628
|
]
|
|
3420
3629
|
});
|
package/bin/cli.js
CHANGED
|
@@ -1,37 +1,64 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import path, { dirname } from "path";
|
|
3
2
|
import Database from "better-sqlite3";
|
|
4
|
-
import {
|
|
3
|
+
import os, { homedir } from "os";
|
|
5
4
|
import { existsSync, mkdirSync } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
6
|
import { Logger } from "@h3ravel/shared";
|
|
7
|
+
import CliTable3 from "cli-table3";
|
|
7
8
|
import axios from "axios";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
8
10
|
import { Command, Kernel } from "@h3ravel/musket";
|
|
9
|
-
import
|
|
11
|
+
import { H3, serve } from "h3";
|
|
12
|
+
import { detect } from "detect-port";
|
|
10
13
|
import { createRequire } from "module";
|
|
11
|
-
import os from "os";
|
|
12
14
|
import crypto from "crypto";
|
|
13
15
|
import ngrok from "@ngrok/ngrok";
|
|
14
16
|
|
|
17
|
+
//#region src/utils/global.ts
|
|
18
|
+
String.prototype.toKebabCase = function() {
|
|
19
|
+
return this.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
20
|
+
};
|
|
21
|
+
String.prototype.toCamelCase = function() {
|
|
22
|
+
return this.replace(/[-_ ]+([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()).replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
23
|
+
};
|
|
24
|
+
String.prototype.toPascalCase = function() {
|
|
25
|
+
return this.replace(/(^\w|[-_ ]+\w)/g, (match) => match.replace(/[-_ ]+/, "").toUpperCase());
|
|
26
|
+
};
|
|
27
|
+
String.prototype.toSnakeCase = function() {
|
|
28
|
+
return this.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
29
|
+
};
|
|
30
|
+
String.prototype.toTitleCase = function() {
|
|
31
|
+
return this.toLowerCase().replace(/(^|\s)\w/g, (match) => match.toUpperCase());
|
|
32
|
+
};
|
|
33
|
+
String.prototype.toCleanCase = function() {
|
|
34
|
+
return this.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ").trim().replace(/\b\w{1,3}\b/g, (match) => match.toUpperCase());
|
|
35
|
+
};
|
|
36
|
+
String.prototype.truncate = function(n, suffix = "…") {
|
|
37
|
+
return this.length > n ? this.slice(0, n - 1) + suffix : this.toString();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
15
41
|
//#region src/db.ts
|
|
16
42
|
let db;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
43
|
+
let dbPath = path.join(homedir(), ".paystackcli");
|
|
44
|
+
mkdirSync(dbPath, { recursive: true });
|
|
45
|
+
const useDbPath = () => [dbPath, (path$1) => {
|
|
46
|
+
dbPath = path$1;
|
|
47
|
+
}];
|
|
21
48
|
/**
|
|
22
49
|
* Hook to get or set the database instance.
|
|
23
50
|
*
|
|
24
51
|
* @returns
|
|
25
52
|
*/
|
|
26
53
|
const useDb = () => {
|
|
27
|
-
return [() => db, (
|
|
28
|
-
db =
|
|
54
|
+
return [() => db, (filename) => {
|
|
55
|
+
db = new Database(path.join(dbPath, filename));
|
|
29
56
|
const [{ journal_mode }] = db.pragma("journal_mode");
|
|
30
57
|
if (journal_mode !== "wal") db.pragma("journal_mode = WAL");
|
|
31
58
|
}];
|
|
32
59
|
};
|
|
33
60
|
const [getDatabase, setDatabase] = useDb();
|
|
34
|
-
setDatabase(
|
|
61
|
+
setDatabase("app.db");
|
|
35
62
|
/**
|
|
36
63
|
* Initialize the database
|
|
37
64
|
*
|
|
@@ -79,7 +106,7 @@ function remove(key) {
|
|
|
79
106
|
* @param key
|
|
80
107
|
* @returns
|
|
81
108
|
*/
|
|
82
|
-
function read(key) {
|
|
109
|
+
function read(key, defaultValue) {
|
|
83
110
|
const db$1 = getDatabase();
|
|
84
111
|
try {
|
|
85
112
|
const row = db$1.prepare("SELECT * FROM json_store WHERE key = ?").get(key);
|
|
@@ -89,7 +116,7 @@ function read(key) {
|
|
|
89
116
|
return row.value;
|
|
90
117
|
}
|
|
91
118
|
} catch {}
|
|
92
|
-
return null;
|
|
119
|
+
return defaultValue ?? null;
|
|
93
120
|
}
|
|
94
121
|
|
|
95
122
|
//#endregion
|
|
@@ -363,6 +390,15 @@ const findCLIPackageJson = (startDir = __dirname) => {
|
|
|
363
390
|
}
|
|
364
391
|
return null;
|
|
365
392
|
};
|
|
393
|
+
const objectToTable = (obj, titleKeys = false) => {
|
|
394
|
+
const table = new CliTable3();
|
|
395
|
+
for (const rawKey in obj) {
|
|
396
|
+
if (typeof obj[rawKey] === "object") continue;
|
|
397
|
+
const key = logger((titleKeys ? rawKey.toCleanCase() : rawKey).truncate(30), ["bold"]);
|
|
398
|
+
table.push({ [key]: String(obj[rawKey]).truncate(40) });
|
|
399
|
+
}
|
|
400
|
+
return table.toString();
|
|
401
|
+
};
|
|
366
402
|
|
|
367
403
|
//#endregion
|
|
368
404
|
//#region src/paystack/apis.ts
|
|
@@ -2530,7 +2566,7 @@ const buildSignature = (param, cmd) => {
|
|
|
2530
2566
|
};
|
|
2531
2567
|
|
|
2532
2568
|
//#endregion
|
|
2533
|
-
//#region src/utils/
|
|
2569
|
+
//#region src/utils/builders.ts
|
|
2534
2570
|
/**
|
|
2535
2571
|
* We will recursively map through the result data and log each key value pair
|
|
2536
2572
|
* as we apply coloring based on the value type.
|
|
@@ -2545,7 +2581,7 @@ const dataRenderer = (data) => {
|
|
|
2545
2581
|
for (const key in obj) {
|
|
2546
2582
|
const value = obj[key];
|
|
2547
2583
|
if (typeof value === "object" && value !== null) {
|
|
2548
|
-
console.log(`${indentation}${
|
|
2584
|
+
console.log(`${indentation}${key.toCleanCase()}:`);
|
|
2549
2585
|
render(value, indent + 2);
|
|
2550
2586
|
} else {
|
|
2551
2587
|
let coloredValue;
|
|
@@ -2561,24 +2597,33 @@ const dataRenderer = (data) => {
|
|
|
2561
2597
|
break;
|
|
2562
2598
|
default: coloredValue = value;
|
|
2563
2599
|
}
|
|
2564
|
-
console.log(`${indentation}${
|
|
2600
|
+
console.log(`${indentation}${key.toCleanCase()}: ${coloredValue}`);
|
|
2565
2601
|
}
|
|
2566
2602
|
}
|
|
2567
2603
|
};
|
|
2568
2604
|
render(data);
|
|
2569
2605
|
};
|
|
2570
2606
|
/**
|
|
2571
|
-
*
|
|
2572
|
-
* capitalizing the first letter of every word,
|
|
2573
|
-
* converting camelCase to spaced words,
|
|
2574
|
-
* and trimming any leading or trailing spaces.
|
|
2575
|
-
* If a sentence is only two letters long we will make it uppercase.
|
|
2607
|
+
* Starts a mini HTTP server on the specified port to listen for incoming webhook requests.
|
|
2576
2608
|
*
|
|
2577
|
-
* @param
|
|
2609
|
+
* @param port
|
|
2578
2610
|
* @returns
|
|
2579
2611
|
*/
|
|
2580
|
-
const
|
|
2581
|
-
|
|
2612
|
+
const miniServer = async (port = 3e3) => {
|
|
2613
|
+
const route = async (event) => {
|
|
2614
|
+
console.log("Incoming Webhook Request [", event.req.method, "]");
|
|
2615
|
+
const payload = JSON.parse(await event.req.text() || "{}");
|
|
2616
|
+
return Object.assign({}, { signature: event.req.headers.get("x-paystack-signature") ?? "N/A" }, payload);
|
|
2617
|
+
};
|
|
2618
|
+
const app = new H3().get("/webhook", route).post("/webhook", route);
|
|
2619
|
+
port = await detect(port);
|
|
2620
|
+
const server = serve(app, {
|
|
2621
|
+
port,
|
|
2622
|
+
silent: true
|
|
2623
|
+
});
|
|
2624
|
+
const url = `http://localhost:${port}/webhook`;
|
|
2625
|
+
Logger.log([["🚀 Mini server is running at:", "green"], [url, "cyan"]], " ");
|
|
2626
|
+
return Object.assign({}, server, { url });
|
|
2582
2627
|
};
|
|
2583
2628
|
|
|
2584
2629
|
//#endregion
|
|
@@ -2603,14 +2648,14 @@ var Commands_default = () => {
|
|
|
2603
2648
|
signature = `${key} \n${args}`;
|
|
2604
2649
|
description = schema.description || "No description available.";
|
|
2605
2650
|
handle = async () => {
|
|
2606
|
-
const [
|
|
2651
|
+
const [command$1, setCommand] = useCommand();
|
|
2607
2652
|
setCommand(this);
|
|
2608
2653
|
for (const param of schema.params) if (param.required && !this.argument(param.parameter)) return void this.newLine().error(`Missing required argument: ${param.parameter}`).newLine();
|
|
2609
2654
|
const selected_integration = read("selected_integration")?.id;
|
|
2610
2655
|
const user = read("user")?.id;
|
|
2611
2656
|
if (!selected_integration || !user) return void this.error("ERROR: You're not signed in, please run the [login] command before you begin").newLine();
|
|
2612
2657
|
this.newLine();
|
|
2613
|
-
const spinner =
|
|
2658
|
+
const spinner = command$1().spinner("Loading...\n").start();
|
|
2614
2659
|
const [err, result] = await promiseWrapper(executeSchema(schema, {
|
|
2615
2660
|
...this.options(),
|
|
2616
2661
|
...this.arguments()
|
|
@@ -2711,31 +2756,32 @@ var InfoCommand = class extends Command {
|
|
|
2711
2756
|
version: "unknown",
|
|
2712
2757
|
dependencies: {}
|
|
2713
2758
|
};
|
|
2759
|
+
const user = read("user");
|
|
2714
2760
|
const pkgPath = findCLIPackageJson();
|
|
2715
2761
|
const require = createRequire(import.meta.url);
|
|
2716
|
-
const [
|
|
2717
|
-
const [dbPath] = useDbPath();
|
|
2762
|
+
const [, setCommand] = useCommand();
|
|
2763
|
+
const [dbPath$1] = useDbPath();
|
|
2718
2764
|
setCommand(this);
|
|
2719
2765
|
init();
|
|
2720
|
-
const spinner =
|
|
2766
|
+
const spinner = this.spinner("Gathering application information...\n").start();
|
|
2721
2767
|
if (pkgPath) try {
|
|
2722
2768
|
pkg = require(pkgPath);
|
|
2723
2769
|
} catch {}
|
|
2724
2770
|
wait(500, () => {
|
|
2725
|
-
spinner.
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
username: os.userInfo().username,
|
|
2736
|
-
database: dbPath + "/app.db",
|
|
2737
|
-
dependencies: Object.keys(pkg.dependencies).join(", ")
|
|
2771
|
+
spinner.succeed("Application Information Loaded.\n");
|
|
2772
|
+
const out = objectToTable({
|
|
2773
|
+
"App Version": pkg.version,
|
|
2774
|
+
"Platform": `${os.platform()} ${os.arch()} (${os.release()})`,
|
|
2775
|
+
"CPUs": os.cpus().length,
|
|
2776
|
+
"Host": `${os.userInfo().username}@${os.hostname()}`,
|
|
2777
|
+
"Memory": `${(os.freemem() / 1024 ** 3).toFixed(2)} GB / ${(os.totalmem() / 1024 ** 3).toFixed(2)} GB`,
|
|
2778
|
+
"Database Path": dbPath$1 + "/app.db",
|
|
2779
|
+
"Paystack User": user ? `${user.first_name} ${user.last_name} (ID: ${user.id})` : "Not logged in",
|
|
2780
|
+
"Default Integration": read("selected_integration")?.business_name || "Not set"
|
|
2738
2781
|
});
|
|
2782
|
+
console.log(out.toString());
|
|
2783
|
+
logger("\nDependencies:", "yellow");
|
|
2784
|
+
logger(Object.keys(pkg.dependencies).map((dep) => `${dep}`).join(", "), "green");
|
|
2739
2785
|
this.newLine();
|
|
2740
2786
|
});
|
|
2741
2787
|
}
|
|
@@ -2754,6 +2800,21 @@ var InitCommand = class extends Command {
|
|
|
2754
2800
|
}
|
|
2755
2801
|
};
|
|
2756
2802
|
|
|
2803
|
+
//#endregion
|
|
2804
|
+
//#region src/Commands/IntegrationInfoCommand.ts
|
|
2805
|
+
var IntegrationInfoCommand = class extends Command {
|
|
2806
|
+
signature = "integration:info";
|
|
2807
|
+
description = "Get information about the currently selected integration.";
|
|
2808
|
+
async handle() {
|
|
2809
|
+
const [_, setCommand] = useCommand();
|
|
2810
|
+
setCommand(this);
|
|
2811
|
+
const selected_integration = read("selected_integration");
|
|
2812
|
+
if (!selected_integration) return void this.error(`ERROR: No integration selected, please run the ${logger("integration:set", ["grey", "italic"])} command to select an integration before proceeding.`);
|
|
2813
|
+
console.log(objectToTable(selected_integration, true));
|
|
2814
|
+
this.newLine();
|
|
2815
|
+
}
|
|
2816
|
+
};
|
|
2817
|
+
|
|
2757
2818
|
//#endregion
|
|
2758
2819
|
//#region src/paystack/webhooks.ts
|
|
2759
2820
|
const webhook = {
|
|
@@ -2761,7 +2822,7 @@ const webhook = {
|
|
|
2761
2822
|
event: "charge.success",
|
|
2762
2823
|
data: {
|
|
2763
2824
|
id: 302961,
|
|
2764
|
-
domain: "
|
|
2825
|
+
domain: "test",
|
|
2765
2826
|
status: "success",
|
|
2766
2827
|
reference: "qTPrJoy9Bx",
|
|
2767
2828
|
amount: 1e4,
|
|
@@ -2828,14 +2889,14 @@ const webhook = {
|
|
|
2828
2889
|
"transfer.success": {
|
|
2829
2890
|
event: "transfer.success",
|
|
2830
2891
|
data: {
|
|
2831
|
-
domain: "
|
|
2892
|
+
domain: "test",
|
|
2832
2893
|
amount: 1e4,
|
|
2833
2894
|
currency: "NGN",
|
|
2834
2895
|
source: "balance",
|
|
2835
2896
|
source_details: null,
|
|
2836
2897
|
reason: "Bless you",
|
|
2837
2898
|
recipient: {
|
|
2838
|
-
domain: "
|
|
2899
|
+
domain: "test",
|
|
2839
2900
|
type: "nuban",
|
|
2840
2901
|
currency: "NGN",
|
|
2841
2902
|
name: "Someone",
|
|
@@ -2910,7 +2971,7 @@ const webhook = {
|
|
|
2910
2971
|
source_details: null,
|
|
2911
2972
|
reason: "Test",
|
|
2912
2973
|
recipient: {
|
|
2913
|
-
domain: "
|
|
2974
|
+
domain: "test",
|
|
2914
2975
|
type: "nuban",
|
|
2915
2976
|
currency: "NGN",
|
|
2916
2977
|
name: "Test account",
|
|
@@ -2930,6 +2991,95 @@ const webhook = {
|
|
|
2930
2991
|
transferred_at: null,
|
|
2931
2992
|
created_at: "2017-12-01T08:51:37.000Z"
|
|
2932
2993
|
}
|
|
2994
|
+
},
|
|
2995
|
+
"customeridentification.failed": {
|
|
2996
|
+
"event": "customeridentification.failed",
|
|
2997
|
+
"data": {
|
|
2998
|
+
"customer_id": 82796315,
|
|
2999
|
+
"customer_code": "CUS_XXXXXXXXXXXXXXX",
|
|
3000
|
+
"email": "email@email.com",
|
|
3001
|
+
"identification": {
|
|
3002
|
+
"country": "NG",
|
|
3003
|
+
"type": "bank_account",
|
|
3004
|
+
"bvn": "123*****456",
|
|
3005
|
+
"account_number": "012****345",
|
|
3006
|
+
"bank_code": "999991"
|
|
3007
|
+
},
|
|
3008
|
+
"reason": "Account number or BVN is incorrect"
|
|
3009
|
+
}
|
|
3010
|
+
},
|
|
3011
|
+
"customeridentification.success": {
|
|
3012
|
+
"event": "customeridentification.success",
|
|
3013
|
+
"data": {
|
|
3014
|
+
"customer_id": "9387490384",
|
|
3015
|
+
"customer_code": "CUS_xnxdt6s1zg1f4nx",
|
|
3016
|
+
"email": "bojack@horsinaround.com",
|
|
3017
|
+
"identification": {
|
|
3018
|
+
"country": "NG",
|
|
3019
|
+
"type": "bvn",
|
|
3020
|
+
"value": "200*****677"
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
},
|
|
3024
|
+
"dedicatedaccount.assign.failed": {
|
|
3025
|
+
"event": "dedicatedaccount.assign.failed",
|
|
3026
|
+
"data": {
|
|
3027
|
+
"customer": {
|
|
3028
|
+
"id": 100110,
|
|
3029
|
+
"first_name": "John",
|
|
3030
|
+
"last_name": "Doe",
|
|
3031
|
+
"email": "johndoe@test.com",
|
|
3032
|
+
"customer_code": "CUS_hcekca0j0bbg2m4",
|
|
3033
|
+
"phone": "+2348100000000",
|
|
3034
|
+
"metadata": {},
|
|
3035
|
+
"risk_action": "default",
|
|
3036
|
+
"international_format_phone": "+2348100000000"
|
|
3037
|
+
},
|
|
3038
|
+
"dedicated_account": null,
|
|
3039
|
+
"identification": { "status": "failed" }
|
|
3040
|
+
}
|
|
3041
|
+
},
|
|
3042
|
+
"dedicatedaccount.assign.success": {
|
|
3043
|
+
"event": "dedicatedaccount.assign.success",
|
|
3044
|
+
"data": {
|
|
3045
|
+
"customer": {
|
|
3046
|
+
"id": 100110,
|
|
3047
|
+
"first_name": "John",
|
|
3048
|
+
"last_name": "Doe",
|
|
3049
|
+
"email": "johndoe@test.com",
|
|
3050
|
+
"customer_code": "CUS_hp05n9khsqcesz2",
|
|
3051
|
+
"phone": "+2348100000000",
|
|
3052
|
+
"metadata": {},
|
|
3053
|
+
"risk_action": "default",
|
|
3054
|
+
"international_format_phone": "+2348100000000"
|
|
3055
|
+
},
|
|
3056
|
+
"dedicated_account": {
|
|
3057
|
+
"bank": {
|
|
3058
|
+
"name": "Test Bank",
|
|
3059
|
+
"id": 20,
|
|
3060
|
+
"slug": "test-bank"
|
|
3061
|
+
},
|
|
3062
|
+
"account_name": "PAYSTACK/John Doe",
|
|
3063
|
+
"account_number": "1234567890",
|
|
3064
|
+
"assigned": true,
|
|
3065
|
+
"currency": "NGN",
|
|
3066
|
+
"metadata": null,
|
|
3067
|
+
"active": true,
|
|
3068
|
+
"id": 987654,
|
|
3069
|
+
"created_at": "2022-06-21T17:12:40.000Z",
|
|
3070
|
+
"updated_at": "2022-08-12T14:02:51.000Z",
|
|
3071
|
+
"assignment": {
|
|
3072
|
+
"integration": 100123,
|
|
3073
|
+
"assignee_id": 100110,
|
|
3074
|
+
"assignee_type": "Customer",
|
|
3075
|
+
"expired": false,
|
|
3076
|
+
"account_type": "PAY-WITH-TRANSFER-RECURRING",
|
|
3077
|
+
"assigned_at": "2022-08-12T14:02:51.614Z",
|
|
3078
|
+
"expired_at": null
|
|
3079
|
+
}
|
|
3080
|
+
},
|
|
3081
|
+
"identification": { "status": "success" }
|
|
3082
|
+
}
|
|
2933
3083
|
}
|
|
2934
3084
|
};
|
|
2935
3085
|
var webhooks_default = webhook;
|
|
@@ -3066,6 +3216,7 @@ function getKeys(token, type = "secret", domain = "test") {
|
|
|
3066
3216
|
*/
|
|
3067
3217
|
async function pingWebhook(options, event = "charge.success") {
|
|
3068
3218
|
const [command] = useCommand();
|
|
3219
|
+
const cmd = command();
|
|
3069
3220
|
let canProceed = false;
|
|
3070
3221
|
try {
|
|
3071
3222
|
canProceed = await refreshIntegration();
|
|
@@ -3076,33 +3227,35 @@ async function pingWebhook(options, event = "charge.success") {
|
|
|
3076
3227
|
if (options.domain) domain = options.domain;
|
|
3077
3228
|
if (options.event) event = options.event;
|
|
3078
3229
|
const key = await getKeys(read("token"), "secret", domain);
|
|
3079
|
-
return
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
if (eventObject) {
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
reject();
|
|
3230
|
+
if (!canProceed) return void cmd.error("ERROR: Unable to ping webhook URL");
|
|
3231
|
+
const eventObject = webhooks_default[event];
|
|
3232
|
+
if (eventObject) {
|
|
3233
|
+
if (options.mod) for (const [key$1, val] of Object.entries(eventObject.data)) if (["string", "number"].includes(typeof val)) eventObject.data[key$1] = await cmd.ask(`Enter new value for '${key$1}':`, String(val));
|
|
3234
|
+
else if (typeof val === "boolean") eventObject.data[key$1] = await cmd.choice(`Enter new value for '${key$1}':`, ["true", "false"], val ? 0 : 1) === "true";
|
|
3235
|
+
else continue;
|
|
3236
|
+
const hash = crypto.createHmac("sha512", key).update(JSON.stringify(eventObject)).digest("hex");
|
|
3237
|
+
const uri = read("selected_integration")[domain + "_webhook_endpoint"];
|
|
3238
|
+
const spinner = cmd.spinner(`Sending sample ${event} event payload to ${uri}`).start();
|
|
3239
|
+
try {
|
|
3240
|
+
const response = await axios_default.post(uri, eventObject, { headers: { "x-paystack-signature": hash } });
|
|
3241
|
+
spinner.succeed(`Sample ${event} event payload sent to ${uri}`);
|
|
3242
|
+
return {
|
|
3243
|
+
code: response.status,
|
|
3244
|
+
text: response.statusText,
|
|
3245
|
+
data: response.data
|
|
3246
|
+
};
|
|
3247
|
+
} catch (e) {
|
|
3248
|
+
spinner.fail(`Failed to send sample ${event} event payload to ${uri}`);
|
|
3249
|
+
return {
|
|
3250
|
+
code: e.response?.status ?? 0,
|
|
3251
|
+
text: e.response?.statusText || "No response",
|
|
3252
|
+
data: typeof e.response?.data === "string" && e.response?.data?.includes("<html") ? { response: "HTML Response" } : e.response?.data || "No response data"
|
|
3253
|
+
};
|
|
3104
3254
|
}
|
|
3105
|
-
}
|
|
3255
|
+
} else {
|
|
3256
|
+
cmd.error("ERROR: Invalid event type - " + event);
|
|
3257
|
+
throw new Error("Invalid event type: " + event);
|
|
3258
|
+
}
|
|
3106
3259
|
}
|
|
3107
3260
|
/**
|
|
3108
3261
|
* Get integration
|
|
@@ -3113,7 +3266,7 @@ async function pingWebhook(options, event = "charge.success") {
|
|
|
3113
3266
|
*/
|
|
3114
3267
|
function getIntegration(id, token) {
|
|
3115
3268
|
const [command] = useCommand();
|
|
3116
|
-
const spinner =
|
|
3269
|
+
const spinner = command().spinner("getting integration").start();
|
|
3117
3270
|
return new Promise((resolve, reject) => {
|
|
3118
3271
|
axios_default.get("/integration/" + id, { headers: {
|
|
3119
3272
|
Authorization: "Bearer " + token,
|
|
@@ -3137,7 +3290,7 @@ function getIntegration(id, token) {
|
|
|
3137
3290
|
*/
|
|
3138
3291
|
async function signIn(email, password) {
|
|
3139
3292
|
const [command] = useCommand();
|
|
3140
|
-
const spinner =
|
|
3293
|
+
const spinner = command().spinner("Logging in...").start();
|
|
3141
3294
|
try {
|
|
3142
3295
|
const { data: response } = await axios_default.post("/login", {
|
|
3143
3296
|
email,
|
|
@@ -3257,7 +3410,7 @@ var LogoutCommand = class extends Command {
|
|
|
3257
3410
|
const [_, setCommand] = useCommand();
|
|
3258
3411
|
setCommand(this);
|
|
3259
3412
|
this.newLine();
|
|
3260
|
-
const spinner =
|
|
3413
|
+
const spinner = this.spinner("Logging out...").start();
|
|
3261
3414
|
try {
|
|
3262
3415
|
await wait(1e3, () => clearAuth());
|
|
3263
3416
|
spinner.succeed("Logged out successfully");
|
|
@@ -3269,15 +3422,47 @@ var LogoutCommand = class extends Command {
|
|
|
3269
3422
|
}
|
|
3270
3423
|
};
|
|
3271
3424
|
|
|
3425
|
+
//#endregion
|
|
3426
|
+
//#region src/Commands/SetIntegrationCommand.ts
|
|
3427
|
+
var SetIntegrationCommand = class extends Command {
|
|
3428
|
+
signature = "integration:set";
|
|
3429
|
+
description = "Set the active integration for Paystack CLI usage.";
|
|
3430
|
+
async handle() {
|
|
3431
|
+
const [_, setCommand] = useCommand();
|
|
3432
|
+
setCommand(this);
|
|
3433
|
+
const user = read("user");
|
|
3434
|
+
const token = read("token");
|
|
3435
|
+
if (!token || !user) return void this.error(`ERROR: You're not signed in, please run the ${logger("login", ["grey", "italic"])} command before you begin`);
|
|
3436
|
+
const [err, integration] = await promiseWrapper(selectIntegration(user.integrations, token));
|
|
3437
|
+
if (err || !integration) this.error("ERROR: " + (err ?? "Integration selection failed")).newLine();
|
|
3438
|
+
else {
|
|
3439
|
+
write("selected_integration", integration);
|
|
3440
|
+
const user_role = read("selected_integration").logged_in_user_role;
|
|
3441
|
+
const [err$1, integrationData] = await promiseWrapper(getIntegration(integration.id, token));
|
|
3442
|
+
if (err$1 || !integrationData) return void this.error("ERROR: " + (err$1 ?? "Failed to fetch integration data")).newLine();
|
|
3443
|
+
integrationData.logged_in_user_role = user_role;
|
|
3444
|
+
write("selected_integration", integrationData);
|
|
3445
|
+
Logger.log([
|
|
3446
|
+
["Switched to", "white"],
|
|
3447
|
+
[integration.business_name, "green"],
|
|
3448
|
+
["(" + integration.id + ")", "white"]
|
|
3449
|
+
], " ");
|
|
3450
|
+
this.newLine();
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
};
|
|
3454
|
+
|
|
3272
3455
|
//#endregion
|
|
3273
3456
|
//#region src/Commands/WebhookCommand.ts
|
|
3274
3457
|
var WebhookCommand = class extends Command {
|
|
3275
3458
|
signature = `webhook
|
|
3276
3459
|
{command=listen : The command to run to listen for webhooks locally : [listen, ping]}
|
|
3277
|
-
{
|
|
3460
|
+
{url? : Specify the url to listen on for webhooks (only for listen command, should be an accessible local url)}
|
|
3278
3461
|
{--D|domain=test : Specify the domain to ping the webhook : [test, live]}
|
|
3279
3462
|
{--F|forward? : Specify a URL to forward the webhook to instead of the saved webhook URL}
|
|
3280
|
-
{--E|event? : Specify the event type to simulate : [charge.success,transfer.success,transfer.failed,subscription.create]}
|
|
3463
|
+
{--E|event? : Specify the event type to simulate (leave empty to prompt with more options) : [charge.success,transfer.success,transfer.failed,subscription.create]}
|
|
3464
|
+
{--S|serve : Start a local server to receive webhooks (only for listen command, ignored if url is provided)}
|
|
3465
|
+
{--M|mod : Show options to modify the webhook payload before sending (only for ping command)}
|
|
3281
3466
|
`;
|
|
3282
3467
|
description = "Listen for webhook events locally, runs a webhook endpoint health check and listens for incoming webhooks, and ping your webhook URL from the CLI.";
|
|
3283
3468
|
async handle() {
|
|
@@ -3287,12 +3472,15 @@ var WebhookCommand = class extends Command {
|
|
|
3287
3472
|
this.newLine();
|
|
3288
3473
|
const config = getConfig();
|
|
3289
3474
|
let event = this.option("event");
|
|
3290
|
-
let
|
|
3475
|
+
let server = null;
|
|
3476
|
+
let localUrl = this.argument("url");
|
|
3291
3477
|
const selected_integration = read("selected_integration")?.id;
|
|
3292
3478
|
const user = read("user")?.id;
|
|
3293
|
-
if (!selected_integration || !user) return void this.error(
|
|
3294
|
-
if (this.argument("command") == "listen" && !
|
|
3295
|
-
|
|
3479
|
+
if (!selected_integration || !user) return void this.error(`ERROR: You're not signed in, please run the ${logger("login", ["grey", "italic"])} command before you begin`);
|
|
3480
|
+
if (this.argument("command") == "listen" && !localUrl) {
|
|
3481
|
+
if (this.option("serve")) server = await miniServer(3e3);
|
|
3482
|
+
localUrl = server?.url ?? await this.ask("Enter the url to listen on for webhooks: ", "http://localhost:8080/webhook");
|
|
3483
|
+
} else if (this.argument("command") == "ping" && !event) event = await this.choice("Select event to simulate", [
|
|
3296
3484
|
{
|
|
3297
3485
|
name: "Charge Success",
|
|
3298
3486
|
value: "charge.success"
|
|
@@ -3308,6 +3496,22 @@ var WebhookCommand = class extends Command {
|
|
|
3308
3496
|
{
|
|
3309
3497
|
name: "Subscription Create",
|
|
3310
3498
|
value: "subscription.create"
|
|
3499
|
+
},
|
|
3500
|
+
{
|
|
3501
|
+
name: "Customer Identification Failed",
|
|
3502
|
+
value: "customeridentification.failed"
|
|
3503
|
+
},
|
|
3504
|
+
{
|
|
3505
|
+
name: "Customer Identification Success",
|
|
3506
|
+
value: "customeridentification.success"
|
|
3507
|
+
},
|
|
3508
|
+
{
|
|
3509
|
+
name: "DVA Assign Failed",
|
|
3510
|
+
value: "dedicatedaccount.assign.failed"
|
|
3511
|
+
},
|
|
3512
|
+
{
|
|
3513
|
+
name: "DVA Assign Success",
|
|
3514
|
+
value: "dedicatedaccount.assign.success"
|
|
3311
3515
|
}
|
|
3312
3516
|
], 0);
|
|
3313
3517
|
const domain = this.option("domain", "test");
|
|
@@ -3318,7 +3522,7 @@ var WebhookCommand = class extends Command {
|
|
|
3318
3522
|
this.error("ERROR: Your session has expired. Please run the `login` command to sign in again.");
|
|
3319
3523
|
return;
|
|
3320
3524
|
}
|
|
3321
|
-
const url = parseURL(
|
|
3525
|
+
const url = parseURL(localUrl);
|
|
3322
3526
|
if (!url.port) url.port = "8000";
|
|
3323
3527
|
if (!url.search || url.search == "?") url.search = "";
|
|
3324
3528
|
try {
|
|
@@ -3326,17 +3530,18 @@ var WebhookCommand = class extends Command {
|
|
|
3326
3530
|
} catch {
|
|
3327
3531
|
this.debug("No existing ngrok process found to kill.");
|
|
3328
3532
|
}
|
|
3329
|
-
const
|
|
3533
|
+
const listener = await ngrok.forward({
|
|
3330
3534
|
addr: url.port,
|
|
3331
3535
|
authtoken: config.ngrokAuthToken || process.env.NGROK_AUTH_TOKEN,
|
|
3332
3536
|
domain: process.env.NGROK_DOMAIN
|
|
3333
|
-
})
|
|
3537
|
+
});
|
|
3538
|
+
const webhookUrl = new URL(listener.url() + url.pathname + url.search);
|
|
3334
3539
|
const domain$1 = this.option("domain", "test");
|
|
3335
|
-
const spinner =
|
|
3336
|
-
const [err, result] = await promiseWrapper(setWebhook(
|
|
3540
|
+
const spinner = this.spinner("Tunelling webhook events to " + logger(localUrl)).start();
|
|
3541
|
+
const [err, result] = await promiseWrapper(setWebhook(webhookUrl.toString(), token, read("selected_integration").id));
|
|
3337
3542
|
if (err || !result) return void this.error("ERROR: " + (err ?? "Failed to set webhook URL")).newLine();
|
|
3338
|
-
spinner.succeed(
|
|
3339
|
-
this.newLine().success(`INFO:
|
|
3543
|
+
spinner.succeed();
|
|
3544
|
+
this.newLine().success(`INFO: Listening for incoming webhook events at: ${logger(localUrl)}`).success(`INFO: Webhook URL set to: ${logger(webhookUrl.toString())} for ${domain$1} domain`).success(`INFO: Press ${logger("Ctrl+C", ["grey", "italic"])} to stop listening for webhook events.`).newLine();
|
|
3340
3545
|
process.stdin.resume();
|
|
3341
3546
|
} else if (this.argument("command") == "ping") {
|
|
3342
3547
|
await promiseWrapper(refreshIntegration());
|
|
@@ -3380,6 +3585,8 @@ Kernel.init(new Application(), {
|
|
|
3380
3585
|
LogoutCommand,
|
|
3381
3586
|
ConfigCommand,
|
|
3382
3587
|
WebhookCommand,
|
|
3588
|
+
SetIntegrationCommand,
|
|
3589
|
+
IntegrationInfoCommand,
|
|
3383
3590
|
...Commands_default()
|
|
3384
3591
|
]
|
|
3385
3592
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toneflix/paystack-cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7",
|
|
5
5
|
"description": "Interact with the Paystack API, test webhooks locally, and manage your integration settings without leaving your command line.",
|
|
6
6
|
"main": "bin/cli.js",
|
|
7
7
|
"private": false,
|
|
@@ -37,27 +37,30 @@
|
|
|
37
37
|
"toneflix"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@h3ravel/musket": "^0.
|
|
40
|
+
"@h3ravel/musket": "^0.10.1",
|
|
41
41
|
"@h3ravel/shared": "^0.28.4",
|
|
42
42
|
"@ngrok/ngrok": "^1.7.0",
|
|
43
43
|
"axios": "^1.13.2",
|
|
44
44
|
"better-sqlite3": "^12.6.2",
|
|
45
|
-
"
|
|
45
|
+
"cli-table3": "^0.6.5",
|
|
46
|
+
"detect-port": "^2.1.0",
|
|
47
|
+
"h3": "2.0.1-rc.11",
|
|
48
|
+
"srvx": "^0.10.1",
|
|
46
49
|
"tsdown": "^0.15.4"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
52
|
+
"@changesets/cli": "^2.29.5",
|
|
49
53
|
"@eslint/js": "^9.39.2",
|
|
50
|
-
"typescript-eslint": "^8.53.0",
|
|
51
54
|
"@eslint/markdown": "^7.5.1",
|
|
52
|
-
"sass-embedded": "^1.90.0",
|
|
53
|
-
"@changesets/cli": "^2.29.5",
|
|
54
55
|
"@swc/core": "^1.6.1",
|
|
55
56
|
"@types/better-sqlite3": "^7.6.13",
|
|
56
57
|
"@types/node": "^20.14.5",
|
|
57
58
|
"eslint": "^9.39.2",
|
|
59
|
+
"sass-embedded": "^1.90.0",
|
|
58
60
|
"ts-node": "^10.9.2",
|
|
59
61
|
"tsx": "^4.20.3",
|
|
60
62
|
"typescript": "^5.4.5",
|
|
63
|
+
"typescript-eslint": "^8.53.0",
|
|
61
64
|
"vite-tsconfig-paths": "^5.1.4",
|
|
62
65
|
"vitepress": "^1.5.0",
|
|
63
66
|
"vitest": "^3.2.4",
|