check
This commit is contained in:
@@ -12,15 +12,29 @@ const AdvertisingModel = require('../../../models/AdvertisingModel')
|
||||
const {
|
||||
canUseFreeCreation,
|
||||
consumeFreeCreation,
|
||||
freeCreationLimitResponse,
|
||||
freeCreationLimitResponse
|
||||
} = require('../../../utils/freeCreationLimit')
|
||||
const {
|
||||
PROJECT_ACCEPT_RATE,
|
||||
PROJECT_COMPLETION_COMMISSION_RATE,
|
||||
creditProjectShare
|
||||
} = require('../../../utils/projectCommission')
|
||||
const { isVerifiedBadge } = require('../../../utils/verifyBadge')
|
||||
|
||||
const apiKey = process.env.MERCHENT_CODE // Replace with your ZarinPal API Key
|
||||
const apiKey = process.env.ZARINPAL_MERCHANT_ID || process.env.MERCHENT_CODE // Replace with your ZarinPal API Key
|
||||
|
||||
// درگاه قدیمیِ REST زرینپال (/pg/rest/WebGate) منسوخ شده — مبلغ به API نسخهی v4
|
||||
// باید به ریال ارسال بشه، نه تومان (برخلاف API قدیمی)
|
||||
function toRial (tomanAmount) {
|
||||
return Number(tomanAmount) * 10
|
||||
}
|
||||
|
||||
// کاربرِ بدون تیک وریفای طلایی/سبز/آبی باید از پنل ادمین تایید بشه تا پروژه/ویترینِ
|
||||
// پرداختشدهش مستقیم توی اپ/وب نمایش داده نشه
|
||||
async function resolveApprovalStatus (userId, acceptedStatus, pendingStatus = 'pending') {
|
||||
const user = await UserModel.findById(userId).select('verify_badge').lean()
|
||||
return isVerifiedBadge(user?.verify_badge) ? acceptedStatus : pendingStatus
|
||||
}
|
||||
|
||||
// Function to get price based on item name from database
|
||||
const getPriceFromDatabase = async (itemName) => {
|
||||
@@ -68,28 +82,31 @@ const initiatePayment = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // 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?projectId=${projectId}&itemName=${itemName}&userId=${userId}` // Assuming your backend URL for handling payment callback
|
||||
})
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(price),
|
||||
description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
callback_url: `${process.env.APP_URL}/projects/payment?projectId=${projectId}&itemName=${itemName}&userId=${userId}` // Assuming your backend URL for handling payment callback
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100) {
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority (شناسه پرداخت)
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
}
|
||||
@@ -112,16 +129,15 @@ const handlePaymentCallback = async (req, res) => {
|
||||
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 verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority, amount: toRial(price) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: Number(price),
|
||||
status: 'successful',
|
||||
@@ -142,7 +158,7 @@ const handlePaymentCallback = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // Set project status to 'done'
|
||||
await project.save()
|
||||
|
||||
// Send event based on payment status
|
||||
@@ -189,95 +205,6 @@ const handlePaymentCallback = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Bazaar
|
||||
const handleSuccessfulPayment = async (req, res) => {
|
||||
try {
|
||||
const { projectId } = req.body
|
||||
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 project = await ProjectModel.findById(projectId)
|
||||
if (!project) {
|
||||
res.status(404).send('Project not found')
|
||||
return
|
||||
}
|
||||
|
||||
const price = await getPriceFromDatabase(project.project_type)
|
||||
|
||||
// Create a new payment entry
|
||||
const payment = new PaymentModel({
|
||||
amount: Number(price),
|
||||
status: 'successful',
|
||||
authority: '', // Add authority if available
|
||||
user_id: userId,
|
||||
project_id: projectId,
|
||||
type: 'create',
|
||||
installment_step: null
|
||||
})
|
||||
await payment.save()
|
||||
|
||||
// Update the project status to 'done' or 'paid'
|
||||
project.payment_status = 'done'
|
||||
project.status = 'temp_accept'
|
||||
await project.save()
|
||||
|
||||
// Emit a success event
|
||||
if ('eventEmitter' in req.app) {
|
||||
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
|
||||
} else {
|
||||
console.error('Error: eventEmitter is not defined or not properly configured.')
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Payment recorded successfully', projectId, price })
|
||||
} catch (error) {
|
||||
console.error('Error occurred:', error)
|
||||
res.status(500).json({ error: 'Internal server error' })
|
||||
}
|
||||
}
|
||||
// Controller function for handling failed payment
|
||||
const handleFailedPayment = async (req, res) => {
|
||||
try {
|
||||
const { projectId } = req.body
|
||||
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 project = await ProjectModel.findById(projectId)
|
||||
if (!project) {
|
||||
res.status(404).send('Project not found')
|
||||
return
|
||||
}
|
||||
|
||||
const price = await getPriceFromDatabase(project.project_type)
|
||||
|
||||
// Create a new payment entry
|
||||
const payment = new PaymentModel({
|
||||
amount: Number(price),
|
||||
status: 'failed',
|
||||
authority: '', // Add authority if available
|
||||
user_id: userId,
|
||||
project_id: projectId,
|
||||
type: 'create'
|
||||
})
|
||||
await payment.save()
|
||||
|
||||
// Emit a failure event
|
||||
if ('eventEmitter' in req.app) {
|
||||
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment failed' })
|
||||
} else {
|
||||
console.error('Error: eventEmitter is not defined or not properly configured.')
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Payment failed', projectId, price })
|
||||
} catch (error) {
|
||||
console.error('Error occurred:', error)
|
||||
res.status(500).json({ error: 'Internal server error' })
|
||||
}
|
||||
}
|
||||
|
||||
const paymentRequestStepOne = async (req, res) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
@@ -300,21 +227,24 @@ const paymentRequestStepOne = async (req, res) => {
|
||||
const acceptAmount = Math.round(Number(request.price) * PROJECT_ACCEPT_RATE)
|
||||
|
||||
// Proceed with ZarinPal payment request
|
||||
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
|
||||
MerchantID: apiKey,
|
||||
Amount: parseInt(acceptAmount + acceptAmount / 100 * 9),
|
||||
Description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
CallbackURL: `${process.env.APP_URL}/projects/payment-request-step-one-callback?projectId=${projectId}&requestId=${requestId}&userId=${userId}` // Callback URL including requestId
|
||||
})
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(acceptAmount),
|
||||
description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
callback_url: `${process.env.APP_URL}/projects/payment-request-step-one-callback?projectId=${projectId}&requestId=${requestId}&userId=${userId}` // Callback URL including requestId
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100) {
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -341,21 +271,24 @@ const paymentRequestStepTwo = async (req, res) => {
|
||||
const totalAmount = project.final_price
|
||||
const secondInstallmentAmount = totalAmount / 4
|
||||
// Proceed with ZarinPal payment request for the second installment
|
||||
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
|
||||
MerchantID: apiKey,
|
||||
Amount: parseInt(secondInstallmentAmount + secondInstallmentAmount / 100 * 9),
|
||||
Description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
CallbackURL: `${process.env.APP_URL}/projects/payment-request-step-two-callback?projectId=${projectId}&userId=${userId}&installmentNumber=${installmentNumber}` // Callback URL
|
||||
})
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(secondInstallmentAmount),
|
||||
description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
callback_url: `${process.env.APP_URL}/projects/payment-request-step-two-callback?projectId=${projectId}&userId=${userId}&installmentNumber=${installmentNumber}` // Callback URL
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100) {
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -383,21 +316,24 @@ const paymentRequestStepAll = async (req, res) => {
|
||||
const secondInstallmentAmount = totalAmount / 2
|
||||
|
||||
// Proceed with ZarinPal payment request for the second installment
|
||||
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
|
||||
MerchantID: apiKey,
|
||||
Amount: parseInt(secondInstallmentAmount + secondInstallmentAmount / 100 * 9),
|
||||
Description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
CallbackURL: `${process.env.APP_URL}/projects/payment-request-step-all-callback?projectId=${projectId}&userId=${userId}` // Callback URL
|
||||
})
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(secondInstallmentAmount),
|
||||
description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
callback_url: `${process.env.APP_URL}/projects/payment-request-step-all-callback?projectId=${projectId}&userId=${userId}` // Callback URL
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100) {
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -415,20 +351,19 @@ const paymentRequestStepOneCallback = async (req, res) => {
|
||||
const userId = req.query.userId
|
||||
|
||||
// Verify payment status with ZarinPal
|
||||
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
|
||||
const request = await RequestModel.findById(requestId)
|
||||
if (!request) {
|
||||
return res.status(404).json({ error: 'Request not found' })
|
||||
}
|
||||
const price = Math.round(Number(request.price) * PROJECT_ACCEPT_RATE)
|
||||
const verificationResponse = await axios.post(verificationUrl, {
|
||||
MerchantID: apiKey,
|
||||
Authority: authority,
|
||||
Amount: parseInt(price + price / 100 * 9)
|
||||
})
|
||||
const verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority, amount: toRial(price) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
// Payment verification is successful
|
||||
const payment = new PaymentModel({
|
||||
amount: price,
|
||||
@@ -517,21 +452,20 @@ const paymentRequestStepTwoCallback = async (req, res) => {
|
||||
const installmentNumber = req.query.installmentNumber
|
||||
|
||||
// Verify payment status with ZarinPal
|
||||
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
|
||||
const project = await ProjectModel.findById(projectId)
|
||||
if (!project) {
|
||||
return res.status(404).json({ error: 'Project not found' })
|
||||
}
|
||||
const totalAmount = project.final_price
|
||||
const secondInstallmentAmount = totalAmount / 4
|
||||
const verificationResponse = await axios.post(verificationUrl, {
|
||||
MerchantID: apiKey,
|
||||
Authority: authority,
|
||||
Amount: parseInt(secondInstallmentAmount + secondInstallmentAmount / 100 * 9)
|
||||
})
|
||||
const verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority, amount: toRial(secondInstallmentAmount) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
// Payment verification is successful
|
||||
const payment = new PaymentModel({
|
||||
amount: secondInstallmentAmount,
|
||||
@@ -612,21 +546,20 @@ const paymentRequestStepAllCallback = async (req, res) => {
|
||||
const userId = req.query.userId
|
||||
|
||||
// Verify payment status with ZarinPal
|
||||
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
|
||||
const project = await ProjectModel.findById(projectId)
|
||||
if (!project) {
|
||||
return res.status(404).json({ error: 'Project not found' })
|
||||
}
|
||||
const totalAmount = project.final_price
|
||||
const secondInstallmentAmount = totalAmount / 2
|
||||
const verificationResponse = await axios.post(verificationUrl, {
|
||||
MerchantID: apiKey,
|
||||
Authority: authority,
|
||||
Amount: parseInt(secondInstallmentAmount + secondInstallmentAmount / 100 * 9)
|
||||
})
|
||||
const verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority, amount: toRial(secondInstallmentAmount) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
// Payment verification is successful
|
||||
const payment = new PaymentModel({
|
||||
amount: secondInstallmentAmount,
|
||||
@@ -726,27 +659,30 @@ const initiateAdvertisingPayment = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // 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) : Number(price),
|
||||
Description: `پرداخت برای سفارش شماره ${advertisingId}`,
|
||||
CallbackURL: `${process.env.APP_URL}/advertising/payment?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
|
||||
})
|
||||
const data = response.data
|
||||
if (data.Status === 100) {
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)),
|
||||
description: `پرداخت برای سفارش شماره ${advertisingId}`,
|
||||
callback_url: `${process.env.APP_URL}/advertising/payment?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority (شناسه پرداخت)
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
}
|
||||
@@ -769,16 +705,19 @@ const handleAdvertisingPaymentCallback = async (req, res) => {
|
||||
// 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 verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
authority,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price))
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
|
||||
status: 'successful',
|
||||
@@ -799,7 +738,7 @@ const handleAdvertisingPaymentCallback = async (req, res) => {
|
||||
return
|
||||
}
|
||||
advertising.payment_status = 'done' // Set advertising status to 'done'
|
||||
advertising.status = 'temp_accept' // Set advertising status to 'done'
|
||||
advertising.status = await resolveApprovalStatus(userId, 'temp_accept') // Set advertising status to 'done'
|
||||
await advertising.save()
|
||||
|
||||
// Send event based on payment status
|
||||
@@ -846,96 +785,6 @@ const handleAdvertisingPaymentCallback = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Controller function for handling successful advertising payment
|
||||
const handleSuccessfulAdvertisingPayment = async (req, res) => {
|
||||
try {
|
||||
const { advertisingId } = req.body
|
||||
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 showDiscount = req.query.showDiscount // Get project ID from session
|
||||
|
||||
const advertising = await AdvertisingModel.findById(advertisingId)
|
||||
if (!advertising) {
|
||||
res.status(404).send('Advertising not found')
|
||||
return
|
||||
}
|
||||
|
||||
const price = await getAdvertisingPriceFromDatabase(advertising.type)
|
||||
|
||||
// Create a new payment entry
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
|
||||
status: 'successful',
|
||||
authority: '', // Add authority if available
|
||||
user_id: userId,
|
||||
advertising_id: advertisingId,
|
||||
type: 'advertising',
|
||||
installment_step: null
|
||||
})
|
||||
await payment.save()
|
||||
|
||||
// Update the advertising status to 'done' or 'paid'
|
||||
advertising.payment_status = 'done'
|
||||
advertising.status = 'temp_accept'
|
||||
await advertising.save()
|
||||
|
||||
// Emit a success event
|
||||
if ('eventEmitter' in req.app) {
|
||||
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
|
||||
} else {
|
||||
console.error('Error: eventEmitter is not defined or not properly configured.')
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Payment recorded successfully', advertisingId, price })
|
||||
} catch (error) {
|
||||
console.error('Error occurred:', error)
|
||||
res.status(500).json({ error: 'Internal server error' })
|
||||
}
|
||||
}
|
||||
// Controller function for handling failed advertising payment
|
||||
const handleFailedAdvertisingPayment = async (req, res) => {
|
||||
try {
|
||||
const { advertisingId } = req.body
|
||||
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 showDiscount = req.query.showDiscount // Get project ID from session
|
||||
const advertising = await AdvertisingModel.findById(advertisingId)
|
||||
if (!advertising) {
|
||||
res.status(404).send('Advertising not found')
|
||||
return
|
||||
}
|
||||
|
||||
const price = await getAdvertisingPriceFromDatabase(advertising.type)
|
||||
|
||||
// Create a new payment entry
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
|
||||
status: 'failed',
|
||||
authority: '', // Add authority if available
|
||||
user_id: userId,
|
||||
advertising_id: advertisingId,
|
||||
type: 'advertising'
|
||||
})
|
||||
await payment.save()
|
||||
|
||||
// Emit a failure event
|
||||
if ('eventEmitter' in req.app) {
|
||||
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment failed' })
|
||||
} else {
|
||||
console.error('Error: eventEmitter is not defined or not properly configured.')
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Payment failed', advertisingId, price })
|
||||
} catch (error) {
|
||||
console.error('Error occurred:', error)
|
||||
res.status(500).json({ error: 'Internal server error' })
|
||||
}
|
||||
}
|
||||
|
||||
const republishAdvertisingPayment = async (req, res) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
@@ -966,27 +815,30 @@ const republishAdvertisingPayment = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // 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?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
|
||||
})
|
||||
const data = response.data
|
||||
if (data.Status === 100) {
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)),
|
||||
description: `پرداخت برای سفارش شماره ${advertisingId}`,
|
||||
callback_url: `${process.env.APP_URL}/advertising/republish-verify-payment?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority (شناسه پرداخت)
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
}
|
||||
@@ -1008,16 +860,19 @@ const handleAdvertisingRepublishPaymentCallback = async (req, res) => {
|
||||
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 verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
authority,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100))
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
|
||||
status: 'successful',
|
||||
@@ -1038,7 +893,7 @@ const handleAdvertisingRepublishPaymentCallback = async (req, res) => {
|
||||
return
|
||||
}
|
||||
advertising.payment_status = 'done' // Set advertising status to 'done'
|
||||
advertising.status = 'accepted' // Set advertising status to 'done'
|
||||
advertising.status = await resolveApprovalStatus(userId, 'accepted') // Set advertising status to 'done'
|
||||
advertising.acceptedAt = new Date()
|
||||
await advertising.save()
|
||||
|
||||
@@ -1085,100 +940,6 @@ const handleAdvertisingRepublishPaymentCallback = async (req, res) => {
|
||||
res.status(500).send('Internal server error')
|
||||
}
|
||||
}
|
||||
// Controller function for handling successful republish advertising payment
|
||||
const handleSuccessfulAdvertisingRepublishPayment = async (req, res) => {
|
||||
try {
|
||||
const { advertisingId } = req.body
|
||||
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 showDiscount = req.query.showDiscount
|
||||
|
||||
const advertising = await AdvertisingModel.findById(advertisingId)
|
||||
if (!advertising) {
|
||||
res.status(404).send('Advertising not found')
|
||||
return
|
||||
}
|
||||
|
||||
const price = await getAdvertisingPriceFromDatabase(advertising.type)
|
||||
|
||||
// Create a new payment entry
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number((price + 20000) * 0.7) : Number(price * 0.7),
|
||||
status: 'successful',
|
||||
authority: '', // Add authority if available
|
||||
user_id: userId,
|
||||
advertising_id: advertisingId,
|
||||
type: 'advertising-republish',
|
||||
installment_step: null
|
||||
})
|
||||
await payment.save()
|
||||
|
||||
// Update the advertising status to 'done' or 'accepted'
|
||||
advertising.payment_status = 'done'
|
||||
advertising.status = 'accepted'
|
||||
advertising.acceptedAt = new Date()
|
||||
await advertising.save()
|
||||
|
||||
// Emit a success event
|
||||
if ('eventEmitter' in req.app) {
|
||||
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Republish payment successful' })
|
||||
} else {
|
||||
console.error('Error: eventEmitter is not defined or not properly configured.')
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Republish payment recorded successfully', advertisingId, price })
|
||||
} catch (error) {
|
||||
console.error('Error occurred:', error)
|
||||
res.status(500).json({ error: 'Internal server error' })
|
||||
}
|
||||
}
|
||||
// Controller function for handling failed republish advertising payment
|
||||
const handleFailedAdvertisingRepublishPayment = async (req, res) => {
|
||||
try {
|
||||
const { advertisingId } = req.body
|
||||
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 showDiscount = req.query.showDiscount
|
||||
|
||||
const advertising = await AdvertisingModel.findById(advertisingId)
|
||||
if (!advertising) {
|
||||
res.status(404).send('Advertising not found')
|
||||
return
|
||||
}
|
||||
|
||||
const price = await getAdvertisingPriceFromDatabase(advertising.type)
|
||||
|
||||
// Create a new payment entry
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number((price + 20000) * 0.7) : Number(price * 0.7),
|
||||
status: 'failed',
|
||||
authority: '', // Add authority if available
|
||||
user_id: userId,
|
||||
advertising_id: advertisingId,
|
||||
type: 'advertising-republish'
|
||||
})
|
||||
await payment.save()
|
||||
|
||||
// Emit a failure event
|
||||
if ('eventEmitter' in req.app) {
|
||||
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Republish payment failed' })
|
||||
} else {
|
||||
console.error('Error: eventEmitter is not defined or not properly configured.')
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Republish payment failed', advertisingId, price })
|
||||
} catch (error) {
|
||||
console.error('Error occurred:', error)
|
||||
res.status(500).json({ error: 'Internal server error' })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initiatePayment,
|
||||
handlePaymentCallback,
|
||||
@@ -1191,11 +952,5 @@ module.exports = {
|
||||
initiateAdvertisingPayment,
|
||||
handleAdvertisingPaymentCallback,
|
||||
republishAdvertisingPayment,
|
||||
handleAdvertisingRepublishPaymentCallback,
|
||||
handleSuccessfulPayment,
|
||||
handleFailedPayment,
|
||||
handleSuccessfulAdvertisingPayment,
|
||||
handleFailedAdvertisingPayment,
|
||||
handleSuccessfulAdvertisingRepublishPayment,
|
||||
handleFailedAdvertisingRepublishPayment
|
||||
handleAdvertisingRepublishPaymentCallback
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/* 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 WalletTransactionModel = require('../../../models/WalletTransactionModel');
|
||||
const { PROJECT_ACCEPT_RATE, PROJECT_COMPLETION_COMMISSION_RATE, creditProjectShare } = require('../../../utils/projectCommission');
|
||||
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 WalletTransactionModel = require('../../../models/WalletTransactionModel')
|
||||
const { PROJECT_ACCEPT_RATE, PROJECT_COMPLETION_COMMISSION_RATE, creditProjectShare } = require('../../../utils/projectCommission')
|
||||
const { isVerifiedBadge } = require('../../../utils/verifyBadge')
|
||||
|
||||
const OFFER_COMMISSION_RATE = 0.3
|
||||
|
||||
@@ -22,30 +23,43 @@ const apiKey =
|
||||
process.env.MERCHENT_CODE ||
|
||||
'c7c41e8a-918f-4741-bcd5-58f3bc51db73'
|
||||
|
||||
// درگاه قدیمیِ REST زرینپال (/pg/rest/WebGate) منسوخ شده — مبلغ به API نسخهی v4
|
||||
// باید به ریال ارسال بشه، نه تومان (برخلاف API قدیمی)
|
||||
function toRial (tomanAmount) {
|
||||
return Number(tomanAmount) * 10
|
||||
}
|
||||
|
||||
// کاربرِ بدون تیک وریفای طلایی/سبز/آبی باید از پنل ادمین تایید بشه تا پروژه/ویترینِ
|
||||
// پرداختشدهش مستقیم توی اپ/وب نمایش داده نشه
|
||||
async function resolveApprovalStatus (userId, acceptedStatus, pendingStatus = 'pending') {
|
||||
const user = await UserModel.findById(userId).select('verify_badge').lean()
|
||||
return isVerifiedBadge(user?.verify_badge) ? acceptedStatus : pendingStatus
|
||||
}
|
||||
|
||||
// Function to get price based on item name from database
|
||||
const getPriceFromDatabase = async (itemName) => {
|
||||
const item = await TypeAndPriceModel.findOne({ name: itemName });
|
||||
const item = await TypeAndPriceModel.findOne({ name: itemName })
|
||||
if (!item) {
|
||||
throw new Error(`Item "${itemName}" not found in TypeAndPriceModel`);
|
||||
throw new Error(`Item "${itemName}" not found in TypeAndPriceModel`)
|
||||
}
|
||||
return item.price;
|
||||
};
|
||||
return item.price
|
||||
}
|
||||
|
||||
const getAdvertisingPriceFromDatabase = async (itemName) => {
|
||||
const item = await AdvertisingTypeModel.findOne({ name: itemName });
|
||||
const item = await AdvertisingTypeModel.findOne({ name: itemName })
|
||||
if (!item) {
|
||||
throw new Error(`Item "${itemName}" not found in AdvertisingTypeModel`);
|
||||
throw new Error(`Item "${itemName}" not found in AdvertisingTypeModel`)
|
||||
}
|
||||
return item.price;
|
||||
};
|
||||
return item.price
|
||||
}
|
||||
|
||||
const getOfferPriceFromDatabase = async (offerType) => {
|
||||
const item = await OfferTypeModel.findOne({ name: offerType });
|
||||
const item = await OfferTypeModel.findOne({ name: offerType })
|
||||
if (!item) {
|
||||
throw new Error(`Offer type "${offerType}" not found in OfferTypeModel`);
|
||||
throw new Error(`Offer type "${offerType}" not found in OfferTypeModel`)
|
||||
}
|
||||
return item.price;
|
||||
};
|
||||
return item.price
|
||||
}
|
||||
// Controller function for initiating payment
|
||||
const initiatePaymentWeb = async (req, res) => {
|
||||
try {
|
||||
@@ -99,7 +113,7 @@ const initiatePaymentWeb = async (req, res) => {
|
||||
return res.status(404).json({ error: true, message: 'پروژه یافت نشد' })
|
||||
}
|
||||
project.payment_status = 'done'
|
||||
project.status = 'temp_accept'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept')
|
||||
await project.save()
|
||||
return res.status(200).json({
|
||||
message: 'پروژه رایگان با موفقیت ایجاد شد',
|
||||
@@ -122,26 +136,26 @@ const initiatePaymentWeb = async (req, res) => {
|
||||
}
|
||||
|
||||
const response = await axios.post(
|
||||
'https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json',
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
MerchantID: apiKey,
|
||||
Amount: Number(price),
|
||||
Description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
CallbackURL: `${callbackBase}/projects/payment-web?projectId=${projectId}&itemName=${encodeURIComponent(itemName)}&userId=${userId}`
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(price),
|
||||
description: `پرداخت برای سفارش شماره ${projectId}`,
|
||||
callback_url: `${callbackBase}/projects/payment-web?projectId=${projectId}&itemName=${encodeURIComponent(itemName)}&userId=${userId}`
|
||||
},
|
||||
{ timeout: 20000 }
|
||||
{ headers: { 'Content-Type': 'application/json' }, timeout: 20000 }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100 && data.Authority) {
|
||||
return res.status(200).json({ authority: data.Authority })
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
return res.status(200).json({ authority: result.authority })
|
||||
}
|
||||
|
||||
console.error('ZarinPal initiate error:', data)
|
||||
console.error('ZarinPal initiate error:', response.data)
|
||||
return res.status(502).json({
|
||||
error: true,
|
||||
message: 'درگاه پرداخت پاسخ نامعتبر داد. لطفاً دوباره تلاش کنید.',
|
||||
zarinpalStatus: data?.Status
|
||||
zarinpalStatus: result?.code
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('initiatePaymentWeb error:', error?.response?.data || error?.message || error)
|
||||
@@ -166,16 +180,15 @@ const handlePaymentCallbackWeb = async (req, res) => {
|
||||
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 verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority, amount: toRial(price) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: Number(price),
|
||||
status: 'successful',
|
||||
@@ -196,7 +209,7 @@ const handlePaymentCallbackWeb = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // Set project status to 'done'
|
||||
await project.save()
|
||||
|
||||
// Send event based on payment status
|
||||
@@ -288,25 +301,25 @@ const paymentRequestAcceptWeb = async (req, res) => {
|
||||
}
|
||||
|
||||
const response = await axios.post(
|
||||
'https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json',
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
MerchantID: apiKey,
|
||||
Amount: acceptAmount,
|
||||
Description: `پرداخت پذیرش همکاری برای پروژه ${project.title || projectId}`,
|
||||
CallbackURL: `${callbackBase}/projects/payment-request-step-one-callback/web?projectId=${projectId}&requestId=${requestId}&userId=${userId}`
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(acceptAmount),
|
||||
description: `پرداخت پذیرش همکاری برای پروژه ${project.title || projectId}`,
|
||||
callback_url: `${callbackBase}/projects/payment-request-step-one-callback/web?projectId=${projectId}&requestId=${requestId}&userId=${userId}`
|
||||
},
|
||||
{ timeout: 20000 }
|
||||
{ headers: { 'Content-Type': 'application/json' }, timeout: 20000 }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100 && data.Authority) {
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
return res.status(200).json({
|
||||
authority: data.Authority,
|
||||
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${data.Authority}`
|
||||
authority: result.authority,
|
||||
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`
|
||||
})
|
||||
}
|
||||
|
||||
console.error('paymentRequestAcceptWeb ZarinPal error:', data)
|
||||
console.error('paymentRequestAcceptWeb ZarinPal error:', response.data)
|
||||
return res.status(502).json({ error: true, message: 'درگاه پرداخت پاسخ نامعتبر داد. لطفاً دوباره تلاش کنید.' })
|
||||
} catch (error) {
|
||||
console.error('paymentRequestAcceptWeb error:', error?.response?.data || error?.message || error)
|
||||
@@ -332,12 +345,13 @@ const paymentRequestAcceptWebCallback = async (req, res) => {
|
||||
const acceptAmount = Math.round(Number(request.price) * PROJECT_ACCEPT_RATE)
|
||||
|
||||
const verificationResponse = await axios.post(
|
||||
'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json',
|
||||
{ MerchantID: apiKey, Authority, Amount: acceptAmount }
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority: Authority, amount: toRial(acceptAmount) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status !== 100 && verificationData.Status !== 101) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code !== 100 && verificationData?.code !== 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: acceptAmount,
|
||||
status: 'failed',
|
||||
@@ -433,25 +447,25 @@ const initiateProjectCompletionPaymentWeb = async (req, res) => {
|
||||
}
|
||||
|
||||
const response = await axios.post(
|
||||
'https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json',
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
MerchantID: apiKey,
|
||||
Amount: remainingAmount,
|
||||
Description: `پرداخت باقیمانده پروژه ${project.title || projectId}`,
|
||||
CallbackURL: `${callbackBase}/projects/payment-completion-callback/web?projectId=${projectId}&userId=${userId}`
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(remainingAmount),
|
||||
description: `پرداخت باقیمانده پروژه ${project.title || projectId}`,
|
||||
callback_url: `${callbackBase}/projects/payment-completion-callback/web?projectId=${projectId}&userId=${userId}`
|
||||
},
|
||||
{ timeout: 20000 }
|
||||
{ headers: { 'Content-Type': 'application/json' }, timeout: 20000 }
|
||||
)
|
||||
|
||||
const data = response.data
|
||||
if (data.Status === 100 && data.Authority) {
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
return res.status(200).json({
|
||||
authority: data.Authority,
|
||||
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${data.Authority}`
|
||||
authority: result.authority,
|
||||
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`
|
||||
})
|
||||
}
|
||||
|
||||
console.error('initiateProjectCompletionPaymentWeb ZarinPal error:', data)
|
||||
console.error('initiateProjectCompletionPaymentWeb ZarinPal error:', response.data)
|
||||
return res.status(502).json({ error: true, message: 'درگاه پرداخت پاسخ نامعتبر داد. لطفاً دوباره تلاش کنید.' })
|
||||
} catch (error) {
|
||||
console.error('initiateProjectCompletionPaymentWeb error:', error?.response?.data || error?.message || error)
|
||||
@@ -476,12 +490,13 @@ const handleProjectCompletionPaymentCallbackWeb = async (req, res) => {
|
||||
const remainingAmount = Math.round(Number(project.final_price)) - acceptAmount
|
||||
|
||||
const verificationResponse = await axios.post(
|
||||
'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json',
|
||||
{ MerchantID: apiKey, Authority, Amount: remainingAmount }
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{ merchant_id: apiKey, authority: Authority, amount: toRial(remainingAmount) },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status !== 100 && verificationData.Status !== 101) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code !== 100 && verificationData?.code !== 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: remainingAmount,
|
||||
status: 'failed',
|
||||
@@ -551,27 +566,30 @@ const initiateAdvertisingPaymentWeb = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // 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) {
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: process.env.ZARINPAL_MERCHANT_ID,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)),
|
||||
description: `پرداخت برای سفارش شماره ${advertisingId}`,
|
||||
callback_url: `${process.env.APP_SITE_CallBack}/advertising/payment-web?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority (شناسه پرداخت)
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
}
|
||||
@@ -594,16 +612,19 @@ const handleAdvertisingPaymentCallbackWeb = async (req, res) => {
|
||||
// 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 verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
authority,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price))
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
|
||||
status: 'successful',
|
||||
@@ -624,7 +645,7 @@ const handleAdvertisingPaymentCallbackWeb = async (req, res) => {
|
||||
return
|
||||
}
|
||||
advertising.payment_status = 'done' // Set advertising status to 'done'
|
||||
advertising.status = 'temp_accept' // Set advertising status to 'done'
|
||||
advertising.status = await resolveApprovalStatus(userId, 'temp_accept') // Set advertising status to 'done'
|
||||
await advertising.save()
|
||||
|
||||
// Send event based on payment status
|
||||
@@ -699,27 +720,30 @@ const republishAdvertisingPaymentWeb = async (req, res) => {
|
||||
return
|
||||
}
|
||||
project.payment_status = 'done' // Set project status to 'done'
|
||||
project.status = 'temp_accept' // Set project status to 'done'
|
||||
project.status = await resolveApprovalStatus(userId, 'temp_accept') // 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) {
|
||||
const response = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)),
|
||||
description: `پرداخت برای سفارش شماره ${advertisingId}`,
|
||||
callback_url: `${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
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
const result = response.data?.data
|
||||
if (result?.code === 100 && result?.authority) {
|
||||
// Payment request was successful
|
||||
const authority = data.Authority // The payment authority (شناسه پرداخت)
|
||||
res.status(200).json({ authority })
|
||||
res.status(200).json({ authority: result.authority })
|
||||
} else {
|
||||
// Payment request failed
|
||||
console.error('Error occurred while initiating payment:', data)
|
||||
console.error('Error occurred while initiating payment:', response.data)
|
||||
res.status(500).json({ error: 'Error occurred while initiating payment' })
|
||||
}
|
||||
}
|
||||
@@ -741,16 +765,19 @@ const handleAdvertisingRepublishPaymentCallbackWeb = async (req, res) => {
|
||||
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 verificationResponse = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/verify.json',
|
||||
{
|
||||
merchant_id: apiKey,
|
||||
authority,
|
||||
amount: toRial(showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100))
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
)
|
||||
|
||||
const verificationData = verificationResponse.data
|
||||
if (verificationData.Status === 100) {
|
||||
const verificationData = verificationResponse.data?.data
|
||||
if (verificationData?.code === 100 || verificationData?.code === 101) {
|
||||
const payment = new PaymentModel({
|
||||
amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
|
||||
status: 'successful',
|
||||
@@ -771,7 +798,7 @@ const handleAdvertisingRepublishPaymentCallbackWeb = async (req, res) => {
|
||||
return
|
||||
}
|
||||
advertising.payment_status = 'done' // Set advertising status to 'done'
|
||||
advertising.status = 'accepted' // Set advertising status to 'done'
|
||||
advertising.status = await resolveApprovalStatus(userId, 'accepted') // Set advertising status to 'done'
|
||||
advertising.acceptedAt = new Date()
|
||||
await advertising.save()
|
||||
|
||||
@@ -818,121 +845,38 @@ const handleAdvertisingRepublishPaymentCallbackWeb = async (req, res) => {
|
||||
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 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 decoded = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decoded.id
|
||||
|
||||
const { receiverId, offerType, platform } = req.body;
|
||||
if (!receiverId || !offerType)
|
||||
return res.status(400).json({ error: 'Invalid data' });
|
||||
const { receiverId, offerType, platform } = 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' });
|
||||
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); // ✅ ریال
|
||||
const 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,
|
||||
});
|
||||
transaction_id: null
|
||||
})
|
||||
|
||||
user.monthly_free_offer -= 1;
|
||||
await user.save();
|
||||
user.monthly_free_offer -= 1
|
||||
await user.save()
|
||||
|
||||
return res.json({
|
||||
type: 'free',
|
||||
offerId: offer._id,
|
||||
});
|
||||
offerId: offer._id
|
||||
})
|
||||
}
|
||||
|
||||
// 🎁 گیرنده «دریافت پرداختی از چت» رو غیرفعال کرده — درخواست همکاری براش همیشه
|
||||
@@ -941,23 +885,23 @@ const initiateOfferPaymentWeb = async (req, res) => {
|
||||
const offer = await OfferModel.create({
|
||||
sender: userId,
|
||||
receiver: receiverId,
|
||||
transaction_id: null,
|
||||
});
|
||||
transaction_id: null
|
||||
})
|
||||
|
||||
return res.json({
|
||||
type: 'free',
|
||||
offerId: offer._id,
|
||||
});
|
||||
offerId: offer._id
|
||||
})
|
||||
}
|
||||
|
||||
const amount = Number(price) * 10; // ✅ ریال – بدون ×10
|
||||
const platformSuffix = platform ? `&platform=${encodeURIComponent(platform)}` : '';
|
||||
const callbackUrl = `https://api.modstagram.com/api/v1/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}${platformSuffix}`;
|
||||
|
||||
const amount = Number(price) * 10 // ✅ ریال – بدون ×10
|
||||
const platformSuffix = platform ? `&platform=${encodeURIComponent(platform)}` : ''
|
||||
const callbackUrl = `https://api.modstagram.com/api/v1/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}${platformSuffix}`
|
||||
|
||||
const zarinpalRes = await axios.post(
|
||||
'https://api.zarinpal.com/pg/v4/payment/request.json',
|
||||
{
|
||||
merchant_id:"c7c41e8a-918f-4741-bcd5-58f3bc51db73",
|
||||
merchant_id: 'c7c41e8a-918f-4741-bcd5-58f3bc51db73',
|
||||
amount,
|
||||
description: `ارسال آفر به ${receiver.first_name}`,
|
||||
callback_url: callbackUrl
|
||||
@@ -965,216 +909,73 @@ const initiateOfferPaymentWeb = async (req, res) => {
|
||||
// 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 });
|
||||
|
||||
|
||||
|
||||
|
||||
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}`,
|
||||
});
|
||||
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(500).json({ error: 'ZarinPal error' });
|
||||
return res.status(500).json({ error: 'ZarinPal error' })
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return res.status(500).json({ error: 'Server error' });
|
||||
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');
|
||||
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);
|
||||
console.log('🔔 CALLBACK QUERY:', req.query)
|
||||
|
||||
const isAndroid = req.query.platform === 'android';
|
||||
const isAndroid = req.query.platform === 'android'
|
||||
|
||||
try {
|
||||
const { Authority, Status, receiverId, offerType, userId } = req.query;
|
||||
const { Authority, Status, receiverId, offerType, userId } = req.query
|
||||
|
||||
if (Status !== 'OK')
|
||||
if (Status !== 'OK') {
|
||||
return isAndroid
|
||||
? res.redirect(`modstagram://FailedPay?type=offer`)
|
||||
: res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
|
||||
? res.redirect('modstagram://FailedPay?type=offer')
|
||||
: res.redirect(`${process.env.APP_SITE}/offer/payment/failed`)
|
||||
}
|
||||
|
||||
const price = await getOfferPriceFromDatabase(offerType); // ریال
|
||||
const amount = Number(price) * 10;
|
||||
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",
|
||||
merchant_id: 'c7c41e8a-918f-4741-bcd5-58f3bc51db73',
|
||||
authority: Authority,
|
||||
amount,
|
||||
amount
|
||||
},
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
)
|
||||
|
||||
const data = verify.data.data;
|
||||
console.log('🔔payment:', data.code);
|
||||
const data = verify.data.data
|
||||
console.log('🔔payment:', data.code)
|
||||
if (data.code === 100) {
|
||||
const payment = await PaymentModel.create({
|
||||
amount : (amount / 10),
|
||||
amount: (amount / 10),
|
||||
status: 'successful',
|
||||
authority: Authority,
|
||||
ref_id: data.ref_id,
|
||||
user_id: userId,
|
||||
type: 'offer',
|
||||
});
|
||||
type: 'offer'
|
||||
})
|
||||
|
||||
console.log('🔔payment:', payment);
|
||||
console.log('🔔payment:', payment)
|
||||
|
||||
const offerPriceToman = amount / 10
|
||||
const receiverShare = Math.round(offerPriceToman * OFFER_COMMISSION_RATE)
|
||||
@@ -1185,31 +986,26 @@ const handleOfferPaymentCallback = async (req, res) => {
|
||||
sender: userId,
|
||||
receiver: receiverId,
|
||||
transaction_id: payment._id,
|
||||
pendingCommissionAmount: receiverShare,
|
||||
});
|
||||
pendingCommissionAmount: receiverShare
|
||||
})
|
||||
|
||||
return isAndroid
|
||||
? res.redirect(`modstagram://SuccessPay?type=offer&offerId=${offer._id}`)
|
||||
: res.redirect(
|
||||
`${process.env.APP_SITE}/offer/payment/success?offerId=${offer._id}`
|
||||
);
|
||||
`${process.env.APP_SITE}/offer/payment/success?offerId=${offer._id}`
|
||||
)
|
||||
}
|
||||
|
||||
return isAndroid
|
||||
? res.redirect(`modstagram://FailedPay?type=offer`)
|
||||
: res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
|
||||
? res.redirect('modstagram://FailedPay?type=offer')
|
||||
: res.redirect(`${process.env.APP_SITE}/offer/payment/failed`)
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error(err)
|
||||
return isAndroid
|
||||
? res.redirect(`modstagram://FailedPay?type=offer`)
|
||||
: res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
|
||||
? res.redirect('modstagram://FailedPay?type=offer')
|
||||
: res.redirect(`${process.env.APP_SITE}/offer/payment/failed`)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initiatePaymentWeb,
|
||||
@@ -1223,5 +1019,5 @@ module.exports = {
|
||||
republishAdvertisingPaymentWeb,
|
||||
handleAdvertisingRepublishPaymentCallbackWeb,
|
||||
initiateOfferPaymentWeb,
|
||||
handleOfferPaymentCallback,
|
||||
};
|
||||
handleOfferPaymentCallback
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user