VAT check with gorilla-xmlrpc


(Markus Bolder) #1

Hi, I am trying to make a post to a xmlrpc server in this format:

https://evatr.bff-online.de/evatrRPC?UstId_1=DE123456789&UstId_2=AB1234567890
&Firmenname=Firmenname einschl. Rechtsform&Ort=Ort der Firma&PLZ=12345&Strasse=Strasse der Firma
&Druck=ja

Read the results and access them in go lang by name:

An example result xml looks like this:

<params>
  <param>
    <value><array><data>
      <value><string>Datum</string></value>
      <value><string>27.07.2006</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Uhrzeit</string></value>
      <value><string>13:35:53</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>ErrorCode</string></value>
      <value><string>200</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>UstId_1</string></value>
      <value><string>DE123456789</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>UstId_2</string></value>
      <value><string>AB1234567890</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Firmenname</string></value>
      <value><string>Firma XY Rechtsform</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Ort</string></value>
      <value><string>Firmenort</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>PLZ</string></value>
      <value><string>1234</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Strasse</string></value>
      <value><string>Firmenstrasse</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Erg_Name</string></value>
      <value><string>A</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Erg_Ort</string></value>
      <value><string>A</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Erg_PLZ</string></value>
      <value><string>A</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Erg_Str</string></value>
      <value><string>A</string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Gueltig_ab</string></value>
      <value><string></string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Gueltig_bis</string></value>
      <value><string></string></value>
    </data></array></value>
  </param>
  <param>
    <value><array><data>
      <value><string>Druck</string></value>
      <value><string>ja</string></value>
    </data></array></value>
  </param>
</params>

I got this far:

type Params struct {
	XMLName xml2.Name   `xml:"params"`
	Text    string   `xml:",chardata"`
	Param   []struct {
		Text  string `xml:",chardata"`
		Value struct {
			Text  string `xml:",chardata"`
			Array struct {
				Text string `xml:",chardata"`
				Data struct {
					Text  string `xml:",chardata"`
					Value []struct {
						Text   string `xml:",chardata"`
						String string `xml:"string"`
					} `xml:"value"`
				} `xml:"data"`
			} `xml:"array"`
		} `xml:"value"`
	} `xml:"param"`
}

func XmlRpcCall(method string, args struct{UstId_1,UstId_2,Firmenname,Ort,PLZ,Strasse,Druck string}) (reply struct{Message string}, err error) {
	buf, _ := xml.EncodeClientRequest(method, &args)	
	resp, err := http.Post("https://evatr.bff-online.de/", "text/xml", bytes.NewBuffer(buf))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	err = xml.DecodeClientResponse(resp.Body, &reply)
	return
}

func main() {

	reply, err := XmlRpcCall("evatrRPC", struct{UstId_1,UstId_2,Firmenname,Ort,PLZ,Strasse,Druck string}{"VAT1","VAT2","Name","Town","ZIP","Street","No"})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(reply.Message)
}

My code prints the xml message, but how do get it into the struct and access for example. The Error Code value of 200?

I am pretty new to go , so anything will be helpful.


(Johan Dahl) #2

Hi

You should use Unmarshal from the encoding/xml package https://golang.org/pkg/encoding/xml/#Unmarshal

See this page for an example. You could use the same technice and define the big struct by using smaller parts otherwise will it be too complex and hard to understand.

https://larry-price.com/blog/2015/12/04/xml-parsing-in-go/


(Markus Bolder) #3

So, I tried to follow that example, with this code, but I am still not managing, to access the values singly. eg, CodeError = 200 …

package main

import (
	"encoding/xml"
	"fmt"
)

var reply string

type Params struct {
	XMLName xml.Name `xml:"params"`
	Text    string   `xml:",chardata"`
	Param   []struct {
		Text  string `xml:",chardata"`
		Value struct {
			Text  string `xml:",chardata"`
			Array struct {
				Text string `xml:",chardata"`
				Data struct {
					Text  string `xml:",chardata"`
					Value []struct {
						Text   string `xml:",chardata"`
						String string `xml:"string"`
					} `xml:"value"`
				} `xml:"data"`
			} `xml:"array"`
		} `xml:"value"`
	} `xml:"param"`
}

func main() {
	reply = `
<params>
<param>
<value><array><data>
<value><string>Datum</string></value>
<value><string>27.07.2006</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Uhrzeit</string></value>
<value><string>13:35:53</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>ErrorCode</string></value>
<value><string>200</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_1</string></value>
<value><string>DE123456789</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_2</string></value>
<value><string>AB1234567890</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Firmenname</string></value>
<value><string>Firma XY Rechtsform</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Ort</string></value>
<value><string>Firmenort</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>PLZ</string></value>
<value><string>1234</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Strasse</string></value>
<value><string>Firmenstrasse</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Name</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Ort</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_PLZ</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Str</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_ab</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_bis</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Druck</string></value>
<value><string>ja</string></value>
</data></array></value>
</param>
</params>
`

	container := Params{}
	err := xml.Unmarshal([]byte(reply), &container)

	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(container)
	}

}

Result:

{{ params}

[{

{ { {

[{ Datum} { 27.07.2006}]}}}} {

{ { {

[{ Uhrzeit} { 13:35:53}]}}}} {

{ { {

[{ ErrorCode} { 200}]}}}} {

{ { {

[{ UstId_1} { DE123456789}]}}}} {

{ { {

[{ UstId_2} { AB1234567890}]}}}} {

{ { {

[{ Firmenname} { Firma XY Rechtsform}]}}}} {

{ { {

[{ Ort} { Firmenort}]}}}} {

{ { {

[{ PLZ} { 1234}]}}}} {

{ { {

[{ Strasse} { Firmenstrasse}]}}}} {

{ { {

[{ Erg_Name} { A}]}}}} {

{ { {

[{ Erg_Ort} { A}]}}}} {

{ { {

[{ Erg_PLZ} { A}]}}}} {

{ { {

[{ Erg_Str} { A}]}}}} {

{ { {

[{ Gueltig_ab} { }]}}}} {

{ { {

[{ Gueltig_bis} { }]}}}} {

{ { {

[{ Druck} { ja}]}}}}]}


(Johan Dahl) #4

Hi

Good work with this large structure!
Lines below will print ErrorCode and 200. I get a member of a struct by using the dot notation and works my way down into the structures.

		fmt.Println(container.Param[2].Value.Array.Data.Value[0].String)
		fmt.Println(container.Param[2].Value.Array.Data.Value[1].String)

(Johan Dahl) #5

If you want to iterate over all values can you do like this

	for _, p := range container.Param {
		for _, v := range p.Value.Array.Data.Value {
			fmt.Println(v.String)
		}
	}

then iterating over a slice with for range will the first value be the index but I want the value which is optionally the second value. Here I use the placeholder variable _ (underscore) because I’m not interested in the index just the value.


(Johan Dahl) #6

Do you need these? If you skip them will the struct be much smaller

type Params struct {
	Param []struct {
		Value struct {
			Array struct {
				Data struct {
					Value []struct {
						String string `xml:"string"`
					} `xml:"value"`
				} `xml:"data"`
			} `xml:"array"`
		} `xml:"value"`
	} `xml:"param"`
}

And it works as good what I can see


(Johan Dahl) #7

You could even use the > instruction in the xml tags to make the struct even simpler

type Params struct {
	Param []struct {
		String []string `xml:"value>array>data>value>string"`	
	} `xml:"param"`
}

and print ErrorCode and 300 like this

fmt.Println(container.Param[2].String[0])
fmt.Println(container.Param[2].String[1])

I just found this then reading the documentation


(Markus Bolder) #8

That’s brilliant, thank you very much. I will post the finished function, once I have completed it, in case somebody else needs to check VAT-Id´s in the future.


(Markus Bolder) #9

Here are the final functions to check a European VAT ID number:

package main

import (import (
	"bytes"
	xml2 "encoding/xml"
	"fmt"
	"github.com/dannyvankooten/vat"	
	"github.com/divan/gorilla-xmlrpc/xml"
)

func checkVat(UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck string) {

	validity, err := vat.ValidateNumberFormat(UstId_2)
	if err != nil {
		fmt.Println("Format der Umsatzsteuer ID unzulässig")
	}
	if validity == true {
		fmt.Println("Format der Umsatzsteuer ID zulässig")
	}
	validity2, err2 := vat.ValidateNumberExistence(UstId_2)
	if err2 != nil {
		fmt.Println("Umsatzsteuer ID ist unzulässig")
	}
	if validity2 == true {
		fmt.Println("Umsatzsteuer ID zulässig")
	}
	if validity && validity2 == true {
		XmlRpcCall("evatrRPC", struct{ UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck string }{UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck})
	}
}

func XmlRpcCall(method string, args struct{ UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck string }) {

	type Params struct {
		Param []struct {
			Value struct {
				Array struct {
					Data struct {
						Value []struct {
							String string `xml:"string"`
						} `xml:"value"`
					} `xml:"data"`
				} `xml:"array"`
			} `xml:"value"`
		} `xml:"param"`
	}

	type rep struct {
		Message string
	}

	var reply rep
	result := make(map[string]string)
	var Key string

	buf, _ := xml.EncodeClientRequest(method, &args)
	resp, err := http.Post("https://evatr.bff-online.de/", "text/xml", bytes.NewBuffer(buf))
	defer resp.Body.Close()
	err = xml.DecodeClientResponse(resp.Body, &reply)
	container := Params{}
	err = xml2.Unmarshal([]byte(reply.Message), &container)

	if err != nil {
		fmt.Println("Error:", err)
	}
	for _, p := range container.Param {

		for i, v := range p.Value.Array.Data.Value {
			if i == 0 {
				Key = v.String
			}
			if i == 1 {
				result[Key] = v.String
			}
		}
	}
	if result["ErrorCode"] == "200" {
		fmt.Println("Die angefragte USt-IdNr. ist gĂĽltig.")
	} else {
		fmt.Println("Die angefragte USt-IdNr. ist ungĂĽltig!")
	}
	fmt.Println(result)
	return

}

func main() {

	checkVat("YourVATId", "CustomerVATId", "Customer Company Name", "Customer City", "Customer Postcode", "Customer Street", "Print Yes/No")

}

The function checkVat first checks if the VAT Id number format is correct, then if the number has been issued, then if both checks are ok, it will ask for the Number to be checked against the customer address. The Print Yes/No, triggers the letter from the German TAX Department with the official result of your query. The result of the query are saved in a map called result, which keys are according to the fields (UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck).

Hope somebody else will also find some use for it. If there are any comments on how to streamline the code. Please let me know.