@erulabs-tech/medusa-plugin-wonyapay 0.0.1
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 +23 -0
- package/.medusa/server/src/admin/index.mjs +24 -0
- package/.medusa/server/src/api/admin/plugin/route.js +7 -0
- package/.medusa/server/src/api/store/use-wonya/route.js +8 -0
- package/.medusa/server/src/providers/wonyapay/index.js +26 -0
- package/.medusa/server/src/providers/wonyapay/service.js +171 -0
- package/.medusa/server/src/providers/wonyapay/types.js +3 -0
- package/.medusa/server/src/providers/wonyapay/utils.js +13 -0
- package/README.md +327 -0
- package/package.json +66 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const i18nTranslations0 = {};
|
|
3
|
+
const widgetModule = { widgets: [] };
|
|
4
|
+
const routeModule = {
|
|
5
|
+
routes: []
|
|
6
|
+
};
|
|
7
|
+
const menuItemModule = {
|
|
8
|
+
menuItems: []
|
|
9
|
+
};
|
|
10
|
+
const formModule = { customFields: {} };
|
|
11
|
+
const displayModule = {
|
|
12
|
+
displays: {}
|
|
13
|
+
};
|
|
14
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
15
|
+
const plugin = {
|
|
16
|
+
widgetModule,
|
|
17
|
+
routeModule,
|
|
18
|
+
menuItemModule,
|
|
19
|
+
formModule,
|
|
20
|
+
displayModule,
|
|
21
|
+
i18nModule
|
|
22
|
+
};
|
|
23
|
+
module.exports = plugin;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const i18nTranslations0 = {};
|
|
2
|
+
const widgetModule = { widgets: [] };
|
|
3
|
+
const routeModule = {
|
|
4
|
+
routes: []
|
|
5
|
+
};
|
|
6
|
+
const menuItemModule = {
|
|
7
|
+
menuItems: []
|
|
8
|
+
};
|
|
9
|
+
const formModule = { customFields: {} };
|
|
10
|
+
const displayModule = {
|
|
11
|
+
displays: {}
|
|
12
|
+
};
|
|
13
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
14
|
+
const plugin = {
|
|
15
|
+
widgetModule,
|
|
16
|
+
routeModule,
|
|
17
|
+
menuItemModule,
|
|
18
|
+
formModule,
|
|
19
|
+
displayModule,
|
|
20
|
+
i18nModule
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
plugin as default
|
|
24
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
async function GET(req, res) {
|
|
5
|
+
res.sendStatus(200);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3BsdWdpbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLGtCQUtDO0FBTE0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN0QixDQUFDIn0=
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = void 0;
|
|
4
|
+
const GET = async (req, res) => {
|
|
5
|
+
res.status(200).json({ message: "Pay with MoMo with Partner" });
|
|
6
|
+
};
|
|
7
|
+
exports.GET = GET;
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3VzZS13b255YS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFTyxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDbkUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsQ0FBQyxDQUFDO0FBQ2xFLENBQUMsQ0FBQztBQUZXLFFBQUEsR0FBRyxPQUVkIn0=
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
21
|
+
const service_1 = __importDefault(require("./service"));
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
exports.default = (0, utils_1.ModuleProvider)(utils_1.Modules.PAYMENT, {
|
|
24
|
+
services: [service_1.default],
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3dvbnlhcGF5L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxxREFBb0U7QUFDcEUsd0RBQWdEO0FBRWhELDBDQUF3QjtBQUV4QixrQkFBZSxJQUFBLHNCQUFjLEVBQUMsZUFBTyxDQUFDLE9BQU8sRUFBRTtJQUM3QyxRQUFRLEVBQUUsQ0FBQyxpQkFBdUIsQ0FBQztDQUNwQyxDQUFDLENBQUMifQ==
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
4
|
+
const utils_2 = require("./utils");
|
|
5
|
+
const BASE_API_URL = "https://app-api.wonyasoft.com";
|
|
6
|
+
class WonyaPayProviderService extends utils_1.AbstractPaymentProvider {
|
|
7
|
+
constructor(deps, options) {
|
|
8
|
+
super(deps, options);
|
|
9
|
+
this.deps = deps;
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
static validateOptions(options) {
|
|
14
|
+
if (!options.apiKey) {
|
|
15
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Missing API Key");
|
|
16
|
+
}
|
|
17
|
+
if (!options.refPartner) {
|
|
18
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Missing Partner ID");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async authorizePayment(input) {
|
|
22
|
+
// TODO: Authorize payment
|
|
23
|
+
this.deps.logger.info("Authorize payment");
|
|
24
|
+
const paymentId = (0, utils_2.generateTransactionId)();
|
|
25
|
+
const response = await fetch(`${BASE_API_URL}/payment`, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
Authorization: `Bearer ${this.options.apiKey}`,
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
RefPartenaire: this.options.refPartner,
|
|
33
|
+
RefTransa: paymentId,
|
|
34
|
+
Montant: 100,
|
|
35
|
+
Devise: "CDF" /*|| (input.data?.currency_code as string).toUpperCase()*/,
|
|
36
|
+
Action: (input.data?.action).toUpperCase(),
|
|
37
|
+
mobileMoney: input.data?.mobileMoney,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
this.deps.logger.info("Waiting for transaction response");
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
return { data: {}, status: "error" };
|
|
43
|
+
}
|
|
44
|
+
const transactionData = await response.json();
|
|
45
|
+
console.log(transactionData);
|
|
46
|
+
const refTransa = transactionData.data.RefTransa;
|
|
47
|
+
// Poll to check if status has changed
|
|
48
|
+
await (0, utils_2.sleep)(10000);
|
|
49
|
+
let status = await this.getPaymentStatus({ data: { id: refTransa } });
|
|
50
|
+
console.log("First status");
|
|
51
|
+
console.log(status);
|
|
52
|
+
if (status.status === "authorized")
|
|
53
|
+
return {
|
|
54
|
+
status: "authorized",
|
|
55
|
+
};
|
|
56
|
+
else if (status.status === "canceled")
|
|
57
|
+
return { status: "canceled" };
|
|
58
|
+
else if (status.status === "error")
|
|
59
|
+
return { status: "error" };
|
|
60
|
+
// Second poll
|
|
61
|
+
await (0, utils_2.sleep)(10000);
|
|
62
|
+
status = await this.getPaymentStatus({ data: { id: refTransa } });
|
|
63
|
+
console.log("Second status");
|
|
64
|
+
console.log(status);
|
|
65
|
+
if (status.status === "authorized")
|
|
66
|
+
return {
|
|
67
|
+
status: "authorized",
|
|
68
|
+
};
|
|
69
|
+
else if (status.status === "canceled")
|
|
70
|
+
return { status: "canceled" };
|
|
71
|
+
else if (status.status === "error")
|
|
72
|
+
return { status: "error" };
|
|
73
|
+
// Third poll
|
|
74
|
+
await (0, utils_2.sleep)(10000);
|
|
75
|
+
status = await this.getPaymentStatus({ data: { id: refTransa } });
|
|
76
|
+
console.log("Third status");
|
|
77
|
+
console.log(status);
|
|
78
|
+
if (status.status === "authorized")
|
|
79
|
+
return {
|
|
80
|
+
status: "authorized",
|
|
81
|
+
};
|
|
82
|
+
else if (status.status === "canceled")
|
|
83
|
+
return { status: "canceled" };
|
|
84
|
+
else if (status.status === "error")
|
|
85
|
+
return { status: "error" };
|
|
86
|
+
// All polls failed
|
|
87
|
+
return {
|
|
88
|
+
data: {},
|
|
89
|
+
status: "pending",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async cancelPayment(input) {
|
|
93
|
+
// TODO: Cancel payment
|
|
94
|
+
this.deps.logger.info("Cancel payment");
|
|
95
|
+
return {
|
|
96
|
+
data: {},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async capturePayment(input) {
|
|
100
|
+
// TODO: Capture payment
|
|
101
|
+
this.deps.logger.info("Capture payment");
|
|
102
|
+
return { data: {} };
|
|
103
|
+
}
|
|
104
|
+
async getPaymentStatus(input) {
|
|
105
|
+
// TODO: Get payment status
|
|
106
|
+
this.deps.logger.info(`Get payment status`);
|
|
107
|
+
const transactionId = input.data?.id;
|
|
108
|
+
console.log(`Transaction ID ${transactionId}`);
|
|
109
|
+
const response = await fetch(`${BASE_API_URL}/transactionStatus/status/${transactionId}`, {
|
|
110
|
+
method: "GET",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
Authorization: `Bearer ${this.options.apiKey}`,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
console.log("An error from the response");
|
|
118
|
+
return { status: "error" };
|
|
119
|
+
}
|
|
120
|
+
const data = (await response.json());
|
|
121
|
+
console.log("Status data");
|
|
122
|
+
console.log(data);
|
|
123
|
+
switch (data.data.StatutWonya) {
|
|
124
|
+
case "Echec":
|
|
125
|
+
return { status: "canceled" };
|
|
126
|
+
case "pending":
|
|
127
|
+
case "Attente":
|
|
128
|
+
return { status: "pending" };
|
|
129
|
+
case "Reçu":
|
|
130
|
+
case "Succes":
|
|
131
|
+
return { status: "authorized" };
|
|
132
|
+
default:
|
|
133
|
+
return { status: "error" };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async initiatePayment(input) {
|
|
137
|
+
// TODO: Initiate payment session
|
|
138
|
+
this.deps.logger.info("Initiate payment");
|
|
139
|
+
const { amount, currency_code, data: customerDetails } = input;
|
|
140
|
+
console.log(input);
|
|
141
|
+
return { id: "", data: { amount, currency_code, customerDetails } };
|
|
142
|
+
}
|
|
143
|
+
async refundPayment(input) {
|
|
144
|
+
// TODO: Initiate refund payment
|
|
145
|
+
this.deps.logger.info("Initiate refund");
|
|
146
|
+
return { data: {} };
|
|
147
|
+
}
|
|
148
|
+
async retrievePayment(input) {
|
|
149
|
+
// TODO: Retrieve payment
|
|
150
|
+
this.deps.logger.info("Retrieve payment");
|
|
151
|
+
return { data: {} };
|
|
152
|
+
}
|
|
153
|
+
async getWebhookActionAndData(data) {
|
|
154
|
+
// TODO: Get webhook action and data
|
|
155
|
+
this.deps.logger.info("Get webhook action and data");
|
|
156
|
+
return { action: "authorized" };
|
|
157
|
+
}
|
|
158
|
+
async updatePayment(input) {
|
|
159
|
+
// TODO: Update payment
|
|
160
|
+
this.deps.logger.info("Update payment");
|
|
161
|
+
return {};
|
|
162
|
+
}
|
|
163
|
+
async deletePayment(input) {
|
|
164
|
+
// TODO: Delete payment
|
|
165
|
+
this.deps.logger.info("Delete payment");
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
WonyaPayProviderService.identifier = "payment-wonyapay";
|
|
170
|
+
exports.default = WonyaPayProviderService;
|
|
171
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9wcm92aWRlcnMvd29ueWFwYXkvc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUFpRjtBQXdCakYsbUNBQXVEO0FBRXZELE1BQU0sWUFBWSxHQUFHLCtCQUErQixDQUFDO0FBRXJELE1BQU0sdUJBQXdCLFNBQVEsK0JBQWdDO0lBR3BFLFlBQ1UsSUFBMEIsRUFDMUIsT0FBZ0I7UUFFeEIsS0FBSyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUhiLFNBQUksR0FBSixJQUFJLENBQXNCO1FBQzFCLFlBQU8sR0FBUCxPQUFPLENBQVM7UUFJeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVELE1BQU0sQ0FBQyxlQUFlLENBQUMsT0FBeUI7UUFDOUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNwQixNQUFNLElBQUksbUJBQVcsQ0FBQyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztRQUMzRSxDQUFDO1FBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUN4QixNQUFNLElBQUksbUJBQVcsQ0FBQyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztRQUM5RSxDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxLQUE0QjtRQUNqRCwwQkFBMEI7UUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDM0MsTUFBTSxTQUFTLEdBQUcsSUFBQSw2QkFBcUIsR0FBRSxDQUFDO1FBRTFDLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsWUFBWSxVQUFVLEVBQUU7WUFDdEQsTUFBTSxFQUFFLE1BQU07WUFDZCxPQUFPLEVBQUU7Z0JBQ1AsY0FBYyxFQUFFLGtCQUFrQjtnQkFDbEMsYUFBYSxFQUFFLFVBQVUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUU7YUFDL0M7WUFDRCxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztnQkFDbkIsYUFBYSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVTtnQkFDdEMsU0FBUyxFQUFFLFNBQVM7Z0JBQ3BCLE9BQU8sRUFBRSxHQUFHO2dCQUNaLE1BQU0sRUFBRSxLQUFLLENBQUMsMERBQTBEO2dCQUN4RSxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLE1BQWlCLENBQUEsQ0FBQyxXQUFXLEVBQUU7Z0JBQ3BELFdBQVcsRUFBRSxLQUFLLENBQUMsSUFBSSxFQUFFLFdBQXFCO2FBQy9DLENBQUM7U0FDSCxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsa0NBQWtDLENBQUMsQ0FBQztRQUUxRCxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ2pCLE9BQU8sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsTUFBTSxlQUFlLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDOUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUM3QixNQUFNLFNBQVMsR0FBRyxlQUFlLENBQUMsSUFBSSxDQUFDLFNBQW1CLENBQUM7UUFFM0Qsc0NBQXNDO1FBQ3RDLE1BQU0sSUFBQSxhQUFLLEVBQUMsS0FBSyxDQUFDLENBQUM7UUFDbkIsSUFBSSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3RFLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDNUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNwQixJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssWUFBWTtZQUNoQyxPQUFPO2dCQUNMLE1BQU0sRUFBRSxZQUFZO2FBQ3JCLENBQUM7YUFDQyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssVUFBVTtZQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLENBQUM7YUFDaEUsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLE9BQU87WUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxDQUFDO1FBRS9ELGNBQWM7UUFDZCxNQUFNLElBQUEsYUFBSyxFQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ25CLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDbEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUM3QixPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3BCLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxZQUFZO1lBQ2hDLE9BQU87Z0JBQ0wsTUFBTSxFQUFFLFlBQVk7YUFDckIsQ0FBQzthQUNDLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxVQUFVO1lBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsQ0FBQzthQUNoRSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssT0FBTztZQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFFL0QsYUFBYTtRQUNiLE1BQU0sSUFBQSxhQUFLLEVBQUMsS0FBSyxDQUFDLENBQUM7UUFDbkIsTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNsRSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDcEIsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLFlBQVk7WUFDaEMsT0FBTztnQkFDTCxNQUFNLEVBQUUsWUFBWTthQUNyQixDQUFDO2FBQ0MsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLFVBQVU7WUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDO2FBQ2hFLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxPQUFPO1lBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUUvRCxtQkFBbUI7UUFDbkIsT0FBTztZQUNMLElBQUksRUFBRSxFQUFFO1lBQ1IsTUFBTSxFQUFFLFNBQVM7U0FDbEIsQ0FBQztJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsYUFBYSxDQUFDLEtBQXlCO1FBQzNDLHVCQUF1QjtRQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUV4QyxPQUFPO1lBQ0wsSUFBSSxFQUFFLEVBQUU7U0FDVCxDQUFDO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxjQUFjLENBQUMsS0FBMEI7UUFDN0Msd0JBQXdCO1FBQ3hCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBRXpDLE9BQU8sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVELEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxLQUE0QjtRQUNqRCwyQkFBMkI7UUFDM0IsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFDNUMsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUM7UUFDckMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsYUFBYSxFQUFFLENBQUMsQ0FBQztRQUUvQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLFlBQVksNkJBQTZCLGFBQWEsRUFBRSxFQUFFO1lBQ3hGLE1BQU0sRUFBRSxLQUFLO1lBQ2IsT0FBTyxFQUFFO2dCQUNQLGNBQWMsRUFBRSxrQkFBa0I7Z0JBQ2xDLGFBQWEsRUFBRSxVQUFVLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFO2FBQy9DO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNqQixPQUFPLENBQUMsR0FBRyxDQUFDLDRCQUE0QixDQUFDLENBQUM7WUFDMUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUM3QixDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQUcsQ0FBQyxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBbUMsQ0FBQztRQUN2RSxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzNCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEIsUUFBUSxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQzlCLEtBQUssT0FBTztnQkFDVixPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDO1lBQ2hDLEtBQUssU0FBUyxDQUFDO1lBQ2YsS0FBSyxTQUFTO2dCQUNaLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUM7WUFDL0IsS0FBSyxNQUFNLENBQUM7WUFDWixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsQ0FBQztZQUNsQztnQkFDRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQsS0FBSyxDQUFDLGVBQWUsQ0FBQyxLQUEyQjtRQUMvQyxpQ0FBaUM7UUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFDMUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRSxHQUFHLEtBQUssQ0FBQztRQUMvRCxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRW5CLE9BQU8sRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxFQUFFLE1BQU0sRUFBRSxhQUFhLEVBQUUsZUFBZSxFQUFFLEVBQUUsQ0FBQztJQUN0RSxDQUFDO0lBRUQsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUF5QjtRQUMzQyxnQ0FBZ0M7UUFDaEMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFekMsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRUQsS0FBSyxDQUFDLGVBQWUsQ0FBQyxLQUEyQjtRQUMvQyx5QkFBeUI7UUFDekIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFFMUMsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRUQsS0FBSyxDQUFDLHVCQUF1QixDQUMzQixJQUF1QztRQUV2QyxvQ0FBb0M7UUFDcEMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDZCQUE2QixDQUFDLENBQUM7UUFFckQsT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0lBRUQsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUF5QjtRQUMzQyx1QkFBdUI7UUFDdkIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFeEMsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUF5QjtRQUMzQyx1QkFBdUI7UUFDdkIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFeEMsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDOztBQTdMTSxrQ0FBVSxHQUFXLGtCQUFrQixDQUFDO0FBZ01qRCxrQkFBZSx1QkFBdUIsQ0FBQyJ9
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3dvbnlhcGF5L3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sleep = void 0;
|
|
4
|
+
exports.generateTransactionId = generateTransactionId;
|
|
5
|
+
const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
6
|
+
function generateTransactionId(length = 20) {
|
|
7
|
+
const array = new Uint8Array(length);
|
|
8
|
+
crypto.getRandomValues(array);
|
|
9
|
+
return Array.from(array, (byte) => CHARS[byte % CHARS.length]).join("");
|
|
10
|
+
}
|
|
11
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
exports.sleep = sleep;
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3dvbnlhcGF5L3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHNEQUtDO0FBUEQsTUFBTSxLQUFLLEdBQUcsc0NBQXNDLENBQUM7QUFFckQsU0FBZ0IscUJBQXFCLENBQUMsU0FBaUIsRUFBRTtJQUN2RCxNQUFNLEtBQUssR0FBRyxJQUFJLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNyQyxNQUFNLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRTlCLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQzFFLENBQUM7QUFFTSxNQUFNLEtBQUssR0FBRyxDQUFDLEVBQVUsRUFBRSxFQUFFLENBQUMsSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUExRSxRQUFBLEtBQUssU0FBcUUifQ==
|
package/README.md
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# WonYaSoft Payment Plugin for Medusa
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[![npm version][npm-version]][npm-version-url]
|
|
6
|
+
[![License][license]][license-url]
|
|
7
|
+
[![Medusa v2][medusa-badge]][medusa-url]
|
|
8
|
+
[![Node.js >=20][node-badge]][node-url]
|
|
9
|
+
[![Under Active Development][dev-badge]][dev-badge]
|
|
10
|
+
[![TypeScript][typescript-badge]][typescript-url]
|
|
11
|
+
[![WonYaSoft Payment][wonyapay-badge]][wonyapay-url]
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
> :warning: **Under Active Development** - This plugin is currently being developed and is not yet production-ready.
|
|
16
|
+
|
|
17
|
+
A Medusa v2 payment plugin for WonYaSoft (WonyaPay) - Mobile Money payment solutions for the Democratic Republic of Congo.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Table of Contents
|
|
22
|
+
|
|
23
|
+
- [Features](#features)
|
|
24
|
+
- [Requirements](#requirements)
|
|
25
|
+
- [Installation](#installation)
|
|
26
|
+
- [Configuration](#configuration)
|
|
27
|
+
- [Payment Flow](#payment-flow)
|
|
28
|
+
- [API Usage](#api-usage)
|
|
29
|
+
- [Supported Providers](#supported-providers)
|
|
30
|
+
- [Development](#development)
|
|
31
|
+
- [Known Limitations](#known-limitations)
|
|
32
|
+
- [Troubleshooting](#troubleshooting)
|
|
33
|
+
- [Support](#support)
|
|
34
|
+
- [License](#license)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Mobile Money Support**: M-Pesa, Airtel Money, Orange Money, AfriMoney
|
|
41
|
+
- **Currency Support**: CDF (Congolese Franc) and USD
|
|
42
|
+
- **C2B Payments**: Collect payments from customers via Mobile Money
|
|
43
|
+
- **B2C Payments**: Process refunds to customer Mobile Money accounts
|
|
44
|
+
- **Automatic Authorization**: Built-in polling for payment confirmation
|
|
45
|
+
- **Transaction Status**: Real-time status checking
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
| Requirement | Version |
|
|
50
|
+
|------------|---------|
|
|
51
|
+
| Medusa | v2.x |
|
|
52
|
+
| Node.js | >= 20 |
|
|
53
|
+
| Medusa CLI | 2.x |
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install @erulabs-tech/medusa-plugin-wonyapay
|
|
59
|
+
# or
|
|
60
|
+
pnpm add @erulabs-tech/medusa-plugin-wonyapay
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
### 1. Add to `medusa-config.ts`
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
module.exports = defineConfig({
|
|
69
|
+
// ... other config
|
|
70
|
+
modules: [
|
|
71
|
+
{
|
|
72
|
+
resolve: "@medusajs/medusa/payment",
|
|
73
|
+
options: {
|
|
74
|
+
providers: [
|
|
75
|
+
{
|
|
76
|
+
resolve: "@erulabs-tech/medusa-plugin-wonyapay/providers/wonyapay",
|
|
77
|
+
id: "payment-wonyapay",
|
|
78
|
+
options: {
|
|
79
|
+
apiKey: process.env.WONYAPAY_API_KEY,
|
|
80
|
+
defaultReceiverID: process.env.WONYAPAY_DEFAULT_RECEIVER_ID,
|
|
81
|
+
baseUrl: process.env.WONYAPAY_BASE_URL,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2. Environment Variables
|
|
92
|
+
|
|
93
|
+
Create a `.env` file with:
|
|
94
|
+
|
|
95
|
+
```env
|
|
96
|
+
# WonYaSoft Payment Configuration
|
|
97
|
+
WONYAPAY_API_KEY=your_api_key_here
|
|
98
|
+
WONYAPAY_DEFAULT_RECEIVER_ID=your_receiver_id_here
|
|
99
|
+
WONYAPAY_BASE_URL=https://app-api.wonyasoft.com
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Environment Variables Reference
|
|
103
|
+
|
|
104
|
+
| Variable | Required | Description | Default |
|
|
105
|
+
|----------|----------|-------------|---------|
|
|
106
|
+
| `WONYAPAY_API_KEY` | Yes | Your WonYaSoft API key (Bearer token) | - |
|
|
107
|
+
| `WONYAPAY_DEFAULT_RECEIVER_ID` | Yes | Your merchant receiver ID | - |
|
|
108
|
+
| `WONYAPAY_BASE_URL` | No | WonYaSoft API base URL | `https://app-api.wonyasoft.com` |
|
|
109
|
+
|
|
110
|
+
## Payment Flow
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
114
|
+
│ Payment Flow │
|
|
115
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
116
|
+
│ │
|
|
117
|
+
│ 1. Customer selects "Mobile Money" at checkout │
|
|
118
|
+
│ │
|
|
119
|
+
│ 2. Customer enters: │
|
|
120
|
+
│ - Phone number (Mobile Money account) │
|
|
121
|
+
│ - Provider (M-Pesa, Airtel, Orange, AfriMoney) │
|
|
122
|
+
│ │
|
|
123
|
+
│ 3. initiatePayment() → POST /payment │
|
|
124
|
+
│ - Creates transaction │
|
|
125
|
+
│ - Sends USSD prompt to customer phone │
|
|
126
|
+
│ │
|
|
127
|
+
│ 4. authorizePayment() → Polls for confirmation │
|
|
128
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
129
|
+
│ │ Poll 1: 60 seconds after initiatePayment │ │
|
|
130
|
+
│ │ Poll 2: 30 seconds after Poll 1 │ │
|
|
131
|
+
│ │ Poll 3: 30 seconds after Poll 2 │ │
|
|
132
|
+
│ │ Max wait: 2 minutes │ │
|
|
133
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
134
|
+
│ │
|
|
135
|
+
│ 5. Customer confirms payment on phone (enters PIN) │
|
|
136
|
+
│ │
|
|
137
|
+
│ 6. Payment authorized → Order completed │
|
|
138
|
+
│ │
|
|
139
|
+
│ 7. If polls exhausted without confirmation → Payment failed │
|
|
140
|
+
│ │
|
|
141
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## API Usage
|
|
145
|
+
|
|
146
|
+
### Create Payment Session
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
POST /store/payment-collections/:id/payment-sessions
|
|
150
|
+
Content-Type: application/json
|
|
151
|
+
x-publishable-api-key: pk_...
|
|
152
|
+
|
|
153
|
+
{
|
|
154
|
+
"provider_id": "payment-wonyapay",
|
|
155
|
+
"data": {
|
|
156
|
+
"senderPhone": "+243851858485",
|
|
157
|
+
"mobileProvider": "mpesa"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Request Body Parameters
|
|
163
|
+
|
|
164
|
+
| Parameter | Type | Required | Description |
|
|
165
|
+
|-----------|------|----------|-------------|
|
|
166
|
+
| `senderPhone` | string | Yes | Customer's Mobile Money phone number |
|
|
167
|
+
| `mobileProvider` | string | No | Provider: `mpesa`, `airtel`, `orange`, `afrimoney` |
|
|
168
|
+
|
|
169
|
+
### Using Medusa JS SDK
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Create payment session with Mobile Money details
|
|
173
|
+
await sdk.store.paymentCollection.createPaymentSession(
|
|
174
|
+
paymentCollectionId,
|
|
175
|
+
{
|
|
176
|
+
provider_id: "payment-wonyapay",
|
|
177
|
+
data: {
|
|
178
|
+
senderPhone: "+243851858485",
|
|
179
|
+
mobileProvider: "mpesa",
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Complete payment (triggers authorizePayment)
|
|
185
|
+
await sdk.store.cart.complete(cartId);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### cURL Example
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
curl -X POST http://localhost:9000/store/payment-collections/paycol_123/payment-sessions \
|
|
192
|
+
-H "Content-Type: application/json" \
|
|
193
|
+
-H "x-publishable-api-key: pk_your_key" \
|
|
194
|
+
-d '{
|
|
195
|
+
"provider_id": "payment-wonyapay",
|
|
196
|
+
"data": {
|
|
197
|
+
"senderPhone": "+243851858485",
|
|
198
|
+
"mobileProvider": "mpesa"
|
|
199
|
+
}
|
|
200
|
+
}'
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Supported Providers
|
|
204
|
+
|
|
205
|
+
| Provider | Code | Status |
|
|
206
|
+
|----------|------|--------|
|
|
207
|
+
| M-Pesa | `mpesa` | :white_check_mark: Supported |
|
|
208
|
+
| Airtel Money | `airtel` | :white_check_mark: Supported |
|
|
209
|
+
| Orange Money | `orange` | :white_check_mark: Supported |
|
|
210
|
+
| AfriMoney | `afrimoney` | :white_check_mark: Supported |
|
|
211
|
+
|
|
212
|
+
## Supported Currencies
|
|
213
|
+
|
|
214
|
+
| Currency | Code | Description |
|
|
215
|
+
|----------|------|-------------|
|
|
216
|
+
| Congolese Franc | `CDF` | Default currency |
|
|
217
|
+
| US Dollar | `USD` | Supported |
|
|
218
|
+
|
|
219
|
+
## WonYaSoft API Endpoints
|
|
220
|
+
|
|
221
|
+
| Endpoint | Method | Description |
|
|
222
|
+
|----------|--------|-------------|
|
|
223
|
+
| `/payment` | POST | Initiate payment (C2B/B2C) |
|
|
224
|
+
| `/transactionStatus/status/:ref` | GET | Check transaction status |
|
|
225
|
+
|
|
226
|
+
## Error Handling
|
|
227
|
+
|
|
228
|
+
### Payment Status Mapping
|
|
229
|
+
|
|
230
|
+
| WonYaSoft Status | Medusa Status | Description |
|
|
231
|
+
|------------------|---------------|-------------|
|
|
232
|
+
| `success` | `authorized` | Payment confirmed |
|
|
233
|
+
| `pending` | `pending` | Awaiting confirmation |
|
|
234
|
+
| `failed` | `error` | Payment failed |
|
|
235
|
+
| `cancelled` | `canceled` | Customer cancelled |
|
|
236
|
+
|
|
237
|
+
### Error Scenarios
|
|
238
|
+
|
|
239
|
+
| Scenario | Result | Action |
|
|
240
|
+
|----------|--------|--------|
|
|
241
|
+
| Customer doesn't confirm within 2 minutes | `error` | Customer must retry |
|
|
242
|
+
| Invalid phone number | `error` | Check and retry |
|
|
243
|
+
| Insufficient funds | `error` | Customer must add funds |
|
|
244
|
+
| API failure | `error` | Check API credentials |
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
### Prerequisites
|
|
249
|
+
|
|
250
|
+
- Node.js >= 20
|
|
251
|
+
- Medusa v2.x
|
|
252
|
+
- WonYaSoft API credentials
|
|
253
|
+
|
|
254
|
+
### Build
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npm run build
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Watch Mode
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npm run dev
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Testing
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
npm run test
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Known Limitations
|
|
273
|
+
|
|
274
|
+
> :warning: **Under Active Development** - The following features are planned:
|
|
275
|
+
|
|
276
|
+
- [ ] Webhook support for instant notifications
|
|
277
|
+
- [ ] Admin dashboard configuration
|
|
278
|
+
- [ ] Payment retry mechanism
|
|
279
|
+
- [ ] Transaction history in admin
|
|
280
|
+
- [ ] SMS notifications for payment status
|
|
281
|
+
- [ ] Multiple receiver ID support
|
|
282
|
+
|
|
283
|
+
## Troubleshooting
|
|
284
|
+
|
|
285
|
+
### Payment stuck in "pending"
|
|
286
|
+
|
|
287
|
+
The customer may not have confirmed the payment on their phone. The payment will auto-fail after 2 minutes.
|
|
288
|
+
|
|
289
|
+
### "Invalid phone number" error
|
|
290
|
+
|
|
291
|
+
Ensure the phone number:
|
|
292
|
+
- Starts with country code (e.g., `+243` for DRC)
|
|
293
|
+
- Is registered with a Mobile Money provider
|
|
294
|
+
- Has sufficient funds
|
|
295
|
+
|
|
296
|
+
### API connection errors
|
|
297
|
+
|
|
298
|
+
1. Verify `WONYAPAY_API_KEY` is correct
|
|
299
|
+
2. Verify `WONYAPAY_DEFAULT_RECEIVER_ID` is correct
|
|
300
|
+
3. Check network connectivity to WonYaSoft API
|
|
301
|
+
|
|
302
|
+
## Support
|
|
303
|
+
|
|
304
|
+
- **Email**: contact@wonso.cd
|
|
305
|
+
- **Phone**: +243 892 672 472
|
|
306
|
+
- **Website**: https://wonyasoft.com
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
This project is licensed under the MIT License.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
<!-- Badges -->
|
|
315
|
+
[npm-version]: https://img.shields.io/npm/v/@erulabs-tech/medusa-plugin-wonyapay?style=flat
|
|
316
|
+
[npm-version-url]: https://www.npmjs.com/package/@erulabs-tech/medusa-plugin-wonyapay
|
|
317
|
+
[license]: https://img.shields.io/badge/license-MIT-green?style=flat
|
|
318
|
+
[license-url]: https://opensource.org/licenses/MIT
|
|
319
|
+
[medusa-badge]: https://img.shields.io/badge/Medusa-v2-blue?style=flat
|
|
320
|
+
[medusa-url]: https://medusajs.com
|
|
321
|
+
[node-badge]: https://img.shields.io/badge/node-%3E%3D20-brightgreen?style=flat
|
|
322
|
+
[node-url]: https://nodejs.org
|
|
323
|
+
[dev-badge]: https://img.shields.io/badge/development-active-orange?style=flat
|
|
324
|
+
[typescript-badge]: https://img.shields.io/badge/TypeScript-blue?style=flat
|
|
325
|
+
[typescript-url]: https://www.typescriptlang.org
|
|
326
|
+
[wonyapay-badge]: https://img.shields.io/badge/WonYaPay-Mobile%20Money-red?style=flat
|
|
327
|
+
[wonyapay-url]: https://wonyasoft.com
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@erulabs-tech/medusa-plugin-wonyapay",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A starter for Medusa plugins.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"medusa",
|
|
7
|
+
"medusa-plugin",
|
|
8
|
+
"medusa-plugin-other",
|
|
9
|
+
"medusa-v2",
|
|
10
|
+
"plugin"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Medusa (https://medusajs.com)",
|
|
14
|
+
"files": [
|
|
15
|
+
".medusa/server"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
"./package.json": "./package.json",
|
|
19
|
+
"./workflows": "./.medusa/server/src/workflows/index.js",
|
|
20
|
+
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
21
|
+
"./modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
22
|
+
"./providers/*": "./.medusa/server/src/providers/*/index.js",
|
|
23
|
+
"./*": "./.medusa/server/src/*.js",
|
|
24
|
+
"./admin": {
|
|
25
|
+
"import": "./.medusa/server/src/admin/index.mjs",
|
|
26
|
+
"require": "./.medusa/server/src/admin/index.js",
|
|
27
|
+
"default": "./.medusa/server/src/admin/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@medusajs/admin-sdk": "2.12.4",
|
|
32
|
+
"@medusajs/cli": "2.12.4",
|
|
33
|
+
"@medusajs/framework": "2.12.4",
|
|
34
|
+
"@medusajs/icons": "2.12.4",
|
|
35
|
+
"@medusajs/medusa": "2.12.4",
|
|
36
|
+
"@medusajs/test-utils": "2.12.4",
|
|
37
|
+
"@medusajs/ui": "4.0.25",
|
|
38
|
+
"@swc/core": "^1.7.28",
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"@types/react": "^18.3.2",
|
|
41
|
+
"@types/react-dom": "^18.2.25",
|
|
42
|
+
"prop-types": "^15.8.1",
|
|
43
|
+
"react": "^18.2.0",
|
|
44
|
+
"react-dom": "^18.2.0",
|
|
45
|
+
"ts-node": "^10.9.2",
|
|
46
|
+
"typescript": "^5.6.2",
|
|
47
|
+
"vite": "^5.2.11",
|
|
48
|
+
"yalc": "^1.0.0-pre.53"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@medusajs/admin-sdk": "2.12.4",
|
|
52
|
+
"@medusajs/cli": "2.12.4",
|
|
53
|
+
"@medusajs/framework": "2.12.4",
|
|
54
|
+
"@medusajs/icons": "2.12.4",
|
|
55
|
+
"@medusajs/medusa": "2.12.4",
|
|
56
|
+
"@medusajs/test-utils": "2.12.4",
|
|
57
|
+
"@medusajs/ui": "4.0.25"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=20"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "medusa plugin:build",
|
|
64
|
+
"dev": "medusa plugin:develop"
|
|
65
|
+
}
|
|
66
|
+
}
|