Dela via


Integrering av elektronisk fakturering i Saudiarabien

Onboarding är obligatoriskt för alla skattebetalare som är föremål för elektronisk fakturering i Saudiarabien. Som ett resultat av introduktionsprocessen får skattebetalarna kryptografiska stämpelidentifierare (CSID). CSID:n krävs för integration med portalen för elektronisk fakturering som hanteras av den saudiarabiska skattemyndigheten (ZATCA) och för ytterligare inlämning av elektroniska fakturor.

Den här artikeln förklarar hur du registrerar skattebetalare och deras programvara för elektronisk fakturering hos saudiarabiska skattemyndigheter.

Krav

  • Den juridiska personen måste vara registrerad som skattebetalare i Saudiarabien och måste ha ett giltigt momsregistreringsnummer.
  • Den juridiska personen måste ha tillgång till Saudiarabiens skatteportal (ERAD).

Integrationsprocess

Onboarding-processen består av två steg:

  1. Skaffa ett CSID för efterlevnad (CCSID), som ZATCA tilldelar för att utföra efterlevnadskontroller av lösningar för generering av elektroniska fakturor (EGS).
  2. Skaffa ett produktions-CSID (PCSID), som ZATCA tilldelar kompatibla EGS:er.

Arbetsflöde för onboarding.

Skaffa ett CCSID

  1. I den saudiarabiska skatteportalen (ERAD) går du till registrerings- och hanteringsportalen genom att välja relevant panel.

  2. På huvudlandningssidan i registrerings- och hanteringsportalen Välj panelen Registrera ny lösningsenhet/enhet och Välj sedan Generera OTP-kod. OTP-koden är endast giltig i en timme efter att den har genererats. Se till att den används inom den tiden ram.

  3. Välj antalet koder för engångslösenord (OTP) som ska genereras. Antalet beror på antalet enheter (enheter) för e-fakturering som ska användas.

  4. Spara de genererade OTP-koderna så att du kan använda dem i senare steg.

  5. Förbered en konfigurationsfil för begäran om certifikatsignering. Den här konfigurationsfilen ska vara i form av en oformaterad textfil som innehåller följande data.

    oid_section = OIDs
    [OIDs]
    certificateTemplateName = 1.3.6.1.4.1.311.20.2
    [req]
    default_bits = 2048
    emailAddress = MyEmail@email.com
    req_extensions = v3_req
    x509_extensions = v3_ca
    prompt = no
    default_md = sha 256
    req_extensions = req_ext
    distinguished_name = dn
    [dn]
    C=SA
    OU=Riyad Branch
    O=Contoso
    CN=EA123456789
    [v3_req]
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment
    [req_ext]
    certificateTemplateName = ASN1:PRINTABLESTRING:ZATCA-Code-Signing
    subjectAltName = dirName:alt_names
    [alt_names]
    SN=1-TST|2-TST|3-ed22f1d8-e6a2-1118-9b58-d9a8f11e445f
    UID=310122393500003
    title=1100
    registeredAddress= MyAddress
    businessCategory=Industry
    
  6. Spara filen på samma plats som ombordstigningsskriptet med namnet csr_config.txt.

  7. I konfigurationsfilen uppdaterar du värdet emailAddress och följande specifika data.

    Kod beskrivning Specifikation
    C Lands-/regionkoden. En kod med två bokstäver (ISO 3166 Alpha-2)
    OU Namnet på organisationsenheten. För vanliga skattebetalare är värdet fritext. För momsgrupper identifierar du värdet genom att den elfte siffran i organisationsidentifieraren är "1". Verifiera att indata är ett 10-siffrigt skatteidentifikationsnummer (TIN).
    O Namnet på organisationen eller skattebetalaren. Fritext
    CN Det unika namnet på lösningen eller enheten. Fritext
    SN Den unika identifieringskoden för lösningen. Fritext
    Uid Den skattskyldiges momsregistreringsnummer. Femton siffror. Detta nummer börjar med "3" och slutar med "3".
    rubrik Den dokumenttyp som skattebetalarens lösningsenhet kommer att utfärda. Fyrsiffriga numeriska indata som använder "0" och "1" mappade till "TSCZ": 0 = Falskt/Stöds inte, 1 = Sant/Stöds. T = Momsfaktura (standard), S = Förenklad momsfaktura, C = För framtida användning, Z = För framtida användning.
    registreradAdress Adressen till den filial eller plats där enheten eller lösningsenheten huvudsakligen finns. Fritext
    företagKategori Den bransch eller sektor som enheten eller lösningen ska generera fakturor för. Fritext

    Notering

    Värdena för CN och certificateTemplateName i konfigurationsfilen skiljer sig åt när du använder simuleringsportalen.

    I simuleringsportalen:

    • CN- PREZATCA-kodsignering
    • certificateTemplateName - ASN1:PRINTABLESTRING:PREZATCA-kodsignering

    I alla andra fall använder du värdena enligt anvisningarna ovan.

  8. Kör onboarding-skriptet som finns senare i den här artikeln. Ange OTP och konfigurationsfilen som indataparametrar. Hit är ett exempel: Skriptet har två möjliga slutpunkter simulering och prod.

    .\OnboardingScript.ps1 -action getComplianceCSID -endpoint prod -otp 123345 -csrconfig .\csr_config.txt -password 123

    Notering

    Lösenordsparametern är valfri och kan utelämnas. Om det ingår kommer certifikatet som genereras att ha det angivna lösenordet.

  9. CCSID:t tas emot som en certifikatfil "CCSID.pfx" och hemligheten för CCSID sparas som txt-filen "CCSIDSecret.txt". Spara den här CCSID-certifikatfilen i nyckelvalvscertifikatet Microsoft Azure och spara hemligheten i Microsoft Azure nyckelvalvshemligheten. Mer information finns i Kundcertifikat och hemligheter.

  10. Konfigurera de relaterade funktionsinställningarna i den saudiarabiska ZATCA-efterlevnadskontrollen (SA) för elektronisk fakturering och referera till CCSID-certifikatet som du sparade i nyckelvalvet. Certifikatet kommer att användas för kommunikation med ZATCA:s portal för elektronisk fakturering.

Kontroll av överensstämmelse

När du har erhållit CSID för efterlevnad med hjälp av skriptet PowerShell kräver ZATCA att du slutför vissa efterlevnadskontroller genom att skicka exempelfakturor. Det här steget är en förutsättning för att begära ett CSID för produktion.

Se till att alla typer av exempelfakturor som har konfigurerats i konfigurationsfilen för begäran om certifikatsignering (CSR) har skickats till ZATCA. Använd standardprocessen för att utfärda elektroniska fakturor. Mer information finns i Utfärda elektroniska fakturor i Finance och försörjningskedja Management.

Använd funktionen "Saudiarabiens ZATCA-efterlevnadskontroll (SA)" i RCS och följ stegen i avsnittet Lands-/regionspecifik konfiguration med hjälp av det CSID för efterlevnad som du erhöll.

När efterlevnadskontrollerna har slutförts använder du skriptet PowerShell för att hämta CSID för produktion (se skriptet för ombordstigning).

Notering

Om dokumenttypen i konfigurationsfilen är inställd på 1000 i fältet Titelmåste tre exempelfakturor skickas in för efterlevnadskontrollen:

  • Standardfaktura för moms
  • Standarddebetfaktura
  • Standard kreditfaktura

Om dokumenttypen i konfigurationsfilen är inställd på 0100 i fältet Titelmåste tre exempelfakturor skickas in för efterlevnadskontrollen:

  • Förenklad momsfaktura
  • Förenklad debetfaktura
  • Förenklad kreditfaktura

Om dokumenttypen är inställd på 1100 måste alla sex exempelfakturor skickas in för efterlevnadskontrollen.

Skaffa ett PCSID

För att få ett PCSID måste du konfigurera lösningen för generering och överföring av elektroniska fakturor korrekt, och lösningen måste fungera fullt ut. För att uppnå detta resultat måste du slutföra alla nödvändiga preliminära konfigurationssteg. Mer information finns i Kom igång med elektronisk fakturering för Saudiarabien.

  1. Se till att alla elektroniska fakturor har skickats till ZATCA.

  2. Kör onboarding-skriptet som finns senare i den här artikeln. Ange CCSID som en indataparameter. Hit är ett exempel: Skriptet har två möjliga slutpunkter simulation & prod

    .\OnboardingScript.ps1 -action getProductionCSID -endpoint prod -password 123

    Notering

    Lösenordsparametern är valfri och kan utelämnas. Om det ingår kommer certifikatet som genereras att ha det angivna lösenordet.

  3. PCSID:t tas emot som en certifikatfil i PFX-format. Spara det här PCSID-certifikatet och den hemliga filen i nyckelvalvet Azure.

  4. Konfigurera de relaterade funktionsinställningarna i den saudiarabiska Zatca-överföringsfunktionen (SA) för elektronisk fakturering. Inkludera PCSID-certifikatet och hemligheten i nyckelvalvsparametrarna i RCS.

När du har slutfört alla konfigurationssteg är systemet redo att användas i produktionsläge.

Om du vill granska erhållna CSID:n på ZATCA-sidan använder du panelen Granska befintlig kryptografisk stämpelidentifierare (CSID) på landningssidan i registrerings- och hanteringsportalen. Denna portal är tillgänglig från Saudiarabiens huvudportal för beskattning (ERAD).

Skript för registrering

Notering

Exempelskripten stöds inte under något Microsoft standardsupportprogram eller -tjänst. Exempelskripten tillhandahålls i befintligt skick utan garanti av något slag. Microsoft frånsäger sig vidare alla underförstådda garantier inklusive, utan begränsning, alla underförstådda garantier om säljbarhet eller lämplighet för ett visst ändamål. Hela risken som uppstår vid användning eller utförande av exempelskript och dokumentation ligger kvar hos dig. Under inga omständigheter ska Microsoft, dess författare eller någon annan som är involverad i skapandet, produktionen eller leveransen av skripten hållas ansvariga för några som helst skador (inklusive, utan begränsning, skador för förlust av affärsvinst, affärsavbrott, förlust av affärsinformation eller annan ekonomisk förlust) som uppstår till följd av användning av eller oförmåga att använda exempelskripten eller dokumentationen, även om Microsoft har informerats om risken för sådana skador.

  1. Använd följande Windows PowerShell-skript för att hämta ett CCSID och ett PCSID.

    #Saudi Arabian electronic invoice onboarding script
     #Version 1.1
     param($action, $endpoint, $otp, $csrconfig, $password)
     $env:path = $env:path + ";C:\Program Files\Git\usr\bin"
    
     $simulationEndpoint = 'https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation'
     $prodEndpoint = 'https://gw-fatoora.zatca.gov.sa/e-invoicing/core'
    
     if ($endpoint -eq "simulation")
     {
     	$serviceEndpoint = $simulationEndpoint
     } elseif ($endpoint -eq "prod") {
     	$serviceEndpoint = $prodEndpoint
     } else {
     	Write-Host "`nMissing parameter (with values simulation/prod): endpoint"
     	Break
     }
    
     if ($action -eq "getComplianceCSID")
     {
     	if (-not (Test-Path -Path $csrconfig))
     	{
     		throw "CSR configuration file does not exist, please make sure to provide a valid file path for the '-csrconfig' parameter."
     	}
    
     	if ($otp -eq $null)
     	{
     		throw "OTP code is not provided, please carry correct parameters."
     	}
    
     	#Generate private key
     	openssl ecparam -name secp256k1 -genkey -noout -out privatekey.pem
     	Write-Host "Private key generated."
    
     	#Generate public key
     	openssl ec -in privatekey.pem -pubout -conv_form compressed -out publickey.pem
     	Write-Host "Public key generated."
    
     	#Generate CSR(Certificate signing request)
     	openssl base64 -d -in publickey.pem -out publickey.bin
     	openssl req -new -sha256 -key privatekey.pem -extensions v3_req -config $csrconfig -out .\taxpayer.csr 
     	openssl base64 -in taxpayer.csr -out taxpayerCSRbase64Encoded.txt
     	$CSRbase64Encoded = Get-Content -path taxpayerCSRbase64Encoded.txt -Raw
     	$CSRbase64Encoded = $CSRbase64Encoded -replace "`n",""
     	$CSRbase64Encoded = $CSRbase64Encoded -replace "`r",""
    
     	#Init request for CCSID
     	$postParams = @{"csr"=$CSRbase64Encoded} | ConvertTo-Json
     	$postHeader = @{
     		   "Accept"="application/json"
     		   "OTP"=$otp
     		   "Content-Type"="application/json"
     		   "Accept-Version"="V2"}
     	echo $CSRbase64Encoded
     	try
     	{
     		$response = Invoke-WebRequest -Uri $serviceEndpoint'/compliance' -Method POST -Body $postParams -Headers $postHeader 
     	}
     	catch
     	{
     		$respStream = $_.Exception.Response.GetResponseStream()
     		$reader = New-Object System.IO.StreamReader($respStream)
     		$respBody = $reader.ReadToEnd()
     		$reader.Close()
    
     		Write-Host "`nZatca service communication error:"
     		Write-Host $_.Exception.Message
     		Write-Host "Detailed error message: " $respBody
     		Write-Host "The process of obtaining a Compliance CSID (CCSID) is interrupted."
     	}
    
     	if ($response -ne $null)
     	{
     		$response = $response | ConvertFrom-Json
     		$requestId = $response.requestID
     		Write-Host "Request ID:"
     		Write-Host $requestId
     		$requestId | Out-File -FilePath .\requestId.txt -Encoding utf8 -NoNewline
    
     		$CCSIDbase64 = $response.binarySecurityToken
     		Write-Host "`nCompliance CSID received from Zatca:"
     		Write-Host $CCSIDbase64
     		$CCSID = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($CCSIDbase64))
     		$CCSIDCertString = "-----BEGIN CERTIFICATE-----`n" + $CCSID + "`n" + "-----END CERTIFICATE-----"
    
     		$CCSIDSecret = $response.secret
     		Write-Host "`nCompliance CSID secret received from Zatca:"
     		Write-Host $CCSIDSecret
    
     		$CCSIDStringFileName = "CCSIDString.txt"
     		$CCSIDSecretFileName = "CCSIDSecret.txt"
     		$CCSIDCertFileName = "CCSID.pem"
     		$CCSIDFolderPath = Get-Location
     		$CCSIDCertFilePath = Join-Path $CCSIDFolderPath $CCSIDCertFileName
     		$CCSIDStringFilePath = Join-Path $CCSIDFolderPath $CCSIDStringFileName
     		$CCSIDSecretFilePath = Join-Path $CCSIDFolderPath $CCSIDSecretFileName
    
     		$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
     		[System.IO.File]::WriteAllLines($CCSIDCertFilePath, $CCSIDCertString, $Utf8NoBomEncoding)
     		[System.IO.File]::WriteAllLines($CCSIDStringFilePath, $CCSIDbase64, $Utf8NoBomEncoding)
     		[System.IO.File]::WriteAllLines($CCSIDSecretFilePath, $CCSIDSecret, $Utf8NoBomEncoding)
    
     		openssl pkcs12 -inkey privatekey.pem -in CCSID.pem -export -passout pass:$password -out CCSID.pfx
     		Write-Host "`nCertificate is saved to CCSID.pfx file and secret is saved to CCSIDSecret.txt file."
     		Write-Host "The process of obtaining a Compliance CSID (CCSID) is complete, please process the compliance check and do not delete or move any created files before getting PCSID."
     	}
    
     }
    
    
     if ($action -eq "getProductionCSID")
     {
     	if (-not (Test-Path -Path requestId.txt))
     	{
     		throw "'requestId.txt' file is missing, please make sure you're running the script in the same location where the results of getting the CCSID are stored."
     	}
     	if (-not (Test-Path -Path CCSIDString.txt))
     	{
     		throw "'CCSIDString.txt' file is missing, please make sure you're running the script in the same location where the results of getting the CCSID are stored."
     	}
     	if (-not (Test-Path -Path CCSIDSecret.txt))
     	{
     		throw "'CCSIDSecret.txt' file is missing, please make sure you're running the script in the same location where the results of getting the CCSID are stored."
     	}
    
     	$requestId = Get-Content -path requestId.txt -Raw
     	$requestId = $requestId -replace "`n",""
     	$requestId = $requestId -replace "`r",""
     	Write-Host "Request ID is:" $requestId
     	$CCSID = Get-Content -path CCSIDString.txt -Raw
     	$CCSID = $CCSID -replace "`n",""
     	$CCSID = $CCSID -replace "`r",""
     	Write-Host "`nCompliance CSID read locally:"
     	Write-Host $CCSID
     	$CCSIDSecretString = Get-Content -path CCSIDSecret.txt -Raw
     	$CCSIDSecretString = $CCSIDSecretString -replace "`n",""
     	$CCSIDSecretString = $CCSIDSecretString -replace "`r",""
     	Write-Host "`nCompliance CSID secret read locally:"
     	Write-Host $CCSIDSecretString
     	$AuthTokenString = $CCSID + ":" + $CCSIDSecretString
     	$BasicAuthToken = "Basic " + [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($AuthTokenString))
    
     	#Init request for Production CSID (PCSID)
     	$postParams = @{"compliance_request_id"=$requestId} | ConvertTo-Json
     	$postHeader = @{
     		   "Accept"="application/json"
     		   "Authorization"=$BasicAuthToken
     		   "Content-Type"="application/json"
     		   "Accept-Version"="V2"}
    
     	try
     	{
     		$response = Invoke-WebRequest -Uri $serviceEndpoint'/production/csids' -Method POST -Body $postParams -Headers $postHeader
     	}
     	catch
     	{
     		$respStream = $_.Exception.Response.GetResponseStream()
     		$reader = New-Object System.IO.StreamReader($respStream)
     		$respBody = $reader.ReadToEnd()
     		$reader.Close() 
    
     		Write-Host "`nZatca service communication error:"
     		Write-Host $_.Exception.Message
     		Write-Host "Detailed error message: " $respBody 
     		Write-Host "Please make sure the compliance check process has been done before obtaining a Production CSID (PCSID)."
     		Write-Host "The process of obtaining a Production CSID (PCSID) is interrupted."
     	}
    
     	if ($response -ne $null)
     	{
     		$response = $response | ConvertFrom-Json
     		$PCSIDbase64 = $response.binarySecurityToken
     		Write-Host "`nProduction CSID received from Zatca:"
     		Write-Host $PCSIDbase64
    
     		$PCSID = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($PCSIDbase64))
     		$PCSIDCertString = "-----BEGIN CERTIFICATE-----`n" + $PCSID + "`n" + "-----END CERTIFICATE-----"
    
     		$PCSIDSecret = $response.secret
     		Write-Host "`nProduction CSID secret received from Zatca:"
     		Write-Host $PCSIDSecret
    
     		$PCSIDCertFileName = "PCSID.pem"
     		$PCSIDSecretFileName = "PCSIDSecret.txt"
     		$PCSIDFolderPath = Get-Location
     		$PCSIDCertFilePath = Join-Path $PCSIDFolderPath $PCSIDCertFileName
     		$PCSIDSecretFilePath = Join-Path $PCSIDFolderPath $PCSIDSecretFileName
    
     		$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
     		[System.IO.File]::WriteAllLines($PCSIDCertFilePath, $PCSIDCertString, $Utf8NoBomEncoding)
     		[System.IO.File]::WriteAllLines($PCSIDSecretFilePath, $PCSIDSecret, $Utf8NoBomEncoding)
    
     		# Sandbox API will get error: openssl : No certificate matches private key
     		openssl pkcs12 -inkey privatekey.pem -in PCSID.pem -export -passout pass:$password -out PCSID.pfx
    
     		if (Test-Path -Path PCSID.pfx)
     		{
     			Write-Host "`nCertificate is saved to PCSID.pfx file and secret is saved to PCSIDSecret.txt file."
     			Write-Host "The process of obtaining a Production CSID (PCSID) is complete."
     		}
     		else
     		{
     			Write-Host "`nThe process of obtaining a Production CSID (PCSID) is interrupted."
     		}
     	}
     }
    
  2. Spara utdatacertifikatfilen .pfx som tas emot i nyckelvalvet.

Ytterligare resurser