Search code examples
pythonsoapwsdlsoapuidhl

DHL SOAP Request


I'm trying to use the API from DHL to add shipment details and get a label in return. I'm using the soapbox with SoapUI and be able to make requests. Now I want to do this in Python. I'm not really sure about the steps. I stumbled across this: DHL Soap Request Python

Can somebody help me with getting this to run? The authentication is working but I don't know how to build the soap request.

First trial was to use the SoapUI Software for doing some simple requests or to test everything. With my credentials I was able to send a request and get a label in return.

As endpoint the following URL was used, taken from the doc: https://cig.dhl.de/services/sandbox/soap

The XML looks like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cis="http://dhl.de/webservice/cisbase" xmlns:ns="http://dhl.de/webservices/businesscustomershipping/3.0"> 
   <soapenv:Header> 
      <cis:Authentification> 
         <cis:user>2222222222_01</cis:user> 
         <cis:signature>pass</cis:signature> 
      </cis:Authentification> 
   </soapenv:Header> 
   <soapenv:Body> 
      <ns:CreateShipmentOrderRequest> 
         <ns:Version> 
            <majorRelease>3</majorRelease> 
            <minorRelease>1</minorRelease> 
         </ns:Version> 
         <ShipmentOrder> 
            <sequenceNumber></sequenceNumber> 
            <Shipment> 
               <ShipmentDetails> 
                  <product>V62WP</product> 
                  <cis:accountNumber>${#Project#testAccountNumberV62WP}</cis:accountNumber> 
                  <customerReference>123456</customerReference> 
                  <shipmentDate>2021-09-03</shipmentDate> 
                  <costCentre></costCentre> 
                  <ShipmentItem> 
                     <weightInKG>1</weightInKG> 
                     <lengthInCM>25</lengthInCM> 
                     <widthInCM>15</widthInCM> 
                     <heightInCM>1</heightInCM> 
                  </ShipmentItem> 
                  <Service> 
                  </Service> 
                  <Notification> 
                     <recipientEmailAddress>[email protected]</recipientEmailAddress> 
                  </Notification> 
               </ShipmentDetails> 
               <Shipper> 
                  <Name> 
                     <cis:name1>Absender Zeile 1</cis:name1> 
                     <cis:name2>Absender Zeile 2</cis:name2> 
                     <cis:name3>Absender Zeile 3</cis:name3> 
                  </Name> 
                  <Address> 
                     <cis:streetName>Vegesacker Heerstr.</cis:streetName> 
                     <cis:streetNumber>111</cis:streetNumber> 
                     <cis:zip>28757</cis:zip> 
                     <cis:city>Bremen</cis:city> 
                     <cis:Origin> 
                        <cis:country></cis:country> 
                        <cis:countryISOCode>DE</cis:countryISOCode> 
                     </cis:Origin> 
                  </Address> 
                  <Communication> 
                     <!--Optional:--> 
                     <cis:phone>+49421987654321</cis:phone> 
                     <cis:email>[email protected]</cis:email> 
                     <!--Optional:--> 
                     <cis:contactPerson>Kontaktperson Absender</cis:contactPerson> 
                  </Communication> 
               </Shipper> 
               <Receiver> 
                  <cis:name1>Name</cis:name1> 
                  <Address> 
                     <cis:name2>Empfänger Zeile 2</cis:name2> 
                     <cis:name3>Empfänger Zeile 3</cis:name3> 
                     <cis:streetName>Street</cis:streetName> 
                     <cis:streetNumber>Number</cis:streetNumber> 
                     <cis:zip>zipCode</cis:zip> 
                     <cis:city>City</cis:city> 
                     <cis:Origin> 
                        <cis:country></cis:country> 
                        <cis:countryISOCode>DE</cis:countryISOCode> 
                     </cis:Origin> 
                  </Address> 
                  <Communication> 
                     <cis:phone>+49421123456789</cis:phone> 
                     <cis:email>[email protected]</cis:email> 
                     <cis:contactPerson>Kontaktperson Empfänger</cis:contactPerson> 
                  </Communication> 
               </Receiver> 
            </Shipment> 
            <PrintOnlyIfCodeable active="1"/> 
         </ShipmentOrder> 
         <labelResponseType>URL</labelResponseType> 
         <groupProfileName></groupProfileName> 
         <labelFormat></labelFormat> 
         <labelFormatRetoure></labelFormatRetoure> 
         <combinedPrinting>0</combinedPrinting> 
      </ns:CreateShipmentOrderRequest> 
   </soapenv:Body> 
</soapenv:Envelope>

My Python code looks like this:

from requests import Session
from requests.auth import HTTPBasicAuth
from zeep import Client, xsd
from zeep.transports import Transport

user = "my_user_name"
password = "my_password"
USER = "2222222222_01" #from doc
PASSWORD = "pass" #from doc
EKP = "2222222222"

wsdl = "./geschaeftskundenversand-api-3.1.8.wsdl" #downloaded and stored local

session = Session()

# Authenticate  with gateway
session.auth = HTTPBasicAuth(user, password)
client = Client(wsdl, transport=Transport(session=session))

# Build Authentification header for API-Endpoint using zeep xsd
header = xsd.Element(
    '{http://test.python-zeep.org}Authentification',
    xsd.ComplexType([
        xsd.Element(
            '{http://test.python-zeep.org}user',
            xsd.String()),
        xsd.Element(
            '{http://test.python-zeep.org}signature',
            xsd.String()),
    ])
)
header_value = header(user = USER, signature = PASSWORD)
client.service.createShipmentOrder(_soapheaders=[header_value])

As a result I'm getting a traceback: Missing Element Version

It seems to me that the authentication is working and now I need to insert the body parts of the XML. What I understood, that the next step should be:

client.service.createShipmentOrder

and here I could pass a dictionary for example. createShipmentOrder is one of the requests as stated in the documentary. It gives back a XML with Status OK, a shipment number and a URL to the label for printing. In my opinion now I have to pass the address and so on as stated in the XML. In the SoupUI the request is send to the endpoint URL https://cig.dhl.de/services/sandbox/soap. But in my Python Code the Endpoint isn't stated. Do I need it or is it part of the wsdl file I stored locally?


Solution

  • So as nobody was able to help me, I did it myself :)

    I needed some hours to understand the API but now it is clear...

    You can use mostly of the code from my question. You have to generate a nested dictionary with all the needed data. For Example:

    dict = {'Version':{'majorRelease' : '3', 'minorRelease' : '1'},
                        'ShipmentOrder':
                            {'sequenceNumber':'','Shipment':
                                {'ShipmentDetails':
                                    {'product':product, 'accountNumber':accountNumber, 'customerReference':customerReference,
                                    'shipmentDate':shipmentDate,
                                    'ShipmentItem':{'weightInKG':'0.9', 'lengthInCM':'35', 'widthInCM':'24', 'heightInCM':'5'}
                                    },
                                'Shipper':{
                                    'Name':{'name1':'your name'},
                                    'Address':{'streetName':'your street','streetNumber':'1234', 'zip':'11111', city':'New York',
                                        'Origin':{'country':'', 'countryISOCode':'DE'}}
                                },
                                'Receiver':{
                                'name1':receiver_name,
                                    'Address':{'streetName':receiver_street,'streetNumber':receiver_streetnumber, 'zip':receiver_zip, 'city':receiver_city,
                                    'Origin':{'country':'', 'countryISOCode':'DE'}}
                                }
                                }
                            },
                            'labelFormat':label_groesse
                }
    

    save the wsdl files into a folder and link your script to it like:

    wsdl = "./geschaeftskundenversand-api-3.1.8.wsdl"
    

    You need a header for the request and the authentication at the gateway. The authentication is run over a simple request as shown already in the question.

    All you need now is the soap request:

    response = client.service.createShipmentOrder(_soapheaders=[header_value], **dict)
    

    where you put in the header and the dict. If you run everything then you will get a response which says 'OK' and has a shipment number and the url to your label in it.

    That's it.

    I'm running the script with some streamlit parts on a Raspberry Pi4 and let the label printer print everything. Here my code for printing the label:

    import subprocess
    conn = cups.Connection() #Connection to CUPS
    printers = conn.getPrinters() #Get all listed printers
    use_printer = printers['QL-1110NWB']["device-uri"] #choose the one label printer
     
    file = requests.get(label_url) #Save the label from url into pdf document locally
    
    with open('label.pdf', 'wb') as pdf:
       pdf.write(file.content)
    
                   
    subprocess.run(['lp', '-d', 'QL-1110NWB', '-o fit-to-page', 'label.pdf']) # print the saved label