Issue with ZATCA Integration in Manager.io

@Mabaega is ZATCA Phase 2 implementation relying on specific custom theme?

I’ve noticed that there is QRCodePhasell theme.

Every custom theme needs to call:

window.parent.postMessage({
  type: "resize",
  width: document.documentElement.scrollWidth + 1,
  height: document.documentElement.scrollHeight + 1
}, "*");

to resize itself. Usually this is done at the end of all processing.

To all ZatcaEGS Service users,

Following the update in Manager version 25.7.7.2477, adjustments are required for the QRCodePhaseII Custom Theme.

Please go to Settings → Themes, edit the QRCodePhaseII theme, and replace the existing code with the updated code below:

Full Script QRCodePhaseII

<!DOCTYPE html>
<html>
<head>
	<meta charset='UTF-8' />
	<meta name='viewport' content='width=device-width, initial-scale=1.0' />
	<style>
		*, ::after, ::before, ::backdrop, ::file-selector-button {
			margin: 0;
			padding: 0;
		}
		*, ::after, ::before, ::backdrop, ::file-selector-button {
			box-sizing: border-box;
			border: 0 solid;
		}
		body {
			margin: 0;
			padding: 30px;
			font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
			color: #171717;
			font-size: 12px;
			line-height: 1.428571429;
		}
		table {
			font-size: 12px;
		}
		tr#table-headers th {
			font-weight: bold;
			padding: 5px 10px;
			border: 1px solid #000;
			text-align: start
		}
		tbody#table-rows td {
			padding: 5px 10px;
			border-left: 1px solid #000;
			border-right: 1px solid #000;
			text-align: start;
			vertical-align: top
		}
		tbody#table-rows tr:last-child td {
			padding-bottom: 30px;
			border-bottom: 1px solid #000;
		}
	</style>
</head>
<body>
	<table style='margin-bottom: 20px; width: 100%'>
		<tbody>
			<tr>
				<td style='vertical-align: top'>
					<div style='font-size: 32px; line-height: 32px; font-weight: bold' id='title'>Invoice</div>
				</td>
				<td style='text-align: end' id='business-logo'>
				</td>
			</tr>
		</tbody>
	</table>
	<table style='margin-bottom: 20px; width: 100%'>
		<tbody>
			<tr>
				<td style='vertical-align: top' id='recipient-info'></td>
				<td style='text-align: end; vertical-align: top' id='fields'></td>
				<td style='width: 20px'></td>
				<td style='width: 1px; border-left-width: 1px; border-left-color: #000; border-left-style: solid'></td>
				<td style='width: 20px'></td>
				<td style='width: 1px; white-space: nowrap; vertical-align: top' id='business-info'></td>
			</tr>
		</tbody>
	</table>
	<div style='font-weight: bold; font-size: 14px; margin-bottom: 20px' id='description'></div>
	<table style='border-collapse: collapse; width: 100%'>
		<thead>
			<tr id='table-headers'></tr>
		</thead>
		<tbody id='table-rows'>
		</tbody>
		<tbody id='totals'>
		</tbody>
	</table>
	<script src='resources/qrcode/qrcode.js'></script>
	<div id='qrcode' style='margin-bottom: 20px'></div>
	<div id='custom-fields'></div>
	<table><tr><td><div id='footers'></div></td></tr></table>
	<div id='status' style='text-align: center'></div>
	<script>
		window.addEventListener('message', (event) => {
			if (event.source !== window.parent) return; 
			if (event.data.type !== 'context-response') return; 
			const data = event.data.body;
			document.documentElement.dir = data.direction;
			document.getElementById('title').innerHTML = data.title || 'No title';
			document.getElementById('description').innerHTML = data.description || '';
			var businessLogoTd = document.getElementById('business-logo');
			if (data.business.logo) {
				const img = document.createElement('img');
				img.src = data.business.logo;
				img.style = 'max-height: 150px; max-width: 300px; display: inline';
				businessLogoTd.appendChild(img);
			}
			const business = data.business || {};
			document.getElementById('business-info').innerHTML = `<strong>${business.name || ''}</strong><br>${business.address ? business.address.replace(/\n/g, '<br>') : ''}`;
			const recipient = data.recipient || {};
			document.getElementById('recipient-info').innerHTML = `<strong>${recipient.name || ''}</strong><br>${recipient.address ? recipient.address.replace(/\n/g, '<br>') : ''}`;
			const fieldsDiv = document.getElementById('fields');
			fieldsDiv.innerHTML = '';
			(data.fields || []).forEach(f => {
				const div = document.createElement('div');
				div.innerHTML = `<strong>${f.label}</strong><br />${f.text || ''}<br /><br />`;
				fieldsDiv.appendChild(div);
			});
			const headersRow = document.getElementById('table-headers');
			headersRow.innerHTML = '';
			(data.table.columns || []).forEach(col => {
				const th = document.createElement('th');
				th.innerHTML = col.label;
				th.style.textAlign = col.align;
				if (col.minWidth) {
					th.style.whiteSpace = 'nowrap';
					th.style.width = '1px';
				}
				else if (col.nowrap) {
					th.style.whiteSpace = 'nowrap';
					th.style.width = '80px';
				}
				headersRow.appendChild(th);
			});
			const rowsBody = document.getElementById('table-rows');
			rowsBody.innerHTML = '';
			(data.table.rows || []).forEach(row => {
				const tr = document.createElement('tr');
				row.cells.forEach((cell, i) => {
					var col = data.table.columns[i];
					const td = document.createElement('td');
					td.innerHTML = (cell.text || '').split('\n').join('<br />');
					td.style.textAlign = col.align;
					if (col.minWidth) {
						td.style.whiteSpace = 'nowrap';
						td.style.width = '1px';
					}
					else if (col.nowrap) {
						td.style.whiteSpace = 'nowrap';
						td.style.width = '80px';
					}
					tr.appendChild(td);
				});
				rowsBody.appendChild(tr);
			});
			const totalsBody = document.getElementById('totals');
			totalsBody.innerHTML = '';
			(data.table.totals || []).forEach(total => {
				const tr = document.createElement('tr');
				const tdLabel = document.createElement('td');
				const tdValue = document.createElement('td');
				tdLabel.innerHTML = total.label;
				tdLabel.colSpan = data.table.columns.length - 1;
				tdLabel.style = 'padding: 5px 10px; text-align: end; vertical-align: top';
				tdValue.innerHTML = total.text;
				tdValue.id = total.key;
				if (total.class) tdValue.classList.add(total.class);
				tdValue.dataset.value = total.number;
				tdValue.style = 'padding: 5px 10px; border: 1px solid #000; text-align: right; white-space: nowrap; vertical-align: top';
				if (total.emphasis) {
					tdLabel.style.fontWeight = 'bold';
					tdValue.style.fontWeight = 'bold';
				}
				tr.appendChild(tdLabel);
				tr.appendChild(tdValue);
				totalsBody.appendChild(tr);
			});
			const customFieldsDiv = document.getElementById('custom-fields');
			customFieldsDiv.innerHTML = '';
			let qrcodetext = '';
			(data.custom_fields || []).forEach(f => {
				if (f.label === 'Base64 QRCode') {
					qrcodetext = f.text;
				} else {
					const div = document.createElement('div');
					div.innerHTML = `<strong>${f.label}</strong><br />${(f.text || '').split('\n').join('<br />')}<br /><br />`;
					if (f.displayAtTheTop === true) {
						fieldsDiv.appendChild(div);
					} else {
						customFieldsDiv.appendChild(div);
					}
				}
			});
			const footersDiv = document.getElementById('footers');
			footersDiv.innerHTML = '';
			(data.footers || []).forEach(f => {
				const div = document.createElement('div');
				div.style = 'margin-top: 20px';
				div.innerHTML = f;
				footersDiv.appendChild(div);
				const scripts = div.querySelectorAll('script');
				scripts.forEach(script => {
					const newScript = document.createElement('script');
					for (const attr of script.attributes) {
						newScript.setAttribute(attr.name, attr.value);
					}
					if (script.textContent) {
						newScript.textContent = script.textContent;
					}
					script.parentNode.replaceChild(newScript, script);
				});
			});
			const statusDiv = document.getElementById('status');
			if (data.emphasis?.text != null) {
				statusDiv.style.marginTop = '40px';
				const span = document.createElement('span');
				span.style = 'border-width: 5px; border-color: #FF0000; border-style: solid; padding: 10px; font-size: 20px; text-transform: uppercase';
				if (data.emphasis.positive) {
					span.style.color = 'green';
					span.style.borderColor = 'green';
				}
				if (data.emphasis.negative) {
					span.style.color = 'red';
					span.style.borderColor = 'red';
				}
				span.innerHTML = data.emphasis.text;
				statusDiv.appendChild(span);
			}
			const qrcodeDiv = document.getElementById('qrcode');
			if (qrcodetext && qrcodetext.length > 10 && qrcodeDiv) {
				qrcodeDiv.innerHTML = '';
				renderQRCode(qrcodetext, 'qrcode'); 
			} else {
				if (data.type === 'salesinvoice') {
					if (data.business.country === 'ar-SA' || data.business.country === 'en-SA') {
						function appendTLV(tag, text, byteList) {
							const encoded = new TextEncoder().encode(text);
							byteList.push(tag);
							byteList.push(encoded.length);
							for (let b of encoded) byteList.push(b);
						}
						const byteList = [];
						let businessName = 'No name';
						if (data.business) businessName = data.business.name;
						let vatNumber = '0000000000000';
						let vatField = data.business.custom_fields.find(item => item.key === 'd96d97e8-c857-42c6-8360-443c06a13de9');
						if (vatField) vatNumber = vatField.text;
						let timestamp = new Date((data.timestamp - 621355968000000000) / 10000).toISOString();
						let total = 0;
						let totalElement = document.getElementById('Total');
						if (totalElement != null) total = parseFloat(totalElement.getAttribute('data-value'));
						let vat = 0;
						let taxAmounts = document.getElementsByClassName('taxAmount');
						for (let i = 0; i < taxAmounts.length; i++) {
							vat += parseFloat(taxAmounts[i].getAttribute('data-value'));
						}
						appendTLV(1, businessName, byteList);
						appendTLV(2, vatNumber, byteList);
						appendTLV(3, timestamp, byteList);
						appendTLV(4, total.toFixed(2), byteList);
						appendTLV(5, vat.toFixed(2), byteList);
						const tlvBytes = Uint8Array.from(byteList);
						const qrData = btoa(String.fromCharCode(...tlvBytes));
						console.log(qrData);
						new QRCode(document.getElementById('qrcode'), {
							text: qrData,
							width: 128,
							height: 128,
							colorDark: '#000000',
							colorLight: '#ffffff',
							correctLevel: QRCode.CorrectLevel.L
						});
					}
				}
			}
			function renderQRCode(content, elementId) {
				new QRCode(document.getElementById(elementId), {
					text: content,
					width: 160,
					height: 160,
					colorDark: '#000000',
					colorLight: '#fafafa',
					correctLevel: QRCode.CorrectLevel.L
				});
				let details = parseQRCodeContent(content);
				if (details.size > 0) {
					let title = Array.from(details)
						.map(([tag, value]) => `Tag ${tag} : ${value.join(', ')}`)
						.join('\n');
					const qrCodeDiv = document.getElementById(elementId);
					if (qrCodeDiv) qrCodeDiv.title = title.trim();
				}
			}
			function parseQRCodeContent(qrCodeBase64) {
				let details = new Map();
				try {
					let data = atob(qrCodeBase64.replace(/\+/g, '+'));
					let index = 0;
					while (index < data.length) {
						let tag = data.charCodeAt(index++);
						let length = data.charCodeAt(index++);
						let value = data.substr(index, length);
						index += length;
						let decodedValue = (tag === 8 || tag === 9)
							? [...value].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' ').toUpperCase()
							: new TextDecoder('utf-8').decode(new Uint8Array([...value].map(c => c.charCodeAt(0))));
						details.set(tag, [decodedValue]);
					}
				} catch (ex) {
					console.error('Error decoding QR code: ' + ex.message);
				}
				return details;
			}
			
			window.parent.postMessage({ type: "resize", width: document.documentElement.scrollWidth + 1, height: document.documentElement.scrollHeight + 1 }, "*");
			
		}, false);
		
		window.addEventListener('load', () =>
			window.parent.postMessage({ type: 'context-request' }, '*')
		);
	</script>
</body>
</html>

If you have previously modified this custom theme, you can simply add the auto-resize line

window.parent.postMessage({ type: "resize", width: document.documentElement.scrollWidth + 1, height: document.documentElement.scrollHeight + 1 }, "*");

after the function declaration parseQRCodeContent:

			function parseQRCodeContent(qrCodeBase64) {
				let details = new Map();
				try {
					let data = atob(qrCodeBase64.replace(/\+/g, '+'));
					let index = 0;
					while (index < data.length) {
						let tag = data.charCodeAt(index++);
						let length = data.charCodeAt(index++);
						let value = data.substr(index, length);
						index += length;
						let decodedValue = (tag === 8 || tag === 9)
							? [...value].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' ').toUpperCase()
							: new TextDecoder('utf-8').decode(new Uint8Array([...value].map(c => c.charCodeAt(0))));
						details.set(tag, [decodedValue]);
					}
				} catch (ex) {
					console.error('Error decoding QR code: ' + ex.message);
				}
				return details;
			}
			
			window.parent.postMessage({ type: "resize", width: document.documentElement.scrollWidth + 1, height: document.documentElement.scrollHeight + 1 }, "*");
		
		}, false);

Kindly advice where can I past access token

Can you send me steps how can I make integrated

Many times I’m trying to make integrated but can’t
Could you pls access on my computer to show me how I can do it

If some one did integrated ZACTA can help me pls to show me how can I do it avoid penalty from ZACTA
I should do it at 30-06-2025
**
عزيزي المكلف
نصره بن عفنان بن عفنان البلوي
الرقم المميز

نأمل المسارعة بإتمام إجراءات الربط والتكامل مع منصة فاتورة تفادياً لتطبيق المخالفات بسبب عدم التزامكم بربط كل أنظمة الفوترة الإلكترونية التي تستـخدم في إصدار الفواتير الإلكترونية أو الإشعارات الإلكترونية بأنظمة الهيئة -ابتداء من التاريخ الفعلي المحـدد لوجـوب الربط بأنظمة الهيئة.
لمزيد من التفاصيل , يرجى الدخول على حسابك بموقعنا الالكتروني :
zatca.gov.sa
وللاستفسارات نسعد بتواصلك عبر الرقم الموحد 19993
هيئة الزكاة والضريبة والجمارك

I can’t use cells token secret

The programme in my pc not accepted to fill this page

This should still be relevant to follow, although slightly different.

Kindly find screen when do update business data

Can I give you access in any desk for minute pls to check the issue in the programme

Paste Generated Token above the Update Business Data button

image

Done the previous steps but in this step coming error server

This previous step is ok then I went to fatoora


And copy to past but issue error

Please read all my posts in this thread again, so that you can better understand how Zatca and this application work.

Done dear
I did invoice and now is working ok

Appreciate your support

Thanks and regards
Bahaeldin

I have a problem. Although the invoice was created correctly and sent to Zatka, it was submitted correctly. However, the QR code on the invoice, after reporting it, does not respond to scanning.



The QR Code you shared looks fine, it reads fine on my phone.

ASlLaGFsZWQgQWwtR2hhbWRpIEZvb2RzdHVmZnMgRXN0YWJsaXNobWVudAIPMzAxMjEwNzc0MzAwMDAzAxMyMDI1LTA3LTI1VDEwOjE2OjQ0BAUzMy4wMAUENC4zMAYsY1gyL09EK0paaUVjaVRvbzB3dWhScDRzWHo2cWtqL0FOWmNBejBLMk80UT0HYE1FVUNJSGVyRmVWVlBydGpJTXREdklHS2lQZ3dzcEMxSzUrTEFjazhNUWV6ZFkxNEFpRUE2VW05OFcxdzk0VEV2WDFrdWtDWGpDR3hwSzRFOW82akQyVVhXdlI2U1UwPQhYMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAErBxSMt0OE9CcXb0OhHoY2mio/HAw/FpYcLsbySznzbjCOvNs4k31ww7LjIKx4Uuqfp1dXK2oR8/WzHxp6cLdMwlGMEQCICN7at9YV95Ul8HrPBzL6XEcknYcLbFPtS9Dq6/TgI18AiBiavDD5+in8ogzj5AQmHQJx9SOC3IX0dfrl7AYM66Smg==

Hi,

we are trying to integrate, we are facing following error. i have already review previous chat but could not find the solution.

will you please check these out.

regards
Hasnain

Make sure you are using an active access token from your business data.

You may need to create a new access token and try again.

Please explain what you are doing and when this error appears.