@nehorai/payments 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/config/index.cjs +116 -0
- package/dist/config/index.cjs.map +1 -0
- package/dist/config/index.d.cts +125 -0
- package/dist/config/index.d.ts +125 -0
- package/dist/config/index.js +83 -0
- package/dist/config/index.js.map +1 -0
- package/dist/factory.cjs +807 -0
- package/dist/factory.cjs.map +1 -0
- package/dist/factory.d.cts +96 -0
- package/dist/factory.d.ts +96 -0
- package/dist/factory.js +777 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.cjs +1341 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +1260 -0
- package/dist/index.js.map +1 -0
- package/dist/payment-orchestrator-CPaLmDM5.d.ts +404 -0
- package/dist/payment-orchestrator-Co_X6T_V.d.cts +404 -0
- package/dist/payment-types-68W-PlGg.d.cts +211 -0
- package/dist/payment-types-68W-PlGg.d.ts +211 -0
- package/dist/providers/interfaces/index.cjs +19 -0
- package/dist/providers/interfaces/index.cjs.map +1 -0
- package/dist/providers/interfaces/index.d.cts +80 -0
- package/dist/providers/interfaces/index.d.ts +80 -0
- package/dist/providers/interfaces/index.js +1 -0
- package/dist/providers/interfaces/index.js.map +1 -0
- package/dist/repository/interfaces/index.cjs +19 -0
- package/dist/repository/interfaces/index.cjs.map +1 -0
- package/dist/repository/interfaces/index.d.cts +556 -0
- package/dist/repository/interfaces/index.d.ts +556 -0
- package/dist/repository/interfaces/index.js +1 -0
- package/dist/repository/interfaces/index.js.map +1 -0
- package/dist/routing-engine.interface-DJzGXor9.d.cts +194 -0
- package/dist/routing-engine.interface-h9_GmQ4b.d.ts +194 -0
- package/dist/services/index.cjs +806 -0
- package/dist/services/index.cjs.map +1 -0
- package/dist/services/index.d.cts +75 -0
- package/dist/services/index.d.ts +75 -0
- package/dist/services/index.js +763 -0
- package/dist/services/index.js.map +1 -0
- package/dist/state-machine-Cu6_qKnv.d.cts +109 -0
- package/dist/state-machine-Cu6_qKnv.d.ts +109 -0
- package/dist/types/index.cjs +173 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +127 -0
- package/dist/types/index.d.ts +127 -0
- package/dist/types/index.js +130 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.cjs +167 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +102 -0
- package/dist/utils/index.d.ts +102 -0
- package/dist/utils/index.js +127 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { k as PaymentProvider } from '../payment-types-68W-PlGg.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @nehorai/payments - Idempotency Utilities
|
|
5
|
+
*
|
|
6
|
+
* Generates and validates idempotency keys to prevent duplicate charges.
|
|
7
|
+
* Framework-agnostic utility that can be used anywhere.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generate a unique internal payment ID
|
|
11
|
+
* Format: pay_{uuid}
|
|
12
|
+
*/
|
|
13
|
+
declare function generateInternalPaymentId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Generate an idempotency key for API calls
|
|
16
|
+
* Format: idem_{uuid}
|
|
17
|
+
*/
|
|
18
|
+
declare function generateIdempotencyKey(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a deterministic idempotency key based on inputs
|
|
21
|
+
* Useful when you need the same key for retries
|
|
22
|
+
*
|
|
23
|
+
* @param components - Array of values to hash together
|
|
24
|
+
*/
|
|
25
|
+
declare function generateDeterministicKey(...components: (string | number)[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate idempotency key for a specific operation
|
|
28
|
+
*
|
|
29
|
+
* @param operation - Type of operation (e.g., 'capture', 'refund')
|
|
30
|
+
* @param transactionId - Associated transaction ID
|
|
31
|
+
*/
|
|
32
|
+
declare function generateOperationKey(operation: string, transactionId: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Validate idempotency key format
|
|
35
|
+
*/
|
|
36
|
+
declare function isValidIdempotencyKey(key: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Validate internal payment ID format
|
|
39
|
+
*/
|
|
40
|
+
declare function isValidInternalPaymentId(id: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Extract UUID from payment ID or idempotency key
|
|
43
|
+
*/
|
|
44
|
+
declare function extractUuid(key: string): string | null;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @nehorai/payments - Webhook Signature Verification
|
|
48
|
+
*
|
|
49
|
+
* Verifies HMAC signatures from payment providers to prevent spoofing.
|
|
50
|
+
* Provides generic verification functions plus a registry pattern
|
|
51
|
+
* for provider-specific verification strategies.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
interface SignatureVerificationParams {
|
|
55
|
+
provider: PaymentProvider;
|
|
56
|
+
payload: string;
|
|
57
|
+
signature: string;
|
|
58
|
+
secret: string;
|
|
59
|
+
/** Tolerance in seconds for timestamp validation (default: 300) */
|
|
60
|
+
tolerance?: number;
|
|
61
|
+
}
|
|
62
|
+
interface SignatureVerificationResult {
|
|
63
|
+
valid: boolean;
|
|
64
|
+
error?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A function that verifies a webhook signature for a specific provider
|
|
68
|
+
*/
|
|
69
|
+
type SignatureVerifier = (payload: string, signature: string, secret: string, tolerance: number) => SignatureVerificationResult;
|
|
70
|
+
/**
|
|
71
|
+
* Verify a Stripe-style webhook signature
|
|
72
|
+
* Stripe uses: t={timestamp},v1={signature}
|
|
73
|
+
*/
|
|
74
|
+
declare function verifyStripeStyleSignature(payload: string, signature: string, secret: string, tolerance: number): SignatureVerificationResult;
|
|
75
|
+
/**
|
|
76
|
+
* Verify an HMAC-SHA256 signature of sorted payload fields
|
|
77
|
+
*/
|
|
78
|
+
declare function verifySortedFieldsHmacSignature(payload: string, signature: string, secret: string, _tolerance: number): SignatureVerificationResult;
|
|
79
|
+
/**
|
|
80
|
+
* Verify a simple HMAC-SHA256 signature of the raw payload
|
|
81
|
+
*/
|
|
82
|
+
declare function verifyHmacSha256Signature(payload: string, signature: string, secret: string, _tolerance: number): SignatureVerificationResult;
|
|
83
|
+
/**
|
|
84
|
+
* Register a custom signature verifier for a provider
|
|
85
|
+
*/
|
|
86
|
+
declare function registerSignatureVerifier(provider: PaymentProvider, verifier: SignatureVerifier): void;
|
|
87
|
+
/**
|
|
88
|
+
* Get the registered verifier for a provider
|
|
89
|
+
*/
|
|
90
|
+
declare function getSignatureVerifier(provider: PaymentProvider): SignatureVerifier | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Verify webhook signature based on provider.
|
|
93
|
+
* Uses registered verifiers. Falls back to HMAC-SHA256 if no verifier is registered.
|
|
94
|
+
*/
|
|
95
|
+
declare function verifyWebhookSignature(params: SignatureVerificationParams): SignatureVerificationResult;
|
|
96
|
+
/**
|
|
97
|
+
* Get signature header name for a provider.
|
|
98
|
+
* Returns a generic default; override per provider if needed.
|
|
99
|
+
*/
|
|
100
|
+
declare function getSignatureHeaderName(provider: PaymentProvider): string;
|
|
101
|
+
|
|
102
|
+
export { type SignatureVerificationParams, type SignatureVerificationResult, type SignatureVerifier, extractUuid, generateDeterministicKey, generateIdempotencyKey, generateInternalPaymentId, generateOperationKey, getSignatureHeaderName, getSignatureVerifier, isValidIdempotencyKey, isValidInternalPaymentId, registerSignatureVerifier, verifyHmacSha256Signature, verifySortedFieldsHmacSignature, verifyStripeStyleSignature, verifyWebhookSignature };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { k as PaymentProvider } from '../payment-types-68W-PlGg.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @nehorai/payments - Idempotency Utilities
|
|
5
|
+
*
|
|
6
|
+
* Generates and validates idempotency keys to prevent duplicate charges.
|
|
7
|
+
* Framework-agnostic utility that can be used anywhere.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generate a unique internal payment ID
|
|
11
|
+
* Format: pay_{uuid}
|
|
12
|
+
*/
|
|
13
|
+
declare function generateInternalPaymentId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Generate an idempotency key for API calls
|
|
16
|
+
* Format: idem_{uuid}
|
|
17
|
+
*/
|
|
18
|
+
declare function generateIdempotencyKey(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a deterministic idempotency key based on inputs
|
|
21
|
+
* Useful when you need the same key for retries
|
|
22
|
+
*
|
|
23
|
+
* @param components - Array of values to hash together
|
|
24
|
+
*/
|
|
25
|
+
declare function generateDeterministicKey(...components: (string | number)[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate idempotency key for a specific operation
|
|
28
|
+
*
|
|
29
|
+
* @param operation - Type of operation (e.g., 'capture', 'refund')
|
|
30
|
+
* @param transactionId - Associated transaction ID
|
|
31
|
+
*/
|
|
32
|
+
declare function generateOperationKey(operation: string, transactionId: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Validate idempotency key format
|
|
35
|
+
*/
|
|
36
|
+
declare function isValidIdempotencyKey(key: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Validate internal payment ID format
|
|
39
|
+
*/
|
|
40
|
+
declare function isValidInternalPaymentId(id: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Extract UUID from payment ID or idempotency key
|
|
43
|
+
*/
|
|
44
|
+
declare function extractUuid(key: string): string | null;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @nehorai/payments - Webhook Signature Verification
|
|
48
|
+
*
|
|
49
|
+
* Verifies HMAC signatures from payment providers to prevent spoofing.
|
|
50
|
+
* Provides generic verification functions plus a registry pattern
|
|
51
|
+
* for provider-specific verification strategies.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
interface SignatureVerificationParams {
|
|
55
|
+
provider: PaymentProvider;
|
|
56
|
+
payload: string;
|
|
57
|
+
signature: string;
|
|
58
|
+
secret: string;
|
|
59
|
+
/** Tolerance in seconds for timestamp validation (default: 300) */
|
|
60
|
+
tolerance?: number;
|
|
61
|
+
}
|
|
62
|
+
interface SignatureVerificationResult {
|
|
63
|
+
valid: boolean;
|
|
64
|
+
error?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A function that verifies a webhook signature for a specific provider
|
|
68
|
+
*/
|
|
69
|
+
type SignatureVerifier = (payload: string, signature: string, secret: string, tolerance: number) => SignatureVerificationResult;
|
|
70
|
+
/**
|
|
71
|
+
* Verify a Stripe-style webhook signature
|
|
72
|
+
* Stripe uses: t={timestamp},v1={signature}
|
|
73
|
+
*/
|
|
74
|
+
declare function verifyStripeStyleSignature(payload: string, signature: string, secret: string, tolerance: number): SignatureVerificationResult;
|
|
75
|
+
/**
|
|
76
|
+
* Verify an HMAC-SHA256 signature of sorted payload fields
|
|
77
|
+
*/
|
|
78
|
+
declare function verifySortedFieldsHmacSignature(payload: string, signature: string, secret: string, _tolerance: number): SignatureVerificationResult;
|
|
79
|
+
/**
|
|
80
|
+
* Verify a simple HMAC-SHA256 signature of the raw payload
|
|
81
|
+
*/
|
|
82
|
+
declare function verifyHmacSha256Signature(payload: string, signature: string, secret: string, _tolerance: number): SignatureVerificationResult;
|
|
83
|
+
/**
|
|
84
|
+
* Register a custom signature verifier for a provider
|
|
85
|
+
*/
|
|
86
|
+
declare function registerSignatureVerifier(provider: PaymentProvider, verifier: SignatureVerifier): void;
|
|
87
|
+
/**
|
|
88
|
+
* Get the registered verifier for a provider
|
|
89
|
+
*/
|
|
90
|
+
declare function getSignatureVerifier(provider: PaymentProvider): SignatureVerifier | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Verify webhook signature based on provider.
|
|
93
|
+
* Uses registered verifiers. Falls back to HMAC-SHA256 if no verifier is registered.
|
|
94
|
+
*/
|
|
95
|
+
declare function verifyWebhookSignature(params: SignatureVerificationParams): SignatureVerificationResult;
|
|
96
|
+
/**
|
|
97
|
+
* Get signature header name for a provider.
|
|
98
|
+
* Returns a generic default; override per provider if needed.
|
|
99
|
+
*/
|
|
100
|
+
declare function getSignatureHeaderName(provider: PaymentProvider): string;
|
|
101
|
+
|
|
102
|
+
export { type SignatureVerificationParams, type SignatureVerificationResult, type SignatureVerifier, extractUuid, generateDeterministicKey, generateIdempotencyKey, generateInternalPaymentId, generateOperationKey, getSignatureHeaderName, getSignatureVerifier, isValidIdempotencyKey, isValidInternalPaymentId, registerSignatureVerifier, verifyHmacSha256Signature, verifySortedFieldsHmacSignature, verifyStripeStyleSignature, verifyWebhookSignature };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/utils/idempotency.ts
|
|
2
|
+
import { randomUUID, createHash } from "crypto";
|
|
3
|
+
function generateInternalPaymentId() {
|
|
4
|
+
return `pay_${randomUUID()}`;
|
|
5
|
+
}
|
|
6
|
+
function generateIdempotencyKey() {
|
|
7
|
+
return `idem_${randomUUID()}`;
|
|
8
|
+
}
|
|
9
|
+
function generateDeterministicKey(...components) {
|
|
10
|
+
const data = components.join(":");
|
|
11
|
+
const hash = createHash("sha256").update(data).digest("hex");
|
|
12
|
+
return `idem_${hash.substring(0, 32)}`;
|
|
13
|
+
}
|
|
14
|
+
function generateOperationKey(operation, transactionId) {
|
|
15
|
+
return `${operation}_${transactionId}`;
|
|
16
|
+
}
|
|
17
|
+
function isValidIdempotencyKey(key) {
|
|
18
|
+
return /^idem_[a-f0-9-]{32,36}$/.test(key);
|
|
19
|
+
}
|
|
20
|
+
function isValidInternalPaymentId(id) {
|
|
21
|
+
return /^pay_[a-f0-9-]{36}$/.test(id);
|
|
22
|
+
}
|
|
23
|
+
function extractUuid(key) {
|
|
24
|
+
const match = key.match(/^(?:pay|idem)_([a-f0-9-]+)$/);
|
|
25
|
+
return match ? match[1] : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/utils/signature-verification.ts
|
|
29
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
30
|
+
function verifyStripeStyleSignature(payload, signature, secret, tolerance) {
|
|
31
|
+
try {
|
|
32
|
+
const elements = signature.split(",");
|
|
33
|
+
const timestamp = elements.find((e) => e.startsWith("t="))?.slice(2);
|
|
34
|
+
const sig = elements.find((e) => e.startsWith("v1="))?.slice(3);
|
|
35
|
+
if (!timestamp || !sig) {
|
|
36
|
+
return { valid: false, error: "Invalid signature format" };
|
|
37
|
+
}
|
|
38
|
+
const timestampNum = parseInt(timestamp, 10);
|
|
39
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
40
|
+
if (Math.abs(now - timestampNum) > tolerance) {
|
|
41
|
+
return { valid: false, error: "Timestamp outside tolerance" };
|
|
42
|
+
}
|
|
43
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
44
|
+
const expectedSig = createHmac("sha256", secret).update(signedPayload).digest("hex");
|
|
45
|
+
const valid = timingSafeEqual(
|
|
46
|
+
Buffer.from(sig),
|
|
47
|
+
Buffer.from(expectedSig)
|
|
48
|
+
);
|
|
49
|
+
return { valid };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
valid: false,
|
|
53
|
+
error: error instanceof Error ? error.message : "Verification failed"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function verifySortedFieldsHmacSignature(payload, signature, secret, _tolerance) {
|
|
58
|
+
try {
|
|
59
|
+
const data = JSON.parse(payload);
|
|
60
|
+
const sortedKeys = Object.keys(data).sort();
|
|
61
|
+
const sortedPayload = sortedKeys.map((k) => `${k}=${data[k]}`).join("&");
|
|
62
|
+
const expectedSig = createHmac("sha256", secret).update(sortedPayload).digest("hex");
|
|
63
|
+
const valid = timingSafeEqual(
|
|
64
|
+
Buffer.from(signature.toLowerCase()),
|
|
65
|
+
Buffer.from(expectedSig)
|
|
66
|
+
);
|
|
67
|
+
return { valid };
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
error: error instanceof Error ? error.message : "Verification failed"
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function verifyHmacSha256Signature(payload, signature, secret, _tolerance) {
|
|
76
|
+
try {
|
|
77
|
+
const expectedSig = createHmac("sha256", secret).update(payload).digest("hex");
|
|
78
|
+
const valid = timingSafeEqual(
|
|
79
|
+
Buffer.from(signature.toLowerCase()),
|
|
80
|
+
Buffer.from(expectedSig.toLowerCase())
|
|
81
|
+
);
|
|
82
|
+
return { valid };
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
valid: false,
|
|
86
|
+
error: error instanceof Error ? error.message : "Verification failed"
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
var verifierRegistry = /* @__PURE__ */ new Map();
|
|
91
|
+
function registerSignatureVerifier(provider, verifier) {
|
|
92
|
+
verifierRegistry.set(provider, verifier);
|
|
93
|
+
}
|
|
94
|
+
function getSignatureVerifier(provider) {
|
|
95
|
+
return verifierRegistry.get(provider);
|
|
96
|
+
}
|
|
97
|
+
function verifyWebhookSignature(params) {
|
|
98
|
+
const { provider, payload, signature, secret, tolerance = 300 } = params;
|
|
99
|
+
if (!signature || !secret) {
|
|
100
|
+
return { valid: false, error: "Missing signature or secret" };
|
|
101
|
+
}
|
|
102
|
+
const verifier = verifierRegistry.get(provider);
|
|
103
|
+
if (verifier) {
|
|
104
|
+
return verifier(payload, signature, secret, tolerance);
|
|
105
|
+
}
|
|
106
|
+
return verifyHmacSha256Signature(payload, signature, secret, tolerance);
|
|
107
|
+
}
|
|
108
|
+
function getSignatureHeaderName(provider) {
|
|
109
|
+
return `x-${provider}-signature`;
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
extractUuid,
|
|
113
|
+
generateDeterministicKey,
|
|
114
|
+
generateIdempotencyKey,
|
|
115
|
+
generateInternalPaymentId,
|
|
116
|
+
generateOperationKey,
|
|
117
|
+
getSignatureHeaderName,
|
|
118
|
+
getSignatureVerifier,
|
|
119
|
+
isValidIdempotencyKey,
|
|
120
|
+
isValidInternalPaymentId,
|
|
121
|
+
registerSignatureVerifier,
|
|
122
|
+
verifyHmacSha256Signature,
|
|
123
|
+
verifySortedFieldsHmacSignature,
|
|
124
|
+
verifyStripeStyleSignature,
|
|
125
|
+
verifyWebhookSignature
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/idempotency.ts","../../src/utils/signature-verification.ts"],"sourcesContent":["/**\r\n * @nehorai/payments - Idempotency Utilities\r\n *\r\n * Generates and validates idempotency keys to prevent duplicate charges.\r\n * Framework-agnostic utility that can be used anywhere.\r\n */\r\n\r\nimport { randomUUID, createHash } from 'crypto'\r\n\r\n/**\r\n * Generate a unique internal payment ID\r\n * Format: pay_{uuid}\r\n */\r\nexport function generateInternalPaymentId(): string {\r\n return `pay_${randomUUID()}`\r\n}\r\n\r\n/**\r\n * Generate an idempotency key for API calls\r\n * Format: idem_{uuid}\r\n */\r\nexport function generateIdempotencyKey(): string {\r\n return `idem_${randomUUID()}`\r\n}\r\n\r\n/**\r\n * Generate a deterministic idempotency key based on inputs\r\n * Useful when you need the same key for retries\r\n *\r\n * @param components - Array of values to hash together\r\n */\r\nexport function generateDeterministicKey(\r\n ...components: (string | number)[]\r\n): string {\r\n const data = components.join(':')\r\n const hash = createHash('sha256').update(data).digest('hex')\r\n return `idem_${hash.substring(0, 32)}`\r\n}\r\n\r\n/**\r\n * Generate idempotency key for a specific operation\r\n *\r\n * @param operation - Type of operation (e.g., 'capture', 'refund')\r\n * @param transactionId - Associated transaction ID\r\n */\r\nexport function generateOperationKey(\r\n operation: string,\r\n transactionId: string\r\n): string {\r\n return `${operation}_${transactionId}`\r\n}\r\n\r\n/**\r\n * Validate idempotency key format\r\n */\r\nexport function isValidIdempotencyKey(key: string): boolean {\r\n return /^idem_[a-f0-9-]{32,36}$/.test(key)\r\n}\r\n\r\n/**\r\n * Validate internal payment ID format\r\n */\r\nexport function isValidInternalPaymentId(id: string): boolean {\r\n return /^pay_[a-f0-9-]{36}$/.test(id)\r\n}\r\n\r\n/**\r\n * Extract UUID from payment ID or idempotency key\r\n */\r\nexport function extractUuid(key: string): string | null {\r\n const match = key.match(/^(?:pay|idem)_([a-f0-9-]+)$/)\r\n return match ? match[1] : null\r\n}\r\n","/**\r\n * @nehorai/payments - Webhook Signature Verification\r\n *\r\n * Verifies HMAC signatures from payment providers to prevent spoofing.\r\n * Provides generic verification functions plus a registry pattern\r\n * for provider-specific verification strategies.\r\n */\r\n\r\nimport { createHmac, createHash, timingSafeEqual } from 'crypto'\r\nimport type { PaymentProvider } from '../types/index.js'\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface SignatureVerificationParams {\r\n provider: PaymentProvider\r\n payload: string\r\n signature: string\r\n secret: string\r\n /** Tolerance in seconds for timestamp validation (default: 300) */\r\n tolerance?: number\r\n}\r\n\r\nexport interface SignatureVerificationResult {\r\n valid: boolean\r\n error?: string\r\n}\r\n\r\n/**\r\n * A function that verifies a webhook signature for a specific provider\r\n */\r\nexport type SignatureVerifier = (\r\n payload: string,\r\n signature: string,\r\n secret: string,\r\n tolerance: number\r\n) => SignatureVerificationResult\r\n\r\n// ============================================================================\r\n// Built-in Verification Strategies\r\n// ============================================================================\r\n\r\n/**\r\n * Verify a Stripe-style webhook signature\r\n * Stripe uses: t={timestamp},v1={signature}\r\n */\r\nexport function verifyStripeStyleSignature(\r\n payload: string,\r\n signature: string,\r\n secret: string,\r\n tolerance: number\r\n): SignatureVerificationResult {\r\n try {\r\n const elements = signature.split(',')\r\n const timestamp = elements.find((e) => e.startsWith('t='))?.slice(2)\r\n const sig = elements.find((e) => e.startsWith('v1='))?.slice(3)\r\n\r\n if (!timestamp || !sig) {\r\n return { valid: false, error: 'Invalid signature format' }\r\n }\r\n\r\n const timestampNum = parseInt(timestamp, 10)\r\n const now = Math.floor(Date.now() / 1000)\r\n if (Math.abs(now - timestampNum) > tolerance) {\r\n return { valid: false, error: 'Timestamp outside tolerance' }\r\n }\r\n\r\n const signedPayload = `${timestamp}.${payload}`\r\n const expectedSig = createHmac('sha256', secret)\r\n .update(signedPayload)\r\n .digest('hex')\r\n\r\n const valid = timingSafeEqual(\r\n Buffer.from(sig),\r\n Buffer.from(expectedSig)\r\n )\r\n\r\n return { valid }\r\n } catch (error) {\r\n return {\r\n valid: false,\r\n error: error instanceof Error ? error.message : 'Verification failed',\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Verify an HMAC-SHA256 signature of sorted payload fields\r\n */\r\nexport function verifySortedFieldsHmacSignature(\r\n payload: string,\r\n signature: string,\r\n secret: string,\r\n _tolerance: number\r\n): SignatureVerificationResult {\r\n try {\r\n const data = JSON.parse(payload)\r\n const sortedKeys = Object.keys(data).sort()\r\n const sortedPayload = sortedKeys.map((k) => `${k}=${data[k]}`).join('&')\r\n\r\n const expectedSig = createHmac('sha256', secret)\r\n .update(sortedPayload)\r\n .digest('hex')\r\n\r\n const valid = timingSafeEqual(\r\n Buffer.from(signature.toLowerCase()),\r\n Buffer.from(expectedSig)\r\n )\r\n\r\n return { valid }\r\n } catch (error) {\r\n return {\r\n valid: false,\r\n error: error instanceof Error ? error.message : 'Verification failed',\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Verify a simple HMAC-SHA256 signature of the raw payload\r\n */\r\nexport function verifyHmacSha256Signature(\r\n payload: string,\r\n signature: string,\r\n secret: string,\r\n _tolerance: number\r\n): SignatureVerificationResult {\r\n try {\r\n const expectedSig = createHmac('sha256', secret)\r\n .update(payload)\r\n .digest('hex')\r\n\r\n const valid = timingSafeEqual(\r\n Buffer.from(signature.toLowerCase()),\r\n Buffer.from(expectedSig.toLowerCase())\r\n )\r\n\r\n return { valid }\r\n } catch (error) {\r\n return {\r\n valid: false,\r\n error: error instanceof Error ? error.message : 'Verification failed',\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Verifier Registry\r\n// ============================================================================\r\n\r\nconst verifierRegistry = new Map<string, SignatureVerifier>()\r\n\r\n/**\r\n * Register a custom signature verifier for a provider\r\n */\r\nexport function registerSignatureVerifier(\r\n provider: PaymentProvider,\r\n verifier: SignatureVerifier\r\n): void {\r\n verifierRegistry.set(provider, verifier)\r\n}\r\n\r\n/**\r\n * Get the registered verifier for a provider\r\n */\r\nexport function getSignatureVerifier(provider: PaymentProvider): SignatureVerifier | undefined {\r\n return verifierRegistry.get(provider)\r\n}\r\n\r\n// ============================================================================\r\n// Main Verification Function\r\n// ============================================================================\r\n\r\n/**\r\n * Verify webhook signature based on provider.\r\n * Uses registered verifiers. Falls back to HMAC-SHA256 if no verifier is registered.\r\n */\r\nexport function verifyWebhookSignature(\r\n params: SignatureVerificationParams\r\n): SignatureVerificationResult {\r\n const { provider, payload, signature, secret, tolerance = 300 } = params\r\n\r\n if (!signature || !secret) {\r\n return { valid: false, error: 'Missing signature or secret' }\r\n }\r\n\r\n const verifier = verifierRegistry.get(provider)\r\n if (verifier) {\r\n return verifier(payload, signature, secret, tolerance)\r\n }\r\n\r\n // Default: simple HMAC-SHA256\r\n return verifyHmacSha256Signature(payload, signature, secret, tolerance)\r\n}\r\n\r\n/**\r\n * Get signature header name for a provider.\r\n * Returns a generic default; override per provider if needed.\r\n */\r\nexport function getSignatureHeaderName(provider: PaymentProvider): string {\r\n return `x-${provider}-signature`\r\n}\r\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nehorai/payments",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generic payment orchestration library with circuit breaker and multi-provider routing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./types": {
|
|
15
|
+
"types": "./dist/types/index.d.ts",
|
|
16
|
+
"import": "./dist/types/index.js",
|
|
17
|
+
"require": "./dist/types/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./providers": {
|
|
20
|
+
"types": "./dist/providers/interfaces/index.d.ts",
|
|
21
|
+
"import": "./dist/providers/interfaces/index.js",
|
|
22
|
+
"require": "./dist/providers/interfaces/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./repository": {
|
|
25
|
+
"types": "./dist/repository/interfaces/index.d.ts",
|
|
26
|
+
"import": "./dist/repository/interfaces/index.js",
|
|
27
|
+
"require": "./dist/repository/interfaces/index.cjs"
|
|
28
|
+
},
|
|
29
|
+
"./services": {
|
|
30
|
+
"types": "./dist/services/index.d.ts",
|
|
31
|
+
"import": "./dist/services/index.js",
|
|
32
|
+
"require": "./dist/services/index.cjs"
|
|
33
|
+
},
|
|
34
|
+
"./config": {
|
|
35
|
+
"types": "./dist/config/index.d.ts",
|
|
36
|
+
"import": "./dist/config/index.js",
|
|
37
|
+
"require": "./dist/config/index.cjs"
|
|
38
|
+
},
|
|
39
|
+
"./utils": {
|
|
40
|
+
"types": "./dist/utils/index.d.ts",
|
|
41
|
+
"import": "./dist/utils/index.js",
|
|
42
|
+
"require": "./dist/utils/index.cjs"
|
|
43
|
+
},
|
|
44
|
+
"./factory": {
|
|
45
|
+
"types": "./dist/factory.d.ts",
|
|
46
|
+
"import": "./dist/factory.js",
|
|
47
|
+
"require": "./dist/factory.cjs"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist"
|
|
52
|
+
],
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"author": "Nehorai Hadad",
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "^5.7.3",
|
|
57
|
+
"tsup": "^8.0.0",
|
|
58
|
+
"@types/node": "^22.13.4"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"uuid": "^11.1.0"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsup",
|
|
65
|
+
"dev": "tsup --watch",
|
|
66
|
+
"typecheck": "tsc --noEmit"
|
|
67
|
+
}
|
|
68
|
+
}
|