Add full project files

This commit is contained in:
root
2026-07-04 19:24:56 +03:30
parent d54f5441a3
commit b245e7b71a
835 changed files with 30149 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,935 @@
/* eslint-disable eqeqeq */
const TypeAndPriceModel = require('../../../models/TypeAndPriceModel');
const ProjectModel = require('../../../models/ProjectModel');
const PaymentModel = require('../../../models/PaymentModel');
const jwt = require('jsonwebtoken');
const RequestModel = require('../../../models/RequestModel');
const NotificationModel = require('../../../models/NotificationModel');
const UserModel = require('../../../models/UserModel');
const AdvertisingTypeModel = require('../../../models/AdvertisingTypeModel');
const AdvertisingModel = require('../../../models/AdvertisingModel');
const OfferTypeModel = require('../../../models/OfferTypeModel');
const OfferModel = require('../../../models/OfferModel');
const { default: axios } = require('axios');
const apiKey = "c7c41e8a-918f-4741-bcd5-58f3bc51db73"; // اصلاح نام متغیر محیطی
// Function to get price based on item name from database
const getPriceFromDatabase = async (itemName) => {
const item = await TypeAndPriceModel.findOne({ name: itemName });
if (!item) {
throw new Error(`Item "${itemName}" not found in TypeAndPriceModel`);
}
return item.price;
};
const getAdvertisingPriceFromDatabase = async (itemName) => {
const item = await AdvertisingTypeModel.findOne({ name: itemName });
if (!item) {
throw new Error(`Item "${itemName}" not found in AdvertisingTypeModel`);
}
return item.price;
};
const getOfferPriceFromDatabase = async (offerType) => {
const item = await OfferTypeModel.findOne({ name: offerType });
if (!item) {
throw new Error(`Offer type "${offerType}" not found in OfferTypeModel`);
}
return item.price;
};
// Controller function for initiating payment
const initiatePaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const itemName = req.body.item_name // Get item name from request body
const price = await getPriceFromDatabase(itemName) // Get price from database
const projectId = req.body.projectId // Get project ID from request body
// Save project ID for later use in callback URL
req.session.projectId = projectId
const item = await TypeAndPriceModel.findOne({ name: itemName })
const user = await UserModel.findById(userId)
if (item.name === 'free' && item.price === 0) {
if (user.daily_free_request > 0) {
user.daily_free_request -= 1
await user.save()
const project = await ProjectModel.findById(projectId)
if (!project) {
res.status(404).send('Project not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
res.status(200).json({ message: 'پروژه رایگان با موفقیت ایجاد شد', projectId, type: 'free' })
} else {
res.status(403).json({ message: 'شما قبلاً از درخواست رایگان امروز استفاده کرده‌اید.' })
}
} else {
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
MerchantID: apiKey,
Amount: Number(price),
Description: `پرداخت برای سفارش شماره ${projectId}`,
CallbackURL: `${process.env.APP_URL}/projects/payment-web?projectId=${projectId}&itemName=${itemName}&userId=${userId}` // Assuming your backend URL for handling payment callback
})
const data = response.data
if (data.Status === 100) {
// Payment request was successful
const authority = data.Authority // The payment authority (شناسه پرداخت)
res.status(200).json({ authority })
} else {
// Payment request failed
console.error('Error occurred while initiating payment:', data)
res.status(500).json({ error: 'Error occurred while initiating payment' })
}
}
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
// Controller function for handling payment callback from ZarinPal
const handlePaymentCallbackWeb = async (req, res) => {
try {
// console.log(req.query)
// Extract payment status and authority from ZarinPal callback
const status = req.query.Status
const authority = req.query.Authority
const projectId = req.query.projectId // Get project ID from session
const itemName = req.query.itemName // Get project ID from session
const userId = req.query.userId // Get project ID from session
// Clear project ID from session
// Query ZarinPal API to verify payment status
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
const price = await getPriceFromDatabase(itemName) // Get price from database
const verificationResponse = await axios.post(verificationUrl, {
MerchantID: apiKey,
Authority: authority,
Amount: Number(price)
})
const verificationData = verificationResponse.data
if (verificationData.Status === 100) {
const payment = new PaymentModel({
amount: Number(price),
status: 'successful',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
project_id: projectId,
type: 'create',
installment_step: null
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
// Payment verification is successful
// Now, update project status to 'done' or desired status
const project = await ProjectModel.findById(projectId)
if (!project) {
res.status(404).send('Project not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
// Send event based on payment status
if ('eventEmitter' in req.app) {
if (status === 'OK') {
// Payment is successful
// Emit an event to be listened to in the frontend
// Example event name: 'paymentSuccess'
// Example data: { message: 'Payment successful' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
} else {
// Payment is canceled or failed
// Emit an event to be listened to in the frontend
// Example event name: 'paymentFailed'
// Example data: { message: 'Payment canceled or failed' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment canceled or failed' })
}
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
// Redirect to the callback URL with the project ID
res.redirect(`https://modstagram.com/projects/payment/success?projectId=${projectId}&price=${price}&type=create`)
} else {
// Payment verification failed
const payment = new PaymentModel({
amount: Number(price),
status: 'failed',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
project_id: projectId,
type: 'create'
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
res.redirect(`https://modstagram.com/projects/payment/failed?projectId=${projectId}&price=${price}&type=create`)
}
} catch (error) {
// Error occurred while verifying payment
console.error('Error occurred:', error)
res.status(500).send('Internal server error')
}
}
const paymentRequestAcceptWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const projectId = req.body.projectId
// Find the project
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({ error: 'Project not found' })
}
if (project.status == 'ongoing') {
return res.status(400).json({ message: 'برای این پروژه از قبل کاربر انتخاب شده' })
}
// eslint-disable-next-line eqeqeq
if (userId != project.creator_id.toString()) {
return res.status(422).json({
error: true,
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
})
}
// Get the final price from the request model
const requestId = req.body.requestId
const request = await RequestModel.findById(requestId)
if (!request) {
return res.status(404).json({ error: 'Request not found' })
}
request.status = 'accepted'
await request.save()
project.selected_user = request.user
project.status = 'ongoing'
project.final_price = request.price
project.final_time = request.time
await project.save()
// Send notification to selected user
const notification = new NotificationModel({
user_id: request.user,
project_post_id: projectId,
type: 'request_accepted',
title: 'انتخاب پیشنهاد',
description: `کارفرما پیشنهاد کاری شما برای پروژه ${project.title} را پذیرفت.`
})
await notification.save()
// Sms
const userReciver = await UserModel.findById(request.user)
const data = JSON.stringify({
mobile: userReciver?.mobile,
templateId: '302876',
parameters: [
{ name: 'USER', value: userReciver?.first_name + ' ' + userReciver?.last_name },
{ name: 'PROJECT', value: project?.title }
]
})
const config = {
method: 'post',
url: 'https://api.sms.ir/v1/send/verify',
headers: {
'Content-Type': 'application/json',
Accept: 'text/plain',
'x-api-key': '2JKxGbTzE64t0YTkjip6RYHnm9mq1haMlidF8WWwQunwubHFVW6iaMmfFQbMXEDt'
},
data
}
axios(config)
.then(function (response) {
})
.catch(function (error) {
console.log(error)
})
res.status(200).json({
message: 'درخواست با موفقیت تایید شد'
})
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const initiateAdvertisingPaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const itemName = req.body.item_name // Get item name from request body
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const advertisingId = req.body.advertisingId // Get project ID from request body
const showDiscount = req.body.showDiscount
// Save project ID for later use in callback URL
req.session.advertisingId = advertisingId
const item = await AdvertisingTypeModel.findOne({ name: itemName })
const user = await UserModel.findById(userId)
if (item.name === 'free' && item.price === 0) {
if (user.daily_free_request > 0) {
user.daily_free_request -= 1
await user.save()
const project = await AdvertisingModel.findById(advertisingId)
if (!project) {
res.status(404).send('Advertising not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
res.status(200).json({ message: 'بیلبورد رایگان با موفقیت ایجاد شد', advertisingId, type: 'free' })
} else {
res.status(403).json({ message: 'شما قبلاً از بیلبورد رایگان این ماه استفاده کرده‌اید.' })
}
} else {
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
MerchantID: process.env.ZARINPAL_MERCHANT_ID,
Amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
Description: `پرداخت برای سفارش شماره ${advertisingId}`,
CallbackURL: `${process.env.APP_SITE_CallBack}/advertising/payment-web?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
})
const data = response.data
if (data.Status === 100) {
// Payment request was successful
const authority = data.Authority // The payment authority (شناسه پرداخت)
res.status(200).json({ authority })
} else {
// Payment request failed
console.error('Error occurred while initiating payment:', data)
res.status(500).json({ error: 'Error occurred while initiating payment' })
}
}
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const handleAdvertisingPaymentCallbackWeb = async (req, res) => {
try {
// console.log(req.query)
// Extract payment status and authority from ZarinPal callback
const status = req.query.Status
const authority = req.query.Authority
const advertisingId = req.query.advertisingId // Get project ID from session
const itemName = req.query.itemName // Get project ID from session
const userId = req.query.userId // Get project ID from session
const showDiscount = req.query.showDiscount // Get project ID from session
// Clear project ID from session
// Query ZarinPal API to verify payment status
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const verificationResponse = await axios.post(verificationUrl, {
MerchantID: apiKey,
Authority: authority,
Amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)
})
const verificationData = verificationResponse.data
if (verificationData.Status === 100) {
const payment = new PaymentModel({
amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
status: 'successful',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising',
installment_step: null
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
// Payment verification is successful
// Now, update project status to 'done' or desired status
const advertising = await AdvertisingModel.findById(advertisingId)
if (!advertising) {
res.status(404).send('Advertising not found')
return
}
advertising.payment_status = 'done' // Set advertising status to 'done'
advertising.status = 'paid' // Set advertising status to 'done'
await advertising.save()
// Send event based on payment status
if ('eventEmitter' in req.app) {
if (status === 'OK') {
// Payment is successful
// Emit an event to be listened to in the frontend
// Example event name: 'paymentSuccess'
// Example data: { message: 'Payment successful' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
} else {
// Payment is canceled or failed
// Emit an event to be listened to in the frontend
// Example event name: 'paymentFailed'
// Example data: { message: 'Payment canceled or failed' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment canceled or failed' })
}
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
// Redirect to the callback URL with the advertising ID
res.redirect(`https://modstagram.com/billboards/payment/success?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)}&type=advertising&showDiscount=${showDiscount}`)
} else {
// Payment verification failed
const payment = new PaymentModel({
amount: showDiscount ? Number(price + 20000) : Number(price),
status: 'failed',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising'
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
console.log('ss')
await payment.save()
res.redirect(`https://modstagram.com/billboards/payment/failed?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)}&type=advertising&showDiscount=${showDiscount}`)
}
} catch (error) {
// Error occurred while verifying payment
console.error('Error occurred:', error)
res.status(500).send('Internal server error')
}
}
const republishAdvertisingPaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const itemName = req.body.item_name // Get item name from request body
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const advertisingId = req.body.advertisingId // Get project ID from request body
const showDiscount = req.body.showDiscount
// Save project ID for later use in callback URL
req.session.advertisingId = advertisingId
const item = await AdvertisingTypeModel.findOne({ name: itemName })
const user = await UserModel.findById(userId)
if (item.name === 'free' && item.price === 0) {
if (user.daily_free_request > 0) {
user.daily_free_request -= 1
await user.save()
const project = await AdvertisingModel.findById(advertisingId)
if (!project) {
res.status(404).send('Project not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
res.status(200).json({ message: 'پروژه رایگان با موفقیت ایجاد شد', advertisingId, type: 'free' })
} else {
res.status(403).json({ message: 'شما قبلاً از درخواست رایگان امروز استفاده کرده‌اید.' })
}
} else {
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
MerchantID: apiKey,
Amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
Description: `پرداخت برای سفارش شماره ${advertisingId}`,
CallbackURL: `${process.env.APP_URL}/advertising/republish-verify-payment-web?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
})
const data = response.data
if (data.Status === 100) {
// Payment request was successful
const authority = data.Authority // The payment authority (شناسه پرداخت)
res.status(200).json({ authority })
} else {
// Payment request failed
console.error('Error occurred while initiating payment:', data)
res.status(500).json({ error: 'Error occurred while initiating payment' })
}
}
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const handleAdvertisingRepublishPaymentCallbackWeb = async (req, res) => {
try {
// console.log(req.query)
// Extract payment status and authority from ZarinPal callback
const status = req.query.Status
const authority = req.query.Authority
const advertisingId = req.query.advertisingId // Get project ID from session
const itemName = req.query.itemName // Get project ID from session
const userId = req.query.userId // Get project ID from session
const showDiscount = req.query.showDiscount // Get project ID from session
// Clear project ID from session
// Query ZarinPal API to verify payment status
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const verificationResponse = await axios.post(verificationUrl, {
MerchantID: apiKey,
Authority: authority,
Amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)
})
const verificationData = verificationResponse.data
if (verificationData.Status === 100) {
const payment = new PaymentModel({
amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
status: 'successful',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising',
installment_step: null
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
// Payment verification is successful
// Now, update project status to 'done' or desired status
const advertising = await AdvertisingModel.findById(advertisingId)
if (!advertising) {
res.status(404).send('Advertising not found')
return
}
advertising.payment_status = 'done' // Set advertising status to 'done'
advertising.status = 'accepted' // Set advertising status to 'done'
advertising.acceptedAt = new Date()
await advertising.save()
// Send event based on payment status
if ('eventEmitter' in req.app) {
if (status === 'OK') {
// Payment is successful
// Emit an event to be listened to in the frontend
// Example event name: 'paymentSuccess'
// Example data: { message: 'Payment successful' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
} else {
// Payment is canceled or failed
// Emit an event to be listened to in the frontend
// Example event name: 'paymentFailed'
// Example data: { message: 'Payment canceled or failed' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment canceled or failed' })
}
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
// Redirect to the callback URL with the advertising ID
res.redirect(`https://modstagram.com/billboards/payment/success?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)}&type=advertising-republish&showDiscount=${showDiscount}`)
} else {
// Payment verification failed
const payment = new PaymentModel({
amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
status: 'failed',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising'
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
res.redirect(`https://modstagram.com/billboards/payment/failed?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)}&type=advertising-republish&showDiscount=${showDiscount}`)
}
} catch (error) {
// Error occurred while verifying payment
console.error('Error occurred:', error)
res.status(500).send('Internal server error')
}
}
// const initiateOfferPaymentWeb = async (req, res) => {
// try {
// const token = req.header('Authorization')?.split(' ')[1];
// if (!token) {
// return res.status(401).json({ error: 'Access Denied: No token provided' });
// }
// let decodedToken;
// try {
// decodedToken = jwt.verify(token, process.env.APP_SECRET);
// } catch (err) {
// return res.status(401).json({ error: 'Invalid token' });
// }
// const userId = decodedToken.id;
// const { receiverId, offerType } = req.body;
// // اعتبارسنجی ورودی‌ها
// if (!receiverId || !offerType) {
// return res.status(400).json({ error: 'receiverId and offerType are required' });
// }
// const user = await UserModel.findById(userId);
// if (!user) {
// return res.status(404).json({ error: 'User not found' });
// }
// const receiverUser = await UserModel.findById(receiverId);
// if (!receiverUser) {
// return res.status(404).json({ error: 'Receiver not found' });
// }
// let price;
// if (user.monthly_free_offer > 0 && offerType === 'free') {
// price = 0;
// user.monthly_free_offer -= 1;
// await user.save();
// } else {
// price = await getOfferPriceFromDatabase(offerType);
// }
// if (price === 0) {
// const offer = new OfferModel({
// sender: userId,
// receiver: receiverId,
// transaction_id: null,
// });
// await offer.save();
// return res.status(200).json({ message: 'آفر رایگان با موفقیت ثبت شد', offerId: offer._id, type: 'free' });
// }
// console.log('🔹 Initiating ZarinPal Payment');
// console.log('MerchantID:', apiKey);
// console.log('Amount:', Number(price * 10));
// console.log('Description:', `پرداخت برای ارسال آفر به ${receiverUser.first_name}`);
// console.log('CallbackURL:', `${process.env.APP_URL}/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`);
// const ZARINPAL_URL = 'https://api.zarinpal.com/pg/v4/payment/request.json';
// //
// const response = await axios.post(ZARINPAL_URL, {
// merchant_id: process.env.ZARINPAL_MERCHANT_ID, // ❌ MerchantID نیست!
// amount: Number(price), // ✅ به ریال
// description: `پرداخت برای ارسال آفر به ${receiverUser.first_name}`,
// callback_url: `${process.env.APP_SITE_CallBack}/offer/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`,
// }, { headers: { 'Content-Type': 'application/json' } });
// const result = response.data.data; // ✅ data.data
// if (result.code === 100) { // ✅ code === 100
// return res.status(200).json({
// authority: result.authority,
// paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`
// });
// } else {
// console.error('ZarinPal error:', data);
// return res.status(500).json({ error: `Failed to initiate payment: ZarinPal Status ${data.Status}` });
// }
// } catch (error) {
// console.error('❌ Error in initiateOfferPaymentWeb:', error.message, error.stack);
// return res.status(500).json({ error: error.message || 'Internal server error' });
// }
// }
const initiateOfferPaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization')?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token' });
const decoded = jwt.verify(token, process.env.APP_SECRET);
const userId = decoded.id;
const { receiverId, offerType } = req.body;
if (!receiverId || !offerType)
return res.status(400).json({ error: 'Invalid data' });
const user = await UserModel.findById(userId);
const receiver = await UserModel.findById(receiverId);
if (!user || !receiver)
return res.status(404).json({ error: 'User not found' });
let price = await getOfferPriceFromDatabase(offerType); // ✅ ریال
// 🎁 free offer
if (offerType === 'free' && user.monthly_free_offer > 0) {
const offer = await OfferModel.create({
sender: userId,
receiver: receiverId,
transaction_id: null,
});
user.monthly_free_offer -= 1;
await user.save();
return res.json({
type: 'free',
offerId: offer._id,
});
}
const amount = Number(price) * 10; // ✅ ریال بدون ×10
const callbackUrl = `https://api.modstagram.com/api/v1/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`;
const zarinpalRes = await axios.post(
'https://api.zarinpal.com/pg/v4/payment/request.json',
{
merchant_id:"c7c41e8a-918f-4741-bcd5-58f3bc51db73",
amount,
description: `ارسال آفر به ${receiver.first_name}`,
callback_url: callbackUrl
// callback_url: `${process.env.APP_SITE_CallBack}/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`,
},
{ headers: { 'Content-Type': 'application/json' } }
);
// console.log(`${process.env.APP_SITE_CallBack}/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`);
const result = zarinpalRes.data.data;
console.log(result);
console.log("ZarinPal Callback URL:", callbackUrl);
console.log("Initiate payment request:", { userId, receiverId, offerType, price });
if (result.code === 100) {
return res.json({
authority: result.authority,
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`,
});
}
return res.status(500).json({ error: 'ZarinPal error' });
} catch (err) {
console.error(err);
return res.status(500).json({ error: 'Server error' });
}
};
// try {
// const { receiverId, offerType, userId, Authority, Status } = req.query;
// // ❗ چک اولیه
// if (Status !== 'OK' || !Authority) {
// return res.status(400).json({
// success: false,
// message: 'پرداخت ناموفق یا لغو شد'
// });
// }
// if (!receiverId || !offerType || !userId) {
// return res.status(400).json({
// success: false,
// message: 'پارامترهای ناقص'
// });
// }
// // پیدا کردن کاربرها
// const user = await UserModel.findById(userId);
// const receiverUser = await UserModel.findById(receiverId);
// if (!user || !receiverUser) {
// return res.status(404).json({
// success: false,
// message: 'کاربر یافت نشد'
// });
// }
// const price = await getOfferPriceFromDatabase(offerType);
// const verifyResponse = await axios.post(
// 'https://api.zarinpal.com/pg/v4/payment/verify.json',
// {
// merchant_id: process.env.ZARINPAL_MERCHANT_ID, // یا MERCHENT_CODE
// authority: Authority,
// amount: price, // به ریال
// },
// { headers: { 'Content-Type': 'application/json' } }
// );
// const verifyData = verifyResponse.data.data;
// if (verifyData.code === 100) {
// // ✅ پرداخت موفق
// const payment = new PaymentModel({
// amount: Number(price),
// status: 'successful',
// authority: Authority,
// ref_id: verifyData.ref_id,
// user_id: userId,
// type: 'offer',
// });
// await payment.save();
// const offer = new OfferModel({
// sender: userId,
// receiver: receiverId,
// transaction_id: payment._id,
// });
// await offer.save();
// // نوتیفیکیشن و SMS (همون کد قبلی)
// const notification = new NotificationModel({
// user_id: receiverId,
// project_post_id: offer._id,
// type: 'new_offer',
// title: 'درخواست همکاری جدید',
// description: `${user.first_name} یک درخواست همکاری برای شما ارسال کرده است`,
// });
// await notification.save();
// const data = JSON.stringify({
// mobile: receiverUser?.mobile,
// templateId: '569006',
// parameters: [
// { name: 'FIRSTNAME', value: receiverUser?.first_name },
// { name: 'LASTNAME', value: receiverUser?.last_name },
// ],
// });
// const config = {
// method: 'post',
// url: 'https://api.sms.ir/v1/send/verify',
// headers: {
// 'Content-Type': 'application/json',
// Accept: 'text/plain',
// 'x-api-key': process.env.SMS_VERIFY_KEY,
// },
// data,
// };
// await axios(config).catch((error) => {
// console.error('SMS error:', error.message);
// });
// if ('eventEmitter' in req.app && status === 'OK') {
// req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' });
// } else {
// req.app.get('eventEmitter')?.emit('paymentFailed', { message: 'Payment canceled or failed' });
// }
// res.redirect(`https://modstagram.com/offer/payment/success?userId=${userId}`);
// return res.status(200).json({
// success: true,
// message: 'پرداخت و آفر با موفقیت ثبت شد',
// ref_id: verifyData.ref_id,
// offerId: offer._id
// });
// } else {
// // پرداخت ناموفق
// const payment = new PaymentModel({
// amount: Number(price),
// status: 'failed',
// authority: Authority,
// user_id: userId,
// type: 'offer',
// });
// await payment.save();
// res.redirect(`https://modstagram.com/offer/payment/failed?userId=${userId}`);
// return res.status(400).json({
// success: false,
// message: `خطا در تأیید پرداخت: ${verifyData.code}`
// });
// }
// } catch (error) {
// console.error('❌ Callback Error:', error.message);
// return res.status(500).json({
// success: false,
// message: 'خطای سرور'
// });
// }
// };
const handleOfferPaymentCallback = async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
console.log('🔔 CALLBACK QUERY:', req.query);
try {
const { Authority, Status, receiverId, offerType, userId } = req.query;
if (Status !== 'OK')
return res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
const price = await getOfferPriceFromDatabase(offerType); // ریال
const amount = Number(price) * 10;
const verify = await axios.post(
'https://api.zarinpal.com/pg/v4/payment/verify.json',
{
merchant_id: "c7c41e8a-918f-4741-bcd5-58f3bc51db73",
authority: Authority,
amount,
},
{ headers: { 'Content-Type': 'application/json' } }
);
const data = verify.data.data;
console.log('🔔payment:', data.code);
if (data.code === 100) {
const payment = await PaymentModel.create({
amount : (amount / 10),
status: 'successful',
authority: Authority,
ref_id: data.ref_id,
user_id: userId,
type: 'offer',
});
console.log('🔔payment:', payment);
const offer = await OfferModel.create({
sender: userId,
receiver: receiverId,
transaction_id: payment._id,
});
return res.redirect(
`${process.env.APP_SITE}/offer/payment/success?offerId=${offer._id}`
);
}
return res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
} catch (err) {
console.error(err);
return res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
}
};
module.exports = {
initiatePaymentWeb,
handlePaymentCallbackWeb,
paymentRequestAcceptWeb,
initiateAdvertisingPaymentWeb,
handleAdvertisingPaymentCallbackWeb,
republishAdvertisingPaymentWeb,
handleAdvertisingRepublishPaymentCallbackWeb,
initiateOfferPaymentWeb,
handleOfferPaymentCallback,
};