@wiicode/youcanpay-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +848 -0
  2. package/dist/client.d.ts +17 -0
  3. package/dist/client.js +171 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/constants.d.ts +8 -0
  6. package/dist/constants.js +12 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/enums/currency.enum.d.ts +8 -0
  9. package/dist/enums/currency.enum.js +13 -0
  10. package/dist/enums/currency.enum.js.map +1 -0
  11. package/dist/enums/index.d.ts +2 -0
  12. package/dist/enums/index.js +19 -0
  13. package/dist/enums/index.js.map +1 -0
  14. package/dist/enums/lang.enum.d.ts +5 -0
  15. package/dist/enums/lang.enum.js +10 -0
  16. package/dist/enums/lang.enum.js.map +1 -0
  17. package/dist/errors/index.d.ts +1 -0
  18. package/dist/errors/index.js +18 -0
  19. package/dist/errors/index.js.map +1 -0
  20. package/dist/errors/youcanpay.error.d.ts +16 -0
  21. package/dist/errors/youcanpay.error.js +32 -0
  22. package/dist/errors/youcanpay.error.js.map +1 -0
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.js +57 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/interfaces/index.d.ts +5 -0
  27. package/dist/interfaces/index.js +22 -0
  28. package/dist/interfaces/index.js.map +1 -0
  29. package/dist/interfaces/options.interface.d.ts +13 -0
  30. package/dist/interfaces/options.interface.js +3 -0
  31. package/dist/interfaces/options.interface.js.map +1 -0
  32. package/dist/interfaces/payment.interface.d.ts +22 -0
  33. package/dist/interfaces/payment.interface.js +3 -0
  34. package/dist/interfaces/payment.interface.js.map +1 -0
  35. package/dist/interfaces/token.interface.d.ts +26 -0
  36. package/dist/interfaces/token.interface.js +3 -0
  37. package/dist/interfaces/token.interface.js.map +1 -0
  38. package/dist/interfaces/transaction.interface.d.ts +10 -0
  39. package/dist/interfaces/transaction.interface.js +3 -0
  40. package/dist/interfaces/transaction.interface.js.map +1 -0
  41. package/dist/interfaces/webhook.interface.d.ts +16 -0
  42. package/dist/interfaces/webhook.interface.js +10 -0
  43. package/dist/interfaces/webhook.interface.js.map +1 -0
  44. package/dist/logging/index.d.ts +3 -0
  45. package/dist/logging/index.js +20 -0
  46. package/dist/logging/index.js.map +1 -0
  47. package/dist/logging/interfaces.d.ts +22 -0
  48. package/dist/logging/interfaces.js +3 -0
  49. package/dist/logging/interfaces.js.map +1 -0
  50. package/dist/logging/logger.d.ts +6 -0
  51. package/dist/logging/logger.js +37 -0
  52. package/dist/logging/logger.js.map +1 -0
  53. package/dist/logging/sanitizer.d.ts +1 -0
  54. package/dist/logging/sanitizer.js +55 -0
  55. package/dist/logging/sanitizer.js.map +1 -0
  56. package/dist/nestjs/decorators.d.ts +2 -0
  57. package/dist/nestjs/decorators.js +8 -0
  58. package/dist/nestjs/decorators.js.map +1 -0
  59. package/dist/nestjs/guards/index.d.ts +1 -0
  60. package/dist/nestjs/guards/index.js +9 -0
  61. package/dist/nestjs/guards/index.js.map +1 -0
  62. package/dist/nestjs/guards/webhook.guard.d.ts +16 -0
  63. package/dist/nestjs/guards/webhook.guard.js +59 -0
  64. package/dist/nestjs/guards/webhook.guard.js.map +1 -0
  65. package/dist/nestjs/index.d.ts +5 -0
  66. package/dist/nestjs/index.js +22 -0
  67. package/dist/nestjs/index.js.map +1 -0
  68. package/dist/nestjs/pipes/index.d.ts +1 -0
  69. package/dist/nestjs/pipes/index.js +7 -0
  70. package/dist/nestjs/pipes/index.js.map +1 -0
  71. package/dist/nestjs/pipes/webhook.pipe.d.ts +6 -0
  72. package/dist/nestjs/pipes/webhook.pipe.js +34 -0
  73. package/dist/nestjs/pipes/webhook.pipe.js.map +1 -0
  74. package/dist/nestjs/youcanpay.module.d.ts +6 -0
  75. package/dist/nestjs/youcanpay.module.js +48 -0
  76. package/dist/nestjs/youcanpay.module.js.map +1 -0
  77. package/dist/nestjs/youcanpay.service.d.ts +5 -0
  78. package/dist/nestjs/youcanpay.service.js +30 -0
  79. package/dist/nestjs/youcanpay.service.js.map +1 -0
  80. package/dist/security/index.d.ts +2 -0
  81. package/dist/security/index.js +24 -0
  82. package/dist/security/index.js.map +1 -0
  83. package/dist/security/validators.d.ts +37 -0
  84. package/dist/security/validators.js +183 -0
  85. package/dist/security/validators.js.map +1 -0
  86. package/dist/security/webhook.d.ts +80 -0
  87. package/dist/security/webhook.js +146 -0
  88. package/dist/security/webhook.js.map +1 -0
  89. package/dist/tsconfig.build.tsbuildinfo +1 -0
  90. package/dist/tsconfig.tsbuildinfo +1 -0
  91. package/package.json +49 -0
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUPPORTED_CURRENCIES = void 0;
4
+ exports.validateAmount = validateAmount;
5
+ exports.validateCurrency = validateCurrency;
6
+ exports.validateRedirectURL = validateRedirectURL;
7
+ exports.validateOrderId = validateOrderId;
8
+ exports.validateIP = validateIP;
9
+ exports.validateEmail = validateEmail;
10
+ exports.validatePaymentInput = validatePaymentInput;
11
+ exports.sanitizeString = sanitizeString;
12
+ exports.toCentimes = toCentimes;
13
+ exports.fromCentimes = fromCentimes;
14
+ exports.formatAmount = formatAmount;
15
+ exports.SUPPORTED_CURRENCIES = ['MAD', 'USD', 'EUR'];
16
+ function validateAmount(amount, options = {}) {
17
+ const { min = 100, max = 100000000, currency = 'centimes' } = options;
18
+ if (amount === null || amount === undefined) {
19
+ return { valid: false, error: 'Amount is required' };
20
+ }
21
+ if (typeof amount !== 'number') {
22
+ return { valid: false, error: 'Amount must be a number' };
23
+ }
24
+ const numAmount = amount;
25
+ if (!Number.isInteger(numAmount)) {
26
+ return { valid: false, error: 'Amount must be an integer (in centimes)' };
27
+ }
28
+ if (numAmount < min) {
29
+ return { valid: false, error: `Amount must be at least ${min} ${currency}` };
30
+ }
31
+ if (numAmount > max) {
32
+ return { valid: false, error: `Amount must not exceed ${max} ${currency}` };
33
+ }
34
+ return { valid: true };
35
+ }
36
+ function validateCurrency(currency) {
37
+ if (!currency || typeof currency !== 'string') {
38
+ return { valid: false, error: 'Currency is required' };
39
+ }
40
+ const upperCurrency = currency.toUpperCase();
41
+ if (!exports.SUPPORTED_CURRENCIES.includes(upperCurrency)) {
42
+ return {
43
+ valid: false,
44
+ error: `Currency must be one of: ${exports.SUPPORTED_CURRENCIES.join(', ')}`,
45
+ };
46
+ }
47
+ return { valid: true };
48
+ }
49
+ function validateRedirectURL(url, options = {}) {
50
+ const { protocols = ['https', 'http'], allowedDomains, blockedDomains = [], allowLocalhost = true, requireTLD = false, } = options;
51
+ if (!url) {
52
+ return { valid: true };
53
+ }
54
+ if (typeof url !== 'string') {
55
+ return { valid: false, error: 'URL must be a string' };
56
+ }
57
+ let parsed;
58
+ try {
59
+ parsed = new URL(url);
60
+ }
61
+ catch {
62
+ return { valid: false, error: 'Invalid URL format' };
63
+ }
64
+ const protocol = parsed.protocol.replace(':', '');
65
+ if (!protocols.includes(protocol)) {
66
+ return {
67
+ valid: false,
68
+ error: `URL protocol must be one of: ${protocols.join(', ')}`,
69
+ };
70
+ }
71
+ const hostname = parsed.hostname.toLowerCase();
72
+ const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1';
73
+ if (isLocalhost && !allowLocalhost) {
74
+ return { valid: false, error: 'Localhost URLs are not allowed' };
75
+ }
76
+ if (!isLocalhost && requireTLD) {
77
+ const hasTLD = hostname.includes('.') && !hostname.endsWith('.');
78
+ if (!hasTLD) {
79
+ return { valid: false, error: 'URL must have a valid domain with TLD' };
80
+ }
81
+ }
82
+ for (const blocked of blockedDomains) {
83
+ if (hostname === blocked || hostname.endsWith(`.${blocked}`)) {
84
+ return { valid: false, error: `Domain "${blocked}" is not allowed` };
85
+ }
86
+ }
87
+ if (allowedDomains && allowedDomains.length > 0) {
88
+ const isAllowed = allowedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
89
+ if (!isAllowed) {
90
+ return {
91
+ valid: false,
92
+ error: `Domain must be one of: ${allowedDomains.join(', ')}`,
93
+ };
94
+ }
95
+ }
96
+ if (parsed.username || parsed.password) {
97
+ return { valid: false, error: 'URL must not contain credentials' };
98
+ }
99
+ return { valid: true };
100
+ }
101
+ function validateOrderId(orderId) {
102
+ if (!orderId || typeof orderId !== 'string') {
103
+ return { valid: false, error: 'Order ID is required' };
104
+ }
105
+ if (orderId.length < 1 || orderId.length > 255) {
106
+ return { valid: false, error: 'Order ID must be between 1 and 255 characters' };
107
+ }
108
+ const safePattern = /^[a-zA-Z0-9_\-:.]+$/;
109
+ if (!safePattern.test(orderId)) {
110
+ return {
111
+ valid: false,
112
+ error: 'Order ID contains invalid characters (allowed: alphanumeric, _, -, :, .)',
113
+ };
114
+ }
115
+ return { valid: true };
116
+ }
117
+ function validateIP(ip) {
118
+ if (!ip || typeof ip !== 'string') {
119
+ return { valid: false, error: 'IP address is required' };
120
+ }
121
+ const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
122
+ const ipv6Pattern = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/;
123
+ if (!ipv4Pattern.test(ip) && !ipv6Pattern.test(ip) && ip !== '::1') {
124
+ return { valid: false, error: 'Invalid IP address format' };
125
+ }
126
+ if (ipv4Pattern.test(ip)) {
127
+ const octets = ip.split('.').map(Number);
128
+ if (octets.some((octet) => octet > 255)) {
129
+ return { valid: false, error: 'Invalid IP address: octet out of range' };
130
+ }
131
+ }
132
+ return { valid: true };
133
+ }
134
+ function validateEmail(email) {
135
+ if (!email) {
136
+ return { valid: true };
137
+ }
138
+ if (typeof email !== 'string') {
139
+ return { valid: false, error: 'Email must be a string' };
140
+ }
141
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
142
+ if (!emailPattern.test(email)) {
143
+ return { valid: false, error: 'Invalid email format' };
144
+ }
145
+ if (email.length > 254) {
146
+ return { valid: false, error: 'Email is too long' };
147
+ }
148
+ return { valid: true };
149
+ }
150
+ function validatePaymentInput(input) {
151
+ const checks = [
152
+ validateAmount(input.amount),
153
+ validateCurrency(input.currency),
154
+ input.orderId !== undefined ? validateOrderId(input.orderId) : { valid: true },
155
+ input.customerIp !== undefined ? validateIP(input.customerIp) : { valid: true },
156
+ validateRedirectURL(input.successUrl),
157
+ validateRedirectURL(input.errorUrl),
158
+ validateEmail(input.customerEmail),
159
+ ];
160
+ const failed = checks.find((check) => !check.valid);
161
+ if (failed) {
162
+ return failed;
163
+ }
164
+ return { valid: true };
165
+ }
166
+ function sanitizeString(input) {
167
+ return input
168
+ .replace(/[<>]/g, '')
169
+ .replace(/javascript:/gi, '')
170
+ .replace(/data:/gi, '')
171
+ .trim();
172
+ }
173
+ function toCentimes(amount) {
174
+ return Math.round(amount * 100);
175
+ }
176
+ function fromCentimes(centimes) {
177
+ return centimes / 100;
178
+ }
179
+ function formatAmount(centimes, currency) {
180
+ const amount = fromCentimes(centimes);
181
+ return `${amount.toFixed(2)} ${currency}`;
182
+ }
183
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/security/validators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ export interface YouCanPayRawWebhook {
2
+ id: string;
3
+ event_name: string;
4
+ sandbox: boolean;
5
+ payload: {
6
+ transaction: {
7
+ id: string;
8
+ status: number;
9
+ order_id: string;
10
+ amount: string | number;
11
+ currency: string;
12
+ base_currency?: string | null;
13
+ base_amount?: string | null;
14
+ created_at: string;
15
+ };
16
+ payment_method?: {
17
+ id: number;
18
+ name: string;
19
+ card?: {
20
+ id: string;
21
+ country_code?: string | null;
22
+ brand?: string | null;
23
+ last_digits: string;
24
+ fingerprint: string;
25
+ is_3d_secure: boolean;
26
+ };
27
+ };
28
+ token?: {
29
+ id: string;
30
+ };
31
+ customer?: {
32
+ id: string;
33
+ email?: string | null;
34
+ name?: string | null;
35
+ address?: string | null;
36
+ phone?: string | null;
37
+ country_code?: string | null;
38
+ city?: string | null;
39
+ state?: string | null;
40
+ zip_code?: string | null;
41
+ };
42
+ metadata?: Record<string, string> | unknown[];
43
+ };
44
+ }
45
+ export interface ParsedWebhookPayload {
46
+ webhookId: string;
47
+ eventName: string;
48
+ sandbox: boolean;
49
+ transactionId: string;
50
+ orderId: string;
51
+ amount: number;
52
+ currency: string;
53
+ rawStatus: number;
54
+ status: 'paid' | 'failed' | 'refunded' | 'unknown';
55
+ isSuccess: boolean;
56
+ tokenId?: string;
57
+ cardLastDigits?: string;
58
+ cardBrand?: string;
59
+ is3DSecure?: boolean;
60
+ customerEmail?: string;
61
+ createdAt: string;
62
+ raw: YouCanPayRawWebhook;
63
+ }
64
+ export interface WebhookVerifyOptions {
65
+ secret: string;
66
+ query?: Record<string, string>;
67
+ headers?: Record<string, string>;
68
+ signatureHeader?: string;
69
+ secretParam?: string;
70
+ }
71
+ export declare function parseWebhookPayload(payload: unknown): ParsedWebhookPayload;
72
+ export declare function verifyWebhookSecret(options: WebhookVerifyOptions): boolean;
73
+ export declare function verifyWebhookHMAC(payload: string | Buffer, signature: string, secret: string, algorithm?: 'sha256' | 'sha512'): boolean;
74
+ export declare function createWebhookSignature(payload: string | object, secret: string, algorithm?: 'sha256' | 'sha512'): string;
75
+ export declare class WebhookParseError extends Error {
76
+ constructor(message: string);
77
+ }
78
+ export declare class WebhookVerifyError extends Error {
79
+ constructor(message: string);
80
+ }
@@ -0,0 +1,146 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WebhookVerifyError = exports.WebhookParseError = void 0;
37
+ exports.parseWebhookPayload = parseWebhookPayload;
38
+ exports.verifyWebhookSecret = verifyWebhookSecret;
39
+ exports.verifyWebhookHMAC = verifyWebhookHMAC;
40
+ exports.createWebhookSignature = createWebhookSignature;
41
+ const crypto = __importStar(require("crypto"));
42
+ function parseWebhookPayload(payload) {
43
+ if (!payload || typeof payload !== 'object') {
44
+ throw new WebhookParseError('Invalid webhook payload: not an object');
45
+ }
46
+ const p = payload;
47
+ if (!p['id'] || !p['event_name'] || !p['payload']) {
48
+ throw new WebhookParseError('Invalid webhook payload: missing required fields (id, event_name, payload)');
49
+ }
50
+ const rawPayload = p;
51
+ const transaction = rawPayload.payload?.transaction;
52
+ if (!transaction) {
53
+ throw new WebhookParseError('Invalid webhook payload: missing transaction data');
54
+ }
55
+ if (!transaction.id || !transaction.order_id) {
56
+ throw new WebhookParseError('Invalid webhook payload: missing transaction_id or order_id');
57
+ }
58
+ const eventName = rawPayload.event_name;
59
+ const rawStatus = Number(transaction.status);
60
+ let status;
61
+ if (rawStatus === 1 || eventName === 'transaction.paid') {
62
+ status = 'paid';
63
+ }
64
+ else if (eventName === 'transaction.failed') {
65
+ status = 'failed';
66
+ }
67
+ else if (eventName === 'refund.success' || eventName === 'transaction.refunded') {
68
+ status = 'refunded';
69
+ }
70
+ else {
71
+ status = 'unknown';
72
+ }
73
+ const isSuccess = status === 'paid';
74
+ return {
75
+ webhookId: rawPayload.id,
76
+ eventName,
77
+ sandbox: rawPayload.sandbox,
78
+ transactionId: transaction.id,
79
+ orderId: transaction.order_id,
80
+ amount: Number(transaction.amount),
81
+ currency: transaction.currency,
82
+ rawStatus,
83
+ status,
84
+ isSuccess,
85
+ tokenId: rawPayload.payload.token?.id,
86
+ cardLastDigits: rawPayload.payload.payment_method?.card?.last_digits,
87
+ cardBrand: rawPayload.payload.payment_method?.card?.brand || undefined,
88
+ is3DSecure: rawPayload.payload.payment_method?.card?.is_3d_secure,
89
+ customerEmail: rawPayload.payload.customer?.email || undefined,
90
+ createdAt: transaction.created_at,
91
+ raw: rawPayload,
92
+ };
93
+ }
94
+ function verifyWebhookSecret(options) {
95
+ const { secret, query, headers, secretParam = 'secret', signatureHeader = 'x-ycp-signature' } = options;
96
+ if (!secret) {
97
+ throw new WebhookVerifyError('Webhook secret is required');
98
+ }
99
+ if (headers) {
100
+ const signature = headers[signatureHeader] || headers[signatureHeader.toLowerCase()];
101
+ if (signature && signature === secret) {
102
+ return true;
103
+ }
104
+ }
105
+ if (query) {
106
+ const querySecret = query[secretParam];
107
+ if (querySecret && querySecret === secret) {
108
+ return true;
109
+ }
110
+ }
111
+ return false;
112
+ }
113
+ function verifyWebhookHMAC(payload, signature, secret, algorithm = 'sha256') {
114
+ const expectedSignature = crypto
115
+ .createHmac(algorithm, secret)
116
+ .update(payload)
117
+ .digest('hex');
118
+ try {
119
+ return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
120
+ }
121
+ catch {
122
+ return false;
123
+ }
124
+ }
125
+ function createWebhookSignature(payload, secret, algorithm = 'sha256') {
126
+ const data = typeof payload === 'string' ? payload : JSON.stringify(payload);
127
+ return crypto
128
+ .createHmac(algorithm, secret)
129
+ .update(data)
130
+ .digest('hex');
131
+ }
132
+ class WebhookParseError extends Error {
133
+ constructor(message) {
134
+ super(message);
135
+ this.name = 'WebhookParseError';
136
+ }
137
+ }
138
+ exports.WebhookParseError = WebhookParseError;
139
+ class WebhookVerifyError extends Error {
140
+ constructor(message) {
141
+ super(message);
142
+ this.name = 'WebhookVerifyError';
143
+ }
144
+ }
145
+ exports.WebhookVerifyError = WebhookVerifyError;
146
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/security/webhook.ts"],"names":[],"mappings":"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"}