@sam-ael/medusa-plugin-whatsapp 0.1.1 → 0.2.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/.medusa/server/src/admin/index.js +3 -3
- package/.medusa/server/src/admin/index.mjs +3 -3
- package/.medusa/server/src/api/admin/whatsapp/config/route.js +15 -5
- package/.medusa/server/src/modules/whatsapp/migrations/{Migration20260303172046.js → Migration20260303202037.js} +5 -5
- package/.medusa/server/src/modules/whatsapp/models/whatsapp_config.js +4 -5
- package/.medusa/server/src/subscribers/whatsapp-event-handler.js +10 -15
- package/README.md +165 -99
- package/package.json +1 -1
|
@@ -39,7 +39,7 @@ function ConfigSection() {
|
|
|
39
39
|
const [form, setForm] = react.useState({
|
|
40
40
|
phone_number_id: "",
|
|
41
41
|
access_token: "",
|
|
42
|
-
api_version: "
|
|
42
|
+
api_version: "v25.0",
|
|
43
43
|
default_language_code: "en_US",
|
|
44
44
|
active: true
|
|
45
45
|
});
|
|
@@ -51,7 +51,7 @@ function ConfigSection() {
|
|
|
51
51
|
setForm({
|
|
52
52
|
phone_number_id: data.config.phone_number_id || "",
|
|
53
53
|
access_token: "",
|
|
54
|
-
api_version: data.config.api_version || "
|
|
54
|
+
api_version: data.config.api_version || "v25.0",
|
|
55
55
|
default_language_code: data.config.default_language_code || "en_US",
|
|
56
56
|
active: data.config.active ?? true
|
|
57
57
|
});
|
|
@@ -121,7 +121,7 @@ function ConfigSection() {
|
|
|
121
121
|
ui.Input,
|
|
122
122
|
{
|
|
123
123
|
id: "api-version",
|
|
124
|
-
placeholder: "
|
|
124
|
+
placeholder: "v25.0",
|
|
125
125
|
value: form.api_version,
|
|
126
126
|
onChange: (e) => setForm({ ...form, api_version: e.target.value })
|
|
127
127
|
}
|
|
@@ -38,7 +38,7 @@ function ConfigSection() {
|
|
|
38
38
|
const [form, setForm] = useState({
|
|
39
39
|
phone_number_id: "",
|
|
40
40
|
access_token: "",
|
|
41
|
-
api_version: "
|
|
41
|
+
api_version: "v25.0",
|
|
42
42
|
default_language_code: "en_US",
|
|
43
43
|
active: true
|
|
44
44
|
});
|
|
@@ -50,7 +50,7 @@ function ConfigSection() {
|
|
|
50
50
|
setForm({
|
|
51
51
|
phone_number_id: data.config.phone_number_id || "",
|
|
52
52
|
access_token: "",
|
|
53
|
-
api_version: data.config.api_version || "
|
|
53
|
+
api_version: data.config.api_version || "v25.0",
|
|
54
54
|
default_language_code: data.config.default_language_code || "en_US",
|
|
55
55
|
active: data.config.active ?? true
|
|
56
56
|
});
|
|
@@ -120,7 +120,7 @@ function ConfigSection() {
|
|
|
120
120
|
Input,
|
|
121
121
|
{
|
|
122
122
|
id: "api-version",
|
|
123
|
-
placeholder: "
|
|
123
|
+
placeholder: "v25.0",
|
|
124
124
|
value: form.api_version,
|
|
125
125
|
onChange: (e) => setForm({ ...form, api_version: e.target.value })
|
|
126
126
|
}
|
|
@@ -7,7 +7,11 @@ async function GET(req, res) {
|
|
|
7
7
|
const whatsappModule = req.scope.resolve(WHATSAPP_MODULE);
|
|
8
8
|
const [configs] = await whatsappModule.listAndCountWhatsappConfigs({}, { take: 1 });
|
|
9
9
|
if (configs.length === 0) {
|
|
10
|
-
|
|
10
|
+
// Return env-based status so admin UI can show whether env vars are set
|
|
11
|
+
return res.json({
|
|
12
|
+
config: null,
|
|
13
|
+
env_configured: !!(process.env.WHATSAPP_PHONE_NUMBER_ID && process.env.WHATSAPP_ACCESS_TOKEN),
|
|
14
|
+
});
|
|
11
15
|
}
|
|
12
16
|
// Mask the access token for security
|
|
13
17
|
const config = { ...configs[0] };
|
|
@@ -29,7 +33,7 @@ async function POST(req, res) {
|
|
|
29
33
|
const updateData = {};
|
|
30
34
|
if (phone_number_id !== undefined)
|
|
31
35
|
updateData.phone_number_id = phone_number_id;
|
|
32
|
-
if (access_token !== undefined)
|
|
36
|
+
if (access_token !== undefined && access_token !== "")
|
|
33
37
|
updateData.access_token = access_token;
|
|
34
38
|
if (api_version !== undefined)
|
|
35
39
|
updateData.api_version = api_version;
|
|
@@ -44,11 +48,17 @@ async function POST(req, res) {
|
|
|
44
48
|
config = await whatsappModule.createWhatsappConfigs({
|
|
45
49
|
phone_number_id: phone_number_id || process.env.WHATSAPP_PHONE_NUMBER_ID || "",
|
|
46
50
|
access_token: access_token || process.env.WHATSAPP_ACCESS_TOKEN || "",
|
|
47
|
-
api_version: api_version || process.env.WHATSAPP_API_VERSION || "
|
|
51
|
+
api_version: api_version || process.env.WHATSAPP_API_VERSION || "v25.0",
|
|
48
52
|
default_language_code: default_language_code || "en_US",
|
|
49
53
|
active: active !== undefined ? active : true,
|
|
50
54
|
});
|
|
51
55
|
}
|
|
52
|
-
|
|
56
|
+
// Mask token in response
|
|
57
|
+
const responseConfig = { ...config };
|
|
58
|
+
if (responseConfig.access_token) {
|
|
59
|
+
responseConfig.access_token =
|
|
60
|
+
responseConfig.access_token.substring(0, 8) + "..." + responseConfig.access_token.slice(-4);
|
|
61
|
+
}
|
|
62
|
+
res.json({ config: responseConfig });
|
|
53
63
|
}
|
|
54
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3doYXRzYXBwL2NvbmZpZy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUlBLGtCQXdCQztBQUVELG9CQTBDQztBQXRFRCxNQUFNLGVBQWUsR0FBRyxVQUFVLENBQUE7QUFFM0IsS0FBSyxVQUFVLEdBQUcsQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQzdELE1BQU0sY0FBYyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBRXpELE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxNQUFPLGNBQXNCLENBQUMsMkJBQTJCLENBQ3ZFLEVBQUUsRUFDRixFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsQ0FDZCxDQUFBO0lBRUQsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLHdFQUF3RTtRQUN4RSxPQUFPLEdBQUcsQ0FBQyxJQUFJLENBQUM7WUFDWixNQUFNLEVBQUUsSUFBSTtZQUNaLGNBQWMsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUM7U0FDaEcsQ0FBQyxDQUFBO0lBQ04sQ0FBQztJQUVELHFDQUFxQztJQUNyQyxNQUFNLE1BQU0sR0FBRyxFQUFFLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUE7SUFDaEMsSUFBSSxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDdEIsTUFBTSxDQUFDLFlBQVk7WUFDZixNQUFNLENBQUMsWUFBWSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsS0FBSyxHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDbkYs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
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Migration20260303202037 = void 0;
|
|
4
4
|
const migrations_1 = require("@medusajs/framework/mikro-orm/migrations");
|
|
5
|
-
class
|
|
5
|
+
class Migration20260303202037 extends migrations_1.Migration {
|
|
6
6
|
async up() {
|
|
7
|
-
this.addSql(`create table if not exists "whatsapp_config" ("id" text not null, "
|
|
7
|
+
this.addSql(`create table if not exists "whatsapp_config" ("id" text not null, "phone_number_id" text null, "access_token" text null, "api_version" text not null default 'v25.0', "default_language_code" text not null default 'en_US', "active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "whatsapp_config_pkey" primary key ("id"));`);
|
|
8
8
|
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_whatsapp_config_deleted_at" ON "whatsapp_config" ("deleted_at") WHERE deleted_at IS NULL;`);
|
|
9
9
|
this.addSql(`create table if not exists "whatsapp_event_mapping" ("id" text not null, "event_name" text not null, "template_name" text not null, "language_code" text not null default 'en_US', "template_variables" jsonb not null default '{}', "recipient_type" text not null default 'billing_shipping', "recipient_phone" text null, "active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "whatsapp_event_mapping_pkey" primary key ("id"));`);
|
|
10
10
|
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_whatsapp_event_mapping_deleted_at" ON "whatsapp_event_mapping" ("deleted_at") WHERE deleted_at IS NULL;`);
|
|
@@ -17,5 +17,5 @@ class Migration20260303172046 extends migrations_1.Migration {
|
|
|
17
17
|
this.addSql(`drop table if exists "whatsapp_message_log" cascade;`);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
exports.
|
|
21
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
20
|
+
exports.Migration20260303202037 = Migration20260303202037;
|
|
21
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNjAzMDMyMDIwMzcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy93aGF0c2FwcC9taWdyYXRpb25zL01pZ3JhdGlvbjIwMjYwMzAzMjAyMDM3LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlFQUFxRTtBQUVyRSxNQUFhLHVCQUF3QixTQUFRLHNCQUFTO0lBRTNDLEtBQUssQ0FBQyxFQUFFO1FBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyw4YkFBOGIsQ0FBQyxDQUFDO1FBQzVjLElBQUksQ0FBQyxNQUFNLENBQUMsMkhBQTJILENBQUMsQ0FBQztRQUV6SSxJQUFJLENBQUMsTUFBTSxDQUFDLHFpQkFBcWlCLENBQUMsQ0FBQztRQUNuakIsSUFBSSxDQUFDLE1BQU0sQ0FBQyx5SUFBeUksQ0FBQyxDQUFDO1FBRXZKLElBQUksQ0FBQyxNQUFNLENBQUMsK2xCQUErbEIsQ0FBQyxDQUFDO1FBQzdtQixJQUFJLENBQUMsTUFBTSxDQUFDLHFJQUFxSSxDQUFDLENBQUM7SUFDckosQ0FBQztJQUVRLEtBQUssQ0FBQyxJQUFJO1FBQ2pCLElBQUksQ0FBQyxNQUFNLENBQUMsaURBQWlELENBQUMsQ0FBQztRQUUvRCxJQUFJLENBQUMsTUFBTSxDQUFDLHdEQUF3RCxDQUFDLENBQUM7UUFFdEUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxzREFBc0QsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7Q0FFRjtBQXJCRCwwREFxQkMifQ==
|
|
@@ -3,12 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const utils_1 = require("@medusajs/framework/utils");
|
|
4
4
|
const WhatsappConfig = utils_1.model.define("whatsapp_config", {
|
|
5
5
|
id: utils_1.model.id().primaryKey(),
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
api_version: utils_1.model.text().default("v21.0"),
|
|
6
|
+
phone_number_id: utils_1.model.text().nullable(),
|
|
7
|
+
access_token: utils_1.model.text().nullable(),
|
|
8
|
+
api_version: utils_1.model.text().default("v25.0"),
|
|
10
9
|
default_language_code: utils_1.model.text().default("en_US"),
|
|
11
10
|
active: utils_1.model.boolean().default(true),
|
|
12
11
|
});
|
|
13
12
|
exports.default = WhatsappConfig;
|
|
14
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2hhdHNhcHBfY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvd2hhdHNhcHAvbW9kZWxzL3doYXRzYXBwX2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUFpRDtBQUVqRCxNQUFNLGNBQWMsR0FBRyxhQUFLLENBQUMsTUFBTSxDQUFDLGlCQUFpQixFQUFFO0lBQ25ELEVBQUUsRUFBRSxhQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxFQUFFO0lBQzNCLGVBQWUsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ3hDLFlBQVksRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ3JDLFdBQVcsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztJQUMxQyxxQkFBcUIsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztJQUNwRCxNQUFNLEVBQUUsYUFBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7Q0FDeEMsQ0FBQyxDQUFBO0FBRUYsa0JBQWUsY0FBYyxDQUFBIn0=
|
|
@@ -8,22 +8,17 @@ async function whatsappEventHandler({ event: { name, data }, container, }) {
|
|
|
8
8
|
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
9
9
|
const whatsappModule = container.resolve(WHATSAPP_MODULE);
|
|
10
10
|
try {
|
|
11
|
-
// Get active WhatsApp config
|
|
11
|
+
// Get active WhatsApp config from DB
|
|
12
12
|
const [configs] = await whatsappModule.listAndCountWhatsappConfigs({ active: true }, { take: 1 });
|
|
13
|
-
const config = configs[0];
|
|
14
|
-
|
|
15
|
-
// Fallback to env vars
|
|
16
|
-
const phoneNumberId = process.env.WHATSAPP_PHONE_NUMBER_ID;
|
|
17
|
-
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN;
|
|
18
|
-
if (!phoneNumberId || !accessToken) {
|
|
19
|
-
return; // WhatsApp not configured, silently skip
|
|
20
|
-
}
|
|
21
|
-
}
|
|
13
|
+
const config = configs?.[0];
|
|
14
|
+
// Resolve credentials: DB config takes priority, then env vars
|
|
22
15
|
const phoneNumberId = config?.phone_number_id || process.env.WHATSAPP_PHONE_NUMBER_ID;
|
|
23
16
|
const accessToken = config?.access_token || process.env.WHATSAPP_ACCESS_TOKEN;
|
|
24
|
-
const apiVersion = config?.api_version || process.env.WHATSAPP_API_VERSION || "
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
const apiVersion = config?.api_version || process.env.WHATSAPP_API_VERSION || "v25.0";
|
|
18
|
+
const defaultLangCode = config?.default_language_code || "en_US";
|
|
19
|
+
if (!phoneNumberId || !accessToken) {
|
|
20
|
+
return; // WhatsApp not configured, silently skip
|
|
21
|
+
}
|
|
27
22
|
// Find mappings for this event
|
|
28
23
|
const [mappings] = await whatsappModule.listAndCountWhatsappEventMappings({ event_name: name, active: true });
|
|
29
24
|
if (!mappings || mappings.length === 0)
|
|
@@ -54,7 +49,7 @@ async function whatsappEventHandler({ event: { name, data }, container, }) {
|
|
|
54
49
|
template: {
|
|
55
50
|
name: mapping.template_name,
|
|
56
51
|
language: {
|
|
57
|
-
code: mapping.language_code ||
|
|
52
|
+
code: mapping.language_code || defaultLangCode,
|
|
58
53
|
},
|
|
59
54
|
...(components.length > 0 ? { components } : {}),
|
|
60
55
|
},
|
|
@@ -186,4 +181,4 @@ exports.config = {
|
|
|
186
181
|
"exchange.created",
|
|
187
182
|
],
|
|
188
183
|
};
|
|
189
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
184
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2hhdHNhcHAtZXZlbnQtaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zdWJzY3JpYmVycy93aGF0c2FwcC1ldmVudC1oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUtBLHVDQTZIQztBQWpJRCxxREFBcUU7QUFFckUsTUFBTSxlQUFlLEdBQUcsVUFBVSxDQUFBO0FBRW5CLEtBQUssVUFBVSxvQkFBb0IsQ0FBQyxFQUMvQyxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLEVBQ3JCLFNBQVMsR0FDeUI7SUFDbEMsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUNsRSxNQUFNLGNBQWMsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBRXpELElBQUksQ0FBQztRQUNELHFDQUFxQztRQUNyQyxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsTUFBTyxjQUFzQixDQUFDLDJCQUEyQixDQUN2RSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsRUFDaEIsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQ2QsQ0FBQTtRQUVELE1BQU0sTUFBTSxHQUFHLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBRTNCLCtEQUErRDtRQUMvRCxNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUUsZUFBZSxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsd0JBQXdCLENBQUE7UUFDckYsTUFBTSxXQUFXLEdBQUcsTUFBTSxFQUFFLFlBQVksSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFBO1FBQzdFLE1BQU0sVUFBVSxHQUFHLE1BQU0sRUFBRSxXQUFXLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsSUFBSSxPQUFPLENBQUE7UUFDckYsTUFBTSxlQUFlLEdBQUcsTUFBTSxFQUFF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
|
package/README.md
CHANGED
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/npm/v/@sam-ael/medusa-plugin-whatsapp?style=flat-square&color=25D366" alt="npm version" />
|
|
3
|
+
<img src="https://img.shields.io/badge/medusa-v2-7C3AED?style=flat-square" alt="Medusa v2" />
|
|
4
|
+
<img src="https://img.shields.io/badge/WhatsApp_Cloud_API-v25.0-25D366?style=flat-square&logo=whatsapp&logoColor=white" alt="WhatsApp API v25.0" />
|
|
5
|
+
<img src="https://img.shields.io/npm/l/@sam-ael/medusa-plugin-whatsapp?style=flat-square" alt="license" />
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
# @sam-ael/medusa-plugin-whatsapp
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
A **MedusaJS v2 plugin** for sending WhatsApp Business template messages to customers when store events happen — like order placements, fulfillments, returns, and more.
|
|
11
|
+
|
|
12
|
+
Map any Medusa event to a WhatsApp template, configure recipient logic, and manage everything from a built-in admin dashboard. No code required after installation.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- 📱 **WhatsApp Cloud API** — Sends template messages through the official WhatsApp Business Cloud API (v25.0)
|
|
19
|
+
- 🗺️ **Event → Template Mapping** — Map Medusa events (`order.placed`, `fulfillment.created`, etc.) to your WhatsApp templates from the admin UI
|
|
20
|
+
- 🎛️ **Admin Dashboard** — Full configuration page inside the Medusa admin panel with no extra setup
|
|
21
|
+
- 📋 **Message Logs** — Every sent (and failed) message is logged with request/response payloads for debugging
|
|
22
|
+
- 🧪 **Test Messages** — Send test template messages directly from the admin UI before going live
|
|
23
|
+
- 📦 **Template Variables** — Map dynamic order data (order ID, customer name, total, etc.) to WhatsApp template body parameters
|
|
24
|
+
- 📞 **Smart Recipient Resolution** — Automatically resolves phone numbers from billing address, shipping address, or customer profile
|
|
25
|
+
- 🔑 **Flexible Configuration** — Configure via the admin UI, environment variables, or both (DB config takes priority)
|
|
26
|
+
- 🏗️ **Zero Dependencies** — Uses native `fetch` — no external HTTP libraries needed
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Supported Events
|
|
31
|
+
|
|
32
|
+
The plugin listens to the following events out of the box. You can also type in any custom event name in the admin UI.
|
|
33
|
+
|
|
34
|
+
| Category | Events |
|
|
35
|
+
|---|---|
|
|
36
|
+
| **Orders** | `order.placed` · `order.completed` · `order.canceled` · `order.updated` · `order.fulfillment_created` |
|
|
37
|
+
| **Fulfillment** | `fulfillment.created` · `fulfillment.shipment_created` · `fulfillment.delivery_created` |
|
|
38
|
+
| **Customers** | `customer.created` · `customer.updated` |
|
|
39
|
+
| **Returns & Claims** | `return.created` · `return.received` · `claim.created` · `exchange.created` |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Prerequisites
|
|
44
|
+
|
|
45
|
+
- **MedusaJS v2** (`>= 2.x`)
|
|
46
|
+
- A **WhatsApp Business Account** with access to the [Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api)
|
|
47
|
+
- A **Phone Number ID** and **Access Token** from the [Meta Developer Console](https://developers.facebook.com/apps/)
|
|
48
|
+
- At least one **approved WhatsApp template** in your Business Manager
|
|
6
49
|
|
|
7
50
|
---
|
|
8
51
|
|
|
@@ -12,143 +55,166 @@ This plugin bridges the gap between your Medusa core logic and your customers by
|
|
|
12
55
|
yarn add @sam-ael/medusa-plugin-whatsapp
|
|
13
56
|
```
|
|
14
57
|
|
|
58
|
+
Or with npm:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install @sam-ael/medusa-plugin-whatsapp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
15
66
|
## Configuration
|
|
16
67
|
|
|
17
|
-
|
|
68
|
+
### 1. Add the plugin to `medusa-config.ts`
|
|
18
69
|
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
70
|
+
```ts
|
|
71
|
+
plugins: [
|
|
72
|
+
{
|
|
73
|
+
resolve: "@sam-ael/medusa-plugin-whatsapp",
|
|
74
|
+
options: {},
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
```
|
|
23
78
|
|
|
24
|
-
|
|
25
|
-
|
|
79
|
+
### 2. Set environment variables
|
|
80
|
+
|
|
81
|
+
Add these to your `.env` file:
|
|
82
|
+
|
|
83
|
+
```env
|
|
84
|
+
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
|
|
85
|
+
WHATSAPP_ACCESS_TOKEN=your_access_token
|
|
86
|
+
WHATSAPP_API_VERSION=v25.0 # optional, defaults to v25.0
|
|
26
87
|
```
|
|
27
88
|
|
|
28
|
-
|
|
89
|
+
> **Note:** You can also configure these through the admin dashboard. Database-stored config takes priority over environment variables.
|
|
29
90
|
|
|
30
|
-
|
|
31
|
-
import { defineConfig } from "@medusajs/framework/utils"
|
|
91
|
+
### 3. Run migrations
|
|
32
92
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
resolve: "@sam-ael/medusa-plugin-whatsapp",
|
|
37
|
-
options: {},
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
})
|
|
93
|
+
```bash
|
|
94
|
+
npx medusa db:migrate
|
|
41
95
|
```
|
|
42
96
|
|
|
43
|
-
|
|
97
|
+
### 4. Start Medusa
|
|
44
98
|
|
|
45
99
|
```bash
|
|
46
|
-
npx medusa
|
|
47
|
-
npx medusa db:generate whatsapp # Run to generate plugin configurations
|
|
48
|
-
npx medusa db:migrate # Migrate WhatsApp structures
|
|
100
|
+
npx medusa develop
|
|
49
101
|
```
|
|
50
102
|
|
|
103
|
+
Navigate to **WhatsApp** in the admin sidebar to configure and start mapping events.
|
|
104
|
+
|
|
51
105
|
---
|
|
52
106
|
|
|
53
|
-
##
|
|
107
|
+
## Admin Dashboard
|
|
54
108
|
|
|
55
|
-
|
|
56
|
-
Medusa routes specific core events natively in the framework — such as creating orders or fulfilling packages. This plugin seamlessly introduces a routing subscriber that hooks up directly to Medusa events:
|
|
109
|
+
The plugin adds a **WhatsApp** page to the Medusa admin panel with four sections:
|
|
57
110
|
|
|
58
|
-
|
|
59
|
-
|-------------------------|---------|
|
|
60
|
-
| `order.placed` | Fires exactly when a new user checks-out. |
|
|
61
|
-
| `order.updated` | Notification upon any order state change. |
|
|
62
|
-
| `order.canceled` / `order.completed` | Notify regarding active payment cancelations or when successfully finalized. |
|
|
63
|
-
| `fulfillment.created` / `fulfillment.shipment_created` | Shipment update, AWBs triggers or parcel dispatches. |
|
|
64
|
-
| `customer.created` | Notify sign ups automatically! |
|
|
111
|
+
### Configuration
|
|
65
112
|
|
|
66
|
-
|
|
113
|
+
Set your WhatsApp Cloud API credentials, API version, and default language code. Toggle the integration on/off.
|
|
67
114
|
|
|
68
|
-
###
|
|
69
|
-
We provide a plug-and-play UI directly inside your Medusa Dashboard.
|
|
115
|
+
### Event → Template Mappings
|
|
70
116
|
|
|
71
|
-
|
|
72
|
-
2. **Template Mapping**: Assign WhatsApp App templates (`template_name`) into active Medusa Events. You can specify different templates for `order.placed` & `fulfillment.shipment`.
|
|
73
|
-
3. **Payload Variables via UI**: Automatically inject standard payload nodes dynamically without touching scripts. Select from over **15 Dynamic Identifiers** to route inside the templates payload components (`{{1}}` -> `order.display_id`).
|
|
117
|
+
Create rules that map Medusa events to WhatsApp templates:
|
|
74
118
|
|
|
75
|
-
|
|
76
|
-
|
|
119
|
+
- Select a Medusa event (or type a custom one)
|
|
120
|
+
- Enter the WhatsApp template name (as it appears in your Business Manager)
|
|
121
|
+
- Set the language code
|
|
122
|
+
- Choose the recipient type:
|
|
123
|
+
- **Billing phone** (fallback: shipping) — default
|
|
124
|
+
- **Billing phone only**
|
|
125
|
+
- **Shipping phone only**
|
|
126
|
+
- **Custom phone number** — for sending to a fixed number (e.g., store owner notifications)
|
|
127
|
+
- Map up to 5 template variables to order data paths (e.g., `order.display_id`, `order.total`, `order.customer.first_name`)
|
|
77
128
|
|
|
78
|
-
|
|
79
|
-
- **Billing Strict Mode**.
|
|
80
|
-
- **Shipping Strict Mode**.
|
|
81
|
-
- **Custom Overwrites** - Target explicit admin notifications exclusively whenever a system event hooks.
|
|
129
|
+
### Test Messages
|
|
82
130
|
|
|
83
|
-
|
|
131
|
+
Send a template message to any phone number directly from the admin UI — useful for verifying your templates work before enabling event mappings.
|
|
84
132
|
|
|
85
|
-
|
|
133
|
+
### Message Logs
|
|
86
134
|
|
|
87
|
-
|
|
135
|
+
View a log of all sent and failed messages, including timestamps, recipient, status, and the event that triggered them.
|
|
88
136
|
|
|
89
|
-
|
|
90
|
-
**GET & POST Setup**
|
|
91
|
-
`GET /admin/whatsapp/config`
|
|
92
|
-
`POST /admin/whatsapp/config`
|
|
137
|
+
---
|
|
93
138
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
139
|
+
## Template Variables
|
|
140
|
+
|
|
141
|
+
When creating a mapping, you can map WhatsApp template body parameters to data from the event. Use dot-notation paths to reference nested data:
|
|
142
|
+
|
|
143
|
+
| Path | Description |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `order.display_id` | Human-readable order number |
|
|
146
|
+
| `order.total` | Order total amount |
|
|
147
|
+
| `order.currency_code` | Currency code (e.g., `INR`, `USD`) |
|
|
148
|
+
| `order.email` | Order contact email |
|
|
149
|
+
| `order.shipping_address.first_name` | Shipping first name |
|
|
150
|
+
| `order.shipping_address.last_name` | Shipping last name |
|
|
151
|
+
| `order.shipping_address.phone` | Shipping phone |
|
|
152
|
+
| `order.shipping_address.city` | Shipping city |
|
|
153
|
+
| `order.billing_address.first_name` | Billing first name |
|
|
154
|
+
| `order.billing_address.last_name` | Billing last name |
|
|
155
|
+
| `order.billing_address.phone` | Billing phone |
|
|
156
|
+
| `order.customer.first_name` | Customer first name |
|
|
157
|
+
| `order.customer.last_name` | Customer last name |
|
|
158
|
+
| `order.customer.email` | Customer email |
|
|
159
|
+
| `order.customer.phone` | Customer phone |
|
|
160
|
+
|
|
161
|
+
You can also type any custom dot-path to reference data specific to your event payloads.
|
|
102
162
|
|
|
103
|
-
|
|
104
|
-
**Create Map Link**
|
|
105
|
-
`POST /admin/whatsapp/mappings`
|
|
163
|
+
---
|
|
106
164
|
|
|
107
|
-
|
|
165
|
+
## API Routes
|
|
108
166
|
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
"event_name": "order.completed",
|
|
112
|
-
"template_name": "delivery_confirmation",
|
|
113
|
-
"template_variables": { "1": "order.display_id" },
|
|
114
|
-
"recipient_type": "billing_shipping"
|
|
115
|
-
}
|
|
116
|
-
```
|
|
167
|
+
The plugin exposes the following admin API routes (all require authentication):
|
|
117
168
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
`GET
|
|
169
|
+
| Method | Endpoint | Description |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `GET` | `/admin/whatsapp/config` | Get current WhatsApp configuration |
|
|
172
|
+
| `POST` | `/admin/whatsapp/config` | Create or update configuration |
|
|
173
|
+
| `GET` | `/admin/whatsapp/mappings` | List all event → template mappings |
|
|
174
|
+
| `POST` | `/admin/whatsapp/mappings` | Create a new mapping |
|
|
175
|
+
| `PUT` | `/admin/whatsapp/mappings/:id` | Update a mapping |
|
|
176
|
+
| `DELETE` | `/admin/whatsapp/mappings/:id` | Delete a mapping |
|
|
177
|
+
| `GET` | `/admin/whatsapp/logs` | Get message logs |
|
|
178
|
+
| `POST` | `/admin/whatsapp/test` | Send a test message |
|
|
121
179
|
|
|
122
|
-
|
|
180
|
+
---
|
|
123
181
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
182
|
+
## How It Works
|
|
183
|
+
|
|
184
|
+
1. A Medusa event fires (e.g., `order.placed`)
|
|
185
|
+
2. The subscriber checks if a WhatsApp config is active (DB → env vars fallback)
|
|
186
|
+
3. It looks up all active event mappings for that event
|
|
187
|
+
4. For each mapping, it resolves the recipient phone number based on the `recipient_type`
|
|
188
|
+
5. It builds the WhatsApp Cloud API payload with template name, language, and variable parameters
|
|
189
|
+
6. It sends the request to `https://graph.facebook.com/v25.0/{phone_number_id}/messages`
|
|
190
|
+
7. The result (success or failure) is logged to the `whatsapp_message_log` table
|
|
127
191
|
|
|
128
192
|
---
|
|
129
193
|
|
|
130
|
-
##
|
|
194
|
+
## Database Schema
|
|
131
195
|
|
|
132
|
-
|
|
196
|
+
The plugin creates three tables:
|
|
133
197
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
198
|
+
| Table | Purpose |
|
|
199
|
+
|---|---|
|
|
200
|
+
| `whatsapp_config` | Stores API credentials and settings (phone number ID, access token, API version, language, active toggle) |
|
|
201
|
+
| `whatsapp_event_mapping` | Maps events to templates with recipient type, language, and variable configuration |
|
|
202
|
+
| `whatsapp_message_log` | Logs every message attempt with status, payloads, and WhatsApp message ID |
|
|
137
203
|
|
|
138
|
-
|
|
139
|
-
npx yalc push
|
|
140
|
-
```
|
|
204
|
+
---
|
|
141
205
|
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
npx yalc add @sam-ael/medusa-plugin-whatsapp
|
|
145
|
-
yarn install
|
|
146
|
-
```
|
|
206
|
+
## Environment Variables Reference
|
|
147
207
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
208
|
+
| Variable | Required | Default | Description |
|
|
209
|
+
|---|---|---|---|
|
|
210
|
+
| `WHATSAPP_PHONE_NUMBER_ID` | Yes* | — | Your WhatsApp Business phone number ID |
|
|
211
|
+
| `WHATSAPP_ACCESS_TOKEN` | Yes* | — | Your WhatsApp Cloud API access token |
|
|
212
|
+
| `WHATSAPP_API_VERSION` | No | `v25.0` | Facebook Graph API version |
|
|
213
|
+
|
|
214
|
+
\* *Not required if configured through the admin dashboard.*
|
|
215
|
+
|
|
216
|
+
---
|
|
152
217
|
|
|
153
218
|
## License
|
|
154
|
-
|
|
219
|
+
|
|
220
|
+
MIT — see [LICENSE](./LICENSE) for details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sam-ael/medusa-plugin-whatsapp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A MedusaJS 2 plugin for sending WhatsApp Business template messages on store events.",
|
|
5
5
|
"author": "Samuel Ael (https://www.github.com/SAM-AEL)",
|
|
6
6
|
"license": "MIT",
|