Receive and process a SOAP message - python

Got a service provider (Safaricom) that has decided to use SOAP to send mobile money payment notifications to businesses. When the mobile user pays (either through USSD or via a web interface) the mobile money service will send a SOAP message that we are supposed to consume.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:c2b="http://cps.huawei.com/cpsinterface/c2bpayment">
<soapenv:Header/>
<soapenv:Body>
<c2b:C2BPaymentValidationRequest>
<TransactionType>PayBill</TransactionType>
<TransID>1234560000007031</TransID>
<TransTime>20140227082020</TransTime>
<TransAmount>123.00</TransAmount>
<BusinessShortCode>12345</BusinessShortCode>
<BillRefNumber></BillRefNumber>
<InvoiceNumber></InvoiceNumber>
<MSISDN>254722703614</MSISDN>
<KYCInfo>
<KYCName>[Personal Details][First Name]</KYCName>
<KYCValue>Hoiyor</KYCValue>
</KYCInfo>
<KYCInfo>
<KYCName>[Personal Details][Middle Name]</KYCName>
<KYCValue>G</KYCValue>
</KYCInfo>
<KYCInfo>
<KYCName>[Personal Details][Last Name]</KYCName>
<KYCValue>Chen</KYCValue>
</KYCInfo>
</c2b:C2BPaymentValidationRequest>
</soapenv:Body> </soapenv:Envelope>
Don't worry the above details are public information
Question is, using a framework like bottle (or even Django) how do I "accept" this message and how do I extract the details from within the message.
I've used suds-jurko to consume Soap Services but I've never been on the receiving end of a SOAP call.
At minimum though am able to get the message using payment_data = request.body.read()
from bottle import request
payment_data = request.body.read()
print(payment_data)
From there though I've tried using XML parsers in python but its getting complicated. Is there a way for suds (or zeep) to allow me to get the data from the xml object?

I hope it's not late for an answer:
For the C2B transactions there is a project on github https://github.com/kn9ts/project-mulla it takes the request from the checkout in POST form, converts it to a SOAP request, send it to safaricom, receives the response from safaricom and gives a response in Json format.

Related

How to get webhook data in python

I have a webhook url from messagebird, and post requests are being sent from the LINE Messaging API to that url when some events occur. I need to get the data of those webhooks (JSON data). I'm using Python (it's a Django app).
How can I get the data from the webhook?

Authentication in movie theater projector via python requests and soapenv

I'm trying to create an alternative web interface for a movie theater projector that is on a local network with a computer. Communication is made on XMLHttpRequest with an exemplary Payload structure
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.doremilabs.com/dc/dcp/json/v1_0">
<soapenv:Header/>
<soapenv:Body>
<v1:GetSystemOverview>
<sessionId>937f80a5-1a3b-4e01-a3eb-30a814be3ef4</sessionId>
</v1:GetSystemOverview>
</soapenv:Body>
</soapenv:Envelope>
To get the sessionId, I use Selenium, go through the authentication page once, which contains the username and password input, and get the sessionId from the requests passed to the page.
Then using requests I send a request with the cookies saved and the payload created with sessionId, but I get the following response with error:
{
"Fault": {
"faultcode": "Server",
"faultstring": "not authenticated",
"faultactor": "Doremi_SOAP_Server",
"detail": ""
}
}
But requests that do not require sessionId work without problems.
How do I fix this error and is there a way to not use selenium in this question in general?
The projector uses Dolby IMS2000

SOAP client in python, how to replicate with XML

I am using suds to send XML and I got my request working, but I'm really confused by how to replicate my results using XML. I have the XML request that my suds client is sending by using:
from suds.client import Client
ulr = "xxxxxxx"
client = Client(url)
...
client.last_received.str()
but I'm not sure where I would send that request to if I was using the requests library. How would I replicate the request from the suds client in a python request?
Most SOAP APIs are just over plain HTTP, use POST - and therefore are easily mimicked with any standard HTTP client such as Requests.
First look here to see how to view the headers and body that suds is sending - it is then a matter of replicating these headers/XML body and passing them into the Requests library.
One defining characteristic in 99% of all HTTP SOAP API's is that your request is going to the same end-point for each request (for example 'http://yyy.com:8080/Posting/LoadPosting.svc), and the actual action is specified in the header using SOAPAction header). Contrast this to a RESTful API where the action is implied with the verb + end-point you call (POST /user, GET /menu etc.)

Python: Making a request with suds

i'm testing out SUDS library and I'm trying to make a simple request to an endpoint but i get unusual output. Why?
from suds.client import Client
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
url = "http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/Gateway"
client = Client(url)
print client
Output:
Martynass-MacBook-Air:CH martynas$ python ch.py
DEBUG:suds.xsd.schema:loaded:
schema collection
Schema:0x109a7db90
(raw)
<schema/>
(model)
DEBUG:suds.xsd.schema:MERGED:
Schema:0x109a7db90
(raw)
<schema/>
(model)
You can't use suds for this servce, suds is based on SOAP, which is another web service protocol. What you can do is send an xml request and get a response.
import requests
target_url = "http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/Gateway"
headers={'Content-type': 'text/xml'}
print requests.post(target_url, data=xml, headers=headers).text
Where the xml is defined according to their schemes.
http://xmlgw.companieshouse.gov.uk/example_http.html
This is one examaple
xml = ('''
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/schemas/govtalk/govtalkheader"
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
xmlns:gt="http://www.govtalk.gov.uk/schemas/govtalk/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.govtalk.gov.uk/schemas/govtalk/govtalkheader">
<EnvelopeVersion>1.0</EnvelopeVersion>
<Header>
<MessageDetails>
<Class>CompanyDetails</Class>
<Qualifier>request</Qualifier>
<TransactionID>14456553</TransactionID>
</MessageDetails>
<SenderDetails>
<IDAuthentication>
<SenderID>My_SenderID</SenderID>
<Authentication>
<Method>CHMD5</Method>
<Value>e999e113407884fa410fa2f53bc23952</Value>
</Authentication>
</IDAuthentication>
<EmailAddress>sometest#some.email.address</EmailAddress>
</SenderDetails>
</Header>
<GovTalkDetails>
<Keys/>
</GovTalkDetails>
<Body>
<CompanyDetailsRequest xmlns="http://xmlgw.companieshouse.gov.uk/v1-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/CoDets.xsd">
<CompanyNumber>01002361</CompanyNumber>
<GiveMortTotals>1</GiveMortTotals>
</CompanyDetailsRequest>
</Body>
</GovTalkMessage>
''')
<Class>CompanyDetails</Class> What type of info you're getting. kinda what "function" to call
<Authentication>
<Method>CHMD5</Method>
<Value>e999e113407884fa410fa2f53bc23952</Value>
</Authentication>
</IDAuthentication>
Here you would put the login info i guess
<CompanyDetailsRequest xmlns="http://xmlgw.companieshouse.gov.uk/v1-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/CoDets.xsd">
<CompanyNumber>01002361</CompanyNumber>
<GiveMortTotals>1</GiveMortTotals>
</CompanyDetailsRequest>
The "function" call and it's parameters
Now this will give me a response telling me the authorization failed. So if you have an account there, this should work for you.
Here you can find the list of schemes they have for different types of request. Some of them have sample request to help you out.
http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/SchemaStatusOutput
Here is the complete guide of all their schemes.
http://xmlgw.companieshouse.gov.uk/data_usage_guide_dec_2013.pdf
There aren't any wsdl definitions for that site. Try something like
http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
for your url
then you can try something like
client.service.GetWeatherInformation()
From the suds document, "You will need to know the url for WSDL for each service used."
An explicit example
from suds.client import Client
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
url = " http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"
client = Client(url)
client.service.GetWeatherInformation()
Outputs a ton of data.
Suds does not make it easy to discover the service, it's better to first test a bit with soapui or generate a human-readable doc of the wsdl with this xslt : http://code.google.com/p/wsdl-viewer/ . So you know the structure of requests and replies, and which services are available.
Requests and responses in soap are xml trees, so once you get the result, you need to access the content of the xml tag that contains the information you're interested in. Here is an example that should work ( I don't have a username, but the result.Status.Success works ).
import suds
client = suds.client.Client("http://webservices.data-8.co.uk/companieshouse.asmx?WSDL")
result = client.service.GetCompanyDetails("username", "password", 1234)
print result.Status.Success
print result.Result.CompanyName
You can not make a request against .xsd. XSD is definition of the exchanged message. You must make a request against webservice Looking here you can find more info about that web service. But also there is pricing page indicating that you must pay to use their service. Probably when you pay you will get username and password to authenticate with the service.

Howto send a valid XML POST request to ebay API via Python?

I guess this is rather a general problem than ebay specific, but I am not sure: I am trying to send an XML request to the ebay developer API to retrieve an XML response. When using curl, everything works fine and I get an XML response telling me which API-keys are missing (if I would provide them via HTTP headers I would get a valid XML result):
curl -d '<!xml version="1.0" encoding="utf-8"?><findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services"><keywords>harry potter phoenix</keywords></findItemsByKeywordsRequest>' \
http://svcs.sandbox.ebay.com/services/search/FindingService/v1
Which leads to the correct response:
<?xml version='1.0' encoding='UTF-8'?>
<ms:errorMessage xmlns:ms="http://www.ebay.com/marketplace/services" xmlns="http://www.ebay.com/marketplace/search/v1/services">
<error>
<errorId>2038</errorId>
<domain>CoreRuntime</domain>
<severity>Error</severity>
<category>System</category>
<message>Missing SOA operation name header</message>
<subdomain>System</subdomain>
</error>
</ms:errorMessage>
But when I try to work with Python I just get "500 Internal Server error", no matter how basic I make my examples. I have tried two very basic methods:
Number one:
serverUrl = 'svcs.sandbox.ebay.com'
xmlparameters = '<!xml version="1.0" encoding="utf-8"?><findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services"><keywords>harry potter phoenix</keywords></findItemsByKeywordsRequest>'
webservice = httplib.HTTP(serverUrl)
webservice.putrequest("POST", "/services/search/FindingService/v1")
webservice.putheader("Host", serverUrl)
webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
webservice.putheader("Content-length", "%d" % len(xmlparameters))
webservice.endheaders()
webservice.send(xmlparameters)
Number two (which is my prefered method):
serverUrl = 'svcs.sandbox.ebay.com'
xmlparameters = '<!xml version="1.0" encoding="utf-8"?><findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services"><keywords>harry potter phoenix</keywords></findItemsByKeywordsRequest>'
connection = httplib.HTTPConnection(serverUrl)
connection.request("POST", '/services/search/FindingService/v1', xmlparameters)
As you can see in the CURL example, it does not matter that I do not send the API keys etc., it should return an XML error response anyway and not only HTTP status code "500 Internal server error".
Does anyone see what I am doing wrong with my POST request?
[EDIT]
btw using the URL ValueName API works perfectly with Python, but that’s just a GET request on a URL. Yet, I’d prefer to use the XML API. However, if that’s not possible I’d switch to ValueName URIs of course.
It's returning a 500 status and the xml response:
>>> connection.request("POST", '/services/search/FindingService/v1', xmlparameters)
>>> resp = connection.getresponse()
>>> resp.status
<<< 500
>>> resp.read()
<<< '<?xml version=\'1.0\' encoding=\'UTF-8\'?><ms:errorMessage xmlns:ms="http://www.ebay.com/marketplace/services" xmlns="http://www.ebay.com/marketplace/search/v1/services"><error><errorId>2038</errorId><domain>CoreRuntime</domain><severity>Error</severity><category>System</category><message>Missing SOA operation name header</message><subdomain>System</subdomain></error></ms:errorMessage>'
The 500 response status is rather generic and should come back no matter what error is being thrown on the server. Would you consider using the CGI trackback to return the error message?
http://docs.python.org/library/cgitb.html#module-cgitb

Categories

Resources