@duvdu-v1/duvdu 1.1.267 → 1.1.269
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/build/services/paymob.service.d.ts +10 -192
- package/build/services/paymob.service.js +393 -236
- package/build/types/paymob.types.d.ts +222 -0
- package/build/types/paymob.types.js +39 -0
- package/package.json +1 -1
|
@@ -1,203 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
id: number;
|
|
3
|
-
created_at: string;
|
|
4
|
-
phones: string[];
|
|
5
|
-
company_emails: string[];
|
|
6
|
-
company_name: string;
|
|
7
|
-
state: string;
|
|
8
|
-
country: string;
|
|
9
|
-
city: string;
|
|
10
|
-
postal_code: string;
|
|
11
|
-
street: string;
|
|
12
|
-
}
|
|
13
|
-
interface PaymobOrder {
|
|
14
|
-
id: number;
|
|
15
|
-
created_at: string;
|
|
16
|
-
delivery_needed: boolean;
|
|
17
|
-
merchant: PaymobMerchant;
|
|
18
|
-
collector: null;
|
|
19
|
-
amount_cents: number;
|
|
20
|
-
shipping_data: null;
|
|
21
|
-
currency: string;
|
|
22
|
-
is_payment_locked: boolean;
|
|
23
|
-
is_return: boolean;
|
|
24
|
-
is_cancel: boolean;
|
|
25
|
-
is_returned: boolean;
|
|
26
|
-
is_canceled: boolean;
|
|
27
|
-
merchant_order_id: string;
|
|
28
|
-
wallet_notification: null;
|
|
29
|
-
paid_amount_cents: number;
|
|
30
|
-
notify_user_with_email: boolean;
|
|
31
|
-
items: PaymobOrderItem[];
|
|
32
|
-
order_url: string;
|
|
33
|
-
commission_fee: number;
|
|
34
|
-
delivery_fee_cents: number;
|
|
35
|
-
delivery_voucher_cost: number;
|
|
36
|
-
discount: number;
|
|
37
|
-
metadata: Record<string, any>;
|
|
38
|
-
}
|
|
39
|
-
interface PaymobOrderItem {
|
|
40
|
-
name: string;
|
|
41
|
-
description: string;
|
|
42
|
-
amount: number;
|
|
43
|
-
quantity: number;
|
|
44
|
-
}
|
|
45
|
-
interface PaymobSourceData {
|
|
46
|
-
type: string;
|
|
47
|
-
pan: string;
|
|
48
|
-
sub_type: string;
|
|
49
|
-
}
|
|
50
|
-
interface PaymobWebhookData {
|
|
51
|
-
obj: {
|
|
52
|
-
id: number;
|
|
53
|
-
amount_cents: number;
|
|
54
|
-
success: boolean;
|
|
55
|
-
is_refunded: boolean;
|
|
56
|
-
is_captured: boolean;
|
|
57
|
-
is_voided: boolean;
|
|
58
|
-
is_standalone_payment: boolean;
|
|
59
|
-
is_void: boolean;
|
|
60
|
-
is_refund: boolean;
|
|
61
|
-
is_3d_secure: boolean;
|
|
62
|
-
integration_id: number;
|
|
63
|
-
profile_id: number;
|
|
64
|
-
has_parent_transaction: boolean;
|
|
65
|
-
order: PaymobOrder;
|
|
66
|
-
created_at: string;
|
|
67
|
-
transaction_processed_callback_responses: null;
|
|
68
|
-
currency: string;
|
|
69
|
-
source_data: PaymobSourceData;
|
|
70
|
-
api_source: string;
|
|
71
|
-
terminal_id: string;
|
|
72
|
-
merchant_commission: number;
|
|
73
|
-
merchant_staff_tag: null;
|
|
74
|
-
hmac: string;
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
interface PaymobBillingData {
|
|
78
|
-
first_name: string;
|
|
79
|
-
last_name: string;
|
|
80
|
-
email: string;
|
|
81
|
-
phone_number: string;
|
|
82
|
-
apartment: string;
|
|
83
|
-
floor: string;
|
|
84
|
-
street: string;
|
|
85
|
-
building: string;
|
|
86
|
-
city?: string;
|
|
87
|
-
state: string;
|
|
88
|
-
country: string;
|
|
89
|
-
}
|
|
90
|
-
interface TransactionData {
|
|
91
|
-
orderId: number;
|
|
92
|
-
amount: number;
|
|
93
|
-
success: boolean;
|
|
94
|
-
currency: string;
|
|
95
|
-
transactionId: number;
|
|
96
|
-
createdAt: string;
|
|
97
|
-
isRefunded: boolean;
|
|
98
|
-
isCaptured: boolean;
|
|
99
|
-
isVoided: boolean;
|
|
100
|
-
metadata: Record<string, any>;
|
|
101
|
-
}
|
|
102
|
-
interface WebhookQueryTransactionData extends TransactionData {
|
|
103
|
-
isAuth: boolean;
|
|
104
|
-
isStandalone: boolean;
|
|
105
|
-
is3dSecure: boolean;
|
|
106
|
-
sourceData: {
|
|
107
|
-
type: string;
|
|
108
|
-
pan: string;
|
|
109
|
-
subType: string;
|
|
110
|
-
};
|
|
111
|
-
responseCode: string;
|
|
112
|
-
message: string;
|
|
113
|
-
}
|
|
114
|
-
interface WebhookQueryWithItemsTransactionData extends WebhookQueryTransactionData {
|
|
115
|
-
items: PaymobOrderItem[];
|
|
116
|
-
}
|
|
117
|
-
interface WebhookResult<T> {
|
|
118
|
-
isValid: boolean;
|
|
119
|
-
transactionData: T | null;
|
|
120
|
-
}
|
|
121
|
-
interface TransactionStatusResult {
|
|
122
|
-
success: boolean;
|
|
123
|
-
status: string;
|
|
124
|
-
amount: number;
|
|
125
|
-
currency: string;
|
|
126
|
-
}
|
|
127
|
-
interface OrderDetailsResult {
|
|
128
|
-
id: number;
|
|
129
|
-
amount_cents: number;
|
|
130
|
-
currency: string;
|
|
131
|
-
items: PaymobOrderItem[];
|
|
132
|
-
created_at: string;
|
|
133
|
-
merchant_order_id: string;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Paymob Service Configuration for Flash Integration
|
|
137
|
-
*
|
|
138
|
-
* Required Keys:
|
|
139
|
-
* - secretKey: Your Paymob Secret Key (for API authentication)
|
|
140
|
-
* - publicKey: Your Paymob Public Key (for client-side)
|
|
141
|
-
* - integrationId: Your Paymob Integration ID
|
|
142
|
-
* - hmacSecret: Your Paymob HMAC Secret (for webhook verification)
|
|
143
|
-
*/
|
|
1
|
+
import { PaymobWebhookData, WebhookResult, TransactionData, WebhookQueryTransactionData, OrderDetailsResult, TransactionStatusResult, PaymentIntentionResult, UserPaymentData, PaymobBillingData, PaymobOrderItem, WebhookQueryWithItemsTransactionData } from '../types/paymob.types';
|
|
144
2
|
export declare class PaymobService {
|
|
145
|
-
private readonly
|
|
146
|
-
private readonly
|
|
147
|
-
private readonly
|
|
148
|
-
private readonly
|
|
149
|
-
private readonly
|
|
3
|
+
private readonly config;
|
|
4
|
+
private readonly auth;
|
|
5
|
+
private readonly webhookHandler;
|
|
6
|
+
private readonly orderManager;
|
|
7
|
+
private readonly paymentProcessor;
|
|
150
8
|
constructor();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
* @param amount The payment amount
|
|
154
|
-
* @param billingData The user data for billing
|
|
155
|
-
* @param items The order items
|
|
156
|
-
* @param currency The currency code (default: EGP)
|
|
157
|
-
* @param extras Custom metadata
|
|
158
|
-
* @returns The payment URL and client secret
|
|
159
|
-
*/
|
|
160
|
-
createPaymentIntention(amount: number, billingData: PaymobBillingData, items: PaymobOrderItem[], currency?: string, extras?: Record<string, any>): Promise<{
|
|
161
|
-
paymentUrl: string;
|
|
162
|
-
clientSecret: string;
|
|
163
|
-
}>;
|
|
164
|
-
/**
|
|
165
|
-
* Creates a payment URL with user data and custom metadata
|
|
166
|
-
* @param amount The payment amount
|
|
167
|
-
* @param userId The user ID
|
|
168
|
-
* @param contractId The contract ID
|
|
169
|
-
* @param userData The user data for billing
|
|
170
|
-
* @param serviceType The service type identifier
|
|
171
|
-
* @returns The payment URL and related data
|
|
172
|
-
*/
|
|
173
|
-
createPaymentUrlWithUserData(amount: number, userId: string, contractId: string, userData: {
|
|
174
|
-
firstName: string;
|
|
175
|
-
lastName: string;
|
|
176
|
-
email: string;
|
|
177
|
-
phone: string;
|
|
178
|
-
}, serviceType: string): Promise<{
|
|
9
|
+
createPaymentIntention(amount: number, billingData: PaymobBillingData, items: PaymobOrderItem[], currency?: string, extras?: Record<string, any>): Promise<PaymentIntentionResult>;
|
|
10
|
+
createPaymentUrlWithUserData(amount: number, userId: string, contractId: string, userData: UserPaymentData, serviceType: string): Promise<{
|
|
179
11
|
paymentUrl: string;
|
|
180
12
|
}>;
|
|
181
13
|
verifyPayment(hmac: string, data: Record<string, any>): Promise<boolean>;
|
|
182
|
-
/**
|
|
183
|
-
* Verify webhook HMAC signature for query parameters
|
|
184
|
-
* Uses the correct field order as per Paymob documentation
|
|
185
|
-
*/
|
|
186
14
|
verifyWebhookHmac(queryParams: Record<string, string>): boolean;
|
|
187
15
|
handleWebhook(webhookData: PaymobWebhookData): Promise<WebhookResult<TransactionData>>;
|
|
188
|
-
/**
|
|
189
|
-
* Handle webhook with query parameters (GET request)
|
|
190
|
-
* This is for webhooks that come as URL query parameters instead of JSON body
|
|
191
|
-
*/
|
|
192
16
|
handleWebhookQuery(queryParams: Record<string, string>): WebhookResult<WebhookQueryTransactionData>;
|
|
17
|
+
handleWebhookQueryWithItems(queryParams: Record<string, string>): Promise<WebhookResult<WebhookQueryWithItemsTransactionData>>;
|
|
193
18
|
getTransactionStatus(transactionId: number): Promise<TransactionStatusResult>;
|
|
194
|
-
/**
|
|
195
|
-
* Get order details including metadata
|
|
196
|
-
*/
|
|
197
19
|
getOrderDetails(orderId: number): Promise<OrderDetailsResult>;
|
|
198
|
-
|
|
199
|
-
* Handle webhook query and fetch items from order
|
|
200
|
-
*/
|
|
201
|
-
handleWebhookQueryWithItems(queryParams: Record<string, string>): Promise<WebhookResult<WebhookQueryWithItemsTransactionData>>;
|
|
20
|
+
getAuthToken(): Promise<string>;
|
|
202
21
|
}
|
|
203
|
-
export {};
|
|
@@ -38,171 +38,108 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
38
38
|
exports.PaymobService = void 0;
|
|
39
39
|
const crypto = __importStar(require("crypto"));
|
|
40
40
|
const axios_1 = __importDefault(require("axios"));
|
|
41
|
-
|
|
42
|
-
* Paymob Service Configuration for Flash Integration
|
|
43
|
-
*
|
|
44
|
-
* Required Keys:
|
|
45
|
-
* - secretKey: Your Paymob Secret Key (for API authentication)
|
|
46
|
-
* - publicKey: Your Paymob Public Key (for client-side)
|
|
47
|
-
* - integrationId: Your Paymob Integration ID
|
|
48
|
-
* - hmacSecret: Your Paymob HMAC Secret (for webhook verification)
|
|
49
|
-
*/
|
|
50
|
-
class PaymobService {
|
|
41
|
+
class PaymobConfig {
|
|
51
42
|
constructor() {
|
|
43
|
+
this.validateEnvironmentVariables();
|
|
44
|
+
this.apiKey = process.env.PAYMOB_API_KEY;
|
|
52
45
|
this.secretKey = process.env.PAYMOB_SECRET_KEY;
|
|
53
46
|
this.publicKey = process.env.PAYMOB_PUBLIC_KEY;
|
|
54
47
|
this.integrationId = parseInt(process.env.PAYMOB_INTEGRATION_ID);
|
|
55
48
|
this.baseUrl = process.env.PAYMOB_BASE_URL || 'https://accept.paymob.com';
|
|
56
49
|
this.hmacSecret = process.env.PAYMOB_HMAC_SECRET;
|
|
57
|
-
|
|
50
|
+
this.logConfiguration();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validates that all required environment variables are present
|
|
54
|
+
*/
|
|
55
|
+
validateEnvironmentVariables() {
|
|
56
|
+
const requiredVars = [
|
|
57
|
+
'PAYMOB_API_KEY',
|
|
58
|
+
'PAYMOB_SECRET_KEY',
|
|
59
|
+
'PAYMOB_PUBLIC_KEY',
|
|
60
|
+
'PAYMOB_INTEGRATION_ID',
|
|
61
|
+
'PAYMOB_HMAC_SECRET',
|
|
62
|
+
];
|
|
63
|
+
const missingVars = requiredVars.filter((varName) => !process.env[varName]);
|
|
64
|
+
if (missingVars.length > 0) {
|
|
65
|
+
throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Logs configuration details (without sensitive data)
|
|
70
|
+
*/
|
|
71
|
+
logConfiguration() {
|
|
72
|
+
console.log('PayMob configuration initialized:', {
|
|
58
73
|
integrationId: this.integrationId,
|
|
59
74
|
publicKey: this.publicKey,
|
|
60
75
|
baseUrl: this.baseUrl,
|
|
61
76
|
});
|
|
62
77
|
}
|
|
78
|
+
}
|
|
79
|
+
// ===========================
|
|
80
|
+
// AUTHENTICATION CLASS
|
|
81
|
+
// ===========================
|
|
82
|
+
/**
|
|
83
|
+
* Handles Paymob authentication operations
|
|
84
|
+
*/
|
|
85
|
+
class PaymobAuth {
|
|
86
|
+
constructor(config) {
|
|
87
|
+
this.config = config;
|
|
88
|
+
}
|
|
63
89
|
/**
|
|
64
|
-
*
|
|
65
|
-
* @
|
|
66
|
-
* @param billingData The user data for billing
|
|
67
|
-
* @param items The order items
|
|
68
|
-
* @param currency The currency code (default: EGP)
|
|
69
|
-
* @param extras Custom metadata
|
|
70
|
-
* @returns The payment URL and client secret
|
|
90
|
+
* Authenticate with Paymob to get Bearer token
|
|
91
|
+
* @returns Promise<string> Authentication token
|
|
71
92
|
*/
|
|
72
|
-
|
|
73
|
-
var _a;
|
|
93
|
+
getAuthToken() {
|
|
74
94
|
return __awaiter(this, void 0, void 0, function* () {
|
|
75
95
|
try {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
currency,
|
|
79
|
-
payment_methods: [this.integrationId, 'card'],
|
|
80
|
-
items,
|
|
81
|
-
billing_data: billingData,
|
|
82
|
-
customer: {
|
|
83
|
-
first_name: billingData.first_name,
|
|
84
|
-
last_name: billingData.last_name,
|
|
85
|
-
email: billingData.email,
|
|
86
|
-
extras: extras || {},
|
|
87
|
-
},
|
|
88
|
-
extras: extras || {},
|
|
89
|
-
};
|
|
90
|
-
const response = yield axios_1.default.post(`${this.baseUrl}/v1/intention/`, intentionData, {
|
|
91
|
-
headers: {
|
|
92
|
-
'Authorization': `Token ${this.secretKey}`,
|
|
93
|
-
'Content-Type': 'application/json',
|
|
94
|
-
},
|
|
96
|
+
const response = yield axios_1.default.post(`${this.config.baseUrl}/api/auth/tokens`, {
|
|
97
|
+
api_key: this.config.apiKey,
|
|
95
98
|
});
|
|
96
|
-
|
|
97
|
-
const paymentUrl = `${this.baseUrl}/unifiedcheckout/?publicKey=${this.publicKey}&clientSecret=${response.data.client_secret}`;
|
|
98
|
-
return {
|
|
99
|
-
paymentUrl,
|
|
100
|
-
clientSecret: response.data.client_secret,
|
|
101
|
-
};
|
|
99
|
+
return response.data.token;
|
|
102
100
|
}
|
|
103
101
|
catch (error) {
|
|
104
102
|
const axiosError = error;
|
|
105
|
-
|
|
106
|
-
throw new Error(`Failed to create Paymob payment intention: ${axiosError.message}`);
|
|
103
|
+
throw new Error(`Failed to get Paymob auth token: ${axiosError.message}`);
|
|
107
104
|
}
|
|
108
105
|
});
|
|
109
106
|
}
|
|
110
107
|
/**
|
|
111
|
-
*
|
|
112
|
-
* @
|
|
113
|
-
* @param userId The user ID
|
|
114
|
-
* @param contractId The contract ID
|
|
115
|
-
* @param userData The user data for billing
|
|
116
|
-
* @param serviceType The service type identifier
|
|
117
|
-
* @returns The payment URL and related data
|
|
108
|
+
* Get authorization headers for API requests
|
|
109
|
+
* @returns Promise<Record<string, string>> Headers object
|
|
118
110
|
*/
|
|
119
|
-
|
|
120
|
-
var _a;
|
|
111
|
+
getAuthHeaders() {
|
|
121
112
|
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
userId,
|
|
126
|
-
service_type: serviceType,
|
|
127
|
-
booking_id: 'BOOK_' + Date.now(),
|
|
128
|
-
timestamp: new Date().toISOString(),
|
|
129
|
-
};
|
|
130
|
-
const extras = customData;
|
|
131
|
-
const billingData = {
|
|
132
|
-
first_name: userData.firstName,
|
|
133
|
-
last_name: userData.lastName,
|
|
134
|
-
email: userData.email,
|
|
135
|
-
phone_number: userData.phone,
|
|
136
|
-
apartment: '123',
|
|
137
|
-
floor: '1',
|
|
138
|
-
street: '123 Main St',
|
|
139
|
-
building: '123',
|
|
140
|
-
state: 'Cairo',
|
|
141
|
-
country: 'EGY',
|
|
113
|
+
return {
|
|
114
|
+
Authorization: `Token ${this.config.secretKey}`,
|
|
115
|
+
'Content-Type': 'application/json',
|
|
142
116
|
};
|
|
143
|
-
const items = [
|
|
144
|
-
{
|
|
145
|
-
name: `${serviceType} Payment`,
|
|
146
|
-
description: `Payment for contract ${contractId}`,
|
|
147
|
-
amount,
|
|
148
|
-
quantity: 1,
|
|
149
|
-
},
|
|
150
|
-
];
|
|
151
|
-
// For Flash Integration, we need to create a modified intention request
|
|
152
|
-
// that includes merchant_order_id
|
|
153
|
-
const intentionData = {
|
|
154
|
-
amount,
|
|
155
|
-
currency: 'EGP',
|
|
156
|
-
payment_methods: [this.integrationId, 'card'],
|
|
157
|
-
items,
|
|
158
|
-
billing_data: billingData,
|
|
159
|
-
customer: {
|
|
160
|
-
first_name: billingData.first_name,
|
|
161
|
-
last_name: billingData.last_name,
|
|
162
|
-
email: billingData.email,
|
|
163
|
-
extras: extras || {},
|
|
164
|
-
},
|
|
165
|
-
extras: extras || {},
|
|
166
|
-
merchant_order_id: JSON.stringify(customData), // Store custom data here
|
|
167
|
-
};
|
|
168
|
-
try {
|
|
169
|
-
const response = yield axios_1.default.post(`${this.baseUrl}/v1/intention/`, intentionData, {
|
|
170
|
-
headers: {
|
|
171
|
-
'Authorization': `Token ${this.secretKey}`,
|
|
172
|
-
'Content-Type': 'application/json',
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
// Create the payment URL for Flash Checkout
|
|
176
|
-
const paymentUrl = `${this.baseUrl}/unifiedcheckout/?publicKey=${this.publicKey}&clientSecret=${response.data.client_secret}`;
|
|
177
|
-
return { paymentUrl };
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
const axiosError = error;
|
|
181
|
-
console.log('PayMob intention error:', (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data);
|
|
182
|
-
throw new Error(`Failed to create Paymob payment intention: ${axiosError.message}`);
|
|
183
|
-
}
|
|
184
117
|
});
|
|
185
118
|
}
|
|
186
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Get bearer token headers for API requests
|
|
121
|
+
* @returns Promise<Record<string, string>> Headers object with bearer token
|
|
122
|
+
*/
|
|
123
|
+
getBearerHeaders() {
|
|
187
124
|
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
.createHmac('sha512', this.secretKey)
|
|
194
|
-
.update(concatenatedString)
|
|
195
|
-
.digest('hex');
|
|
196
|
-
return calculatedHmac === hmac;
|
|
125
|
+
const authToken = yield this.getAuthToken();
|
|
126
|
+
return {
|
|
127
|
+
'Content-Type': 'application/json',
|
|
128
|
+
Authorization: `Bearer ${authToken}`,
|
|
129
|
+
};
|
|
197
130
|
});
|
|
198
131
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
132
|
+
}
|
|
133
|
+
// ===========================
|
|
134
|
+
// WEBHOOK HANDLER CLASS
|
|
135
|
+
// ===========================
|
|
136
|
+
/**
|
|
137
|
+
* Handles Paymob webhook operations and verification
|
|
138
|
+
*/
|
|
139
|
+
class PaymobWebhookHandler {
|
|
140
|
+
constructor(config) {
|
|
141
|
+
this.config = config;
|
|
142
|
+
this.WEBHOOK_FIELD_ORDER = [
|
|
206
143
|
'amount_cents',
|
|
207
144
|
'created_at',
|
|
208
145
|
'currency',
|
|
@@ -224,36 +161,53 @@ class PaymobService {
|
|
|
224
161
|
'source_data.type',
|
|
225
162
|
'success',
|
|
226
163
|
];
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Verify payment using HMAC signature
|
|
167
|
+
* @param hmac HMAC signature to verify
|
|
168
|
+
* @param data Payment data to verify
|
|
169
|
+
* @returns Promise<boolean> Verification result
|
|
170
|
+
*/
|
|
171
|
+
verifyPayment(hmac, data) {
|
|
172
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
const concatenatedString = Object.entries(data)
|
|
174
|
+
.sort()
|
|
175
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
176
|
+
.join('');
|
|
177
|
+
const calculatedHmac = crypto
|
|
178
|
+
.createHmac('sha512', this.config.secretKey)
|
|
179
|
+
.update(concatenatedString)
|
|
180
|
+
.digest('hex');
|
|
181
|
+
return calculatedHmac === hmac;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Verify webhook HMAC signature for query parameters
|
|
186
|
+
* Uses the correct field order as per Paymob documentation
|
|
187
|
+
* @param queryParams Query parameters from webhook
|
|
188
|
+
* @returns boolean Verification result
|
|
189
|
+
*/
|
|
190
|
+
verifyWebhookHmac(queryParams) {
|
|
191
|
+
const concatenatedString = this.WEBHOOK_FIELD_ORDER.map((field) => queryParams[field] || '').join('');
|
|
230
192
|
const calculatedHmac = crypto
|
|
231
|
-
.createHmac('sha512', this.hmacSecret)
|
|
193
|
+
.createHmac('sha512', this.config.hmacSecret)
|
|
232
194
|
.update(concatenatedString)
|
|
233
195
|
.digest('hex');
|
|
234
196
|
return calculatedHmac === queryParams.hmac;
|
|
235
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Handle webhook with JSON data (POST request)
|
|
200
|
+
* @param webhookData Webhook data from Paymob
|
|
201
|
+
* @returns Promise<WebhookResult<TransactionData>> Webhook processing result
|
|
202
|
+
*/
|
|
236
203
|
handleWebhook(webhookData) {
|
|
237
204
|
return __awaiter(this, void 0, void 0, function* () {
|
|
238
205
|
try {
|
|
239
|
-
// Verify the webhook signature
|
|
240
206
|
const isValid = yield this.verifyPayment(webhookData.obj.hmac, webhookData.obj);
|
|
241
207
|
if (!isValid) {
|
|
242
208
|
return { isValid: false, transactionData: null };
|
|
243
209
|
}
|
|
244
|
-
|
|
245
|
-
const transactionData = {
|
|
246
|
-
orderId: webhookData.obj.order.id,
|
|
247
|
-
amount: webhookData.obj.amount_cents / 100, // Convert from cents to actual currency
|
|
248
|
-
success: webhookData.obj.success,
|
|
249
|
-
currency: webhookData.obj.currency,
|
|
250
|
-
transactionId: webhookData.obj.id,
|
|
251
|
-
createdAt: webhookData.obj.created_at,
|
|
252
|
-
isRefunded: webhookData.obj.is_refunded,
|
|
253
|
-
isCaptured: webhookData.obj.is_captured,
|
|
254
|
-
isVoided: webhookData.obj.is_voided,
|
|
255
|
-
metadata: webhookData.obj.order.metadata || {},
|
|
256
|
-
};
|
|
210
|
+
const transactionData = this.extractTransactionData(webhookData);
|
|
257
211
|
return { isValid: true, transactionData };
|
|
258
212
|
}
|
|
259
213
|
catch (error) {
|
|
@@ -264,38 +218,16 @@ class PaymobService {
|
|
|
264
218
|
}
|
|
265
219
|
/**
|
|
266
220
|
* Handle webhook with query parameters (GET request)
|
|
267
|
-
*
|
|
221
|
+
* @param queryParams Query parameters from webhook URL
|
|
222
|
+
* @returns WebhookResult<WebhookQueryTransactionData> Webhook processing result
|
|
268
223
|
*/
|
|
269
224
|
handleWebhookQuery(queryParams) {
|
|
270
225
|
try {
|
|
271
|
-
// Verify the webhook HMAC signature
|
|
272
226
|
const isValid = this.verifyWebhookHmac(queryParams);
|
|
273
227
|
if (!isValid) {
|
|
274
228
|
return { isValid: false, transactionData: null };
|
|
275
229
|
}
|
|
276
|
-
|
|
277
|
-
const transactionData = {
|
|
278
|
-
orderId: parseInt(queryParams.order || '0'),
|
|
279
|
-
amount: parseInt(queryParams.amount_cents || '0') / 100, // Convert from cents
|
|
280
|
-
success: queryParams.success === 'true',
|
|
281
|
-
currency: queryParams.currency || 'EGP',
|
|
282
|
-
transactionId: parseInt(queryParams.id || '0'),
|
|
283
|
-
createdAt: queryParams.created_at || '',
|
|
284
|
-
isRefunded: queryParams.is_refunded === 'true',
|
|
285
|
-
isCaptured: queryParams.is_capture === 'true',
|
|
286
|
-
isVoided: queryParams.is_voided === 'true',
|
|
287
|
-
isAuth: queryParams.is_auth === 'true',
|
|
288
|
-
isStandalone: queryParams.is_standalone_payment === 'true',
|
|
289
|
-
is3dSecure: queryParams.is_3d_secure === 'true',
|
|
290
|
-
sourceData: {
|
|
291
|
-
type: queryParams['source_data.type'] || '',
|
|
292
|
-
pan: queryParams['source_data.pan'] || '',
|
|
293
|
-
subType: queryParams['source_data.sub_type'] || '',
|
|
294
|
-
},
|
|
295
|
-
responseCode: queryParams.txn_response_code || '',
|
|
296
|
-
message: queryParams['data.message'] || '',
|
|
297
|
-
metadata: {}, // Adding the missing metadata property
|
|
298
|
-
};
|
|
230
|
+
const transactionData = this.extractQueryTransactionData(queryParams);
|
|
299
231
|
return { isValid: true, transactionData };
|
|
300
232
|
}
|
|
301
233
|
catch (error) {
|
|
@@ -303,15 +235,99 @@ class PaymobService {
|
|
|
303
235
|
throw new Error(`Failed to handle webhook query: ${errorMessage}`);
|
|
304
236
|
}
|
|
305
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Extract transaction data from webhook JSON data
|
|
240
|
+
* @private
|
|
241
|
+
*/
|
|
242
|
+
extractTransactionData(webhookData) {
|
|
243
|
+
return {
|
|
244
|
+
orderId: webhookData.obj.order.id,
|
|
245
|
+
amount: webhookData.obj.amount_cents / 100,
|
|
246
|
+
success: webhookData.obj.success,
|
|
247
|
+
currency: webhookData.obj.currency,
|
|
248
|
+
transactionId: webhookData.obj.id,
|
|
249
|
+
createdAt: webhookData.obj.created_at,
|
|
250
|
+
isRefunded: webhookData.obj.is_refunded,
|
|
251
|
+
isCaptured: webhookData.obj.is_captured,
|
|
252
|
+
isVoided: webhookData.obj.is_voided,
|
|
253
|
+
metadata: webhookData.obj.order.metadata || {},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Extract transaction data from webhook query parameters
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
extractQueryTransactionData(queryParams) {
|
|
261
|
+
return {
|
|
262
|
+
orderId: parseInt(queryParams.order || '0'),
|
|
263
|
+
amount: parseInt(queryParams.amount_cents || '0') / 100,
|
|
264
|
+
success: queryParams.success === 'true',
|
|
265
|
+
currency: queryParams.currency || 'EGP',
|
|
266
|
+
transactionId: parseInt(queryParams.id || '0'),
|
|
267
|
+
createdAt: queryParams.created_at || '',
|
|
268
|
+
isRefunded: queryParams.is_refunded === 'true',
|
|
269
|
+
isCaptured: queryParams.is_capture === 'true',
|
|
270
|
+
isVoided: queryParams.is_voided === 'true',
|
|
271
|
+
isAuth: queryParams.is_auth === 'true',
|
|
272
|
+
isStandalone: queryParams.is_standalone_payment === 'true',
|
|
273
|
+
is3dSecure: queryParams.is_3d_secure === 'true',
|
|
274
|
+
sourceData: {
|
|
275
|
+
type: queryParams['source_data.type'] || '',
|
|
276
|
+
pan: queryParams['source_data.pan'] || '',
|
|
277
|
+
subType: queryParams['source_data.sub_type'] || '',
|
|
278
|
+
},
|
|
279
|
+
responseCode: queryParams.txn_response_code || '',
|
|
280
|
+
message: queryParams['data.message'] || '',
|
|
281
|
+
metadata: {},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// ===========================
|
|
286
|
+
// ORDER MANAGER CLASS
|
|
287
|
+
// ===========================
|
|
288
|
+
/**
|
|
289
|
+
* Handles Paymob order operations
|
|
290
|
+
*/
|
|
291
|
+
class PaymobOrderManager {
|
|
292
|
+
constructor(config, auth) {
|
|
293
|
+
this.config = config;
|
|
294
|
+
this.auth = auth;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get order details including metadata
|
|
298
|
+
* @param orderId Order ID to fetch details for
|
|
299
|
+
* @returns Promise<OrderDetailsResult> Order details
|
|
300
|
+
*/
|
|
301
|
+
getOrderDetails(orderId) {
|
|
302
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
303
|
+
try {
|
|
304
|
+
const headers = yield this.auth.getBearerHeaders();
|
|
305
|
+
const response = yield axios_1.default.get(`${this.config.baseUrl}/api/ecommerce/orders/${orderId}`, { headers });
|
|
306
|
+
return {
|
|
307
|
+
id: response.data.id,
|
|
308
|
+
amount_cents: response.data.amount_cents,
|
|
309
|
+
currency: response.data.currency,
|
|
310
|
+
items: response.data.items || [],
|
|
311
|
+
created_at: response.data.created_at,
|
|
312
|
+
merchant_order_id: response.data.merchant_order_id,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const axiosError = error;
|
|
317
|
+
throw new Error(`Failed to get order details: ${axiosError.message}`);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Get transaction status by transaction ID
|
|
323
|
+
* @param transactionId Transaction ID to check status for
|
|
324
|
+
* @returns Promise<TransactionStatusResult> Transaction status
|
|
325
|
+
*/
|
|
306
326
|
getTransactionStatus(transactionId) {
|
|
307
327
|
return __awaiter(this, void 0, void 0, function* () {
|
|
308
328
|
try {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
'Content-Type': 'application/json',
|
|
312
|
-
'Authorization': `Token ${this.secretKey}`,
|
|
313
|
-
},
|
|
314
|
-
});
|
|
329
|
+
const headers = yield this.auth.getAuthHeaders();
|
|
330
|
+
const response = yield axios_1.default.get(`${this.config.baseUrl}/api/acceptance/transactions/${transactionId}`, { headers });
|
|
315
331
|
return {
|
|
316
332
|
success: response.data.success,
|
|
317
333
|
status: response.data.status,
|
|
@@ -325,77 +341,203 @@ class PaymobService {
|
|
|
325
341
|
}
|
|
326
342
|
});
|
|
327
343
|
}
|
|
344
|
+
}
|
|
345
|
+
// ===========================
|
|
346
|
+
// PAYMENT PROCESSOR CLASS
|
|
347
|
+
// ===========================
|
|
348
|
+
/**
|
|
349
|
+
* Handles Paymob payment processing operations
|
|
350
|
+
*/
|
|
351
|
+
class PaymobPaymentProcessor {
|
|
352
|
+
constructor(config, auth) {
|
|
353
|
+
this.config = config;
|
|
354
|
+
this.auth = auth;
|
|
355
|
+
}
|
|
328
356
|
/**
|
|
329
|
-
*
|
|
357
|
+
* Create payment intention with Paymob
|
|
358
|
+
* @param amount Payment amount
|
|
359
|
+
* @param billingData Customer billing information
|
|
360
|
+
* @param items Order items
|
|
361
|
+
* @param currency Payment currency (default: EGP)
|
|
362
|
+
* @param extras Additional metadata
|
|
363
|
+
* @returns Promise<PaymentIntentionResult> Payment URL and client secret
|
|
330
364
|
*/
|
|
331
|
-
|
|
332
|
-
var _a, _b, _c;
|
|
365
|
+
createPaymentIntention(amount, billingData, items, currency = 'EGP', extras) {
|
|
333
366
|
return __awaiter(this, void 0, void 0, function* () {
|
|
334
367
|
try {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
headers: {
|
|
340
|
-
'Content-Type': 'application/json',
|
|
341
|
-
'Authorization': `Token ${this.secretKey}`,
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
catch (flashError) {
|
|
346
|
-
console.log('Flash API failed, trying legacy API...');
|
|
347
|
-
// Fallback to legacy API endpoint
|
|
348
|
-
response = yield axios_1.default.get(`${this.baseUrl}/api/ecommerce/orders/${orderId}`, {
|
|
349
|
-
headers: {
|
|
350
|
-
'Content-Type': 'application/json',
|
|
351
|
-
'Authorization': `Token ${this.secretKey}`,
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
return {
|
|
356
|
-
id: response.data.id,
|
|
357
|
-
amount_cents: response.data.amount_cents,
|
|
358
|
-
currency: response.data.currency,
|
|
359
|
-
items: response.data.items || [],
|
|
360
|
-
created_at: response.data.created_at,
|
|
361
|
-
merchant_order_id: response.data.merchant_order_id,
|
|
362
|
-
};
|
|
368
|
+
const intentionData = this.buildIntentionRequest(amount, billingData, items, currency, extras);
|
|
369
|
+
const headers = yield this.auth.getAuthHeaders();
|
|
370
|
+
const response = yield axios_1.default.post(`${this.config.baseUrl}/v1/intention/`, intentionData, { headers });
|
|
371
|
+
return this.buildPaymentResult(response.data.client_secret);
|
|
363
372
|
}
|
|
364
373
|
catch (error) {
|
|
365
374
|
const axiosError = error;
|
|
366
|
-
console.
|
|
367
|
-
console.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
375
|
+
console.log('=======================');
|
|
376
|
+
console.log('axiosError', error);
|
|
377
|
+
console.log('=======================');
|
|
378
|
+
throw new Error(`Failed to create Paymob payment intention: ${axiosError.message}`);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Create payment URL with user data for contracts
|
|
384
|
+
* @param amount Payment amount
|
|
385
|
+
* @param userId User ID
|
|
386
|
+
* @param contractId Contract ID
|
|
387
|
+
* @param userData User data for billing
|
|
388
|
+
* @param serviceType Type of service being paid for
|
|
389
|
+
* @returns Promise<{paymentUrl: string}> Payment URL
|
|
390
|
+
*/
|
|
391
|
+
createPaymentUrlWithUserData(amount, userId, contractId, userData, serviceType) {
|
|
392
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
393
|
+
const customData = this.buildCustomMetadata(contractId, userId, serviceType);
|
|
394
|
+
const billingData = this.buildBillingDataFromUser(userData);
|
|
395
|
+
const items = this.buildOrderItems(userId, contractId, serviceType, amount);
|
|
396
|
+
try {
|
|
397
|
+
const intentionData = this.buildIntentionRequest(amount, billingData, items, 'EGP', customData);
|
|
398
|
+
console.log('=======================');
|
|
399
|
+
console.log('intentionData', intentionData);
|
|
400
|
+
console.log('=======================');
|
|
401
|
+
const headers = yield this.auth.getAuthHeaders();
|
|
402
|
+
const response = yield axios_1.default.post(`${this.config.baseUrl}/v1/intention/`, intentionData, { headers });
|
|
403
|
+
const paymentUrl = this.buildPaymentUrl(response.data.client_secret);
|
|
404
|
+
return { paymentUrl };
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
const axiosError = error;
|
|
408
|
+
console.log('=======================');
|
|
409
|
+
console.log('axiosError', error);
|
|
410
|
+
console.log('=======================');
|
|
411
|
+
throw new Error(`Failed to create Paymob payment intention: ${axiosError.message}`);
|
|
382
412
|
}
|
|
383
413
|
});
|
|
384
414
|
}
|
|
385
415
|
/**
|
|
386
|
-
*
|
|
416
|
+
* Build payment intention request data
|
|
417
|
+
* @private
|
|
418
|
+
*/
|
|
419
|
+
buildIntentionRequest(amount, billingData, items, currency, extras) {
|
|
420
|
+
const amountInCents = amount * 100;
|
|
421
|
+
return {
|
|
422
|
+
amount: amountInCents,
|
|
423
|
+
currency,
|
|
424
|
+
payment_methods: [this.config.integrationId, 'card'],
|
|
425
|
+
items,
|
|
426
|
+
billing_data: billingData,
|
|
427
|
+
customer: {
|
|
428
|
+
first_name: billingData.first_name,
|
|
429
|
+
last_name: billingData.last_name,
|
|
430
|
+
email: billingData.email,
|
|
431
|
+
extras: extras || {},
|
|
432
|
+
},
|
|
433
|
+
extras: extras || {},
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Build custom metadata for contract payments
|
|
438
|
+
* @private
|
|
387
439
|
*/
|
|
440
|
+
buildCustomMetadata(contractId, userId, serviceType) {
|
|
441
|
+
return {
|
|
442
|
+
contractId,
|
|
443
|
+
userId,
|
|
444
|
+
service_type: serviceType,
|
|
445
|
+
booking_id: 'BOOK_' + Date.now(),
|
|
446
|
+
timestamp: new Date().toISOString(),
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Build billing data from user data
|
|
451
|
+
* @private
|
|
452
|
+
*/
|
|
453
|
+
buildBillingDataFromUser(userData) {
|
|
454
|
+
return {
|
|
455
|
+
first_name: userData.firstName,
|
|
456
|
+
last_name: userData.lastName,
|
|
457
|
+
email: userData.email,
|
|
458
|
+
phone_number: userData.phone,
|
|
459
|
+
apartment: '123',
|
|
460
|
+
floor: '1',
|
|
461
|
+
street: '123 Main St',
|
|
462
|
+
building: '123',
|
|
463
|
+
state: 'Cairo',
|
|
464
|
+
country: 'EGY',
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Build order items for contract payment
|
|
469
|
+
* @private
|
|
470
|
+
*/
|
|
471
|
+
buildOrderItems(userId, contractId, serviceType, amount) {
|
|
472
|
+
return [
|
|
473
|
+
{
|
|
474
|
+
name: `${userId}-${contractId}`,
|
|
475
|
+
description: serviceType,
|
|
476
|
+
amount: amount * 100,
|
|
477
|
+
quantity: 1,
|
|
478
|
+
},
|
|
479
|
+
];
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Build payment URL from client secret
|
|
483
|
+
* @private
|
|
484
|
+
*/
|
|
485
|
+
buildPaymentUrl(clientSecret) {
|
|
486
|
+
return `${this.config.baseUrl}/unifiedcheckout/?publicKey=${this.config.publicKey}&clientSecret=${clientSecret}`;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Build payment result with URL and client secret
|
|
490
|
+
* @private
|
|
491
|
+
*/
|
|
492
|
+
buildPaymentResult(clientSecret) {
|
|
493
|
+
return {
|
|
494
|
+
paymentUrl: this.buildPaymentUrl(clientSecret),
|
|
495
|
+
clientSecret,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
class PaymobService {
|
|
500
|
+
constructor() {
|
|
501
|
+
this.config = new PaymobConfig();
|
|
502
|
+
this.auth = new PaymobAuth(this.config);
|
|
503
|
+
this.webhookHandler = new PaymobWebhookHandler(this.config);
|
|
504
|
+
this.orderManager = new PaymobOrderManager(this.config, this.auth);
|
|
505
|
+
this.paymentProcessor = new PaymobPaymentProcessor(this.config, this.auth);
|
|
506
|
+
}
|
|
507
|
+
createPaymentIntention(amount, billingData, items, currency = 'EGP', extras) {
|
|
508
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
509
|
+
return this.paymentProcessor.createPaymentIntention(amount, billingData, items, currency, extras);
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
createPaymentUrlWithUserData(amount, userId, contractId, userData, serviceType) {
|
|
513
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
514
|
+
return this.paymentProcessor.createPaymentUrlWithUserData(amount, userId, contractId, userData, serviceType);
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
verifyPayment(hmac, data) {
|
|
518
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
519
|
+
return this.webhookHandler.verifyPayment(hmac, data);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
verifyWebhookHmac(queryParams) {
|
|
523
|
+
return this.webhookHandler.verifyWebhookHmac(queryParams);
|
|
524
|
+
}
|
|
525
|
+
handleWebhook(webhookData) {
|
|
526
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
527
|
+
return this.webhookHandler.handleWebhook(webhookData);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
handleWebhookQuery(queryParams) {
|
|
531
|
+
return this.webhookHandler.handleWebhookQuery(queryParams);
|
|
532
|
+
}
|
|
388
533
|
handleWebhookQueryWithItems(queryParams) {
|
|
389
534
|
return __awaiter(this, void 0, void 0, function* () {
|
|
390
535
|
try {
|
|
391
|
-
|
|
392
|
-
const webhookResult = this.handleWebhookQuery(queryParams);
|
|
536
|
+
const webhookResult = this.webhookHandler.handleWebhookQuery(queryParams);
|
|
393
537
|
if (!webhookResult.isValid || !webhookResult.transactionData) {
|
|
394
538
|
return { isValid: false, transactionData: null };
|
|
395
539
|
}
|
|
396
|
-
|
|
397
|
-
const orderDetails = yield this.getOrderDetails(webhookResult.transactionData.orderId);
|
|
398
|
-
// Combine webhook data with items
|
|
540
|
+
const orderDetails = yield this.orderManager.getOrderDetails(webhookResult.transactionData.orderId);
|
|
399
541
|
const transactionData = Object.assign(Object.assign({}, webhookResult.transactionData), { items: orderDetails.items });
|
|
400
542
|
return { isValid: true, transactionData };
|
|
401
543
|
}
|
|
@@ -405,5 +547,20 @@ class PaymobService {
|
|
|
405
547
|
}
|
|
406
548
|
});
|
|
407
549
|
}
|
|
550
|
+
getTransactionStatus(transactionId) {
|
|
551
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
552
|
+
return this.orderManager.getTransactionStatus(transactionId);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
getOrderDetails(orderId) {
|
|
556
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
557
|
+
return this.orderManager.getOrderDetails(orderId);
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
getAuthToken() {
|
|
561
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
562
|
+
return this.auth.getAuthToken();
|
|
563
|
+
});
|
|
564
|
+
}
|
|
408
565
|
}
|
|
409
566
|
exports.PaymobService = PaymobService;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
export interface PaymobMerchant {
|
|
2
|
+
id: number;
|
|
3
|
+
created_at: string;
|
|
4
|
+
phones: string[];
|
|
5
|
+
company_emails: string[];
|
|
6
|
+
company_name: string;
|
|
7
|
+
state: string;
|
|
8
|
+
country: string;
|
|
9
|
+
city: string;
|
|
10
|
+
postal_code: string;
|
|
11
|
+
street: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PaymobOrderDetailsResponse {
|
|
14
|
+
id: number;
|
|
15
|
+
amount_cents: number;
|
|
16
|
+
currency: string;
|
|
17
|
+
items: PaymobOrderItem[];
|
|
18
|
+
created_at: string;
|
|
19
|
+
merchant_order_id: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PaymobOrder {
|
|
22
|
+
id: number;
|
|
23
|
+
created_at: string;
|
|
24
|
+
delivery_needed: boolean;
|
|
25
|
+
merchant: PaymobMerchant;
|
|
26
|
+
collector: null;
|
|
27
|
+
amount_cents: number;
|
|
28
|
+
shipping_data: null;
|
|
29
|
+
currency: string;
|
|
30
|
+
is_payment_locked: boolean;
|
|
31
|
+
is_return: boolean;
|
|
32
|
+
is_cancel: boolean;
|
|
33
|
+
is_returned: boolean;
|
|
34
|
+
is_canceled: boolean;
|
|
35
|
+
merchant_order_id: string;
|
|
36
|
+
wallet_notification: null;
|
|
37
|
+
paid_amount_cents: number;
|
|
38
|
+
notify_user_with_email: boolean;
|
|
39
|
+
items: PaymobOrderItem[];
|
|
40
|
+
order_url: string;
|
|
41
|
+
commission_fee: number;
|
|
42
|
+
delivery_fee_cents: number;
|
|
43
|
+
delivery_voucher_cost: number;
|
|
44
|
+
discount: number;
|
|
45
|
+
metadata: Record<string, any>;
|
|
46
|
+
}
|
|
47
|
+
export interface PaymobOrderItem {
|
|
48
|
+
name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
amount: number;
|
|
51
|
+
quantity: number;
|
|
52
|
+
}
|
|
53
|
+
export interface PaymobSourceData {
|
|
54
|
+
type: string;
|
|
55
|
+
pan: string;
|
|
56
|
+
sub_type: string;
|
|
57
|
+
}
|
|
58
|
+
export interface PaymobWebhookData {
|
|
59
|
+
obj: {
|
|
60
|
+
id: number;
|
|
61
|
+
amount_cents: number;
|
|
62
|
+
success: boolean;
|
|
63
|
+
is_refunded: boolean;
|
|
64
|
+
is_captured: boolean;
|
|
65
|
+
is_voided: boolean;
|
|
66
|
+
is_standalone_payment: boolean;
|
|
67
|
+
is_void: boolean;
|
|
68
|
+
is_refund: boolean;
|
|
69
|
+
is_3d_secure: boolean;
|
|
70
|
+
integration_id: number;
|
|
71
|
+
profile_id: number;
|
|
72
|
+
has_parent_transaction: boolean;
|
|
73
|
+
order: PaymobOrder;
|
|
74
|
+
created_at: string;
|
|
75
|
+
transaction_processed_callback_responses: null;
|
|
76
|
+
currency: string;
|
|
77
|
+
source_data: PaymobSourceData;
|
|
78
|
+
api_source: string;
|
|
79
|
+
terminal_id: string;
|
|
80
|
+
merchant_commission: number;
|
|
81
|
+
merchant_staff_tag: null;
|
|
82
|
+
hmac: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export interface PaymobIntentionResponse {
|
|
86
|
+
id: string;
|
|
87
|
+
amount: number;
|
|
88
|
+
currency: string;
|
|
89
|
+
payment_methods: any[];
|
|
90
|
+
items: PaymobOrderItem[];
|
|
91
|
+
billing_data: PaymobBillingData;
|
|
92
|
+
customer: PaymobCustomer;
|
|
93
|
+
extras: Record<string, any>;
|
|
94
|
+
client_secret: string;
|
|
95
|
+
status: string;
|
|
96
|
+
created_at: string;
|
|
97
|
+
}
|
|
98
|
+
export interface PaymobTransactionStatusResponse {
|
|
99
|
+
success: boolean;
|
|
100
|
+
status: string;
|
|
101
|
+
amount_cents: number;
|
|
102
|
+
currency: string;
|
|
103
|
+
}
|
|
104
|
+
export interface PaymobIntentionRequest {
|
|
105
|
+
amount: number;
|
|
106
|
+
currency: string;
|
|
107
|
+
payment_methods: (string | number)[];
|
|
108
|
+
items: PaymobOrderItem[];
|
|
109
|
+
billing_data: PaymobBillingData;
|
|
110
|
+
customer?: PaymobCustomer;
|
|
111
|
+
extras?: Record<string, any>;
|
|
112
|
+
merchant_order_id?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface PaymobBillingData {
|
|
115
|
+
first_name: string;
|
|
116
|
+
last_name: string;
|
|
117
|
+
email: string;
|
|
118
|
+
phone_number: string;
|
|
119
|
+
apartment: string;
|
|
120
|
+
floor: string;
|
|
121
|
+
street: string;
|
|
122
|
+
building: string;
|
|
123
|
+
city?: string;
|
|
124
|
+
state: string;
|
|
125
|
+
country: string;
|
|
126
|
+
}
|
|
127
|
+
export interface PaymobCustomer {
|
|
128
|
+
first_name: string;
|
|
129
|
+
last_name: string;
|
|
130
|
+
email: string;
|
|
131
|
+
extras?: Record<string, any>;
|
|
132
|
+
}
|
|
133
|
+
export interface PaymobAuthResponse {
|
|
134
|
+
token: string;
|
|
135
|
+
}
|
|
136
|
+
export interface TransactionData {
|
|
137
|
+
orderId: number;
|
|
138
|
+
amount: number;
|
|
139
|
+
success: boolean;
|
|
140
|
+
currency: string;
|
|
141
|
+
transactionId: number;
|
|
142
|
+
createdAt: string;
|
|
143
|
+
isRefunded: boolean;
|
|
144
|
+
isCaptured: boolean;
|
|
145
|
+
isVoided: boolean;
|
|
146
|
+
metadata: Record<string, any>;
|
|
147
|
+
}
|
|
148
|
+
export interface WebhookQueryTransactionData extends TransactionData {
|
|
149
|
+
isAuth: boolean;
|
|
150
|
+
isStandalone: boolean;
|
|
151
|
+
is3dSecure: boolean;
|
|
152
|
+
sourceData: {
|
|
153
|
+
type: string;
|
|
154
|
+
pan: string;
|
|
155
|
+
subType: string;
|
|
156
|
+
};
|
|
157
|
+
responseCode: string;
|
|
158
|
+
message: string;
|
|
159
|
+
}
|
|
160
|
+
export interface WebhookQueryWithItemsTransactionData extends WebhookQueryTransactionData {
|
|
161
|
+
items: PaymobOrderItem[];
|
|
162
|
+
}
|
|
163
|
+
export interface WebhookResult<T> {
|
|
164
|
+
isValid: boolean;
|
|
165
|
+
transactionData: T | null;
|
|
166
|
+
}
|
|
167
|
+
export interface TransactionStatusResult {
|
|
168
|
+
success: boolean;
|
|
169
|
+
status: string;
|
|
170
|
+
amount: number;
|
|
171
|
+
currency: string;
|
|
172
|
+
}
|
|
173
|
+
export interface OrderDetailsResult {
|
|
174
|
+
id: number;
|
|
175
|
+
amount_cents: number;
|
|
176
|
+
currency: string;
|
|
177
|
+
items: PaymobOrderItem[];
|
|
178
|
+
created_at: string;
|
|
179
|
+
merchant_order_id: string;
|
|
180
|
+
}
|
|
181
|
+
export interface PaymentIntentionResult {
|
|
182
|
+
paymentUrl: string;
|
|
183
|
+
clientSecret: string;
|
|
184
|
+
}
|
|
185
|
+
export interface UserPaymentData {
|
|
186
|
+
firstName: string;
|
|
187
|
+
lastName: string;
|
|
188
|
+
email: string;
|
|
189
|
+
phone: string;
|
|
190
|
+
}
|
|
191
|
+
export interface PaymobConfigOptions {
|
|
192
|
+
apiKey?: string;
|
|
193
|
+
secretKey?: string;
|
|
194
|
+
publicKey?: string;
|
|
195
|
+
integrationId?: number;
|
|
196
|
+
baseUrl?: string;
|
|
197
|
+
hmacSecret?: string;
|
|
198
|
+
}
|
|
199
|
+
export declare class PaymobError extends Error {
|
|
200
|
+
readonly code?: string | undefined;
|
|
201
|
+
readonly details?: any;
|
|
202
|
+
constructor(message: string, code?: string | undefined, details?: any);
|
|
203
|
+
}
|
|
204
|
+
export declare class PaymobWebhookError extends PaymobError {
|
|
205
|
+
constructor(message: string, details?: any);
|
|
206
|
+
}
|
|
207
|
+
export declare class PaymobAuthError extends PaymobError {
|
|
208
|
+
constructor(message: string, details?: any);
|
|
209
|
+
}
|
|
210
|
+
export declare class PaymobPaymentError extends PaymobError {
|
|
211
|
+
constructor(message: string, details?: any);
|
|
212
|
+
}
|
|
213
|
+
export interface ValidationResult {
|
|
214
|
+
isValid: boolean;
|
|
215
|
+
errors: string[];
|
|
216
|
+
}
|
|
217
|
+
export interface PaymentValidationRules {
|
|
218
|
+
minAmount?: number;
|
|
219
|
+
maxAmount?: number;
|
|
220
|
+
allowedCurrencies?: string[];
|
|
221
|
+
requiredBillingFields?: string[];
|
|
222
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ===========================
|
|
3
|
+
// PAYMOB TYPE DEFINITIONS
|
|
4
|
+
// ===========================
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PaymobPaymentError = exports.PaymobAuthError = exports.PaymobWebhookError = exports.PaymobError = void 0;
|
|
7
|
+
// ===========================
|
|
8
|
+
// ERROR TYPES
|
|
9
|
+
// ===========================
|
|
10
|
+
class PaymobError extends Error {
|
|
11
|
+
constructor(message, code, details) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.details = details;
|
|
15
|
+
this.name = 'PaymobError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.PaymobError = PaymobError;
|
|
19
|
+
class PaymobWebhookError extends PaymobError {
|
|
20
|
+
constructor(message, details) {
|
|
21
|
+
super(message, 'WEBHOOK_ERROR', details);
|
|
22
|
+
this.name = 'PaymobWebhookError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.PaymobWebhookError = PaymobWebhookError;
|
|
26
|
+
class PaymobAuthError extends PaymobError {
|
|
27
|
+
constructor(message, details) {
|
|
28
|
+
super(message, 'AUTH_ERROR', details);
|
|
29
|
+
this.name = 'PaymobAuthError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.PaymobAuthError = PaymobAuthError;
|
|
33
|
+
class PaymobPaymentError extends PaymobError {
|
|
34
|
+
constructor(message, details) {
|
|
35
|
+
super(message, 'PAYMENT_ERROR', details);
|
|
36
|
+
this.name = 'PaymobPaymentError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.PaymobPaymentError = PaymobPaymentError;
|