Tcl
Tcl
Create ITIDA Signed JSON and Send to ETA (Egypt Tax Authority) Portal
See more Egypt ITIDA Examples
Demonstrates how to create a .p7s signature that fits Egypt's ITIDA requirements where Chilkat automatically does the ITIDA JSON canonicalization. Also shows the code to send to the ETA Portal.Chilkat Tcl Downloads
load ./chilkat.dll
set success 0
# This example assumes the Chilkat API to have been previously unlocked.
# See Global Unlock Sample for sample code.
set crypt [new_CkCrypt2]
CkCrypt2_put_VerboseLogging $crypt 1
set cert [new_CkCert]
CkCert_put_VerboseLogging $cert 1
# Set the smart card PIN, which will be needed for signing.
CkCert_put_SmartCardPin $cert "12345678"
# There are many ways to load the certificate.
# This example was created for a customer using an ePass2003 USB token.
# Assuming the USB token is the only source of a hardware-based private key..
set success [CkCert_LoadFromSmartcard $cert ""]
if {$success == 0} then {
puts [CkCert_lastErrorText $cert]
delete_CkCrypt2 $crypt
delete_CkCert $cert
exit
}
# Tell the crypt class to use this cert.
set success [CkCrypt2_SetSigningCert $crypt $cert]
if {$success == 0} then {
puts [CkCrypt2_lastErrorText $crypt]
delete_CkCrypt2 $crypt
delete_CkCert $cert
exit
}
set cmsOptions [new_CkJsonObject]
# Setting "DigestData" causes OID 1.2.840.113549.1.7.5 (digestData) to be used.
CkJsonObject_UpdateBool $cmsOptions "DigestData" 1
CkJsonObject_UpdateBool $cmsOptions "OmitAlgorithmIdNull" 1
# Indicate that we are passing normal JSON and we want Chilkat do automatically
# do the ITIDA JSON canonicalization:
CkJsonObject_UpdateBool $cmsOptions "CanonicalizeITIDA" 1
CkCrypt2_put_CmsOptions $crypt [CkJsonObject_emit $cmsOptions]
# The CadesEnabled property applies to all methods that create CMS/PKCS7 signatures.
# To create a CAdES-BES signature, set this property equal to true.
CkCrypt2_put_CadesEnabled $crypt 1
CkCrypt2_put_HashAlgorithm $crypt "sha256"
set jsonSigningAttrs [new_CkJsonObject]
CkJsonObject_UpdateInt $jsonSigningAttrs "contentType" 1
CkJsonObject_UpdateInt $jsonSigningAttrs "signingTime" 1
CkJsonObject_UpdateInt $jsonSigningAttrs "messageDigest" 1
CkJsonObject_UpdateInt $jsonSigningAttrs "signingCertificateV2" 1
CkCrypt2_put_SigningAttributes $crypt [CkJsonObject_emit $jsonSigningAttrs]
# By default, all the certs in the chain of authentication are included in the signature.
# If desired, we can choose to only include the signing certificate:
CkCrypt2_put_IncludeCertChain $crypt 0
# Pass a JSON document such as the following. Chilkat will do the ITIDA canonicalization.
# (It is the canonicalized JSON that gets signed.)
# {
# "issuer":{
# "address":{
# "branchID":"0",
# "country":"EG",
# "regionCity":"Cairo",
# "postalCode":"",
# "buildingNumber":"0",
# "street":"123rd Street",
# "governate":"GOVERNATE"
# },
# "type":"B",
# "id":"209999899",
# "name":"Xyz SAE"
# },
# "receiver":{
# "address":{
# "country":"EG",
# "regionCity":"CAIRO",
# "postalCode":"11435",
# "buildingNumber":"0",
# "street":"Autostrad Road Abc",
# "governate":"GOVERNATE"
# },
# "type":"B",
# "id":"999999999",
# "name":"XYZ EGYPT FOR TRADE"
# },
# "documentType":"I",
# "documentTypeVersion":"1.0",
# "dateTimeIssued":"2020-11-15T11:04:53Z",
# "taxpayerActivityCode":"1073",
# "internalID":"ZZZZ999",
# "purchaseOrderReference":"2009199918",
# "salesOrderReference":"",
# "payment":{
# "bankName":"",
# "bankAddress":"",
# "bankAccountNo":"",
# "bankAccountIBAN":"",
# "swiftCode":"",
# "terms":""
# },
# "delivery":{
# "approach":"",
# "packaging":"",
# "dateValidity":"",
# "exportPort":"",
# "countryOfOrigin":"EG",
# "grossWeight":0,
# "netWeight":0,
# "terms":""
# },
# "invoiceLines":[
# {
# "description":"CDM Widget 48GX99X12BA",
# "itemType":"GS1",
# "itemCode":"7622213335056",
# "unitType":"CS",
# "quantity":1.00,
# "unitValue":{
# "currencySold":"EGP",
# "amountEGP":588.67,
# "amountSold":0,
# "currencyExchangeRate":0
# },
# "salesTotal":588.67,
# "total":603.97,
# "valueDifference":0,
# "totalTaxableFees":0,
# "netTotal":529.8,
# "itemsDiscount":0,
# "discount":{
# "rate":10.00,
# "amount":58.87
# },
# "taxableItems":[
# {
# "taxType":"T1",
# "amount":74.17,
# "subType":"No sub",
# "rate":14.00
# }
# ],
# "internalCode":"9099994"
# }
# ],
# "totalSales":588.67,
# "totalSalesAmount":588.67,
# "totalDiscountAmount":58.87,
# "netAmount":529.80,
# "taxTotals":[
# {
# "taxType":"T1",
# "amount":74.17
# }
# ],
# "extraDiscountAmount":0,
# "totalItemsDiscountAmount":0,
# "totalAmount":603.97,
# }
# Build the above JSON..
# Use this online tool to generate code from sample JSON:
# Generate Code to Create JSON
set json [new_CkJsonObject]
CkJsonObject_UpdateString $json "issuer.address.branchID" "0"
CkJsonObject_UpdateString $json "issuer.address.country" "EG"
CkJsonObject_UpdateString $json "issuer.address.regionCity" "Cairo"
CkJsonObject_UpdateString $json "issuer.address.postalCode" ""
CkJsonObject_UpdateString $json "issuer.address.buildingNumber" "0"
CkJsonObject_UpdateString $json "issuer.address.street" "123rd Street"
CkJsonObject_UpdateString $json "issuer.address.governate" "GOVERNATE"
CkJsonObject_UpdateString $json "issuer.type" "B"
CkJsonObject_UpdateString $json "issuer.id" "209999899"
CkJsonObject_UpdateString $json "issuer.name" "Xyz SAE"
CkJsonObject_UpdateString $json "receiver.address.country" "EG"
CkJsonObject_UpdateString $json "receiver.address.regionCity" "CAIRO"
CkJsonObject_UpdateString $json "receiver.address.postalCode" "11435"
CkJsonObject_UpdateString $json "receiver.address.buildingNumber" "0"
CkJsonObject_UpdateString $json "receiver.address.street" "Autostrad Road Abc"
CkJsonObject_UpdateString $json "receiver.address.governate" "GOVERNATE"
CkJsonObject_UpdateString $json "receiver.type" "B"
CkJsonObject_UpdateString $json "receiver.id" "999999999"
CkJsonObject_UpdateString $json "receiver.name" "XYZ EGYPT FOR TRADE"
CkJsonObject_UpdateString $json "documentType" "I"
CkJsonObject_UpdateString $json "documentTypeVersion" "1.0"
CkJsonObject_UpdateString $json "dateTimeIssued" "2020-11-15T11:04:53Z"
CkJsonObject_UpdateString $json "taxpayerActivityCode" "1073"
CkJsonObject_UpdateString $json "internalID" "ZZZZ999"
CkJsonObject_UpdateString $json "purchaseOrderReference" "2009199918"
CkJsonObject_UpdateString $json "salesOrderReference" ""
CkJsonObject_UpdateString $json "payment.bankName" ""
CkJsonObject_UpdateString $json "payment.bankAddress" ""
CkJsonObject_UpdateString $json "payment.bankAccountNo" ""
CkJsonObject_UpdateString $json "payment.bankAccountIBAN" ""
CkJsonObject_UpdateString $json "payment.swiftCode" ""
CkJsonObject_UpdateString $json "payment.terms" ""
CkJsonObject_UpdateString $json "delivery.approach" ""
CkJsonObject_UpdateString $json "delivery.packaging" ""
CkJsonObject_UpdateString $json "delivery.dateValidity" ""
CkJsonObject_UpdateString $json "delivery.exportPort" ""
CkJsonObject_UpdateString $json "delivery.countryOfOrigin" "EG"
CkJsonObject_UpdateInt $json "delivery.grossWeight" 0
CkJsonObject_UpdateInt $json "delivery.netWeight" 0
CkJsonObject_UpdateString $json "delivery.terms" ""
CkJsonObject_UpdateString $json "invoiceLines[0].description" "CDM Widget 48GX99X12BA"
CkJsonObject_UpdateString $json "invoiceLines[0].itemType" "GS1"
CkJsonObject_UpdateString $json "invoiceLines[0].itemCode" "7622213335056"
CkJsonObject_UpdateString $json "invoiceLines[0].unitType" "CS"
CkJsonObject_UpdateNumber $json "invoiceLines[0].quantity" "1.00"
CkJsonObject_UpdateString $json "invoiceLines[0].unitValue.currencySold" "EGP"
CkJsonObject_UpdateNumber $json "invoiceLines[0].unitValue.amountEGP" "588.67"
CkJsonObject_UpdateInt $json "invoiceLines[0].unitValue.amountSold" 0
CkJsonObject_UpdateInt $json "invoiceLines[0].unitValue.currencyExchangeRate" 0
CkJsonObject_UpdateNumber $json "invoiceLines[0].salesTotal" "588.67"
CkJsonObject_UpdateNumber $json "invoiceLines[0].total" "603.97"
CkJsonObject_UpdateInt $json "invoiceLines[0].valueDifference" 0
CkJsonObject_UpdateInt $json "invoiceLines[0].totalTaxableFees" 0
CkJsonObject_UpdateNumber $json "invoiceLines[0].netTotal" "529.8"
CkJsonObject_UpdateInt $json "invoiceLines[0].itemsDiscount" 0
CkJsonObject_UpdateNumber $json "invoiceLines[0].discount.rate" "10.00"
CkJsonObject_UpdateNumber $json "invoiceLines[0].discount.amount" "58.87"
CkJsonObject_UpdateString $json "invoiceLines[0].taxableItems[0].taxType" "T1"
CkJsonObject_UpdateNumber $json "invoiceLines[0].taxableItems[0].amount" "74.17"
CkJsonObject_UpdateString $json "invoiceLines[0].taxableItems[0].subType" "No sub"
CkJsonObject_UpdateNumber $json "invoiceLines[0].taxableItems[0].rate" "14.00"
CkJsonObject_UpdateString $json "invoiceLines[0].internalCode" "9099994"
CkJsonObject_UpdateNumber $json "totalSales" "588.67"
CkJsonObject_UpdateNumber $json "totalSalesAmount" "588.67"
CkJsonObject_UpdateNumber $json "totalDiscountAmount" "58.87"
CkJsonObject_UpdateNumber $json "netAmount" "529.80"
CkJsonObject_UpdateString $json "taxTotals[0].taxType" "T1"
CkJsonObject_UpdateNumber $json "taxTotals[0].amount" "74.17"
CkJsonObject_UpdateInt $json "extraDiscountAmount" 0
CkJsonObject_UpdateInt $json "totalItemsDiscountAmount" 0
CkJsonObject_UpdateNumber $json "totalAmount" "603.97"
CkJsonObject_put_EmitCompact $json 1
set jsonToSign [CkJsonObject_emit $json]
# Create the CAdES-BES signature.
CkCrypt2_put_EncodingMode $crypt "base64"
# Make sure we sign the utf-8 byte representation of the JSON string
CkCrypt2_put_Charset $crypt "utf-8"
set sigBase64 [CkCrypt2_signStringENC $crypt $jsonToSign]
if {[CkCrypt2_get_LastMethodSuccess $crypt] == 0} then {
puts [CkCrypt2_lastErrorText $crypt]
delete_CkCrypt2 $crypt
delete_CkCert $cert
delete_CkJsonObject $cmsOptions
delete_CkJsonObject $jsonSigningAttrs
delete_CkJsonObject $json
exit
}
puts "Base64 signature:"
puts "$sigBase64"
# Insert the base64 signature into the JSON to be sent
CkJsonObject_UpdateString $json "signatures[0].signatureType" "I"
CkJsonObject_UpdateString $json "signatures[0].value" $sigBase64
# Wrap the JSON in {"documents":[ ... ]}
set sbToSend [new_CkStringBuilder]
CkStringBuilder_Append $sbToSend "{\"documents\":["
CkStringBuilder_Append $sbToSend [CkJsonObject_emit $json]
CkStringBuilder_Append $sbToSend "]}"
# ------------------------------------------------------------------------
# Get an access token using our client ID and client secret key
set clientId "abc999ff-1234"
set clientSecretKey "123fff22-1234-abcd"
set http [new_CkHttp]
# Causes the Authorization: Basic header to be added..
CkHttp_put_Login $http $clientId
CkHttp_put_Password $http $clientSecretKey
CkHttp_put_BasicAuth $http 1
set req [new_CkHttpRequest]
CkHttpRequest_put_HttpVerb $req "POST"
CkHttpRequest_put_Path $req "/connect/token"
CkHttpRequest_put_ContentType $req "application/x-www-form-urlencoded"
CkHttpRequest_AddParam $req "grant_type" "client_credentials"
CkHttpRequest_AddHeader $req "Connection" "close"
CkHttp_put_Accept $http "application/json"
set resp [new_CkHttpResponse]
set success [CkHttp_HttpReq $http "https://id.preprod.eta.gov.eg/connect/token" $req $resp]
if {$success == 0} then {
puts [CkHttp_lastErrorText $http]
delete_CkCrypt2 $crypt
delete_CkCert $cert
delete_CkJsonObject $cmsOptions
delete_CkJsonObject $jsonSigningAttrs
delete_CkJsonObject $json
delete_CkStringBuilder $sbToSend
delete_CkHttp $http
delete_CkHttpRequest $req
delete_CkHttpResponse $resp
exit
}
CkHttp_CloseAllConnections $http
puts "Response status code: [CkHttpResponse_get_StatusCode $resp]"
puts "Response body:"
puts [CkHttpResponse_bodyStr $resp]
if {[CkHttpResponse_get_StatusCode $resp] != 200} then {
puts "Failed."
delete_CkCrypt2 $crypt
delete_CkCert $cert
delete_CkJsonObject $cmsOptions
delete_CkJsonObject $jsonSigningAttrs
delete_CkJsonObject $json
delete_CkStringBuilder $sbToSend
delete_CkHttp $http
delete_CkHttpRequest $req
delete_CkHttpResponse $resp
exit
}
set jsonToken [new_CkJsonObject]
set success [CkJsonObject_Load $jsonToken [CkHttpResponse_bodyStr $resp]]
set accessToken [CkJsonObject_stringOf $jsonToken "access_token"]
puts "access_token = $accessToken"
# ------------------------------------------------------------------------
# Submit the signed JSON to the ETA (Egypt Tax Authority) Portal
# No longer sending basic authentication...
CkHttp_put_Login $http ""
CkHttp_put_Password $http ""
CkHttp_put_BasicAuth $http 0
# Setting the AuthToken property causes the "Authorization: Bearer <token>" header to be added to each request.
CkHttp_put_AuthToken $http $accessToken
set jsonStr [CkStringBuilder_getAsString $sbToSend]
set url "https://api.preprod.invoicing.eta.gov.eg/api/v1/documentsubmissions"
set success [CkHttp_HttpStr $http "POST" $url $jsonStr "utf-8" "application/json; charset=utf-8" $resp]
if {$success == 0} then {
puts [CkHttp_lastErrorText $http]
delete_CkCrypt2 $crypt
delete_CkCert $cert
delete_CkJsonObject $cmsOptions
delete_CkJsonObject $jsonSigningAttrs
delete_CkJsonObject $json
delete_CkStringBuilder $sbToSend
delete_CkHttp $http
delete_CkHttpRequest $req
delete_CkHttpResponse $resp
delete_CkJsonObject $jsonToken
exit
}
puts "Response status code: [CkHttpResponse_get_StatusCode $resp]"
puts "Response body:"
puts [CkHttpResponse_bodyStr $resp]
delete_CkCrypt2 $crypt
delete_CkCert $cert
delete_CkJsonObject $cmsOptions
delete_CkJsonObject $jsonSigningAttrs
delete_CkJsonObject $json
delete_CkStringBuilder $sbToSend
delete_CkHttp $http
delete_CkHttpRequest $req
delete_CkHttpResponse $resp
delete_CkJsonObject $jsonToken