mumz-strapi-plugin-coupon 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +311 -0
- package/dist/bootstrap.d.ts +5 -0
- package/dist/bootstrap.js +6 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +6 -0
- package/dist/content-types/coupon/index.d.ts +90 -0
- package/dist/content-types/coupon/index.js +9 -0
- package/dist/content-types/coupon/schema.d.ts +88 -0
- package/dist/content-types/coupon/schema.js +105 -0
- package/dist/content-types/index.d.ts +155 -0
- package/dist/content-types/index.js +11 -0
- package/dist/content-types/redemption/index.d.ts +64 -0
- package/dist/content-types/redemption/index.js +9 -0
- package/dist/content-types/redemption/schema.d.ts +62 -0
- package/dist/content-types/redemption/schema.js +74 -0
- package/dist/controllers/coupon.d.ts +41 -0
- package/dist/controllers/coupon.js +154 -0
- package/dist/controllers/index.d.ts +5 -0
- package/dist/controllers/index.js +9 -0
- package/dist/destroy.d.ts +5 -0
- package/dist/destroy.js +6 -0
- package/dist/index.d.ts +201 -0
- package/dist/index.js +24 -0
- package/dist/middlewares/index.d.ts +4 -0
- package/dist/middlewares/index.js +9 -0
- package/dist/middlewares/rate-limit.d.ts +6 -0
- package/dist/middlewares/rate-limit.js +42 -0
- package/dist/register.d.ts +5 -0
- package/dist/register.js +6 -0
- package/dist/routes/content-api/index.d.ts +23 -0
- package/dist/routes/content-api/index.js +76 -0
- package/dist/routes/index.d.ts +25 -0
- package/dist/routes/index.js +9 -0
- package/dist/services/coupon.d.ts +61 -0
- package/dist/services/coupon.js +399 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +9 -0
- package/dist/utils/validators.d.ts +14 -0
- package/dist/utils/validators.js +41 -0
- package/package.json +71 -0
- package/strapi-server.js +1 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { Core } from '@strapi/strapi';
|
|
2
|
+
declare const plugin: {
|
|
3
|
+
register: ({ strapi }: {
|
|
4
|
+
strapi: Core.Strapi;
|
|
5
|
+
}) => void;
|
|
6
|
+
bootstrap: ({ strapi }: {
|
|
7
|
+
strapi: Core.Strapi;
|
|
8
|
+
}) => void;
|
|
9
|
+
destroy: ({ strapi }: {
|
|
10
|
+
strapi: Core.Strapi;
|
|
11
|
+
}) => void;
|
|
12
|
+
config: {
|
|
13
|
+
default: {};
|
|
14
|
+
validator(): void;
|
|
15
|
+
};
|
|
16
|
+
controllers: Record<string, (params: {
|
|
17
|
+
strapi: Core.Strapi;
|
|
18
|
+
}) => any>;
|
|
19
|
+
routes: {
|
|
20
|
+
'content-api': {
|
|
21
|
+
type: string;
|
|
22
|
+
routes: ({
|
|
23
|
+
method: string;
|
|
24
|
+
path: string;
|
|
25
|
+
handler: string;
|
|
26
|
+
config: {
|
|
27
|
+
policies: never[];
|
|
28
|
+
middlewares: ((ctx: any, next: any) => Promise<any>)[];
|
|
29
|
+
auth: boolean;
|
|
30
|
+
};
|
|
31
|
+
} | {
|
|
32
|
+
method: string;
|
|
33
|
+
path: string;
|
|
34
|
+
handler: string;
|
|
35
|
+
config: {
|
|
36
|
+
policies: never[];
|
|
37
|
+
auth: boolean;
|
|
38
|
+
middlewares?: undefined;
|
|
39
|
+
};
|
|
40
|
+
})[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
services: Record<string, (params: {
|
|
44
|
+
strapi: Core.Strapi;
|
|
45
|
+
}) => any>;
|
|
46
|
+
contentTypes: {
|
|
47
|
+
coupon: {
|
|
48
|
+
schema: {
|
|
49
|
+
kind: string;
|
|
50
|
+
collectionName: string;
|
|
51
|
+
info: {
|
|
52
|
+
singularName: string;
|
|
53
|
+
pluralName: string;
|
|
54
|
+
displayName: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
options: {
|
|
58
|
+
draftAndPublish: boolean;
|
|
59
|
+
};
|
|
60
|
+
pluginOptions: {
|
|
61
|
+
'content-manager': {
|
|
62
|
+
visible: boolean;
|
|
63
|
+
};
|
|
64
|
+
'content-type-builder': {
|
|
65
|
+
visible: boolean;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
layouts: {
|
|
69
|
+
list: string[];
|
|
70
|
+
edit: {
|
|
71
|
+
name: string;
|
|
72
|
+
size: number;
|
|
73
|
+
}[][];
|
|
74
|
+
};
|
|
75
|
+
attributes: {
|
|
76
|
+
code: {
|
|
77
|
+
type: string;
|
|
78
|
+
required: boolean;
|
|
79
|
+
unique: boolean;
|
|
80
|
+
configurable: boolean;
|
|
81
|
+
};
|
|
82
|
+
discountType: {
|
|
83
|
+
type: string;
|
|
84
|
+
enum: string[];
|
|
85
|
+
required: boolean;
|
|
86
|
+
default: string;
|
|
87
|
+
};
|
|
88
|
+
discountValue: {
|
|
89
|
+
type: string;
|
|
90
|
+
required: boolean;
|
|
91
|
+
min: number;
|
|
92
|
+
};
|
|
93
|
+
maxUsage: {
|
|
94
|
+
type: string;
|
|
95
|
+
required: boolean;
|
|
96
|
+
min: number;
|
|
97
|
+
default: null;
|
|
98
|
+
};
|
|
99
|
+
currentUsage: {
|
|
100
|
+
type: string;
|
|
101
|
+
required: boolean;
|
|
102
|
+
default: number;
|
|
103
|
+
min: number;
|
|
104
|
+
};
|
|
105
|
+
validFrom: {
|
|
106
|
+
type: string;
|
|
107
|
+
required: boolean;
|
|
108
|
+
};
|
|
109
|
+
validTo: {
|
|
110
|
+
type: string;
|
|
111
|
+
required: boolean;
|
|
112
|
+
};
|
|
113
|
+
isActive: {
|
|
114
|
+
type: string;
|
|
115
|
+
default: boolean;
|
|
116
|
+
required: boolean;
|
|
117
|
+
};
|
|
118
|
+
description: {
|
|
119
|
+
type: string;
|
|
120
|
+
required: boolean;
|
|
121
|
+
};
|
|
122
|
+
userRestrictions: {
|
|
123
|
+
type: string;
|
|
124
|
+
required: boolean;
|
|
125
|
+
default: null;
|
|
126
|
+
};
|
|
127
|
+
redemptions: {
|
|
128
|
+
type: string;
|
|
129
|
+
relation: string;
|
|
130
|
+
target: string;
|
|
131
|
+
mappedBy: string;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
redemption: {
|
|
137
|
+
schema: {
|
|
138
|
+
kind: string;
|
|
139
|
+
collectionName: string;
|
|
140
|
+
info: {
|
|
141
|
+
singularName: string;
|
|
142
|
+
pluralName: string;
|
|
143
|
+
displayName: string;
|
|
144
|
+
description: string;
|
|
145
|
+
};
|
|
146
|
+
options: {
|
|
147
|
+
draftAndPublish: boolean;
|
|
148
|
+
};
|
|
149
|
+
pluginOptions: {
|
|
150
|
+
'content-manager': {
|
|
151
|
+
visible: boolean;
|
|
152
|
+
};
|
|
153
|
+
'content-type-builder': {
|
|
154
|
+
visible: boolean;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
layouts: {
|
|
158
|
+
list: string[];
|
|
159
|
+
edit: {
|
|
160
|
+
name: string;
|
|
161
|
+
size: number;
|
|
162
|
+
}[][];
|
|
163
|
+
};
|
|
164
|
+
attributes: {
|
|
165
|
+
coupon: {
|
|
166
|
+
type: string;
|
|
167
|
+
relation: string;
|
|
168
|
+
target: string;
|
|
169
|
+
inversedBy: string;
|
|
170
|
+
};
|
|
171
|
+
orderId: {
|
|
172
|
+
type: string;
|
|
173
|
+
required: boolean;
|
|
174
|
+
};
|
|
175
|
+
phoneNumber: {
|
|
176
|
+
type: string;
|
|
177
|
+
required: boolean;
|
|
178
|
+
};
|
|
179
|
+
redemptionDate: {
|
|
180
|
+
type: string;
|
|
181
|
+
required: boolean;
|
|
182
|
+
};
|
|
183
|
+
discountType: {
|
|
184
|
+
type: string;
|
|
185
|
+
required: boolean;
|
|
186
|
+
};
|
|
187
|
+
discountValue: {
|
|
188
|
+
type: string;
|
|
189
|
+
required: boolean;
|
|
190
|
+
};
|
|
191
|
+
metadata: {
|
|
192
|
+
type: string;
|
|
193
|
+
required: boolean;
|
|
194
|
+
default: null;
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
export default plugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
const register_1 = __importDefault(require("./register"));
|
|
7
|
+
const bootstrap_1 = __importDefault(require("./bootstrap"));
|
|
8
|
+
const destroy_1 = __importDefault(require("./destroy"));
|
|
9
|
+
const config_1 = __importDefault(require("./config"));
|
|
10
|
+
const content_types_1 = __importDefault(require("./content-types"));
|
|
11
|
+
const controllers_1 = __importDefault(require("./controllers"));
|
|
12
|
+
const routes_1 = __importDefault(require("./routes"));
|
|
13
|
+
const services_1 = __importDefault(require("./services"));
|
|
14
|
+
const plugin = {
|
|
15
|
+
register: register_1.default,
|
|
16
|
+
bootstrap: bootstrap_1.default,
|
|
17
|
+
destroy: destroy_1.default,
|
|
18
|
+
config: config_1.default,
|
|
19
|
+
controllers: controllers_1.default,
|
|
20
|
+
routes: routes_1.default,
|
|
21
|
+
services: services_1.default,
|
|
22
|
+
contentTypes: content_types_1.default,
|
|
23
|
+
};
|
|
24
|
+
exports.default = plugin;
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
const rate_limit_1 = __importDefault(require("./rate-limit"));
|
|
7
|
+
exports.default = {
|
|
8
|
+
rateLimit: rate_limit_1.default,
|
|
9
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Simple in-memory rate limiter
|
|
4
|
+
// Uses in-memory storage - suitable for single-instance deployments
|
|
5
|
+
const rateLimitStore = new Map();
|
|
6
|
+
// Clean up old entries every hour
|
|
7
|
+
setInterval(() => {
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
for (const [key, value] of rateLimitStore.entries()) {
|
|
10
|
+
if (now > value.resetAt) {
|
|
11
|
+
rateLimitStore.delete(key);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}, 3600000); // 1 hour
|
|
15
|
+
exports.default = (config) => {
|
|
16
|
+
return async (ctx, next) => {
|
|
17
|
+
const identifier = ctx.ip || ctx.request.ip || 'unknown';
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const record = rateLimitStore.get(identifier);
|
|
20
|
+
// No record or expired, create new
|
|
21
|
+
if (!record || now > record.resetAt) {
|
|
22
|
+
rateLimitStore.set(identifier, {
|
|
23
|
+
count: 1,
|
|
24
|
+
resetAt: now + config.windowMs,
|
|
25
|
+
});
|
|
26
|
+
return next();
|
|
27
|
+
}
|
|
28
|
+
// Check if limit reached
|
|
29
|
+
if (record.count >= config.maxRequests) {
|
|
30
|
+
ctx.status = 429;
|
|
31
|
+
ctx.body = {
|
|
32
|
+
error: 'Too many requests',
|
|
33
|
+
message: `Rate limit exceeded. Please try again in ${Math.ceil((record.resetAt - now) / 1000)} seconds.`,
|
|
34
|
+
retryAfter: Math.ceil((record.resetAt - now) / 1000),
|
|
35
|
+
};
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Increment counter
|
|
39
|
+
record.count++;
|
|
40
|
+
return next();
|
|
41
|
+
};
|
|
42
|
+
};
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
type: string;
|
|
3
|
+
routes: ({
|
|
4
|
+
method: string;
|
|
5
|
+
path: string;
|
|
6
|
+
handler: string;
|
|
7
|
+
config: {
|
|
8
|
+
policies: never[];
|
|
9
|
+
middlewares: ((ctx: any, next: any) => Promise<any>)[];
|
|
10
|
+
auth: boolean;
|
|
11
|
+
};
|
|
12
|
+
} | {
|
|
13
|
+
method: string;
|
|
14
|
+
path: string;
|
|
15
|
+
handler: string;
|
|
16
|
+
config: {
|
|
17
|
+
policies: never[];
|
|
18
|
+
auth: boolean;
|
|
19
|
+
middlewares?: undefined;
|
|
20
|
+
};
|
|
21
|
+
})[];
|
|
22
|
+
};
|
|
23
|
+
export default _default;
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
const rate_limit_1 = __importDefault(require("../../middlewares/rate-limit"));
|
|
7
|
+
exports.default = {
|
|
8
|
+
type: 'content-api',
|
|
9
|
+
routes: [
|
|
10
|
+
{
|
|
11
|
+
method: 'POST',
|
|
12
|
+
path: '/validate',
|
|
13
|
+
handler: 'coupon.validate',
|
|
14
|
+
config: {
|
|
15
|
+
policies: [],
|
|
16
|
+
middlewares: [(0, rate_limit_1.default)({ maxRequests: 10, windowMs: 60000 })], // 10 requests per minute
|
|
17
|
+
auth: false,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
method: 'POST',
|
|
22
|
+
path: '/redeem',
|
|
23
|
+
handler: 'coupon.redeem',
|
|
24
|
+
config: {
|
|
25
|
+
policies: [],
|
|
26
|
+
middlewares: [(0, rate_limit_1.default)({ maxRequests: 5, windowMs: 60000 })], // 5 requests per minute
|
|
27
|
+
auth: false,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
method: 'POST',
|
|
32
|
+
path: '/',
|
|
33
|
+
handler: 'coupon.create',
|
|
34
|
+
config: {
|
|
35
|
+
policies: [],
|
|
36
|
+
auth: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
method: 'GET',
|
|
41
|
+
path: '/',
|
|
42
|
+
handler: 'coupon.find',
|
|
43
|
+
config: {
|
|
44
|
+
policies: [],
|
|
45
|
+
auth: false,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
method: 'GET',
|
|
50
|
+
path: '/:id',
|
|
51
|
+
handler: 'coupon.findOne',
|
|
52
|
+
config: {
|
|
53
|
+
policies: [],
|
|
54
|
+
auth: false,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
method: 'PUT',
|
|
59
|
+
path: '/:id',
|
|
60
|
+
handler: 'coupon.update',
|
|
61
|
+
config: {
|
|
62
|
+
policies: [],
|
|
63
|
+
auth: false,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
method: 'DELETE',
|
|
68
|
+
path: '/:id',
|
|
69
|
+
handler: 'coupon.delete',
|
|
70
|
+
config: {
|
|
71
|
+
policies: [],
|
|
72
|
+
auth: false,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
'content-api': {
|
|
3
|
+
type: string;
|
|
4
|
+
routes: ({
|
|
5
|
+
method: string;
|
|
6
|
+
path: string;
|
|
7
|
+
handler: string;
|
|
8
|
+
config: {
|
|
9
|
+
policies: never[];
|
|
10
|
+
middlewares: ((ctx: any, next: any) => Promise<any>)[];
|
|
11
|
+
auth: boolean;
|
|
12
|
+
};
|
|
13
|
+
} | {
|
|
14
|
+
method: string;
|
|
15
|
+
path: string;
|
|
16
|
+
handler: string;
|
|
17
|
+
config: {
|
|
18
|
+
policies: never[];
|
|
19
|
+
auth: boolean;
|
|
20
|
+
middlewares?: undefined;
|
|
21
|
+
};
|
|
22
|
+
})[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
const content_api_1 = __importDefault(require("./content-api"));
|
|
7
|
+
exports.default = {
|
|
8
|
+
'content-api': content_api_1.default,
|
|
9
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Core } from '@strapi/strapi';
|
|
2
|
+
export declare const ERROR_CODES: {
|
|
3
|
+
readonly COUPON_NOT_FOUND: "COUPON_NOT_FOUND";
|
|
4
|
+
readonly COUPON_EXPIRED: "COUPON_EXPIRED";
|
|
5
|
+
readonly COUPON_ALREADY_USED: "COUPON_ALREADY_USED";
|
|
6
|
+
readonly USAGE_LIMIT_REACHED: "USAGE_LIMIT_REACHED";
|
|
7
|
+
readonly INVALID_REQUEST: "INVALID_REQUEST";
|
|
8
|
+
};
|
|
9
|
+
export declare const ERROR_MESSAGES: {
|
|
10
|
+
readonly COUPON_NOT_FOUND: "Coupon code does not exist.";
|
|
11
|
+
readonly COUPON_EXPIRED: "This coupon has expired.";
|
|
12
|
+
readonly COUPON_ALREADY_USED: "This coupon has been already used.";
|
|
13
|
+
readonly USAGE_LIMIT_REACHED: "Coupon usage limit has been reached.";
|
|
14
|
+
readonly INVALID_REQUEST: "Invalid request parameters.";
|
|
15
|
+
};
|
|
16
|
+
export interface ValidateCouponRequest {
|
|
17
|
+
couponCode: string;
|
|
18
|
+
phoneNumber: string;
|
|
19
|
+
orderAmount?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface RedeemCouponRequest {
|
|
22
|
+
couponCode: string;
|
|
23
|
+
phoneNumber: string;
|
|
24
|
+
orderId: string;
|
|
25
|
+
orderAmount?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface CreateCouponRequest {
|
|
28
|
+
code: string;
|
|
29
|
+
discountType: 'percentage' | 'flat';
|
|
30
|
+
discountValue: number;
|
|
31
|
+
maxUsage?: number;
|
|
32
|
+
validFrom: string;
|
|
33
|
+
validTo: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
userRestrictions?: any;
|
|
36
|
+
}
|
|
37
|
+
export interface ValidationResponse {
|
|
38
|
+
isValid: boolean;
|
|
39
|
+
errorCode?: string;
|
|
40
|
+
message: string;
|
|
41
|
+
discountType?: string;
|
|
42
|
+
discountValue?: number;
|
|
43
|
+
discountAmount?: number;
|
|
44
|
+
finalAmount?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface RedemptionResponse {
|
|
47
|
+
success: boolean;
|
|
48
|
+
errorCode?: string;
|
|
49
|
+
message: string;
|
|
50
|
+
redemptionId?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface CreateResponse {
|
|
53
|
+
success: boolean;
|
|
54
|
+
errorCode?: string;
|
|
55
|
+
message: string;
|
|
56
|
+
couponId?: string;
|
|
57
|
+
}
|
|
58
|
+
declare const couponService: ({ strapi }: {
|
|
59
|
+
strapi: Core.Strapi;
|
|
60
|
+
}) => any;
|
|
61
|
+
export default couponService;
|