New endpoint for the API - No more SOAP!

Based on the feedback we’ve received from developers working with the API, the SOAP protocol (Simple Object Access Protocol) did prove to add a great deal of friction when trying to programmatically post a simple request to the API. Every post needed to be wrapped in a SOAP Envelope, not only did this add extra payload to the request and response stream it was also very unforgiving if the envelope did not precisely confirm to the spec.

Simple, cleaner API communication

Today we have added a new endpoint:

https://quickfile.co.uk/WebServices/API/invoices.ashx

You can now dispense with the SOAP envelope and just HTTP POST the xml (in the message body) directly to this URL. Here is a VB.NET example on how this can be coded in your application, examples in other languages will be posted shortly.

VB.NET Example

    Dim req As HttpWebRequest = WebRequest.Create("https://quickfile.co.uk/WebServices/API/invoices.ashx")
    req.Method = "POST"
    req.ContentType = "application/xml;charset=UTF-8"
    req.KeepAlive = False
    req.Accept = "application/xml;charset=UTF-8"
    Dim writer As New StreamWriter(req.GetRequestStream())
    writer.WriteLine("<Invoice_Get>....</Invoice_Get>") '<<<<<< INSERT YOUR XML HERE
    writer.Close()
    Dim rsp As WebResponse
    rsp = req.GetResponse()
    Dim sr As New StreamReader(rsp.GetResponseStream)
    Dim xmlStringResponse as string = sr.ReadToEnd

C# Example

HttpWebRequest req = WebRequest.Create("https://quickfile.co.uk/WebServices/API/invoices.ashx");
req.Method = "POST";
req.ContentType = "application/xml;charset=UTF-8";
req.KeepAlive = false;
req.Accept = "application/xml;charset=UTF-8";
StreamWriter writer = new StreamWriter(req.GetRequestStream());
writer.WriteLine("<Invoice_Get>....</Invoice_Get>"); //<<<<<< INSERT YOUR XML HERE
writer.Close();
WebResponse rsp = default(WebResponse);
rsp = req.GetResponse();
StreamReader sr = new StreamReader(rsp.GetResponseStream);
string xmlStringResponse = sr.ReadToEnd;
2 Likes

PHP Example

An example using cURL. This will simply show the first client from your account.

    $url = 'https://api.quickfile.co.uk/xml';
    $accountNo = '1234567890';
    $appID = 'abc-12345-def';
    $subNo = '12345678abcd';
    $apiKey = 'ABC-DEF-GHI';
    
    $data = '<Client_Search xmlns="http://www.QuickFile.co.uk" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.QuickFile.co.uk http://www.quickfile.co.uk/WebServices/API/Schemas/invoices/Client_Search.xsd">
      <Header>
        <MessageType>Request</MessageType>
        <TestMode>false</TestMode>
        <SubmissionNumber>'.$subNo.'</SubmissionNumber>
        <Authentication>
          <AccNumber>'.$accountNo.'</AccNumber>
          <MD5Value>'.md5($accountNo.$apiKey.$subNo).'</MD5Value>
    <ApplicationID>'.$appID.'</ApplicationID>
        </Authentication>
      </Header>
      <Body>
          <SearchParameters>
            <ReturnCount>1</ReturnCount>
            <Offset>0</Offset>
            <OrderResultsBy>CompanyName</OrderResultsBy>
            <OrderDirection>ASC</OrderDirection>
        </SearchParameters>
      </Body>
    </Client_Search>';
    
    $ch = curl_init($url);
    curl_setopt( $ch, CURLOPT_URL, $url );
    curl_setopt( $ch, CURLOPT_POST, true );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $data );
    $result = curl_exec($ch);
    
    if($result === false) {
        echo 'Curl error: ' . curl_error($ch);
    } else {
        echo 'Curl successful!';
    }
    
    curl_close($ch);

Note: This is omitting the ‘https’ protocol, and using the ‘http’ protocol. While trying to use HTTPS, I ran into an error, and had to add this line to the cURL:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1 Like

Example in Groovy of creating an invoice using RESTClient from the HttpBuilder library.

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
import groovyx.net.http.*
import static groovyx.net.http.ContentType.XML

def apiKey = '...'
def accountNumber = '...'
def appId = '...'

def http = new RESTClient("https://www.quickfile.co.uk/WebServices/API/invoices.ashx")

// generate a subscription number that's likely to be unique
def subno = System.currentTimeMillis()

// Authentication header - each time this is used it will increment the subno, so
// it is safe to use for several calls within the same script
def header = {
  def digest = java.security.MessageDigest.getInstance("MD5")
  digest.update((accountNumber + apiKey + subno).getBytes('UTF-8'))
  def md5Val = digest.digest().encodeHex()

  MessageType('Request')
  SubmissionNumber(subno++)
  Authentication {
    AccNumber(accountNumber)
    MD5Value(md5Val)
    ApplicationID(appId)
  }
}

// the lines we want to put in the invoice
def lines = [
  [ desc:'Bar of soap', unitCost:0.76, qty:2, vat:20 ],
  [ desc:'Tin of beans', unitCost:0.99, qty:3, vat:0 ],
]

def createResponse = http.post(contentType:XML, requestContentType:XML, body:{
  mkp.declareNamespace('':'http://www.QuickFile.co.uk') 
  mkp.declareNamespace('xsi':'http://www.w3.org/1999/XMLSchema-instance') 
  Invoice_Create('xsi:schemaLocation':'http://www.QuickFile.co.uk http://www.quickfile.co.uk/WebServices/API/Schemas/invoices/Invoice_Create.xsd') {
    Header(header) // uses the header calculation from above
    Body {
      InvoiceData {
        InvoiceType('INVOICE')
        ClientID('...')
        Currency('GBP')
        TermDays('2')
        Language('en')
        SingleInvoiceData {
          IssueDate('2016-10-29')
        }
        InvoiceLines {
          ItemLines {
            for(line in lines) {
              ItemLine {
                ItemID('0')
                ItemDescription(line.desc)
                Tax1 {
                  TaxName('VAT')
                  TaxPercentage(line.vat)
                }
                UnitCost(String.format('%.2f', line.unitCost))
                Qty(line.qty)
              }
            }
          }
        }
      }
    }
  }
})

if(createResponse.data.Error.size()) {
  println "Error creating invoice"
  createResponse.data.Error.each {
    println it.text()
  }
  System.exit(1)
}

def newInvoiceId = createResponse.data.Body.InvoiceID.text()
def newInvoiceNumber = createResponse.data.Body.InvoiceNumber.text()

println "Created invoice ${newInvoiceNumber}"


3 Likes

Python - Invoice_GetPDF

url = 'https://api.quickfile.co.uk/1_2/invoice/getpdf'
accountNo = '123456'
appID = 'ABC-123-XYZ'
apiKey = 'ACDR-98765-ZYXW'
submission_num = 5
invoice_id = 123456789

import json
import requests
from hashlib import md5

encode_md5=(accountNo + apiKey + str(submission_num).zfill(8)).encode('utf-8')
auth={"AccNumber": accountNo, "MD5Value": md5(encode_md5).hexdigest(), "ApplicationID": appID}
header={"MessageType":"Request", "SubmissionNumber": str(submission_num).zfill(8),"Authentication":auth}
payload={"payload":{"Header":header, "Body":{"InvoiceID":invoice_id}}}
#print(json.dumps(payload,indent=4)) #error tracing

resp = requests.post(url, json=payload)
#print(format(resp.json())) #error tracing

if "Invoice_GetPDF" in resp.json():
    print(format(resp.json()["Invoice_GetPDF"]["Body"]["InvoiceDetails"]["PDFUri"]))
elif "Errors" in resp.json():
    for error in resp.json()["Errors"]:
        print(resp.json()["Errors"][error])
3 Likes