From 14532bf6db1220f58a1f686eef99cd53750c3cb6 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Sat, 31 Aug 2024 22:59:45 +0530 Subject: [PATCH] refactor: change help text --- apps/OpenSign/src/components/BulkSendUi.js | 39 +- apps/OpenSign/src/pages/GenerateToken.js | 23 +- .../src/primitives/GetReportDisplay.js | 14 +- .../v1/routes/CreateDocumentWithTemplate.js | 552 ++++++++-------- .../v1/routes/createDocumentwithCoordinate.js | 594 +++++++++--------- apps/OpenSignServer/cloud/main.js | 4 + .../cloud/parsefunction/AllowedCredits.js | 38 ++ .../cloud/parsefunction/BuyCredits.js | 92 +++ .../cloud/parsefunction/createBatchDocs.js | 106 ++-- 9 files changed, 836 insertions(+), 626 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/BuyCredits.js diff --git a/apps/OpenSign/src/components/BulkSendUi.js b/apps/OpenSign/src/components/BulkSendUi.js index 5a6e218fb..d97678e97 100644 --- a/apps/OpenSign/src/components/BulkSendUi.js +++ b/apps/OpenSign/src/components/BulkSendUi.js @@ -24,7 +24,7 @@ const BulkSendUi = (props) => { price: (75.0).toFixed(2), quantity: 500, priceperbulksend: 0.15, - totalQuickSend: 0 + totalcredits: 0 }); const [isQuotaReached, setIsQuotaReached] = useState(false); const [isLoader, setIsLoader] = useState(false); @@ -44,11 +44,16 @@ const BulkSendUi = (props) => { if (subscription?.plan === "freeplan") { setIsFreePlan(true); } - const allowedquicksend = await Parse.Cloud.run("allowedquicksend"); - if (allowedquicksend > 0) { - setIsBulkAvailable(true); + const resCredits = await Parse.Cloud.run("allowedcredits"); + if (resCredits) { + const allowedcredits = resCredits?.allowedcredits || 0; + const addoncredits = resCredits?.addoncredits || 0; + const totalcredits = allowedcredits + addoncredits; + if (totalcredits > 0) { + setIsBulkAvailable(true); + } + setAmount((obj) => ({ ...obj, totalcredits: totalcredits })); } - setAmount((obj) => ({ ...obj, totalQuickSend: allowedquicksend })); const getPlaceholder = props.item?.Placeholders; const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) => placeholderObj?.placeHolder?.some((holder) => @@ -121,7 +126,7 @@ const BulkSendUi = (props) => { const handleAddForm = (e) => { e.preventDefault(); // Check if the quick send limit has been reached - if (isEnableSubscription && forms.length >= amount.totalQuickSend) { + if (isEnableSubscription && forms.length >= amount.totalcredits) { setIsQuotaReached(true); } else { if (forms?.length < allowedForm) { @@ -244,8 +249,8 @@ const BulkSendUi = (props) => { e.stopPropagation(); setIsSubmit(true); try { - const resAddon = await Parse.Cloud.run("buyquicksend", { - quicksend: amount.quantity + const resAddon = await Parse.Cloud.run("buycredits", { + credits: amount.quantity }); if (resAddon) { const _resAddon = JSON.parse(JSON.stringify(resAddon)); @@ -257,7 +262,7 @@ const BulkSendUi = (props) => { quantity: 500, priceperbulksend: 0.15, price: (75.0).toFixed(2), - totalQuickSend: _resAddon.addon + totalcredits: _resAddon.addon })); } } @@ -345,13 +350,15 @@ const BulkSendUi = (props) => { ))}
- + {isEnableSubscription && ( + + )}

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + - sender + - ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

'; - - let replaceVar; - const variables = { - document_title: template.Name, - sender_name: template.ExtUserPtr.Name, - sender_mail: template.ExtUserPtr.Email, - sender_phone: template.ExtUserPtr?.Phone || '', - receiver_name: contactMail[i].name, - receiver_email: contactMail[i].email, - receiver_phone: contactMail[i]?.phone || '', - expiry_date: localExpireDate, - company_name: orgName, - signing_url: signPdf, - }; - if (email_subject && email_body) { - replaceVar = replaceMailVaribles(email_subject, email_body, variables); - } else if (email_subject) { - replaceVar = replaceMailVaribles(email_subject, '', variables); - replaceVar = { subject: replaceVar.subject, body: email_html }; - } else if (email_body) { - replaceVar = replaceMailVaribles( - `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`, - email_body, - variables - ); - } else { - replaceVar = { - subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`, - body: email_html, + if (matchingSigner) { + return { + ...x, + signerObjId: matchingSigner?.contactPtr?.objectId, + signerPtr: matchingSigner?.contactPtr, }; + } else { + return { ...x }; } + }); + object.set('Placeholders', updatedPlaceholder); + } else { + object.set('Signers', templateSigner); + } + object.set('URL', template.URL); + object.set('SignedUrl', template.URL); + object.set('SentToOthers', true); + if (TimeToCompleteDays) { + object.set('TimeToCompleteDays', TimeToCompleteDays); + } + object.set('CreatedBy', template.CreatedBy); + object.set('ExtUserPtr', { + __type: 'Pointer', + className: 'contracts_Users', + objectId: template.ExtUserPtr.objectId, + }); + if (folderId) { + object.set('Folder', folderPtr); + } + const newACL = new Parse.ACL(); + newACL.setPublicReadAccess(false); + newACL.setPublicWriteAccess(false); + newACL.setReadAccess(userPtr.objectId, true); + newACL.setWriteAccess(userPtr.objectId, true); + object.setACL(newACL); + const res = await object.save(null, { useMasterKey: true }); - const subject = replaceVar.subject; - const html = replaceVar.body; - let params = { - recipient: contactMail[i].email, - subject: subject, - from: sender, - html: html, - extUserId: template.ExtUserPtr.objectId, - }; - sendMail = await axios.post(url, params, { headers: headers }); - } catch (error) { - console.log('error', error); + const newDate = new Date(); + newDate.setDate(newDate.getDate() + 15); + const localExpireDate = newDate.toLocaleDateString('en-US', { + day: 'numeric', + month: 'long', + year: 'numeric', + }); + let sender = template.ExtUserPtr.Email; + let sendMail; + if (send_email === false) { + console.log("don't send mail"); + } else { + let contactMail = contact; + if (sendInOrder) { + contactMail = contact.slice(); + contactMail.splice(1); + } + for (let i = 0; i < contactMail.length; i++) { + try { + const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; + let url = `${cloudServerUrl}/functions/sendmailv3/`; + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': process.env.APP_ID, + 'X-Parse-Master-Key': process.env.MASTER_KEY, + }; + + const objectId = contactMail[i].contactPtr.objectId; + const hostUrl = baseUrl.origin; + //encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function + const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`); + let signPdf = `${hostUrl}/login/${encodeBase64}`; + const openSignUrl = 'https://www.opensignlabs.com/contact-us'; + const orgName = template.ExtUserPtr.Company + ? template.ExtUserPtr.Company + : ''; + const themeBGcolor = '#47a3ad'; + const email_html = + "

Digital Signature Request

" + + template.ExtUserPtr.Name + + ' has requested you to review and sign ' + + template.Name + + ".

Sender " + + sender + + "
Organization " + + orgName + + "
Expires on " + + localExpireDate + + "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + sender + + ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

'; + + let replaceVar; + const variables = { + document_title: template.Name, + sender_name: template.ExtUserPtr.Name, + sender_mail: template.ExtUserPtr.Email, + sender_phone: template.ExtUserPtr?.Phone || '', + receiver_name: contactMail[i].name, + receiver_email: contactMail[i].email, + receiver_phone: contactMail[i]?.phone || '', + expiry_date: localExpireDate, + company_name: orgName, + signing_url: signPdf, + }; + if (email_subject && email_body) { + replaceVar = replaceMailVaribles(email_subject, email_body, variables); + } else if (email_subject) { + replaceVar = replaceMailVaribles(email_subject, '', variables); + replaceVar = { subject: replaceVar.subject, body: email_html }; + } else if (email_body) { + replaceVar = replaceMailVaribles( + `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`, + email_body, + variables + ); + } else { + replaceVar = { + subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`, + body: email_html, + }; + } + + const subject = replaceVar.subject; + const html = replaceVar.body; + let params = { + recipient: contactMail[i].email, + subject: subject, + from: sender, + html: html, + extUserId: template.ExtUserPtr.objectId, + }; + sendMail = await axios.post(url, params, { headers: headers }); + } catch (error) { + console.log('error', error); + } } } - } - // if (sendMail.data.result.status === 'success') { - try { - const doc = { - objectId: res.id, - file: template?.URL, - name: template?.Name, - note: template?.Note || '', - description: template?.Description || '', - signers: contact?.map(x => ({ - name: x.name, - email: x.email, - phone: x?.phone || '', - })), - createdAt: res.createdAt, - }; - if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) { - sendDoctoWebhook(doc, template.ExtUserPtr?.Webhook, userPtr?.objectId); + // if (sendMail.data.result.status === 'success') { + try { + const doc = { + objectId: res.id, + file: template?.URL, + name: template?.Name, + note: template?.Note || '', + description: template?.Description || '', + signers: contact?.map(x => ({ + name: x.name, + email: x.email, + phone: x?.phone || '', + })), + createdAt: res.createdAt, + }; + if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) { + sendDoctoWebhook(doc, template.ExtUserPtr?.Webhook, userPtr?.objectId); + } + } catch (err) { + console.log('Err', err); } - } catch (err) { - console.log('Err', err); - } - if (request.posthog) { - request.posthog?.capture({ - distinctId: parseUser.userId.email, - event: 'api_create_document_with_templateid', - properties: { response_code: 200 }, + if (request.posthog) { + request.posthog?.capture({ + distinctId: parseUser.userId.email, + event: 'api_create_document_with_templateid', + properties: { response_code: 200 }, + }); + } + + const subscriptionCls = new Parse.Object('contracts_Subscriptions'); + subscriptionCls.id = resSub.id; + if (allowedCredits > 0) { + const updateAllowedcredits = allowedCredits - 1 || 0; + subscriptionCls.set('AllowedCredits', updateAllowedcredits); + } else { + const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0; + subscriptionCls.set('AddonCredits', updateAddonCredits); + } + const resSubcription = await subscriptionCls.save(null, { useMasterKey: true }); + // console.log("resSubcription ", resSubcription) + return response.json({ + objectId: res.id, + signurl: contact.map(x => ({ + email: x.email, + url: `${baseUrl.origin}/login/${btoa( + `${res.id}/${x.email}/${x.contactPtr.objectId}` + )}`, + })), + message: 'Document sent successfully!', }); + } else { + if (request.posthog) { + request.posthog?.capture({ + distinctId: parseUser.userId.email, + event: 'api_create_document_with_templateid', + properties: { response_code: 400 }, + }); + } + return response.status(400).json({ error: 'Please provide signers properly!' }); } - const updateApiCount = - resSub?.get('AllowedApis') && resSub.get('AllowedApis') > 0 - ? resSub.get('AllowedApis') - 1 - : 0; - const subscriptionCls = new Parse.Object('contracts_Subscriptions'); - subscriptionCls.id = resSub.id; - subscriptionCls.set('AllowedApis', updateApiCount); - const resSubcription = await subscriptionCls.save(null, { useMasterKey: true }); - // console.log("resSubcription ", resSubcription) - return response.json({ - objectId: res.id, - signurl: contact.map(x => ({ - email: x.email, - url: `${baseUrl.origin}/login/${btoa( - `${res.id}/${x.email}/${x.contactPtr.objectId}` - )}`, - })), - message: 'Document sent successfully!', - }); } else { if (request.posthog) { request.posthog?.capture({ @@ -368,32 +387,25 @@ export default async function createDocumentWithTemplate(request, response) { properties: { response_code: 400 }, }); } - return response.status(400).json({ error: 'Please provide signers properly!' }); + return response.status(400).json({ error: 'Please setup template properly!' }); } } else { if (request.posthog) { request.posthog?.capture({ distinctId: parseUser.userId.email, event: 'api_create_document_with_templateid', - properties: { response_code: 400 }, + properties: { response_code: 404 }, }); } - return response.status(400).json({ error: 'Please setup template properly!' }); + return response.status(404).json({ error: 'Invalid template id!' }); } } else { - if (request.posthog) { - request.posthog?.capture({ - distinctId: parseUser.userId.email, - event: 'api_create_document_with_templateid', - properties: { response_code: 404 }, - }); - } - return response.status(404).json({ error: 'Invalid template id!' }); + return response + .status(429) + .json({ error: 'Quota reached, Please buy credits and try again later.' }); } } else { - return response - .status(429) - .json({ error: 'Quota reached, Please buy API credits and try again later.' }); + return response.status(400).json({ error: 'Please buy subscriptions.' }); } } else { return response.status(405).json({ error: 'Invalid API Token!' }); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index ffa310bfd..a4cc211a3 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -95,325 +95,335 @@ export default async function createDocumentwithCoordinate(request, response) { objectId: extUser.get('TenantId').id, }); subscription.include('ExtUserPtr'); - subscription.greaterThan('AllowedApis', 0); const resSub = await subscription.first({ useMasterKey: true }); if (resSub) { - if (signers && signers.length > 0) { - // Check if at least one signature exists among all items in the signers array - let isSignExist = signers.every(item => - item.widgets.some(data => data?.type === 'signature') - ); - if (!isSignExist) { - return response - .status(400) - .json({ error: 'Please add at least one signature widget for all signers' }); - } - let fileUrl; - if (request.files?.[0]) { - const base64 = fileData?.toString('base64'); - const file = new Parse.File(request.files?.[0]?.originalname, { - base64: base64, - }); - await file.save({ useMasterKey: true }); - fileUrl = file.url(); - const buffer = Buffer.from(base64, 'base64'); - saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); - } else { - const filename = sanitizeFileName(`${name}.pdf`); - const file = new Parse.File(filename, { base64: base64File }, 'application/pdf'); - await file.save({ useMasterKey: true }); - fileUrl = file.url(); - const buffer = Buffer.from(base64File, 'base64'); - saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); - } - const contractsUser = new Parse.Query('contracts_Users'); - contractsUser.equalTo('UserId', userPtr); - const extUser = await contractsUser.first({ useMasterKey: true }); - const parseExtUser = JSON.parse(JSON.stringify(extUser)); - - const extUserPtr = { - __type: 'Pointer', - className: 'contracts_Users', - objectId: extUser.id, - }; - - const object = new Parse.Object('contracts_Document'); - object.set('Name', name); - - if (note) { - object.set('Note', note); - } - if (description) { - object.set('Description', description); - } - if (sendInOrder) { - object.set('SendinOrder', sendInOrder); - } - object.set('URL', fileUrl); - object.set('SignedUrl', fileUrl); - object.set('SentToOthers', true); - object.set('CreatedBy', userPtr); - object.set('ExtUserPtr', extUserPtr); - if (TimeToCompleteDays) { - object.set('TimeToCompleteDays', TimeToCompleteDays); - } - object.set('IsSendMail', send_email); - let contact = []; + const _resSub = JSON.parse(JSON.stringify(resSub)); + const allowedCredits = _resSub?.AllowedCredits || 0; + const addonCredits = _resSub?.AddonCredits || 0; + const totalcredits = allowedCredits + addonCredits; + if (totalcredits > 0) { if (signers && signers.length > 0) { - let parseSigners; - if (base64File) { - parseSigners = signers; - } else { - parseSigners = JSON.parse(signers); + // Check if at least one signature exists among all items in the signers array + let isSignExist = signers.every(item => + item.widgets.some(data => data?.type === 'signature') + ); + if (!isSignExist) { + return response + .status(400) + .json({ error: 'Please add at least one signature widget for all signers' }); } - let createContactUrl = protocol + '/v1/createcontact'; + let fileUrl; + if (request.files?.[0]) { + const base64 = fileData?.toString('base64'); + const file = new Parse.File(request.files?.[0]?.originalname, { + base64: base64, + }); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + const buffer = Buffer.from(base64, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); + } else { + const filename = sanitizeFileName(`${name}.pdf`); + const file = new Parse.File(filename, { base64: base64File }, 'application/pdf'); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + const buffer = Buffer.from(base64File, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); + } + const contractsUser = new Parse.Query('contracts_Users'); + contractsUser.equalTo('UserId', userPtr); + const extUser = await contractsUser.first({ useMasterKey: true }); + const parseExtUser = JSON.parse(JSON.stringify(extUser)); - for (const [index, element] of parseSigners.entries()) { - const body = { - name: element?.name || '', - email: element?.email || '', - phone: element?.phone || '', - }; - try { - const res = await axios.post(createContactUrl, body, { - headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken }, - }); - // console.log('res ', res.data); - const contactPtr = { - __type: 'Pointer', - className: 'contracts_Contactbook', - objectId: res.data?.objectId, + const extUserPtr = { + __type: 'Pointer', + className: 'contracts_Users', + objectId: extUser.id, + }; + + const object = new Parse.Object('contracts_Document'); + object.set('Name', name); + + if (note) { + object.set('Note', note); + } + if (description) { + object.set('Description', description); + } + if (sendInOrder) { + object.set('SendinOrder', sendInOrder); + } + object.set('URL', fileUrl); + object.set('SignedUrl', fileUrl); + object.set('SentToOthers', true); + object.set('CreatedBy', userPtr); + object.set('ExtUserPtr', extUserPtr); + if (TimeToCompleteDays) { + object.set('TimeToCompleteDays', TimeToCompleteDays); + } + object.set('IsSendMail', send_email); + let contact = []; + if (signers && signers.length > 0) { + let parseSigners; + if (base64File) { + parseSigners = signers; + } else { + parseSigners = JSON.parse(signers); + } + let createContactUrl = protocol + '/v1/createcontact'; + + for (const [index, element] of parseSigners.entries()) { + const body = { + name: element?.name || '', + email: element?.email || '', + phone: element?.phone || '', }; - const newObj = { ...element, contactPtr: contactPtr, index: index }; - contact.push(newObj); - } catch (err) { - // console.log('err ', err.response); - if (err?.response?.data?.objectId) { + try { + const res = await axios.post(createContactUrl, body, { + headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken }, + }); + // console.log('res ', res.data); const contactPtr = { __type: 'Pointer', className: 'contracts_Contactbook', - objectId: err.response.data?.objectId, + objectId: res.data?.objectId, }; const newObj = { ...element, contactPtr: contactPtr, index: index }; contact.push(newObj); + } catch (err) { + // console.log('err ', err.response); + if (err?.response?.data?.objectId) { + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: err.response.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } } } - } - object.set( - 'Signers', - contact?.map(x => x.contactPtr) - ); - let updatePlaceholders = contact.map(signer => { - const placeHolder = []; + object.set( + 'Signers', + contact?.map(x => x.contactPtr) + ); + let updatePlaceholders = contact.map(signer => { + const placeHolder = []; - for (const widget of signer.widgets) { - const pageNumber = widget.page; - const options = formatWidgetOptions(widget.type, widget.options); - const page = placeHolder.find(page => page.pageNumber === pageNumber); - const widgetData = { - isStamp: widget.type === 'stamp' || widget.type === 'image', - key: randomId(), - isDrag: false, - scale: 1, - isMobile: false, - zIndex: 1, - type: widget.type === 'textbox' ? 'text input' : widget.type, - options: options, - Width: widget.w, - Height: widget.h, - xPosition: widget.x, - yPosition: widget.y, - }; - - if (page) { - page.pos.push(widgetData); - } else { - placeHolder.push({ pageNumber, pos: [widgetData] }); - } - } - - return { - signerObjId: signer?.contactPtr?.objectId, - signerPtr: signer?.contactPtr, - Role: signer.role, - Id: randomId(), - blockColor: color[signer?.index], - placeHolder, - }; - }); - object.set('Placeholders', updatePlaceholders); - } - if (folderId) { - object.set('Folder', { - __type: 'Pointer', - className: 'contracts_Document', - objectId: folderId, - }); - } - const newACL = new Parse.ACL(); - newACL.setPublicReadAccess(false); - newACL.setPublicWriteAccess(false); - newACL.setReadAccess(userPtr.objectId, true); - newACL.setWriteAccess(userPtr.objectId, true); - object.setACL(newACL); - const res = await object.save(null, { useMasterKey: true }); - const doc = { - objectId: res.id, - file: fileUrl, - name: name, - note: note || '', - description: description || '', - signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x?.phone || '' })), - createdAt: res.createdAt, - }; - if (parseExtUser && parseExtUser.Webhook) { - sendDoctoWebhook(doc, parseExtUser?.Webhook, userPtr?.objectId); - } - const newDate = new Date(); - newDate.setDate(newDate.getDate() + 15); - const localExpireDate = newDate.toLocaleDateString('en-US', { - day: 'numeric', - month: 'long', - year: 'numeric', - }); - let sender = parseExtUser.Email; - if (send_email === false) { - console.log("don't send mail"); - } else { - let contactMail = contact; - if (sendInOrder) { - contactMail = contact.slice(); - contactMail.splice(1); - } - for (let i = 0; i < contactMail.length; i++) { - try { - const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; - let url = `${cloudServerUrl}/functions/sendmailv3/`; - const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': process.env.APP_ID, - 'X-Parse-Master-Key': process.env.MASTER_KEY, - }; - - const objectId = contactMail[i].contactPtr.objectId; - const hostUrl = baseUrl.origin; - //encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function - const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`); - let signPdf = `${hostUrl}/login/${encodeBase64}`; - const openSignUrl = 'https://www.opensignlabs.com/contact-us'; - const orgName = parseExtUser.Company ? parseExtUser.Company : ''; - const themeBGcolor = '#47a3ad'; - const email_html = - "

Digital Signature Request

" + - parseExtUser.Name + - ' has requested you to review and sign ' + - name + - ".

Sender " + - sender + - "
Organization " + - orgName + - "
Expires on " + - localExpireDate + - "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + - sender + - ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

'; - let replaceVar; - const variables = { - document_title: name, - sender_name: parseExtUser.Name, - sender_mail: parseExtUser.Email, - sender_phone: parseExtUser?.Phone || '', - receiver_name: contactMail[i].name, - receiver_email: contactMail[i].email, - receiver_phone: contactMail[i]?.phone || '', - expiry_date: localExpireDate, - company_name: orgName, - signing_url: signPdf, - }; - if (email_subject && email_body) { - replaceVar = replaceMailVaribles(email_subject, email_body, variables); - } else if (email_subject) { - replaceVar = replaceMailVaribles(email_subject, '', variables); - replaceVar = { subject: replaceVar.subject, body: email_html }; - } else if (email_body) { - replaceVar = replaceMailVaribles( - `${parseExtUser.Name} has requested you to sign "${name}"`, - email_body, - variables - ); - } else { - replaceVar = { - subject: `${parseExtUser.Name} has requested you to sign "${name}"`, - body: email_html, + for (const widget of signer.widgets) { + const pageNumber = widget.page; + const options = formatWidgetOptions(widget.type, widget.options); + const page = placeHolder.find(page => page.pageNumber === pageNumber); + const widgetData = { + isStamp: widget.type === 'stamp' || widget.type === 'image', + key: randomId(), + isDrag: false, + scale: 1, + isMobile: false, + zIndex: 1, + type: widget.type === 'textbox' ? 'text input' : widget.type, + options: options, + Width: widget.w, + Height: widget.h, + xPosition: widget.x, + yPosition: widget.y, }; + + if (page) { + page.pos.push(widgetData); + } else { + placeHolder.push({ pageNumber, pos: [widgetData] }); + } } - const subject = replaceVar.subject; - const html = replaceVar.body; - let params = { - recipient: contactMail[i].email, - subject: subject, - from: sender, - html: html, - extUserId: extUser.id, + return { + signerObjId: signer?.contactPtr?.objectId, + signerPtr: signer?.contactPtr, + Role: signer.role, + Id: randomId(), + blockColor: color[signer?.index], + placeHolder, }; + }); + object.set('Placeholders', updatePlaceholders); + } + if (folderId) { + object.set('Folder', { + __type: 'Pointer', + className: 'contracts_Document', + objectId: folderId, + }); + } + const newACL = new Parse.ACL(); + newACL.setPublicReadAccess(false); + newACL.setPublicWriteAccess(false); + newACL.setReadAccess(userPtr.objectId, true); + newACL.setWriteAccess(userPtr.objectId, true); + object.setACL(newACL); + const res = await object.save(null, { useMasterKey: true }); + const doc = { + objectId: res.id, + file: fileUrl, + name: name, + note: note || '', + description: description || '', + signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x?.phone || '' })), + createdAt: res.createdAt, + }; + if (parseExtUser && parseExtUser.Webhook) { + sendDoctoWebhook(doc, parseExtUser?.Webhook, userPtr?.objectId); + } + const newDate = new Date(); + newDate.setDate(newDate.getDate() + 15); + const localExpireDate = newDate.toLocaleDateString('en-US', { + day: 'numeric', + month: 'long', + year: 'numeric', + }); + let sender = parseExtUser.Email; + if (send_email === false) { + console.log("don't send mail"); + } else { + let contactMail = contact; + if (sendInOrder) { + contactMail = contact.slice(); + contactMail.splice(1); + } + for (let i = 0; i < contactMail.length; i++) { + try { + const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; + let url = `${cloudServerUrl}/functions/sendmailv3/`; + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': process.env.APP_ID, + 'X-Parse-Master-Key': process.env.MASTER_KEY, + }; - await axios.post(url, params, { headers: headers }); - } catch (error) { - console.log('error', error); + const objectId = contactMail[i].contactPtr.objectId; + const hostUrl = baseUrl.origin; + //encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function + const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`); + let signPdf = `${hostUrl}/login/${encodeBase64}`; + const openSignUrl = 'https://www.opensignlabs.com/contact-us'; + const orgName = parseExtUser.Company ? parseExtUser.Company : ''; + const themeBGcolor = '#47a3ad'; + const email_html = + "

Digital Signature Request

" + + parseExtUser.Name + + ' has requested you to review and sign ' + + name + + ".

Sender " + + sender + + "
Organization " + + orgName + + "
Expires on " + + localExpireDate + + "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + sender + + ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

'; + let replaceVar; + const variables = { + document_title: name, + sender_name: parseExtUser.Name, + sender_mail: parseExtUser.Email, + sender_phone: parseExtUser?.Phone || '', + receiver_name: contactMail[i].name, + receiver_email: contactMail[i].email, + receiver_phone: contactMail[i]?.phone || '', + expiry_date: localExpireDate, + company_name: orgName, + signing_url: signPdf, + }; + if (email_subject && email_body) { + replaceVar = replaceMailVaribles(email_subject, email_body, variables); + } else if (email_subject) { + replaceVar = replaceMailVaribles(email_subject, '', variables); + replaceVar = { subject: replaceVar.subject, body: email_html }; + } else if (email_body) { + replaceVar = replaceMailVaribles( + `${parseExtUser.Name} has requested you to sign "${name}"`, + email_body, + variables + ); + } else { + replaceVar = { + subject: `${parseExtUser.Name} has requested you to sign "${name}"`, + body: email_html, + }; + } + const subject = replaceVar.subject; + const html = replaceVar.body; + + let params = { + recipient: contactMail[i].email, + subject: subject, + from: sender, + html: html, + extUserId: extUser.id, + }; + + await axios.post(url, params, { headers: headers }); + } catch (error) { + console.log('error', error); + } } } - } - // if (sendMail.data.result.status === 'success') { - if (request.posthog) { - request.posthog?.capture({ - distinctId: parseUser.userId.email, - event: 'api_create_document', - properties: { response_code: 200 }, + // if (sendMail.data.result.status === 'success') { + if (request.posthog) { + request.posthog?.capture({ + distinctId: parseUser.userId.email, + event: 'api_create_document', + properties: { response_code: 200 }, + }); + } + + const subscriptionCls = new Parse.Object('contracts_Subscriptions'); + subscriptionCls.id = resSub.id; + if (allowedCredits > 0) { + const updateAllowedcredits = allowedCredits - 1 || 0; + subscriptionCls.set('AllowedCredits', updateAllowedcredits); + } else { + const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0; + subscriptionCls.set('AddonCredits', updateAddonCredits); + } + const resSubcription = await subscriptionCls.save(null, { useMasterKey: true }); + // console.log('resSubcription ', resSubcription); + return response.json({ + objectId: res.id, + signurl: contact.map(x => ({ + email: x.email, + url: `${baseUrl.origin}/login/${btoa( + `${res.id}/${x.email}/${x.contactPtr.objectId}` + )}`, + })), + message: 'Document sent successfully!', }); + // } + } else { + if (request.posthog) { + request.posthog?.capture({ + distinctId: parseUser.userId.email, + event: 'api_create_document', + properties: { response_code: 400 }, + }); + } + return response.status(400).json({ error: 'Please provide signers!' }); } - const updateApiCount = - resSub?.get('AllowedApis') && resSub.get('AllowedApis') > 0 - ? resSub.get('AllowedApis') - 1 - : 0; - const subscriptionCls = new Parse.Object('contracts_Subscriptions'); - subscriptionCls.id = resSub.id; - subscriptionCls.set('AllowedApis', updateApiCount); - const resSubcription = await subscriptionCls.save(null, { useMasterKey: true }); - // console.log('resSubcription ', resSubcription); - return response.json({ - objectId: res.id, - signurl: contact.map(x => ({ - email: x.email, - url: `${baseUrl.origin}/login/${btoa( - `${res.id}/${x.email}/${x.contactPtr.objectId}` - )}`, - })), - message: 'Document sent successfully!', - }); - // } } else { - if (request.posthog) { - request.posthog?.capture({ - distinctId: parseUser.userId.email, - event: 'api_create_document', - properties: { response_code: 400 }, - }); - } - return response.status(400).json({ error: 'Please provide signers!' }); + return response + .status(429) + .json({ error: 'Quota reached, Please buy credits and try again later.' }); } } else { - return response - .status(429) - .json({ error: 'Quota reached, Please buy API credits and try again later.' }); + return response.status(400).json({ error: 'Please buy subscriptions.' }); } } else { return response.status(405).json({ error: 'Invalid API Token!' }); diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index fc4f61817..05178fff1 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -68,6 +68,8 @@ import AllowedQuicksend from './parsefunction/AllowedQuicksend.js'; import BuyQuickSend from './parsefunction/BuyQuicksend.js'; import ExtUserAftersave from './parsefunction/ExtUserAftersave.js'; import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js'; +import AllowedCredits from './parsefunction/AllowedCredits.js'; +import BuyCredits from './parsefunction/BuyCredits.js'; // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); @@ -147,3 +149,5 @@ Parse.Cloud.define('allowedapis', AllowedApis); Parse.Cloud.define('allowedquicksend', AllowedQuicksend); Parse.Cloud.define('buyapis', BuyApis); Parse.Cloud.define('buyquicksend', BuyQuickSend); +Parse.Cloud.define('allowedcredits', AllowedCredits); +Parse.Cloud.define('buycredits', BuyCredits); diff --git a/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js b/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js new file mode 100644 index 000000000..04b0eb79e --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js @@ -0,0 +1,38 @@ +export default async function AllowedCredits(request) { + if (!request?.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } + try { + const extUser = new Parse.Query('contracts_Users'); + extUser.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: request.user.id, + }); + const resExtUser = await extUser.first({ useMasterKey: true }); + if (resExtUser) { + const _resExtUser = JSON.parse(JSON.stringify(resExtUser)); + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.equalTo('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: _resExtUser.TenantId.objectId, + }); + subscription.include('ExtUserPtr'); + const resSub = await subscription.first({ useMasterKey: true }); + if (resSub) { + const _resSub = JSON.parse(JSON.stringify(resSub)); + const AllowedCredits = _resSub?.AllowedCredits || 0; + const AddonCredits = _resSub?.AddonCredits || 0; + return { allowedcredits: AllowedCredits, addoncredits: AddonCredits }; + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + } + } catch (err) { + console.log('err in allowedCredits', err); + const code = err?.code || 400; + const msg = err?.message || 'Something went wrong.'; + throw new Parse.Error(code, msg); + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/BuyCredits.js b/apps/OpenSignServer/cloud/parsefunction/BuyCredits.js new file mode 100644 index 000000000..0e0e99827 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/BuyCredits.js @@ -0,0 +1,92 @@ +import axios from 'axios'; +export default async function BuyCredits(request) { + const credits = request.params.credits; + if (!request?.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } + if (credits) { + try { + const extUser = new Parse.Query('contracts_Users'); + extUser.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: request.user.id, + }); + const resExtUser = await extUser.first({ useMasterKey: true }); + if (resExtUser) { + const _resExtUser = JSON.parse(JSON.stringify(resExtUser)); + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.equalTo('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: _resExtUser.TenantId.objectId, + }); + subscription.include('ExtUserPtr'); + const resSub = await subscription.first({ useMasterKey: true }); + if (resSub) { + const _resSub = JSON.parse(JSON.stringify(resSub)); + // Define the URL + const url = 'https://accounts.zoho.in/oauth/v2/token'; + + // Convert the data to x-www-form-urlencoded format + const formData = new URLSearchParams(); + formData.append('refresh_token', process.env.ZOHO_REFRESH_TOKEN); + formData.append('client_id', process.env.ZOHO_CLIENT_ID); + formData.append('client_secret', process.env.ZOHO_CLIENT_SECRET); + formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI); + formData.append('grant_type', 'refresh_token'); + + const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + + // Make the POST request using Axios + const res = await axios.post(url, formData, { headers }); + if (res.data.access_token) { + const subscriptionId = _resSub.SubscriptionId; + const plan_code = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.plan_code; + const quantity = parseInt(credits); + const addoncode = 'addon-credits'; + const data = JSON.stringify({ + plan: { plan_code: plan_code }, + addons: [ + { addon_code: addoncode, addon_description: 'addon credits', quantity: quantity }, + ], + }); + const creditsUrl = `https://www.zohoapis.in/billing/v1/subscriptions/${subscriptionId}/buyonetimeaddon`; + const resCredits = await axios.post(creditsUrl, data, { + headers: { + Authorization: 'Zoho-oauthtoken ' + res.data.access_token, + 'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID, + }, + }); + // console.log('resCredits ', resCredits.data); + if (resCredits.data) { + const existAddonCredits = _resSub?.AddonCredits ? _resSub.AddonCredits : 0; + const addonCredits = existAddonCredits + quantity; + const updateSub = new Parse.Object('contracts_Subscriptions'); + updateSub.id = resSub.id; + updateSub.set('AddonCredits', addonCredits); + const resupdateSub = await updateSub.save(null, { useMasterKey: true }); + // console.log('resupdateSub ', resupdateSub); + return { status: 'success', addon: addonCredits }; + } + } else { + throw new Parse.Error('400', 'Invalid access token.'); + } + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + } + } catch (err) { + const code = err?.response?.data?.code || err?.response?.status || err?.code || 400; + const msg = + err?.response?.data?.error || + err?.response?.data || + err?.message || + 'Something went wrong.'; + console.log('err in Buyaddonusers', code, msg); + throw new Parse.Error(code, msg); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameters.'); + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js index 35e602cc3..aa5fa961f 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js @@ -3,35 +3,28 @@ import { cloudServerUrl } from '../../Utils.js'; const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const appId = process.env.APP_ID; -async function deductcount(tenantId, docs, extUserId) { +const licenseKey = process.env.LICENSE_KEY; +async function deductcount(docsCount, extUserId, subscription) { try { - const subscription = new Parse.Query('contracts_Subscriptions'); - subscription.equalTo('TenantId', { - __type: 'Pointer', - className: 'partners_Tenant', - objectId: tenantId, - }); - subscription.include('ExtUserPtr'); - subscription.greaterThan('AllowedQuicksend', 0); - const resSub = await subscription.first({ useMasterKey: true }); - if (resSub) { - const updateApiCount = - resSub?.get('AllowedQuicksend') && resSub.get('AllowedQuicksend') > 0 - ? resSub.get('AllowedQuicksend') - docs - : 0; + if (licenseKey) { + const allowedCredits = subscription?.AllowedCredits || 0; + const addonCredits = subscription?.AddonCredits || 0; const subscriptionCls = new Parse.Object('contracts_Subscriptions'); - subscriptionCls.id = resSub.id; - if (updateApiCount > 0) { - subscriptionCls.set('AllowedQuicksend', updateApiCount); + subscriptionCls.id = subscription.objectId; + if (docsCount <= allowedCredits) { + const updateAllowedcredits = allowedCredits - docsCount; + subscriptionCls.set('AllowedCredits', updateAllowedcredits); } else { - subscriptionCls.set('AllowedQuicksend', 0); + const remaingCount = docsCount - allowedCredits; + const updateAddonCredits = addonCredits - remaingCount; + subscriptionCls.set('AllowedCredits', 0); + subscriptionCls.set('AddonCredits', updateAddonCredits); } - const resSubcription = await subscriptionCls.save(null, { useMasterKey: true }); + await subscriptionCls.save(null, { useMasterKey: true }); } - const extCls = new Parse.Object('contracts_Users'); extCls.id = extUserId; - extCls.increment('DocumentCount', docs); + extCls.increment('DocumentCount', docsCount); const resExt = await extCls.save(null, { useMasterKey: true }); } catch (err) { console.log('Err in deduct in quick send', err); @@ -210,24 +203,59 @@ export default async function createBatchDocs(request) { }; }); // console.log('requests ', requests); - - const response = await axios.post('batch', { requests: requests }, parseConfig); - // // Handle the batch query response - // console.log('Batch query response:', response.data); - if (response.data && response.data.length > 0) { - const updateDocuments = Documents.map((x, i) => ({ - ...x, - objectId: response.data[i]?.success?.objectId, - createdAt: response.data[i]?.success?.createdAt, - })); - deductcount(_resExt.TenantId.objectId, response.data.length, resExt.id); - for (let i = 0; i < updateDocuments.length; i++) { - sendMail(updateDocuments[i], sessionToken); + if (licenseKey) { + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.equalTo('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: _resExt.TenantId.objectId, + }); + subscription.include('ExtUserPtr'); + const resSub = await subscription.first({ useMasterKey: true }); + if (resSub) { + const _resSub = JSON.parse(JSON.stringify(resSub)); + const allowedCredits = _resSub?.AllowedCredits || 0; + const addonCredits = _resSub?.AddonCredits || 0; + const totalcredits = allowedCredits + addonCredits; + if (docsCount <= totalcredits) { + const response = await axios.post('batch', { requests: requests }, parseConfig); + // // Handle the batch query response + // console.log('Batch query response:', response.data); + if (response.data && response.data.length > 0) { + const updateDocuments = Documents.map((x, i) => ({ + ...x, + objectId: response.data[i]?.success?.objectId, + createdAt: response.data[i]?.success?.createdAt, + })); + deductcount(response.data.length, resExt.id, _resSub); + for (let i = 0; i < updateDocuments.length; i++) { + sendMail(updateDocuments[i], sessionToken); + } + return 'success'; + } + } else { + throw new Parse.Error( + 429, + 'Quota reached, Please buy credits and try again later.' + ); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please buy subscriptions.'); + } + } else { + const response = await axios.post('batch', { requests: requests[0] }, parseConfig); + // // Handle the batch query response + // console.log('Batch query response:', response.data); + if (response.data && response.data.length > 0) { + const updateDocuments = { + objectId: response.data[0]?.success?.objectId, + createdAt: response.data[0]?.success?.createdAt, + }; + deductcount(response.data.length, resExt.id, _resSub); + sendMail(updateDocuments, sessionToken); + return 'success'; } - return 'success'; } - - // Handle individual responses within response.data.results } catch (error) { const code = err?.response?.data?.code || err?.response?.status || err?.code || 400; const msg = @@ -240,7 +268,7 @@ export default async function createBatchDocs(request) { } } } else { - new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); } } catch (err) { console.log('err in createbatchdoc', err);