customer-registration 0.0.22 → 0.0.23

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.
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const customer_registration_1 = require("../../../../../../modules/customer-registration");
6
+ async function POST(req, res) {
7
+ const { email, customer_id, code } = req.body;
8
+ if (!code || (!email && !customer_id)) {
9
+ res.status(400).json({
10
+ message: "Both code and either email or customer_id are required.",
11
+ type: "invalid_data",
12
+ });
13
+ return;
14
+ }
15
+ const customerRegistrationService = req.scope.resolve(customer_registration_1.CUSTOMER_REGISTRATION_MODULE);
16
+ try {
17
+ const customer = await customerRegistrationService.verifyEmailOtp({
18
+ customerId: customer_id,
19
+ email,
20
+ code,
21
+ });
22
+ res.status(200).json({ customer });
23
+ }
24
+ catch (error) {
25
+ if (utils_1.MedusaError.isMedusaError?.(error)) {
26
+ res.status(mapMedusaErrorToStatus(error.type)).json({
27
+ message: error.message,
28
+ type: error.type,
29
+ });
30
+ return;
31
+ }
32
+ res.status(500).json({
33
+ message: error?.message ?? "Failed to verify email OTP",
34
+ type: "unknown_error",
35
+ });
36
+ }
37
+ }
38
+ const mapMedusaErrorToStatus = (type) => {
39
+ switch (type) {
40
+ case utils_1.MedusaError.Types.NOT_FOUND:
41
+ return 404;
42
+ case utils_1.MedusaError.Types.NOT_ALLOWED:
43
+ case utils_1.MedusaError.Types.INVALID_DATA:
44
+ return 400;
45
+ default:
46
+ return 500;
47
+ }
48
+ };
49
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9lbWFpbC9vdHAvdmVyaWZ5L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBYUEsb0JBcUNDO0FBakRELHFEQUF1RDtBQUN2RCwyRkFFd0Q7QUFTakQsS0FBSyxVQUFVLElBQUksQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQ2hFLE1BQU0sRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxHQUFHLEdBQUcsQ0FBQyxJQUEwQixDQUFBO0lBRW5FLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7UUFDdEMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDbkIsT0FBTyxFQUFFLHlEQUF5RDtZQUNsRSxJQUFJLEVBQUUsY0FBYztTQUNyQixDQUFDLENBQUE7UUFDRixPQUFNO0lBQ1IsQ0FBQztJQUVELE1BQU0sMkJBQTJCLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ25ELG9EQUE0QixDQUNRLENBQUE7SUFFdEMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxRQUFRLEdBQUcsTUFBTSwyQkFBMkIsQ0FBQyxjQUFjLENBQUM7WUFDaEUsVUFBVSxFQUFFLFdBQVc7WUFDdkIsS0FBSztZQUNMLElBQUk7U0FDTCxDQUFDLENBQUE7UUFFRixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUE7SUFDcEMsQ0FBQztJQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7UUFDcEIsSUFBSSxtQkFBVyxDQUFDLGFBQWEsRUFBRSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdkMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxzQkFBc0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2xELE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztnQkFDdEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO2FBQ2pCLENBQUMsQ0FBQTtZQUNGLE9BQU07UUFDUixDQUFDO1FBRUQsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDbkIsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLElBQUksNEJBQTRCO1lBQ3ZELElBQUksRUFBRSxlQUFlO1NBQ3RCLENBQUMsQ0FBQTtJQUNKLENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFO0lBQzlDLFFBQVEsSUFBSSxFQUFFLENBQUM7UUFDYixLQUFLLG1CQUFXLENBQUMsS0FBSyxDQUFDLFNBQVM7WUFDOUIsT0FBTyxHQUFHLENBQUE7UUFDWixLQUFLLG1CQUFXLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQztRQUNuQyxLQUFLLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVk7WUFDakMsT0FBTyxHQUFHLENBQUE7UUFDWjtZQUNFLE9BQU8sR0FBRyxDQUFBO0lBQ2QsQ0FBQztBQUNILENBQUMsQ0FBQSJ9
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const customer_registration_1 = require("../../../../../../modules/customer-registration");
6
+ async function POST(req, res) {
7
+ const { customer_id, email } = req.body;
8
+ if (!customer_id && !email) {
9
+ res.status(400).json({
10
+ message: "Either customer_id or email is required to send a phone OTP.",
11
+ type: "invalid_data",
12
+ });
13
+ return;
14
+ }
15
+ const customerRegistrationService = req.scope.resolve(customer_registration_1.CUSTOMER_REGISTRATION_MODULE);
16
+ try {
17
+ await customerRegistrationService.sendPhoneOtp({
18
+ customerId: customer_id,
19
+ email,
20
+ });
21
+ res.status(200).json({ sent: true });
22
+ }
23
+ catch (error) {
24
+ if (utils_1.MedusaError.isMedusaError?.(error)) {
25
+ res.status(mapMedusaErrorToStatus(error.type)).json({
26
+ message: error.message,
27
+ type: error.type,
28
+ });
29
+ return;
30
+ }
31
+ res.status(500).json({
32
+ message: error?.message ?? "Failed to send phone OTP",
33
+ type: "unknown_error",
34
+ });
35
+ }
36
+ }
37
+ const mapMedusaErrorToStatus = (type) => {
38
+ switch (type) {
39
+ case utils_1.MedusaError.Types.NOT_FOUND:
40
+ return 404;
41
+ case utils_1.MedusaError.Types.NOT_ALLOWED:
42
+ case utils_1.MedusaError.Types.INVALID_DATA:
43
+ return 400;
44
+ default:
45
+ return 500;
46
+ }
47
+ };
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9waG9uZS9vdHAvc2VuZC9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQVlBLG9CQW9DQztBQS9DRCxxREFBdUQ7QUFDdkQsMkZBRXdEO0FBUWpELEtBQUssVUFBVSxJQUFJLENBQUMsR0FBa0IsRUFBRSxHQUFtQjtJQUNoRSxNQUFNLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxHQUFHLEdBQUcsQ0FBQyxJQUF3QixDQUFBO0lBRTNELElBQUksQ0FBQyxXQUFXLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUMzQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUNuQixPQUFPLEVBQUUsOERBQThEO1lBQ3ZFLElBQUksRUFBRSxjQUFjO1NBQ3JCLENBQUMsQ0FBQTtRQUNGLE9BQU07SUFDUixDQUFDO0lBRUQsTUFBTSwyQkFBMkIsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FDbkQsb0RBQTRCLENBQ1EsQ0FBQTtJQUV0QyxJQUFJLENBQUM7UUFDSCxNQUFNLDJCQUEyQixDQUFDLFlBQVksQ0FBQztZQUM3QyxVQUFVLEVBQUUsV0FBVztZQUN2QixLQUFLO1NBQ04sQ0FBQyxDQUFBO1FBRUYsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUN0QyxDQUFDO0lBQUMsT0FBTyxLQUFVLEVBQUUsQ0FBQztRQUNwQixJQUFJLG1CQUFXLENBQUMsYUFBYSxFQUFFLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN2QyxHQUFHLENBQUMsTUFBTSxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztnQkFDbEQsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO2dCQUN0QixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7YUFDakIsQ0FBQyxDQUFBO1lBQ0YsT0FBTTtRQUNSLENBQUM7UUFFRCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUNuQixPQUFPLEVBQUUsS0FBSyxFQUFFLE9BQU8sSUFBSSwwQkFBMEI7WUFDckQsSUFBSSxFQUFFLGVBQWU7U0FDdEIsQ0FBQyxDQUFBO0lBQ0osQ0FBQztBQUNILENBQUM7QUFFRCxNQUFNLHNCQUFzQixHQUFHLENBQUMsSUFBWSxFQUFFLEVBQUU7SUFDOUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztRQUNiLEtBQUssbUJBQVcsQ0FBQyxLQUFLLENBQUMsU0FBUztZQUM5QixPQUFPLEdBQUcsQ0FBQTtRQUNaLEtBQUssbUJBQVcsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDO1FBQ25DLEtBQUssbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWTtZQUNqQyxPQUFPLEdBQUcsQ0FBQTtRQUNaO1lBQ0UsT0FBTyxHQUFHLENBQUE7SUFDZCxDQUFDO0FBQ0gsQ0FBQyxDQUFBIn0=
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const customer_registration_1 = require("../../../../../../modules/customer-registration");
6
+ async function POST(req, res) {
7
+ const { customer_id, email, code } = req.body;
8
+ if (!code || (!customer_id && !email)) {
9
+ res.status(400).json({
10
+ message: "Both code and either customer_id or email are required.",
11
+ type: "invalid_data",
12
+ });
13
+ return;
14
+ }
15
+ const customerRegistrationService = req.scope.resolve(customer_registration_1.CUSTOMER_REGISTRATION_MODULE);
16
+ try {
17
+ const customer = await customerRegistrationService.verifyPhoneOtp({
18
+ customerId: customer_id,
19
+ email,
20
+ code,
21
+ });
22
+ res.status(200).json({ customer });
23
+ }
24
+ catch (error) {
25
+ if (utils_1.MedusaError.isMedusaError?.(error)) {
26
+ res.status(mapMedusaErrorToStatus(error.type)).json({
27
+ message: error.message,
28
+ type: error.type,
29
+ });
30
+ return;
31
+ }
32
+ res.status(500).json({
33
+ message: error?.message ?? "Failed to verify phone OTP",
34
+ type: "unknown_error",
35
+ });
36
+ }
37
+ }
38
+ const mapMedusaErrorToStatus = (type) => {
39
+ switch (type) {
40
+ case utils_1.MedusaError.Types.NOT_FOUND:
41
+ return 404;
42
+ case utils_1.MedusaError.Types.NOT_ALLOWED:
43
+ case utils_1.MedusaError.Types.INVALID_DATA:
44
+ return 400;
45
+ default:
46
+ return 500;
47
+ }
48
+ };
49
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9waG9uZS9vdHAvdmVyaWZ5L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBYUEsb0JBcUNDO0FBakRELHFEQUF1RDtBQUN2RCwyRkFFd0Q7QUFTakQsS0FBSyxVQUFVLElBQUksQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQ2hFLE1BQU0sRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLEdBQUcsQ0FBQyxJQUEwQixDQUFBO0lBRW5FLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLFdBQVcsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDdEMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDbkIsT0FBTyxFQUFFLHlEQUF5RDtZQUNsRSxJQUFJLEVBQUUsY0FBYztTQUNyQixDQUFDLENBQUE7UUFDRixPQUFNO0lBQ1IsQ0FBQztJQUVELE1BQU0sMkJBQTJCLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ25ELG9EQUE0QixDQUNRLENBQUE7SUFFdEMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxRQUFRLEdBQUcsTUFBTSwyQkFBMkIsQ0FBQyxjQUFjLENBQUM7WUFDaEUsVUFBVSxFQUFFLFdBQVc7WUFDdkIsS0FBSztZQUNMLElBQUk7U0FDTCxDQUFDLENBQUE7UUFFRixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUE7SUFDcEMsQ0FBQztJQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7UUFDcEIsSUFBSSxtQkFBVyxDQUFDLGFBQWEsRUFBRSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdkMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxzQkFBc0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2xELE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztnQkFDdEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO2FBQ2pCLENBQUMsQ0FBQTtZQUNGLE9BQU07UUFDUixDQUFDO1FBRUQsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDbkIsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLElBQUksNEJBQTRCO1lBQ3ZELElBQUksRUFBRSxlQUFlO1NBQ3RCLENBQUMsQ0FBQTtJQUNKLENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFO0lBQzlDLFFBQVEsSUFBSSxFQUFFLENBQUM7UUFDYixLQUFLLG1CQUFXLENBQUMsS0FBSyxDQUFDLFNBQVM7WUFDOUIsT0FBTyxHQUFHLENBQUE7UUFDWixLQUFLLG1CQUFXLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQztRQUNuQyxLQUFLLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVk7WUFDakMsT0FBTyxHQUFHLENBQUE7UUFDWjtZQUNFLE9BQU8sR0FBRyxDQUFBO0lBQ2QsQ0FBQztBQUNILENBQUMsQ0FBQSJ9
@@ -2,8 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.POST = POST;
4
4
  const utils_1 = require("@medusajs/framework/utils");
5
+ const customer_registration_1 = require("../../../modules/customer-registration");
6
+ const constants_1 = require("../../../modules/customer-registration/constants");
5
7
  async function POST(req, res) {
6
8
  const customerModuleService = req.scope.resolve(utils_1.Modules.CUSTOMER);
9
+ const customerRegistrationService = req.scope.resolve(customer_registration_1.CUSTOMER_REGISTRATION_MODULE);
10
+ const registrationOptions = req.scope.resolve(constants_1.CUSTOMER_REGISTRATION_OPTIONS_KEY);
7
11
  const body = req.body;
8
12
  try {
9
13
  // Extract customer data from request body
@@ -24,6 +28,9 @@ async function POST(req, res) {
24
28
  // createCustomers expects an array
25
29
  const customers = await customerModuleService.createCustomers([customerData]);
26
30
  const customer = customers[0];
31
+ if (registrationOptions?.email?.autoSendOnRegistration) {
32
+ await customerRegistrationService.sendEmailOtp({ customerId: customer.id });
33
+ }
27
34
  // Retrieve the created customer to return
28
35
  const createdCustomer = await customerModuleService.retrieveCustomer(customer.id);
29
36
  // Return the customer in the expected format
@@ -32,19 +39,39 @@ async function POST(req, res) {
32
39
  });
33
40
  }
34
41
  catch (error) {
35
- // Handle errors appropriately
42
+ if (utils_1.MedusaError.isMedusaError?.(error)) {
43
+ res.status(mapMedusaErrorToStatus(error.type)).json({
44
+ message: error.message,
45
+ type: error.type,
46
+ });
47
+ return;
48
+ }
36
49
  if (error.message?.includes("already exists") || error.message?.includes("duplicate")) {
37
50
  res.status(409).json({
38
51
  message: "A customer with this email already exists",
39
52
  type: "duplicate_error",
40
53
  });
54
+ return;
41
55
  }
42
- else {
43
- res.status(400).json({
44
- message: error.message || "Failed to create customer",
45
- type: "invalid_data",
46
- });
47
- }
56
+ res.status(400).json({
57
+ message: error.message || "Failed to create customer",
58
+ type: "invalid_data",
59
+ });
48
60
  }
49
61
  }
50
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQW1CQSxvQkE0REM7QUE3RUQscURBQW1EO0FBaUI1QyxLQUFLLFVBQVUsSUFBSSxDQUN4QixHQUFrQixFQUNsQixHQUFtQjtJQUVuQixNQUFNLHFCQUFxQixHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUNqRSxNQUFNLElBQUksR0FBRyxHQUFHLENBQUMsSUFBaUMsQ0FBQTtJQUVsRCxJQUFJLENBQUM7UUFDSCwwQ0FBMEM7UUFDMUMsTUFBTSxFQUNKLEtBQUssRUFDTCxVQUFVLEVBQ1YsU0FBUyxFQUNULEtBQUssRUFDTCxZQUFZLEVBQ1osUUFBUSxFQUNSLGNBQWMsRUFBRSxzQkFBc0IsRUFDdEMsY0FBYyxFQUFFLHNCQUFzQixFQUN0QyxHQUFHLElBQUksRUFDUixHQUFHLElBQUksQ0FBQTtRQUVSLDREQUE0RDtRQUM1RCxNQUFNLFlBQVksR0FBOEI7WUFDOUMsR0FBRyxJQUFJO1lBQ1AsS0FBSztZQUNMLFVBQVU7WUFDVixTQUFTO1lBQ1QsS0FBSztZQUNMLFlBQVk7WUFDWixRQUFRLEVBQUUsUUFBUSxJQUFJLEVBQUU7WUFDeEIsY0FBYyxFQUFFLEtBQUs7WUFDckIsY0FBYyxFQUFFLEtBQUs7U0FDdEIsQ0FBQTtRQUVELHdEQUF3RDtRQUN4RCxtQ0FBbUM7UUFDbkMsTUFBTSxTQUFTLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxlQUFlLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFBO1FBQzdFLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUU3QiwwQ0FBMEM7UUFDMUMsTUFBTSxlQUFlLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUE4QixDQUFBO1FBRTlHLDZDQUE2QztRQUM3QyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUNuQixRQUFRLEVBQUUsZUFBZTtTQUMxQixDQUFDLENBQUE7SUFDSixDQUFDO0lBQUMsT0FBTyxLQUFVLEVBQUUsQ0FBQztRQUNwQiw4QkFBOEI7UUFDOUIsSUFBSSxLQUFLLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDdEYsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7Z0JBQ25CLE9BQU8sRUFBRSwyQ0FBMkM7Z0JBQ3BELElBQUksRUFBRSxpQkFBaUI7YUFDeEIsQ0FBQyxDQUFBO1FBQ0osQ0FBQzthQUFNLENBQUM7WUFDTixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztnQkFDbkIsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLElBQUksMkJBQTJCO2dCQUNyRCxJQUFJLEVBQUUsY0FBYzthQUNyQixDQUFDLENBQUE7UUFDSixDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUMifQ==
62
+ const mapMedusaErrorToStatus = (type) => {
63
+ switch (type) {
64
+ case utils_1.MedusaError.Types.NOT_ALLOWED:
65
+ case utils_1.MedusaError.Types.INVALID_DATA:
66
+ return 400;
67
+ case utils_1.MedusaError.Types.NOT_FOUND:
68
+ return 404;
69
+ case utils_1.MedusaError.Types.UNAUTHORIZED:
70
+ return 401;
71
+ case utils_1.MedusaError.Types.DUPLICATE_ERROR:
72
+ return 409;
73
+ default:
74
+ return 500;
75
+ }
76
+ };
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQXlCQSxvQkE4RUM7QUFyR0QscURBQWdFO0FBRWhFLGtGQUUrQztBQUMvQyxnRkFBb0c7QUFrQjdGLEtBQUssVUFBVSxJQUFJLENBQ3hCLEdBQWtCLEVBQ2xCLEdBQW1CO0lBRW5CLE1BQU0scUJBQXFCLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQ2pFLE1BQU0sMkJBQTJCLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ25ELG9EQUE0QixDQUNRLENBQUE7SUFDdEMsTUFBTSxtQkFBbUIsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FDM0MsNkNBQWlDLENBQ0ssQ0FBQTtJQUN4QyxNQUFNLElBQUksR0FBRyxHQUFHLENBQUMsSUFBaUMsQ0FBQTtJQUVsRCxJQUFJLENBQUM7UUFDSCwwQ0FBMEM7UUFDMUMsTUFBTSxFQUNKLEtBQUssRUFDTCxVQUFVLEVBQ1YsU0FBUyxFQUNULEtBQUssRUFDTCxZQUFZLEVBQ1osUUFBUSxFQUNSLGNBQWMsRUFBRSxzQkFBc0IsRUFDdEMsY0FBYyxFQUFFLHNCQUFzQixFQUN0QyxHQUFHLElBQUksRUFDUixHQUFHLElBQUksQ0FBQTtRQUVSLDREQUE0RDtRQUM1RCxNQUFNLFlBQVksR0FBOEI7WUFDOUMsR0FBRyxJQUFJO1lBQ1AsS0FBSztZQUNMLFVBQVU7WUFDVixTQUFTO1lBQ1QsS0FBSztZQUNMLFlBQVk7WUFDWixRQUFRLEVBQUUsUUFBUSxJQUFJLEVBQUU7WUFDeEIsY0FBYyxFQUFFLEtBQUs7WUFDckIsY0FBYyxFQUFFLEtBQUs7U0FDdEIsQ0FBQTtRQUVELHdEQUF3RDtRQUN4RCxtQ0FBbUM7UUFDbkMsTUFBTSxTQUFTLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxlQUFlLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFBO1FBQzdFLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUU3QixJQUFJLG1CQUFtQixFQUFFLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxDQUFDO1lBQ3ZELE1BQU0sMkJBQTJCLENBQUMsWUFBWSxDQUFDLEVBQUUsVUFBVSxFQUFFLFFBQVEsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFBO1FBQzdFLENBQUM7UUFFRCwwQ0FBMEM7UUFDMUMsTUFBTSxlQUFlLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUE4QixDQUFBO1FBRTlHLDZDQUE2QztRQUM3QyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUNuQixRQUFRLEVBQUUsZUFBZTtTQUMxQixDQUFDLENBQUE7SUFDSixDQUFDO0lBQUMsT0FBTyxLQUFVLEVBQUUsQ0FBQztRQUNwQixJQUFJLG1CQUFXLENBQUMsYUFBYSxFQUFFLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN2QyxHQUFHLENBQUMsTUFBTSxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztnQkFDbEQsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO2dCQUN0QixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7YUFDakIsQ0FBQyxDQUFBO1lBQ0YsT0FBTTtRQUNSLENBQUM7UUFFRCxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLGdCQUFnQixDQUFDLElBQUksS0FBSyxDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztZQUN0RixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztnQkFDbkIsT0FBTyxFQUFFLDJDQUEyQztnQkFDcEQsSUFBSSxFQUFFLGlCQUFpQjthQUN4QixDQUFDLENBQUE7WUFDRixPQUFNO1FBQ1IsQ0FBQztRQUVELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO1lBQ25CLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxJQUFJLDJCQUEyQjtZQUNyRCxJQUFJLEVBQUUsY0FBYztTQUNyQixDQUFDLENBQUE7SUFDSixDQUFDO0FBQ0gsQ0FBQztBQUVELE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRTtJQUM5QyxRQUFRLElBQUksRUFBRSxDQUFDO1FBQ2IsS0FBSyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUM7UUFDbkMsS0FBSyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZO1lBQ2pDLE9BQU8sR0FBRyxDQUFBO1FBQ1osS0FBSyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxTQUFTO1lBQzlCLE9BQU8sR0FBRyxDQUFBO1FBQ1osS0FBSyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZO1lBQ2pDLE9BQU8sR0FBRyxDQUFBO1FBQ1osS0FBSyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxlQUFlO1lBQ3BDLE9BQU8sR0FBRyxDQUFBO1FBQ1o7WUFDRSxPQUFPLEdBQUcsQ0FBQTtJQUNkLENBQUM7QUFDSCxDQUFDLENBQUEifQ==
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const awilix_1 = require("@medusajs/framework/awilix");
4
+ const constants_1 = require("../modules/customer-registration/constants");
5
+ const config_1 = require("../modules/customer-registration/config");
6
+ exports.default = async ({ container, options }) => {
7
+ const resolvedOptions = (0, config_1.buildCustomerRegistrationOptions)((options ?? {}));
8
+ container.register({
9
+ [constants_1.CUSTOMER_REGISTRATION_OPTIONS_KEY]: (0, awilix_1.asValue)(resolvedOptions),
10
+ });
11
+ };
12
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbG9hZGVycy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHVEQUFvRDtBQUVwRCwwRUFBOEY7QUFDOUYsb0VBQTBGO0FBRzFGLGtCQUFlLEtBQUssRUFBRSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQWlCLEVBQUUsRUFBRTtJQUM3RCxNQUFNLGVBQWUsR0FBRyxJQUFBLHlDQUFnQyxFQUN0RCxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQXNDLENBQ3JELENBQUE7SUFFRCxTQUFTLENBQUMsUUFBUSxDQUFDO1FBQ2pCLENBQUMsNkNBQWlDLENBQUMsRUFBRSxJQUFBLGdCQUFPLEVBQUMsZUFBZSxDQUFDO0tBQzlELENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQSJ9
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const config_1 = require("../config");
5
+ const cases = [
6
+ {
7
+ name: "defaults kick in when no options provided",
8
+ assertions: (result) => {
9
+ (0, vitest_1.expect)(result.otpLength).toBe(6);
10
+ (0, vitest_1.expect)(result.email.channel).toBe("email");
11
+ (0, vitest_1.expect)(result.phone.channel).toBe("sms");
12
+ },
13
+ },
14
+ {
15
+ name: "otp config respects overrides with validation",
16
+ input: {
17
+ otpLength: 8,
18
+ otpCharset: "alphanumeric",
19
+ otpExpiryMinutes: 20,
20
+ maxAttempts: 7,
21
+ },
22
+ assertions: (result) => {
23
+ (0, vitest_1.expect)(result.otpLength).toBe(8);
24
+ (0, vitest_1.expect)(result.otpCharset).toBe("alphanumeric");
25
+ (0, vitest_1.expect)(result.otpExpiryMinutes).toBe(20);
26
+ (0, vitest_1.expect)(result.maxAttempts).toBe(7);
27
+ },
28
+ },
29
+ {
30
+ name: "channel overrides merge deeply",
31
+ input: {
32
+ email: {
33
+ channel: "custom-email",
34
+ subject: "Custom Subject",
35
+ resendThrottleSeconds: 120,
36
+ autoSendOnRegistration: false,
37
+ },
38
+ phone: {
39
+ channel: "custom-sms",
40
+ template: "otp-sms",
41
+ },
42
+ },
43
+ assertions: (result) => {
44
+ (0, vitest_1.expect)(result.email.channel).toBe("custom-email");
45
+ (0, vitest_1.expect)(result.email.subject).toBe("Custom Subject");
46
+ (0, vitest_1.expect)(result.email.autoSendOnRegistration).toBe(false);
47
+ (0, vitest_1.expect)(result.email.resendThrottleSeconds).toBe(120);
48
+ (0, vitest_1.expect)(result.phone.channel).toBe("custom-sms");
49
+ (0, vitest_1.expect)(result.phone.template).toBe("otp-sms");
50
+ },
51
+ },
52
+ ];
53
+ (0, vitest_1.describe)("buildCustomerRegistrationOptions", () => {
54
+ cases.forEach(({ name, input, assertions }) => {
55
+ (0, vitest_1.it)(name, () => {
56
+ const result = (0, config_1.buildCustomerRegistrationOptions)(input);
57
+ assertions(result);
58
+ });
59
+ });
60
+ });
61
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLnNwZWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9jdXN0b21lci1yZWdpc3RyYXRpb24vX190ZXN0c19fL2NvbmZpZy5zcGVjLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbUNBQTZDO0FBQzdDLHNDQUE0RDtBQVM1RCxNQUFNLEtBQUssR0FBZTtJQUN4QjtRQUNFLElBQUksRUFBRSwyQ0FBMkM7UUFDakQsVUFBVSxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDckIsSUFBQSxlQUFNLEVBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtZQUNoQyxJQUFBLGVBQU0sRUFBQyxNQUFNLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQTtZQUMxQyxJQUFBLGVBQU0sRUFBQyxNQUFNLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUMxQyxDQUFDO0tBQ0Y7SUFDRDtRQUNFLElBQUksRUFBRSwrQ0FBK0M7UUFDckQsS0FBSyxFQUFFO1lBQ0wsU0FBUyxFQUFFLENBQUM7WUFDWixVQUFVLEVBQUUsY0FBYztZQUMxQixnQkFBZ0IsRUFBRSxFQUFFO1lBQ3BCLFdBQVcsRUFBRSxDQUFDO1NBQ2Y7UUFDRCxVQUFVLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUNyQixJQUFBLGVBQU0sRUFBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO1lBQ2hDLElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUE7WUFDOUMsSUFBQSxlQUFNLEVBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ3hDLElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDcEMsQ0FBQztLQUNGO0lBQ0Q7UUFDRSxJQUFJLEVBQUUsZ0NBQWdDO1FBQ3RDLEtBQUssRUFBRTtZQUNMLEtBQUssRUFBRTtnQkFDTCxPQUFPLEVBQUUsY0FBYztnQkFDdkIsT0FBTyxFQUFFLGdCQUFnQjtnQkFDekIscUJBQXFCLEVBQUUsR0FBRztnQkFDMUIsc0JBQXNCLEVBQUUsS0FBSzthQUM5QjtZQUNELEtBQUssRUFBRTtnQkFDTCxPQUFPLEVBQUUsWUFBWTtnQkFDckIsUUFBUSxFQUFFLFNBQVM7YUFDcEI7U0FDRjtRQUNELFVBQVUsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFO1lBQ3JCLElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFBO1lBQ2pELElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUE7WUFDbkQsSUFBQSxlQUFNLEVBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQTtZQUN2RCxJQUFBLGVBQU0sRUFBQyxNQUFNLENBQUMsS0FBSyxDQUFDLHFCQUFxQixDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQ3BELElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFBO1lBQy9DLElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQy9DLENBQUM7S0FDRjtDQUNGLENBQUE7QUFFRCxJQUFBLGlCQUFRLEVBQUMsa0NBQWtDLEVBQUUsR0FBRyxFQUFFO0lBQ2hELEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRTtRQUM1QyxJQUFBLFdBQUUsRUFBQyxJQUFJLEVBQUUsR0FBRyxFQUFFO1lBQ1osTUFBTSxNQUFNLEdBQUcsSUFBQSx5Q0FBZ0MsRUFBQyxLQUFLLENBQUMsQ0FBQTtZQUN0RCxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDcEIsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQyxDQUFBIn0=
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCustomerRegistrationOptions = exports.CUSTOMER_REGISTRATION_DEFAULTS = void 0;
4
+ const EMAIL_DEFAULTS = {
5
+ channel: "email",
6
+ template: null,
7
+ resendThrottleSeconds: 60,
8
+ autoSendOnRegistration: true,
9
+ subject: "Verify your email",
10
+ };
11
+ const PHONE_DEFAULTS = {
12
+ channel: "sms",
13
+ template: null,
14
+ resendThrottleSeconds: 60,
15
+ };
16
+ exports.CUSTOMER_REGISTRATION_DEFAULTS = {
17
+ otpLength: 6,
18
+ otpCharset: "numeric",
19
+ otpExpiryMinutes: 10,
20
+ maxAttempts: 5,
21
+ email: EMAIL_DEFAULTS,
22
+ phone: PHONE_DEFAULTS,
23
+ };
24
+ const NUMERIC_CHARSET = "numeric";
25
+ const ALPHANUMERIC_CHARSET = "alphanumeric";
26
+ const sanitizeCharset = (value) => {
27
+ if (!value) {
28
+ return NUMERIC_CHARSET;
29
+ }
30
+ const normalized = value.toLowerCase();
31
+ return normalized === ALPHANUMERIC_CHARSET ? ALPHANUMERIC_CHARSET : NUMERIC_CHARSET;
32
+ };
33
+ const withPositiveNumber = (maybeValue, fallback, options) => {
34
+ const parsed = Number(maybeValue);
35
+ if (Number.isFinite(parsed) && parsed >= (options?.min ?? 1)) {
36
+ return parsed;
37
+ }
38
+ return fallback;
39
+ };
40
+ const mergeEmailOptions = (user) => {
41
+ return {
42
+ ...EMAIL_DEFAULTS,
43
+ ...user,
44
+ channel: user?.channel ?? EMAIL_DEFAULTS.channel,
45
+ template: user?.template ?? EMAIL_DEFAULTS.template,
46
+ resendThrottleSeconds: withPositiveNumber(user?.resendThrottleSeconds, EMAIL_DEFAULTS.resendThrottleSeconds),
47
+ autoSendOnRegistration: user?.autoSendOnRegistration ?? EMAIL_DEFAULTS.autoSendOnRegistration,
48
+ subject: user?.subject ?? EMAIL_DEFAULTS.subject,
49
+ };
50
+ };
51
+ const mergePhoneOptions = (user) => {
52
+ return {
53
+ ...PHONE_DEFAULTS,
54
+ ...user,
55
+ channel: user?.channel ?? PHONE_DEFAULTS.channel,
56
+ template: user?.template ?? PHONE_DEFAULTS.template,
57
+ resendThrottleSeconds: withPositiveNumber(user?.resendThrottleSeconds, PHONE_DEFAULTS.resendThrottleSeconds),
58
+ };
59
+ };
60
+ const buildCustomerRegistrationOptions = (options) => {
61
+ const email = mergeEmailOptions(options?.email);
62
+ const phone = mergePhoneOptions(options?.phone);
63
+ return {
64
+ otpLength: withPositiveNumber(options?.otpLength, exports.CUSTOMER_REGISTRATION_DEFAULTS.otpLength),
65
+ otpCharset: sanitizeCharset(options?.otpCharset),
66
+ otpExpiryMinutes: withPositiveNumber(options?.otpExpiryMinutes, exports.CUSTOMER_REGISTRATION_DEFAULTS.otpExpiryMinutes),
67
+ maxAttempts: withPositiveNumber(options?.maxAttempts, exports.CUSTOMER_REGISTRATION_DEFAULTS.maxAttempts),
68
+ email,
69
+ phone,
70
+ };
71
+ };
72
+ exports.buildCustomerRegistrationOptions = buildCustomerRegistrationOptions;
73
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFRQSxNQUFNLGNBQWMsR0FBd0I7SUFDMUMsT0FBTyxFQUFFLE9BQU87SUFDaEIsUUFBUSxFQUFFLElBQUk7SUFDZCxxQkFBcUIsRUFBRSxFQUFFO0lBQ3pCLHNCQUFzQixFQUFFLElBQUk7SUFDNUIsT0FBTyxFQUFFLG1CQUFtQjtDQUM3QixDQUFBO0FBRUQsTUFBTSxjQUFjLEdBQXdCO0lBQzFDLE9BQU8sRUFBRSxLQUFLO0lBQ2QsUUFBUSxFQUFFLElBQUk7SUFDZCxxQkFBcUIsRUFBRSxFQUFFO0NBQzFCLENBQUE7QUFFWSxRQUFBLDhCQUE4QixHQUF3QztJQUNqRixTQUFTLEVBQUUsQ0FBQztJQUNaLFVBQVUsRUFBRSxTQUFTO0lBQ3JCLGdCQUFnQixFQUFFLEVBQUU7SUFDcEIsV0FBVyxFQUFFLENBQUM7SUFDZCxLQUFLLEVBQUUsY0FBYztJQUNyQixLQUFLLEVBQUUsY0FBYztDQUN0QixDQUFBO0FBRUQsTUFBTSxlQUFlLEdBQWUsU0FBUyxDQUFBO0FBQzdDLE1BQU0sb0JBQW9CLEdBQWUsY0FBYyxDQUFBO0FBRXZELE1BQU0sZUFBZSxHQUFHLENBQUMsS0FBYyxFQUFjLEVBQUU7SUFDckQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsT0FBTyxlQUFlLENBQUE7SUFDeEIsQ0FBQztJQUVELE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQTtJQUN0QyxPQUFPLFVBQVUsS0FBSyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQTtBQUNyRixDQUFDLENBQUE7QUFFRCxNQUFNLGtCQUFrQixHQUFHLENBQ3pCLFVBQW1CLEVBQ25CLFFBQWdCLEVBQ2hCLE9BQTBCLEVBQ2xCLEVBQUU7SUFDVixNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUE7SUFDakMsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLE1BQU0sSUFBSSxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUM3RCxPQUFPLE1BQU0sQ0FBQTtJQUNmLENBQUM7SUFFRCxPQUFPLFFBQVEsQ0FBQTtBQUNqQixDQUFDLENBQUE7QUFFRCxNQUFNLGlCQUFpQixHQUFHLENBQ3hCLElBQW1DLEVBQ2QsRUFBRTtJQUN2QixPQUFPO1FBQ0wsR0FBRyxjQUFjO1FBQ2pCLEdBQUcsSUFBSTtRQUNQLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxJQUFJLGNBQWMsQ0FBQyxPQUFPO1FBQ2hELFFBQVEsRUFBRSxJQUFJLEVBQUUsUUFBUSxJQUFJLGNBQWMsQ0FBQyxRQUFRO1FBQ25ELHFCQUFxQixFQUFFLGtCQUFrQixDQUN2QyxJQUFJLEVBQUUscUJBQXFCLEVBQzNCLGNBQWMsQ0FBQyxxQkFBcUIsQ0FDckM7UUFDRCxzQkFBc0IsRUFDcEIsSUFBSSxFQUFFLHNCQUFzQixJQUFJLGNBQWMsQ0FBQyxzQkFBc0I7UUFDdkUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLElBQUksY0FBYyxDQUFDLE9BQU87S0FDakQsQ0FBQTtBQUNILENBQUMsQ0FBQTtBQUVELE1BQU0saUJBQWlCLEdBQUcsQ0FDeEIsSUFBbUMsRUFDZCxFQUFFO0lBQ3ZCLE9BQU87UUFDTCxHQUFHLGNBQWM7UUFDakIsR0FBRyxJQUFJO1FBQ1AsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLElBQUksY0FBYyxDQUFDLE9BQU87UUFDaEQsUUFBUSxFQUFFLElBQUksRUFBRSxRQUFRLElBQUksY0FBYyxDQUFDLFFBQVE7UUFDbkQscUJBQXFCLEVBQUUsa0JBQWtCLENBQ3ZDLElBQUksRUFBRSxxQkFBcUIsRUFDM0IsY0FBYyxDQUFDLHFCQUFxQixDQUNyQztLQUNGLENBQUE7QUFDSCxDQUFDLENBQUE7QUFFTSxNQUFNLGdDQUFnQyxHQUFHLENBQzlDLE9BQTJDLEVBQ04sRUFBRTtJQUN2QyxNQUFNLEtBQUssR0FBRyxpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUE7SUFDL0MsTUFBTSxLQUFLLEdBQUcsaUJBQWlCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFBO0lBRS9DLE9BQU87UUFDTCxTQUFTLEVBQUUsa0JBQWtCLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxzQ0FBOEIsQ0FBQyxTQUFTLENBQUM7UUFDM0YsVUFBVSxFQUFFLGVBQWUsQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDO1FBQ2hELGdCQUFnQixFQUFFLGtCQUFrQixDQUNsQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQ3pCLHNDQUE4QixDQUFDLGdCQUFnQixDQUNoRDtRQUNELFdBQVcsRUFBRSxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsV0FBVyxFQUFFLHNDQUE4QixDQUFDLFdBQVcsQ0FBQztRQUNqRyxLQUFLO1FBQ0wsS0FBSztLQUNOLENBQUE7QUFDSCxDQUFDLENBQUE7QUFqQlksUUFBQSxnQ0FBZ0Msb0NBaUI1QyJ9
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CUSTOMER_REGISTRATION_OPTIONS_KEY = void 0;
4
+ exports.CUSTOMER_REGISTRATION_OPTIONS_KEY = "customerRegistrationOptions";
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBYSxRQUFBLGlDQUFpQyxHQUFHLDZCQUE2QixDQUFBIn0=
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CUSTOMER_REGISTRATION_MODULE = void 0;
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const service_1 = __importDefault(require("./service"));
9
+ exports.CUSTOMER_REGISTRATION_MODULE = "customer-registration";
10
+ exports.default = (0, utils_1.Module)(exports.CUSTOMER_REGISTRATION_MODULE, {
11
+ service: service_1.default,
12
+ });
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9jdXN0b21lci1yZWdpc3RyYXRpb24vaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEscURBQWtEO0FBQ2xELHdEQUF5RDtBQUU1QyxRQUFBLDRCQUE0QixHQUFHLHVCQUF1QixDQUFBO0FBRW5FLGtCQUFlLElBQUEsY0FBTSxFQUFDLG9DQUE0QixFQUFFO0lBQ2xELE9BQU8sRUFBRSxpQkFBaUM7Q0FDM0MsQ0FBQyxDQUFBIn0=
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20250118001000 = void 0;
4
+ const migrations_1 = require("@medusajs/framework/mikro-orm/migrations");
5
+ const TABLE_NAME = "customer_registration_otp";
6
+ class Migration20250118001000 extends migrations_1.Migration {
7
+ async up() {
8
+ this.addSql(`
9
+ create table if not exists "${TABLE_NAME}" (
10
+ "id" text not null,
11
+ "customer_id" text not null,
12
+ "type" text not null,
13
+ "code_hash" text not null,
14
+ "delivery_target" text not null,
15
+ "channel" text not null,
16
+ "attempt_count" integer not null default 0,
17
+ "expires_at" timestamptz not null,
18
+ "consumed_at" timestamptz null,
19
+ "metadata" jsonb null,
20
+ "created_at" timestamptz not null default now(),
21
+ "updated_at" timestamptz not null default now(),
22
+ "deleted_at" timestamptz null,
23
+ constraint "${TABLE_NAME}_pkey" primary key ("id"),
24
+ constraint "${TABLE_NAME}_customer_id_foreign" foreign key ("customer_id") references "customer" ("id") on delete cascade
25
+ );
26
+ `);
27
+ this.addSql(`
28
+ create index if not exists "customer_registration_otp_customer_type_idx"
29
+ on "${TABLE_NAME}" ("customer_id", "type")
30
+ where "deleted_at" is null;
31
+ `);
32
+ this.addSql(`
33
+ create index if not exists "customer_registration_otp_expires_idx"
34
+ on "${TABLE_NAME}" ("expires_at")
35
+ where "deleted_at" is null;
36
+ `);
37
+ this.addSql(`
38
+ create index if not exists "customer_registration_otp_consumed_idx"
39
+ on "${TABLE_NAME}" ("consumed_at")
40
+ where "deleted_at" is null;
41
+ `);
42
+ }
43
+ async down() {
44
+ this.addSql(`drop table if exists "${TABLE_NAME}";`);
45
+ }
46
+ }
47
+ exports.Migration20250118001000 = Migration20250118001000;
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNTAxMTgwMDEwMDBDcmVhdGVDdXN0b21lck90cFRhYmxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL21pZ3JhdGlvbnMvTWlncmF0aW9uMjAyNTAxMTgwMDEwMDBDcmVhdGVDdXN0b21lck90cFRhYmxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlFQUFvRTtBQUVwRSxNQUFNLFVBQVUsR0FBRywyQkFBMkIsQ0FBQTtBQUU5QyxNQUFhLHVCQUF3QixTQUFRLHNCQUFTO0lBQ3BELEtBQUssQ0FBQyxFQUFFO1FBQ04sSUFBSSxDQUFDLE1BQU0sQ0FBQztvQ0FDb0IsVUFBVTs7Ozs7Ozs7Ozs7Ozs7c0JBY3hCLFVBQVU7c0JBQ1YsVUFBVTs7S0FFM0IsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQzs7WUFFSixVQUFVOztLQUVqQixDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsTUFBTSxDQUFDOztZQUVKLFVBQVU7O0tBRWpCLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxNQUFNLENBQUM7O1lBRUosVUFBVTs7S0FFakIsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxJQUFJO1FBQ1IsSUFBSSxDQUFDLE1BQU0sQ0FBQyx5QkFBeUIsVUFBVSxJQUFJLENBQUMsQ0FBQTtJQUN0RCxDQUFDO0NBQ0Y7QUE1Q0QsMERBNENDIn0=
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CustomerOtp = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ exports.CustomerOtp = utils_1.model
6
+ .define("customer_registration_otp", {
7
+ id: utils_1.model.id({ prefix: "otp" }),
8
+ customer_id: utils_1.model.text(),
9
+ type: utils_1.model.enum(["email", "phone"]),
10
+ code_hash: utils_1.model.text(),
11
+ delivery_target: utils_1.model.text(),
12
+ channel: utils_1.model.text(),
13
+ attempt_count: utils_1.model.number().default(0),
14
+ expires_at: utils_1.model.dateTime(),
15
+ consumed_at: utils_1.model.dateTime().nullable(),
16
+ metadata: utils_1.model.json().nullable(),
17
+ })
18
+ .indexes([
19
+ {
20
+ name: "customer_registration_otp_customer_type_idx",
21
+ on: ["customer_id", "type"],
22
+ where: "deleted_at IS NULL",
23
+ },
24
+ {
25
+ name: "customer_registration_otp_expires_idx",
26
+ on: ["expires_at"],
27
+ where: "deleted_at IS NULL",
28
+ },
29
+ {
30
+ name: "customer_registration_otp_consumed_idx",
31
+ on: ["consumed_at"],
32
+ where: "deleted_at IS NULL",
33
+ },
34
+ ]);
35
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItb3RwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY3VzdG9tZXItcmVnaXN0cmF0aW9uL21vZGVscy9jdXN0b21lci1vdHAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscURBQWlEO0FBRXBDLFFBQUEsV0FBVyxHQUFHLGFBQUs7S0FDN0IsTUFBTSxDQUFDLDJCQUEyQixFQUFFO0lBQ25DLEVBQUUsRUFBRSxhQUFLLENBQUMsRUFBRSxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxDQUFDO0lBQy9CLFdBQVcsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3pCLElBQUksRUFBRSxhQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3BDLFNBQVMsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3ZCLGVBQWUsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQzdCLE9BQU8sRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3JCLGFBQWEsRUFBRSxhQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUN4QyxVQUFVLEVBQUUsYUFBSyxDQUFDLFFBQVEsRUFBRTtJQUM1QixXQUFXLEVBQUUsYUFBSyxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUN4QyxRQUFRLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtDQUNsQyxDQUFDO0tBQ0QsT0FBTyxDQUFDO0lBQ1A7UUFDRSxJQUFJLEVBQUUsNkNBQTZDO1FBQ25ELEVBQUUsRUFBRSxDQUFDLGFBQWEsRUFBRSxNQUFNLENBQUM7UUFDM0IsS0FBSyxFQUFFLG9CQUFvQjtLQUM1QjtJQUNEO1FBQ0UsSUFBSSxFQUFFLHVDQUF1QztRQUM3QyxFQUFFLEVBQUUsQ0FBQyxZQUFZLENBQUM7UUFDbEIsS0FBSyxFQUFFLG9CQUFvQjtLQUM1QjtJQUNEO1FBQ0UsSUFBSSxFQUFFLHdDQUF3QztRQUM5QyxFQUFFLEVBQUUsQ0FBQyxhQUFhLENBQUM7UUFDbkIsS0FBSyxFQUFFLG9CQUFvQjtLQUM1QjtDQUNGLENBQUMsQ0FBQSJ9
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const crypto_1 = require("crypto");
5
+ const customer_otp_1 = require("./models/customer-otp");
6
+ const config_1 = require("./config");
7
+ const constants_1 = require("./constants");
8
+ const OTP_CHARSET_NUMERIC = "0123456789";
9
+ const OTP_CHARSET_ALPHANUMERIC = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
10
+ class CustomerRegistrationModuleService extends (0, utils_1.MedusaService)({
11
+ CustomerOtp: customer_otp_1.CustomerOtp,
12
+ }) {
13
+ constructor(container, moduleOptions, moduleDeclaration) {
14
+ super(container, moduleOptions, moduleDeclaration);
15
+ this.customerModuleService_ = container[utils_1.Modules.CUSTOMER];
16
+ if (!this.customerModuleService_) {
17
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Customer module is required for customer-registration plugin.");
18
+ }
19
+ this.notificationModuleService_ = container[utils_1.Modules.NOTIFICATION];
20
+ this.options_ =
21
+ container[constants_1.CUSTOMER_REGISTRATION_OPTIONS_KEY] ??
22
+ (0, config_1.buildCustomerRegistrationOptions)();
23
+ }
24
+ async sendEmailOtp(identifier, sharedContext) {
25
+ const customer = await this.resolveCustomer(identifier, sharedContext);
26
+ if (!customer.email) {
27
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Customer is missing an email address.");
28
+ }
29
+ await this.generateAndDispatchOtp({
30
+ customer,
31
+ purpose: "email",
32
+ deliveryTarget: customer.email,
33
+ sharedContext,
34
+ channelOptions: this.options_.email,
35
+ });
36
+ }
37
+ async sendPhoneOtp(identifier, sharedContext) {
38
+ const customer = await this.resolveCustomer(identifier, sharedContext);
39
+ if (!customer.phone) {
40
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Customer phone number is required before requesting a phone OTP.");
41
+ }
42
+ await this.generateAndDispatchOtp({
43
+ customer,
44
+ purpose: "phone",
45
+ deliveryTarget: customer.phone,
46
+ sharedContext,
47
+ channelOptions: this.options_.phone,
48
+ });
49
+ }
50
+ async verifyEmailOtp(payload, sharedContext) {
51
+ const customer = await this.resolveCustomer(payload, sharedContext);
52
+ await this.verifyOtpForCustomer({
53
+ customer,
54
+ purpose: "email",
55
+ code: payload.code,
56
+ sharedContext,
57
+ });
58
+ return await this.customerModuleService_.updateCustomers(customer.id, { email_verified: true }, sharedContext);
59
+ }
60
+ async verifyPhoneOtp(payload, sharedContext) {
61
+ const customer = await this.resolveCustomer(payload, sharedContext);
62
+ if (!customer.phone) {
63
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Customer phone number is required before verification.");
64
+ }
65
+ await this.verifyOtpForCustomer({
66
+ customer,
67
+ purpose: "phone",
68
+ code: payload.code,
69
+ sharedContext,
70
+ });
71
+ return await this.customerModuleService_.updateCustomers(customer.id, { phone_verified: true }, sharedContext);
72
+ }
73
+ async resolveCustomer(identifier, sharedContext) {
74
+ if (identifier.customerId) {
75
+ return await this.customerModuleService_.retrieveCustomer(identifier.customerId, {}, sharedContext);
76
+ }
77
+ if (identifier.email) {
78
+ const [customer] = await this.customerModuleService_.listCustomers({ email: identifier.email }, {}, sharedContext);
79
+ if (customer) {
80
+ return customer;
81
+ }
82
+ }
83
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Unable to locate customer for OTP operation.");
84
+ }
85
+ async generateAndDispatchOtp({ customer, purpose, deliveryTarget, channelOptions, sharedContext, }) {
86
+ await this.assertThrottleWindow(customer.id, purpose, channelOptions.resendThrottleSeconds, sharedContext);
87
+ await this.invalidateActiveOtps(customer.id, purpose, sharedContext);
88
+ const code = this.generateOtpCode();
89
+ const expiresAt = this.calculateExpiry();
90
+ const payload = {
91
+ customer_id: customer.id,
92
+ type: purpose,
93
+ code_hash: this.hashOtp(code),
94
+ delivery_target: deliveryTarget,
95
+ channel: channelOptions.channel,
96
+ expires_at: expiresAt,
97
+ attempt_count: 0,
98
+ };
99
+ const otpRecord = await this.createCustomerOtps(payload, sharedContext);
100
+ try {
101
+ await this.dispatchNotification({
102
+ customer,
103
+ purpose,
104
+ deliveryTarget,
105
+ code,
106
+ channelOptions,
107
+ expiresAt,
108
+ sharedContext,
109
+ });
110
+ }
111
+ catch (error) {
112
+ await this.deleteCustomerOtps(otpRecord.id, sharedContext);
113
+ throw error;
114
+ }
115
+ }
116
+ async verifyOtpForCustomer({ customer, purpose, code, sharedContext, }) {
117
+ const otp = await this.findActiveOtp(customer.id, purpose, sharedContext);
118
+ if (!otp) {
119
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "No OTP found. Please request a new verification code.");
120
+ }
121
+ if (otp.expires_at < new Date()) {
122
+ await this.consumeOtp(otp.id, sharedContext);
123
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "OTP has expired. Please request a new verification code.");
124
+ }
125
+ if (otp.code_hash !== this.hashOtp(code)) {
126
+ await this.handleFailedAttempt(otp, sharedContext);
127
+ }
128
+ await this.updateCustomerOtps({
129
+ selector: { id: otp.id },
130
+ data: { consumed_at: new Date() },
131
+ }, sharedContext);
132
+ }
133
+ async handleFailedAttempt(otp, sharedContext) {
134
+ const nextAttempt = (otp.attempt_count ?? 0) + 1;
135
+ await this.updateCustomerOtps({
136
+ selector: { id: otp.id },
137
+ data: { attempt_count: nextAttempt },
138
+ }, sharedContext);
139
+ if (nextAttempt >= this.options_.maxAttempts) {
140
+ await this.consumeOtp(otp.id, sharedContext);
141
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Maximum OTP attempts exceeded. Please request a new code.");
142
+ }
143
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Invalid OTP supplied.");
144
+ }
145
+ async findActiveOtp(customerId, purpose, sharedContext) {
146
+ const [otp] = await this.listCustomerOtps({
147
+ customer_id: customerId,
148
+ type: purpose,
149
+ consumed_at: null,
150
+ }, {
151
+ order: { created_at: "DESC" },
152
+ take: 1,
153
+ }, sharedContext);
154
+ return otp;
155
+ }
156
+ async consumeOtp(id, sharedContext) {
157
+ await this.updateCustomerOtps({
158
+ selector: { id },
159
+ data: { consumed_at: new Date() },
160
+ }, sharedContext);
161
+ }
162
+ async assertThrottleWindow(customerId, purpose, throttleSeconds, sharedContext) {
163
+ if (!throttleSeconds) {
164
+ return;
165
+ }
166
+ const otp = await this.findActiveOtp(customerId, purpose, sharedContext);
167
+ if (!otp) {
168
+ return;
169
+ }
170
+ const createdAt = new Date(otp.created_at);
171
+ const now = new Date();
172
+ const secondsSince = (now.getTime() - createdAt.getTime()) / 1000;
173
+ if (secondsSince < throttleSeconds) {
174
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `Please wait ${Math.ceil(throttleSeconds - secondsSince)} seconds before requesting a new OTP.`);
175
+ }
176
+ }
177
+ async invalidateActiveOtps(customerId, purpose, sharedContext) {
178
+ await this.updateCustomerOtps({
179
+ selector: {
180
+ customer_id: customerId,
181
+ type: purpose,
182
+ consumed_at: null,
183
+ },
184
+ data: { consumed_at: new Date() },
185
+ }, sharedContext);
186
+ }
187
+ generateOtpCode() {
188
+ const { otpLength, otpCharset } = this.options_;
189
+ const charset = otpCharset === "alphanumeric" ? OTP_CHARSET_ALPHANUMERIC : OTP_CHARSET_NUMERIC;
190
+ let code = "";
191
+ for (let i = 0; i < otpLength; i++) {
192
+ const index = (0, crypto_1.randomInt)(0, charset.length);
193
+ code += charset[index];
194
+ }
195
+ return code;
196
+ }
197
+ calculateExpiry() {
198
+ const expiresAt = new Date();
199
+ expiresAt.setMinutes(expiresAt.getMinutes() + this.options_.otpExpiryMinutes);
200
+ return expiresAt;
201
+ }
202
+ hashOtp(code) {
203
+ return (0, crypto_1.createHash)("sha256").update(code).digest("hex");
204
+ }
205
+ async dispatchNotification({ customer, purpose, deliveryTarget, code, channelOptions, expiresAt, sharedContext, }) {
206
+ const notificationService = this.notificationModuleService_;
207
+ if (!notificationService) {
208
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Notification module is not configured. Please register at least one provider.");
209
+ }
210
+ const payload = {
211
+ to: deliveryTarget,
212
+ channel: channelOptions.channel,
213
+ template: channelOptions.template,
214
+ trigger_type: `customer-registration.${purpose}-otp`,
215
+ resource_type: "customer",
216
+ resource_id: customer.id,
217
+ receiver_id: customer.id,
218
+ data: {
219
+ code,
220
+ expires_at: expiresAt.toISOString(),
221
+ purpose,
222
+ customer_id: customer.id,
223
+ },
224
+ content: this.buildNotificationContent(purpose, code, channelOptions),
225
+ };
226
+ await notificationService.createNotifications(payload, sharedContext);
227
+ }
228
+ buildNotificationContent(purpose, code, channelOptions) {
229
+ if ("subject" in channelOptions) {
230
+ return {
231
+ subject: channelOptions.subject,
232
+ text: `Your ${purpose} verification code is ${code}`,
233
+ html: `<p>Your ${purpose} verification code is <strong>${code}</strong>.</p>`,
234
+ };
235
+ }
236
+ return {
237
+ text: `Your ${purpose} verification code is ${code}`,
238
+ };
239
+ }
240
+ }
241
+ exports.default = CustomerRegistrationModuleService;
242
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2N1c3RvbWVyLXJlZ2lzdHJhdGlvbi9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscURBSWtDO0FBVWxDLG1DQUE4QztBQUM5Qyx3REFBbUQ7QUFLbkQscUNBQTJEO0FBQzNELDJDQUErRDtBQWtCL0QsTUFBTSxtQkFBbUIsR0FBRyxZQUFZLENBQUE7QUFDeEMsTUFBTSx3QkFBd0IsR0FBRyxrQ0FBa0MsQ0FBQTtBQVFuRSxNQUFNLGlDQUFrQyxTQUFRLElBQUEscUJBQWEsRUFBQztJQUM1RCxXQUFXLEVBQVgsMEJBQVc7Q0FDWixDQUFDO0lBS0EsWUFBWSxTQUErQixFQUFFLGFBQXVCLEVBQUUsaUJBQTJCO1FBQy9GLEtBQUssQ0FBQyxTQUFTLEVBQUUsYUFBYSxFQUFFLGlCQUFpQixDQUFDLENBQUE7UUFFbEQsSUFBSSxDQUFDLHNCQUFzQixHQUFHLFNBQVMsQ0FBQyxlQUFPLENBQUMsUUFBUSxDQUEyQixDQUFBO1FBQ25GLElBQUksQ0FBQyxJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztZQUNqQyxNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQ2xDLCtEQUErRCxDQUNoRSxDQUFBO1FBQ0gsQ0FBQztRQUVELElBQUksQ0FBQywwQkFBMEIsR0FBRyxTQUFTLENBQUMsZUFBTyxDQUFDLFlBQVksQ0FBQyxDQUFBO1FBQ2pFLElBQUksQ0FBQyxRQUFRO1lBQ1gsU0FBUyxDQUFDLDZDQUFpQyxDQUFDO2dCQUM1QyxJQUFBLHlDQUFnQyxHQUFFLENBQUE7SUFDdEMsQ0FBQztJQUVELEtBQUssQ0FBQyxZQUFZLENBQUMsVUFBOEIsRUFBRSxhQUF1QjtRQUN4RSxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsVUFBVSxFQUFFLGFBQWEsQ0FBQyxD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
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9jdXN0b21lci1yZWdpc3RyYXRpb24vdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
package/README.md CHANGED
@@ -1,14 +1,15 @@
1
1
  # Medusa Plugin: Customer Registration Guard
2
2
 
3
- A lean Medusa v2 plugin that overrides the store customer registration endpoint and guarantees both `email_verified` and `phone_verified` start as `false`.
3
+ A lean Medusa v2 plugin that hardens customer onboarding, guarantees both `email_verified` and `phone_verified` start as `false`, and now ships a full OTP verification flow for email + phone.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - Overrides `POST /store/customers` with a drop-in route handler.
8
8
  - Forces `email_verified = false` and `phone_verified = false` for every new customer, regardless of the request payload.
9
- - Ships a lightweight Medusa module (`customer-registration`) so the bundled Mikro-ORM migration automatically runs during `npx medusa db:migrate`.
10
- - Adds both verification columns if they’re missing, ensuring downstream logic can rely on them.
11
- - No custom modules, admin extensions, subscribers, or providers just the pieces required for registration hardening.
9
+ - Automatically generates & sends an email OTP right after registration (configurable) using the Notification module + whatever providers your project already registered.
10
+ - Adds dedicated email + phone OTP endpoints for resending and verifying codes while marking the corresponding `*_verified` flags to `true`.
11
+ - Ships a lightweight Medusa module (`customer-registration`) that exposes OTP helpers and bundles Mikro-ORM migrations (email/phone columns + OTP table) that run during `npx medusa db:migrate`.
12
+ - OTP length, charset, expiry, throttling, and provider metadata (channel, template, etc.) are all configurable via plugin options.
12
13
 
13
14
  ## Installation
14
15
 
@@ -55,19 +56,63 @@ yarn dev
55
56
 
56
57
  ### Registration override
57
58
 
58
- When a shopper registers via `POST /store/customers`, the plugin intercepts the request, normalizes the payload, and always persists `email_verified = false` and `phone_verified = false`. The rest of Medusa’s default behavior is preserved by deferring to the Customer Module service.
59
+ When a shopper registers via `POST /store/customers`, the plugin intercepts the request, normalizes the payload, and always persists `email_verified = false` and `phone_verified = false`. The rest of Medusa’s default behavior is preserved by deferring to the Customer Module service. If `email.autoSendOnRegistration` is enabled (default), the plugin immediately issues an email OTP through the Notification module using the user's configured providers.
60
+
61
+ ### OTP endpoints
62
+
63
+ The plugin adds a tiny surface area of Store API routes so storefronts can request and verify codes without touching the Admin API:
64
+
65
+ | Endpoint | Body | Description |
66
+ | --- | --- | --- |
67
+ | `POST /store/customers/email/otp/verify` | `{ email?: string, customer_id?: string, code: string }` | Validates an email OTP and sets `email_verified = true`. |
68
+ | `POST /store/customers/phone/otp/send` | `{ email?: string, customer_id?: string }` | Generates an OTP and sends it to the customer's phone number (stored on the customer record). |
69
+ | `POST /store/customers/phone/otp/verify` | `{ email?: string, customer_id?: string, code: string }` | Validates a phone OTP and sets `phone_verified = true`. |
70
+
71
+ All notification dispatches go through Medusa's Notification module. Whatever providers / templates you configure globally will be reused by the plugin.
72
+
73
+ ### Configuration
74
+
75
+ You can override OTP behavior by passing options when registering the plugin:
76
+
77
+ ```ts
78
+ plugins: [
79
+ {
80
+ resolve: "customer-registration",
81
+ options: {
82
+ otpLength: 6,
83
+ otpCharset: "numeric", // or "alphanumeric"
84
+ otpExpiryMinutes: 15,
85
+ maxAttempts: 5,
86
+ email: {
87
+ channel: "email",
88
+ template: "otp-template", // optional provider template handle
89
+ subject: "Verify your Medusa account",
90
+ resendThrottleSeconds: 90,
91
+ autoSendOnRegistration: true,
92
+ },
93
+ phone: {
94
+ channel: "sms",
95
+ template: null,
96
+ resendThrottleSeconds: 60,
97
+ },
98
+ },
99
+ },
100
+ ]
101
+ ```
102
+
103
+ ### Database migrations
59
104
 
60
- ### Database migration
105
+ - `Migration20250118000000AddEmailVerifiedColumn`: ensures the customer table includes `email_verified` and `phone_verified` booleans.
106
+ - `Migration20250118001000CreateCustomerOtpTable`: creates the `customer_registration_otp` table for storing hashed OTPs, expiry metadata, and delivery context.
61
107
 
62
- - A module-scoped migration (`Migration20250118000000AddEmailVerifiedColumn`) lives under `src/modules/customer-registration/migrations`. Medusa automatically discovers it when the plugin is registered.
63
- - After installing or updating the plugin, run `npx medusa db:migrate` in your Medusa project so the columns exist before handling registrations.
108
+ After installing or updating the plugin, run `npx medusa db:migrate` in your Medusa project so both migrations are applied before handling registrations.
64
109
 
65
110
  ## Requirements
66
111
 
67
112
  - Medusa v2.11.2 or higher
68
- - Customer table must have `email_verified` and `phone_verified` boolean columns (default: `false`) the provided migration handles this.
69
- - Run `npx medusa db:migrate` after installation so the module migration is applied.
70
- - No additional providers, subscribers, or admin extensions are required.
113
+ - Customer table must have `email_verified` and `phone_verified` boolean columns handled by the bundled migration.
114
+ - At least one Notification provider (email + optional SMS) configured in your Medusa project so OTPs can be delivered automatically.
115
+ - Run `npx medusa db:migrate` after installation so the module migrations are applied.
71
116
 
72
117
  ## Development
73
118
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "customer-registration",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Medusa plugin that overrides store customer registration and enforces email/phone verification flags.",
5
5
  "author": "Medusa (https://medusajs.com)",
6
6
  "license": "MIT",
@@ -23,14 +23,16 @@
23
23
  "scripts": {
24
24
  "build": "medusa plugin:build",
25
25
  "dev": "medusa plugin:develop",
26
- "prepublishOnly": "medusa plugin:build"
26
+ "prepublishOnly": "medusa plugin:build",
27
+ "test": "vitest run"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@medusajs/cli": "2.11.2",
30
31
  "@medusajs/framework": "2.11.2",
31
32
  "@medusajs/medusa": "2.11.2",
32
33
  "ts-node": "^10.9.2",
33
- "typescript": "^5.6.2"
34
+ "typescript": "^5.6.2",
35
+ "vitest": "^1.5.2"
34
36
  },
35
37
  "peerDependencies": {
36
38
  "@medusajs/cli": "2.11.2",