vfd

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2023 License: MIT Imports: 25 Imported by: 0

README

tra-vfd a fully compliant golang implementation of the VFD API as specified by the Tanzania Revenue Authority (TRA).

The library currently supports

  • Client Registration
  • Token Fetching
  • Receipt Posting
  • Z Report Posting

You can also generate the receipt/z report in form of xml file as specified by the TRA. Also you can specify the location of the xml file and post it to TRA.

The library is used to power multiple platform in real world scenarios. So yes it has been tested in some real world environment. Still some improvements can be done and contributions are welcome.

Installation

 go get github.com/Golang-Tanzania/tra-vfd

Usage

package main

import (
    "fmt"
    "github.com/Golang-Tanzania/tra-vfd"
    "github.com/Golang-Tanzania/tra-vfd/pkg/env"
)

// Fetching Access Token
func main(){
	tokenURL := vfd.RequestURL(env.DEV,vfd.FetchTokenAction)
	request := &vfd.TokenRequest{
		Username:  "",
		Password:  "",
		GrantType: "",
	}
	response, err := vfd.FetchToken(context.Background(), tokenURL, request)
	if err != nil {
		fmt.Printf("failed to fetch token: %v", err)
		os.Exit(1)
	}

	fmt.Printf("token: %+v", response)
}

Contributing

Contributions are welcome. Please open an issue or submit a pull request.

License

MIT License

Copyright (c) 2023 Golang Tanzania

Documentation

Index

Constants

View Source
const (
	SuccessCode          int64 = 0
	InvalidSignatureCode int64 = 1
	InvalidTaxID         int64 = 3
	ApprovalRequired     int64 = 4
	UnhandledException   int64 = 5
	InvalidSerial        int64 = 6
	InvalidClientHeader  int64 = 7
	InvalidCertificate   int64 = 8
)
View Source
const (
	StandardVATID        = "A"
	StandardVATRATE      = 18.00
	StandardVATCODE      = 1
	SpecialVATID         = "B"
	SpecialVATRATE       = 0.00
	SpecialVATCODE       = 2
	ZeroVATID            = "C"
	ZeroVATRATE          = 0.00
	ZeroVATCODE          = 3
	SpecialReliefVATID   = "D"
	SpecialReliefVATRATE = 0.00
	SpecialReliefVATCODE = 4
	ExemptedVATID        = "E"
	ExemptedVATRATE      = 0.00
	ExemptedVATCODE      = 5
	TaxableItemCode      = 1
	TaxableItemId        = "A"
	NonTaxableItemCode   = 3
	NonTaxableItemId     = "C"
)
View Source
const (
	RegisterProductionURL                  = "https://vfd.tra.go.tz/api/vfdRegReq"
	FetchTokenProductionURL                = "https://vfd.tra.go.tz/vfdtoken" //nolint:gosec
	SubmitReceiptProductionURL             = "https://vfd.tra.go.tz/api/efdmsRctInfo"
	SubmitReportProductionURL              = "https://vfd.tra.go.tz/api/efdmszreport"
	VerifyReceiptProductionURL             = "https://verify.tra.go.tz/"
	RegisterTestingURL                     = "https://virtual.tra.go.tz/efdmsRctApi/api/vfdRegReq"
	FetchTokenTestingURL                   = "https://virtual.tra.go.tz/efdmsRctApi/vfdtoken" //nolint:gosec
	SubmitReceiptTestingURL                = "https://virtual.tra.go.tz/efdmsRctApi/api/efdmsRctInfo"
	SubmitReportTestingURL                 = "https://virtual.tra.go.tz/efdmsRctApi/api/efdmszreport"
	VerifyReceiptTestingURL                = "https://virtual.tra.go.tz/efdmsRctVerify/"
	RegisterClientAction       Action      = "register"
	FetchTokenAction           Action      = "token"
	SubmitReceiptAction        Action      = "receipt"
	SubmitReportAction         Action      = "report"
	ReceiptVerificationAction  Action      = "verification"
	CashPaymentType            PaymentType = "CASH"
	CreditCardPaymentType      PaymentType = "CCARD"
	ChequePaymentType          PaymentType = "CHEQUE"
	InvoicePaymentType         PaymentType = "INVOICE"
	ElectronicPaymentType      PaymentType = "EMONEY"
	TINCustomerID              CustomerID  = 1
	LicenceCustomerID          CustomerID  = 2
	VoterIDCustomerID          CustomerID  = 3
	PassportCustomerID         CustomerID  = 4
	NIDACustomerID             CustomerID  = 5
	NonCustomerID              CustomerID  = 6
	MeterNumberCustomerID      CustomerID  = 7
	SubmitReceiptRoutingKey    string      = "vfdrct"
	SubmitReportRoutingKey     string      = "vfdzreport"
	ContentTypeXML             string      = "application/xml"
	RegistrationRequestClient  string      = "webapi"
)

Variables

View Source
var ErrFetchToken = errors.New("fetch token failed")

ErrFetchToken is the error returned when the token request fails. It is a wrapper for the underlying error.

View Source
var ErrReceiptUploadFailed = errors.New("receipt upload failed")
View Source
var ErrRegistrationFailed = errors.New("registration failed")
View Source
var ErrReportSubmitFailed = fmt.Errorf("report submit failed")

Functions

func IsNetworkError

func IsNetworkError(err error) bool

IsNetworkError returns true if the error is a NetworkError.

func IsSuccess

func IsSuccess(code int64) bool

IsSuccess checks the response ack code and return true if the code means success and false if otherwise

func LoadCert

func LoadCert(path, password string) (*rsa.PrivateKey, *x509.Certificate, error)

func LoadCertChain

func LoadCertChain(certPath string, certPassword string) (*rsa.PrivateKey, *x509.Certificate, []*x509.Certificate, error)

func NetAmount

func NetAmount(taxCode int64, price float64) float64

NetAmount calculates the net price of a product of a certain ValueAddedTax category. This is the NetAmount which is collected by the seller without the ValueAddedTax. The buyer is charged this NetAmount plus the ValueAddedTax NetAmount. After calculating the answer is rounded to 2 decimal places. price = netPrice + netPrice * (vatRate / 100)

func ParseErrorCode

func ParseErrorCode(code int64) string

ParseErrorCode parses the error code and returns the corresponding error message.

func ReceiptBytes

func ReceiptBytes(privateKey *rsa.PrivateKey, params ReceiptParams, customer Customer,
	items []Item, payments []Payment,
) ([]byte, error)
func ReceiptLink(e env.Env, receiptCode string, gc int64, receiptTime string) string

ReceiptLink creates a link to the receipt it accepts RECEIPTCODE, GC and the RECEIPTTIME and env.Env to know if the receipt was created during testing or production.

func ReportBytes

func ReportBytes(privateKey *rsa.PrivateKey, params *ReportParams, address Address,
	vats []VATTOTAL, payments []Payment,
	totals ReportTotals,
) ([]byte, error)

ReportBytes returns the bytes of the report payload. It calls xml.Marshal on the report. then replace all the occurrences of <PAYMENT>, </PAYMENT>, <VATTOTAL>, </VATTOTAL> with empty string "" and then add the xml.Header to the beginning of the payload.

func ReportTaxRateID

func ReportTaxRateID(taxCode int64) string

ReportTaxRateID creates a string that contains the ValueAddedTax rate and the ValueAddedTax id of a certain ValueAddedTax category. It returns "A-18.00" for standard ValueAddedTax, "B-10.00" for special ValueAddedTax, "C-0.00" for zero ValueAddedTax and so on. The ID is then used in Z Report to indicate the ValueAddedTax rate and the ValueAddedTax id.

func RequestURL

func RequestURL(e env.Env, action Action) string

RequestURL returns the URL for the specified Action and specified env.Env returns empty string if either action or env is not recognized

func Sign

func Sign(privateKey *rsa.PrivateKey, payload []byte) ([]byte, error)

func SignPayload

func SignPayload(privateKey *rsa.PrivateKey, payload []byte) ([]byte, error)

func ValueAddedTaxAmount

func ValueAddedTaxAmount(taxCode int64, price float64) float64

ValueAddedTaxAmount calculates the amount of ValueAddedTax that is charged to the buyer. The answer is rounded to 2 decimal places.

func ValueAddedTaxID

func ValueAddedTaxID(taxCode int64) string

ValueAddedTaxID returns the ValueAddedTax id of a certain ValueAddedTax category It returns "A" for standard ValueAddedTax, "B" for special ValueAddedTax, "C" for zero ValueAddedTax,"D" for special relief and "E" for exempted ValueAddedTax.

func ValueAddedTaxRate

func ValueAddedTaxRate(taxCode int64) float64

ValueAddedTaxRate returns the ValueAddedTax rate of a certain ValueAddedTax category

func VerifySignature

func VerifySignature(publicKey *rsa.PublicKey, payload []byte, signature string) error

Types

type Action

type Action string

Action signifies the action to be performed among the four defined actions which are INSTANCE registration, token fetching, submission of receipt and submission of report.

type Address

type Address struct {
	Name    string
	Street  string
	Mobile  string
	City    string
	Country string
}

func (*Address) AsList

func (lines *Address) AsList() []string

type CertLoader

type CertLoader func(certPath string, certPassword string) (*rsa.PrivateKey, *x509.Certificate, error)

CertLoader loads a certificate from a file and returns the private key and the certificate

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(options ...Option) *Client

func (*Client) FetchToken

func (c *Client) FetchToken(ctx context.Context, url string,
	request *TokenRequest,
) (*TokenResponse, error)

func (*Client) FetchTokenWithMw

func (c *Client) FetchTokenWithMw(ctx context.Context, url string,
	request *TokenRequest, callback OnTokenResponse,
) (*TokenResponse, error)

func (*Client) Register

func (c *Client) Register(ctx context.Context,
	url string, privateKey *rsa.PrivateKey,
	request *RegistrationRequest,
) (*RegistrationResponse, error)

func (*Client) SetHttpClient

func (c *Client) SetHttpClient(http *http.Client)

SetHttpClient sets the http client

func (*Client) SubmitReceipt

func (c *Client) SubmitReceipt(
	ctx context.Context,
	url string,
	headers *RequestHeaders,
	privateKey *rsa.PrivateKey,
	receipt *ReceiptRequest,
) (*Response, error)

func (*Client) SubmitReport

func (c *Client) SubmitReport(
	ctx context.Context,
	url string,
	headers *RequestHeaders,
	privateKey *rsa.PrivateKey,
	report *ReportRequest,
) (*Response, error)

type Customer

type Customer struct {
	Type   CustomerID
	ID     string
	Name   string
	Mobile string
}

Customer contains customer information

type CustomerID

type CustomerID int

CustomerID is the type of ID the customer used during purchase The Type of ID is to be included in the receipt. Allowed values for CustomerID are 1 through 7. The number to type mapping are as follows: 1: Tax Identification Number (TIN), 2: Driving License, 3: Voters Number, 4: Travel Passport, 5: National ID, 6: NIL (No Identity Used), 7: Meter Number

type Error

type Error struct {
	Code    int64  `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

type FetchTokenFunc

type FetchTokenFunc func(ctx context.Context, url string, request *TokenRequest) (*TokenResponse, error)

FetchTokenFunc is a function that fetches a token from the VFD server.

type Item

type Item struct {
	ID          string
	Description string
	TaxCode     int64
	Quantity    float64
	UnitPrice   float64
	Discount    float64
}

Item represent a purchased item. TaxCode is an integer that can take the value of 1 for taxable items and 3 for non-taxable items. Discount is for the whole package not a unit discount

type ItemProcessResponse

type ItemProcessResponse struct {
	ITEMS     []*models.ITEM
	VATTOTALS []*models.VATTOTAL
	TOTALS    models.TOTALS
}

func ProcessItems

func ProcessItems(items []Item) *ItemProcessResponse

ProcessItems processes the []Items in the submitted receipt request and create []*models.ITEM which is used to create the xml request also calculates the total discount, total tax exclusive and total tax inclusive

type NetworkError

type NetworkError struct {
	Err     error
	Message string
}

NetworkError is returned when there is an error in the network.

func (*NetworkError) Error

func (e *NetworkError) Error() string

func (*NetworkError) Unwrap

func (e *NetworkError) Unwrap() error

Unwrap returns the underlying error.

type OnTokenResponse

type OnTokenResponse func(context.Context, *TokenResponse) error

OnTokenResponse is a callback function that is called when a token is received.

func WrapTokenResponseMiddleware

func WrapTokenResponseMiddleware(next OnTokenResponse, middlewares ...TokenResponseMiddleware) OnTokenResponse

WrapTokenResponseMiddleware wraps a TokenResponseMiddleware with a OnTokenResponse.

type Option

type Option func(*Client)

func WithHttpClient

func WithHttpClient(http *http.Client) Option

type PayloadSigner

type PayloadSigner func(privateKey *rsa.PrivateKey, payload []byte) ([]byte, error)

PayloadSigner signs a payload using the private key of the signing certificate all requests to the VFD API must be signed.

type Payment

type Payment struct {
	Type   PaymentType
	Amount float64
}

type PaymentType

type PaymentType string

PaymentType represent the type of payment that is recognized by the VFD server There are five types of payments: CASH, CHEQUE, CCARD, EMONEY and INVOICE.

func ParsePayment

func ParsePayment(value any) PaymentType

ParsePayment ...

type RawRequest

type RawRequest struct {
	Env      env.Env
	Action   Action
	FilePath string
}

RawRequest contains information needed to send receipt/z report file to the vfd server.

type ReceiptParams

type ReceiptParams struct {
	Date           string
	Time           string
	TIN            string
	RegistrationID string
	EFDSerial      string
	ReceiptNum     string
	DailyCounter   int64
	GlobalCounter  int64
	ZNum           string
	ReceiptVNum    string
}

ReceiptParams contains parameters icluded while sending the receipts

type ReceiptRequest

type ReceiptRequest struct {
	Params   ReceiptParams
	Customer Customer
	Items    []Item
	Payments []Payment
}

type RegistrationRequest

type RegistrationRequest struct {
	ContentType string
	CertSerial  string
	Tin         string
	CertKey     string
}

type RegistrationResponse

type RegistrationResponse struct {
	ACKCODE     string   `xml:"ACKCODE"`
	ACKMSG      string   `xml:"ACKMSG"`
	REGID       string   `xml:"REGID"`
	SERIAL      string   `xml:"SERIAL"`
	UIN         string   `xml:"UIN"`
	TIN         string   `xml:"TIN"`
	VRN         string   `xml:"VRN"`
	MOBILE      string   `xml:"MOBILE"`
	ADDRESS     string   `xml:"ADDRESS"`
	STREET      string   `xml:"STREET"`
	CITY        string   `xml:"CITY"`
	COUNTRY     string   `xml:"COUNTRY"`
	NAME        string   `xml:"NAME"`
	RECEIPTCODE string   `xml:"RECEIPTCODE"`
	REGION      string   `xml:"REGION"`
	ROUTINGKEY  string   `xml:"ROUTINGKEY"`
	GC          int64    `xml:"GC"`
	TAXOFFICE   string   `xml:"TAXOFFICE"`
	USERNAME    string   `xml:"USERNAME"`
	PASSWORD    string   `xml:"PASSWORD"`
	TOKENPATH   string   `xml:"TOKENPATH"`
	TAXCODES    TAXCODES `xml:"TAXCODES"`
}

func Register

func Register(ctx context.Context, requestURL string, privateKey *rsa.PrivateKey,
	request *RegistrationRequest,
) (*RegistrationResponse, error)

Register send the registration for a Virtual Fiscal Device to the VFD server. The registration request is signed with the private key of the certificate used to authenticate the INSTANCE.

type ReportParams

type ReportParams struct {
	Date             string
	Time             string
	VRN              string
	TIN              string
	UIN              string
	TaxOffice        string
	RegistrationID   string
	ZNumber          string
	EFDSerial        string
	RegistrationDate string
}

type ReportRequest

type ReportRequest struct {
	Params  *ReportParams
	Address *Address
	Totals  *ReportTotals
	VATS    []VATTOTAL
	Payment []Payment
}

type ReportTotals

type ReportTotals struct {
	DailyTotalAmount float64
	Gross            float64
	Corrections      float64
	Discounts        float64
	Surcharges       float64
	TicketsVoid      int64
	TicketsVoidTotal float64
	TicketsFiscal    int64
	TicketsNonFiscal int64
}

ReportTotals contains different number of totals

type RequestHeaders

type RequestHeaders struct {
	CertSerial  string
	BearerToken string
}

RequestHeaders represent collection of request headers during receipt or Z report sending via VFD Service.

type Response

type Response struct {
	Number  int64  `json:"number,omitempty"`
	Date    string `json:"date,omitempty"`
	Time    string `json:"time,omitempty"`
	Code    int64  `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

Response contains details returned when submitting a receipt to the VFD Service or a Z report. Number (int) is the receipt number in case of a receipt submission and the Z report number in case of a Z report submission. Date (string) is the date of the receipt or Z report submission. The format is YYYY-MM-DD. Time (string) is the time of the receipt or Z report submission. The format is HH24:MI:SS Code (int) is the response code. 0 means success. Message (string) is the response message.

func SubmitRawRequest

func SubmitRawRequest(ctx context.Context, headers *RequestHeaders,
	raw *RawRequest) (*Response, error)

SubmitRawRequest is useful for submitting requests that are in form of XML files content of the file is read and submitted to the server as is.

func SubmitReceipt

func SubmitReceipt(ctx context.Context, requestURL string, headers *RequestHeaders, privateKey *rsa.PrivateKey,
	receiptRequest *ReceiptRequest,
) (*Response, error)

SubmitReceipt uploads a receipt to the VFD server.

func SubmitReport

func SubmitReport(ctx context.Context, url string, headers *RequestHeaders, privateKey *rsa.PrivateKey,
	report *ReportRequest,
) (*Response, error)

type Service

type Service interface {
	// Register is used to register a virtual fiscal device (VFD) with the VFD Service.
	// If successful, the VFD Service returns a registration response containing the
	// VFD details and the credentials to use when submitting receipts and Z reports.
	// Registering a VFD is a one-time operation. The subsequent calls to Register will
	// yield the same response.VFD should store the registration response to
	// avoid calling Register again.
	Register(ctx context.Context, url string, privateKey *rsa.PrivateKey, request *RegistrationRequest,
	) (*RegistrationResponse, error)

	// FetchToken is used to fetch a token from the VFD Service. The token is used
	// to authenticate the VFD when submitting receipts and Z reports.
	// credentials used here are the ones returned by the Register method.
	FetchToken(ctx context.Context, url string, request *TokenRequest) (*TokenResponse, error)

	// SubmitReceipt is used to submit a receipt to the VFD Service. The receipt
	// is signed using the private key. The private key is obtained from the certificate
	// issued by the Revenue Authority during integration.
	SubmitReceipt(
		ctx context.Context, url string, headers *RequestHeaders,
		privateKey *rsa.PrivateKey, receipt *ReceiptRequest) (*Response, error)

	// SubmitReport is used to submit a Z report to the VFD Service. The Z report
	// is signed using the private key. The private key is obtained from the certificate
	// issued by the Revenue Authority during integration.
	SubmitReport(
		ctx context.Context, url string, headers *RequestHeaders,
		privateKey *rsa.PrivateKey, report *ReportRequest) (*Response, error)
}

type SignatureVerifier

type SignatureVerifier func(publicKey *rsa.PublicKey, payload []byte, signature string) error

SignatureVerifier verifies the signature of a payload using the public key of the signing certificate

type TAXCODES

type TAXCODES struct {
	XMLName xml.Name `xml:"TAXCODES"`
	Text    string   `xml:",chardata"`
	CODEA   string   `xml:"CODEA"`
	CODEB   string   `xml:"CODEB"`
	CODEC   string   `xml:"CODEC"`
	CODED   string   `xml:"CODED"`
}

type TokenRequest

type TokenRequest struct {
	Username  string
	Password  string
	GrantType string
}

TokenRequest contains the request parameters needed to get a token. GrantType - The type of the grant_type. Username - The username of the user. Password - The password of the user.

type TokenResponse

type TokenResponse struct {
	Code        string `json:"code,omitempty"`
	Message     string `json:"message,omitempty"`
	AccessToken string `json:"access_token,omitempty"`
	TokenType   string `json:"token_type,omitempty"`
	ExpiresIn   int64  `json:"expires_in,omitempty"`
	Error       string `json:"error,omitempty"`
}

TokenResponse contains the response parameters returned by the token endpoint.

func FetchToken

func FetchToken(ctx context.Context, url string, request *TokenRequest) (*TokenResponse, error)

FetchToken retrieves a token from the VFD server. If the status code is not 200, an error is returned. Error Message will contain TokenResponse.Code and TokenResponse.Message FetchToken wraps internally a *http.Client responsible for making http calls. It has a timeout of 70 seconds. It is advised to call this only when the previous token has expired. It will still work if called before the token expires.

func FetchTokenWithMw

func FetchTokenWithMw(ctx context.Context, url string, request *TokenRequest, callback OnTokenResponse) (*TokenResponse, error)

FetchTokenWithMw retrieves a token from the VFD server then passes it to the callback function This is beacuse the response might have a code and message that needs to be handled.

func (*TokenResponse) String

func (tr *TokenResponse) String() string

type TokenResponseMiddleware

type TokenResponseMiddleware func(next OnTokenResponse) OnTokenResponse

TokenResponseMiddleware is a middleware function that is called when a token is received.

type URL

type URL struct {
	Registration  string
	FetchToken    string
	SubmitReceipt string
	SubmitReport  string
	VerifyReceipt string
}

URL is a struct that holds the URLs for the four actions

type VATTOTAL

type VATTOTAL struct {
	ID        string
	Rate      float64
	TaxAmount float64
	NetAmount float64
}

VATTOTAL represent the VAT details.

type ValueAddedTax

type ValueAddedTax struct {
	ID         string // ID is a character that identifies the ValueAddedTax it can be A,B,C,D or E
	Code       int64  // Code is a number that identifies the ValueAddedTax it can be 0,1,2,3 or 4
	Name       string
	Percentage float64
}

func ParseTaxCode

func ParseTaxCode(code int64) ValueAddedTax

func (*ValueAddedTax) Amount

func (v *ValueAddedTax) Amount(totalAmount float64) float64

Amount calculates the amount of ValueAddedTax that is charged to the buyer. The answer is rounded to 2 decimal places.

func (*ValueAddedTax) NetAmount

func (v *ValueAddedTax) NetAmount(totalAmount float64) float64

Directories

Path Synopsis
internal
pkg
env

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL