Del via


Onboarding af elektronisk fakturering i Saudi-Arabien

Onboarding er obligatorisk for alle skatteydere, der er underlagt elektronisk fakturering i Saudi-Arabien. Som et resultat af onboarding-processen opnår skatteydere kryptografiske stempelidentifikatorer (CSID'er). CSID'er er nødvendige for integration med den elektronisk fakturering portal, der administreres af de saudiarabiske skattemyndigheder (ZATCA), og for yderligere indsendelse af elektroniske fakturaer.

Denne artikel forklarer, hvordan du onboarder skatteydere og deres elektronisk fakturering software hos de saudiarabiske skattemyndigheder.

Forudsætninger

  • Den juridiske enhed skal være registreret som skatteyder i Saudi-Arabien og have et gyldigt momsregistreringsnummer.
  • Den juridiske enhed skal have adgang til den saudiarabiske skatteportal (ERAD).

Onboarding proces

Onboarding-processen består af to trin:

  1. Få et CCSID (Compliance CSID), som ZATCA tildeler til at udføre overensstemmelseskontrol af løsninger til generering af elektroniske fakturaer (EGS'er).
  2. Få et produktions-CSID (PCSID), som ZATCA tildeler kompatible EGS'er.

Onboarding arbejdsgang.

Få et CCSID

  1. I den saudiarabiske skatteportal (ERAD) skal du gå til onboarding- og administrationsportalen ved at vælge det relevante felt.

  2. På hovedlandingssiden på Onboarding- og administrationsportalen skal du vælge feltet Onboard ny løsningsenhed/enhed og derefter vælge Generer OTP-kode. OTP-koden er kun gyldig i en time efter, at den er genereret. Sørg for, at det bruges inden for denne tidsramme.

  3. Vælg antallet af engangsadgangskodekoder (OTP), der skal genereres. Antallet afhænger af antallet af enheder (enheder), der skal bruges.

  4. Gem de genererede OTP-koder, så du kan bruge dem i senere trin.

  5. Forbered en konfigurationsfil til anmodningen om certifikatsignering. Denne konfigurationsfil skal være i form af en almindelig tekstfil, der indeholder følgende 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. Gem filen på samme placering som onboardingscriptet med navnet,csr_config.txt .

  7. Opdater værdien emailAddress og følgende specifikke data i konfigurationsfilen.

    Kode Beskrivende tekst Specifikation
    A Lande-/områdekoden. En kode på to bogstaver (ISO 3166 Alpha-2)
    OU Navnet på organisationsenheden. For normale skatteydere er værdien fritekst. For momsgrupper skal du identificere værdien via det ellevte ciffer i organisationsidentifikatoren, der er "1". Kontrollér, at inputtet er et 10-cifret skatteidentifikationsnummer (TIN).
    O Navnet på organisationen eller skatteyderne. Fritekst
    CN Det entydige navn på løsningen eller enheden. Fritekst
    SN Den entydige identifikationskode for løsningen. Fritekst
    UID Den skattepligtiges momsregistreringsnummer. Femten cifre. Dette tal begynder med "3" og slutter med "3".
    titel Den dokumenttype, som skatteyderens løsningsenhed udsteder. Firecifret numerisk input, der bruger "0" og "1" knyttet til "TSCZ": 0 = Falsk/Understøttes ikke, 1 = Sand/Understøttet. T = Momsfaktura (standard), S = Forenklet momsfaktura, C = Til fremtidig brug, Z = Til fremtidig brug.
    registreretAdresse Adressen på den filial eller det sted, hvor enheden eller løsningsenheden primært er placeret. Fritekst
    businessCategory Den branche eller sektor, som enheden eller løsningen genererer fakturaer for. Fritekst

    Bemærk!

    Værdierne for CN og certificateTemplateName i konfigurationsfilen er forskellige, når du bruger simuleringsportalen.

    I simuleringsportalen:

    • CN - PREZATCA-kodesignering
    • certificateTemplateName - ASN1:PRINTABLESTRING:PREZATCA-Code-Signing

    I alle andre tilfælde skal du bruge værdierne som beskrevet ovenfor.

  8. Kør det onboardingscript , der findes senere i denne artikel. Angiv OTP- og konfigurationsfilen som inputparametre. Her er et eksempel: Script har to mulige slutpunkter simulering og prod.

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

    Bemærk!

    Adgangskodeparameteren er valgfri og kan udelades. Hvis det er inkluderet, vil det certifikat, der genereres, have den angivne adgangskode.

  9. CCSID modtages som en certifikatfil "CCSID.pfx", og hemmeligheden for CCSID gemmes som txt-fil "CCSIDSecret.txt". Gem denne CCSID-certifikatfil i Key Vault-certifikatet, Microsoft Azure og gem hemmeligheden i Microsoft Azure Key Vault-hemmeligheden. Du kan finde flere oplysninger i Kundecertifikater og -hemmeligheder.

  10. Konfigurer den relaterede funktionsopsætning i den saudiarabiske ZATCA Compliance Check (SA) elektronisk fakturering funktion, og henvis til det CCSID-certifikat, du gemte i Key Vault. Certifikatet vil blive brugt til kommunikation med ZATCA elektronisk fakturering portalen.

Kontrol af overholdelse

Når du har fået CSID for overholdelse ved hjælp af scriptet PowerShell, kræver ZATCA, at du udfører visse overensstemmelseskontroller ved at sende eksempelfakturaer. Denne trin er en forudsætning for at anmode om et CSID for produktion.

Sørg for, at alle typer eksempelfakturaer, der er konfigureret i CSR-konfigurationsfilen (Certificate Signing Request), sendes korrekt til ZATCA. Brug standardprocessen til udstedelse af elektroniske fakturaer. Du kan finde flere oplysninger i Udstede elektroniske fakturaer i Finance and forsyningskæde Management.

Brug funktionen "Saudi Arabian ZATCA compliance check (SA)" i RCS, og følg trinnene i afsnittet Lande/områdespecifik konfiguration ved hjælp af det CSID for overholdelse, du har opnået.

Når overensstemmelseskontrollerne er fuldført, skal du bruge scriptet PowerShell til at hente produktions-CSID (se onboarding-scriptet).

Bemærk!

Hvis dokumenttypen konfigurationsfilen er angivet til 1000 i feltet Titel, skal der sendes tre eksempelfakturaer til overensstemmelseskontrollen:

  • Standardmomsfaktura
  • Standard debetnota
  • Standard kreditnota

Hvis dokumenttypen konfigurationsfilen er angivet til 0100 i feltet Titel, skal der sendes tre eksempelfakturaer til overensstemmelseskontrollen:

  • Forenklet momsfaktura
  • Forenklet debetnota
  • Forenklet kreditnota

Hvis dokumenttypen er angivet til 1100, skal alle seks eksempelfakturaer sendes til overensstemmelseskontrol.

Få et PCSID

Hvis du vil have et PCSID, skal du konfigurere løsningen til generering og indsendelse af elektroniske fakturaer korrekt, og løsningen skal fungere fuldt ud. For at opnå dette resultat skal du fuldføre alle de nødvendige indledende konfigurationstrin. Du kan finde flere oplysninger under Introduktion til elektronisk fakturering for Saudi-Arabien.

  1. Sørg for, at alle elektroniske fakturaer er sendt til ZATCA.

  2. Kør det onboardingscript , der findes senere i denne artikel. Angiv CCSID som inputparameter. Her er et eksempel: Script har to mulige slutpunkter simulering& prod

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

    Bemærk!

    Adgangskodeparameteren er valgfri og kan udelades. Hvis det er inkluderet, vil det certifikat, der genereres, have den angivne adgangskode.

  3. PCSID'et modtages som en certifikatfil i PFX-format. Gem dette PCSID-certifikat og denne hemmelige fil i Azure Key Vault.

  4. Konfigurer den relaterede funktionsopsætning i den saudiarabiske Zatca-indsendelsesfunktion (SA) elektronisk fakturering. Medtag PCSID-certifikatet og hemmeligheden i Key Vault-parametrene i RCS.

Når du har fuldført alle konfigurationstrinnene, er systemet klar til brug i produktionstilstand.

Hvis du vil gennemse indsamlede CSID'er på ZATCA-siden, skal du bruge feltet Gennemse eksisterende kryptografisk stempelidentifikator (CSID) på landingssiden på onboarding- og administrationsportalen. Denne portal er tilgængelig fra den primære saudiarabiske skatteportal (ERAD).

Onboarding-script

Bemærk!

Eksempelscripts understøttes ikke under noget Microsoft standardsupportprogram eller -tjeneste. Eksempelscripts leveres som de er uden garanti af nogen art. Microsoft fraskriver sig endvidere alle underforståede garantier, herunder, uden begrænsning, enhver underforstået garanti for salgbarhed eller egnethed til et bestemt formål. Hele risikoen som følge af brugen eller udførelsen af eksempelscripts og dokumentation forbliver hos dig. Microsoft, dets forfattere eller andre, der er involveret i oprettelse, produktion eller levering af scripts, er under ingen omstændigheder ansvarlige for nogen form for skader (herunder, uden begrænsning, erstatning for tab af forretningsfortjeneste, forretningsafbrydelse, tab af forretningsoplysninger eller andet økonomisk tab), der opstår som følge af brugen af eller manglende evne til at bruge eksempelscripts eller dokumentation, selvom Microsoft er blevet underrettet om muligheden for sådanne skader.

  1. Brug følgende Windows PowerShell-script til at hente et CCSID og et 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. Gem outputfilen .pfx-certifikat, der modtages i Key Vault.

Yderligere ressourcer