Final qrcode

This commit is contained in:
pooyarhz99 2025-09-13 21:15:29 +03:30
parent 572540154d
commit 18a11ef9ed
5 changed files with 144 additions and 44 deletions

View File

@ -1,34 +1,48 @@
package document
import (
"fmt"
"time"
"github.com/shopspring/decimal"
)
// DocumentExtended represents the ExtendedDocument table from the MyInvois API response.
type ExtendedDocument struct {
UUID string `json:"uuid"`
SubmissionUID string `json:"submissionUid"`
LongID string `json:"longId"`
InternalID string `json:"internalId"`
TypeName string `json:"typeName"`
TypeVersionName string `json:"typeVersionName"`
IssuerTIN string `json:"issuerTin"`
IssuerName string `json:"issuerName"`
ReceiverID string `json:"receiverId"`
ReceiverName string `json:"receiverName"`
DateTimeIssued string `json:"dateTimeIssued"` // ISO8601 UTC
DateTimeReceived string `json:"dateTimeReceived"` // ISO8601 UTC
DateTimeValidated string `json:"dateTimeValidated"` // ISO8601 UTC
TotalExcludingTax decimal.Decimal `json:"totalExcludingTax"`
TotalDiscount decimal.Decimal `json:"totalDiscount"`
TotalNetAmount decimal.Decimal `json:"totalNetAmount"`
TotalPayableAmount decimal.Decimal `json:"totalPayableAmount"`
Status string `json:"status"`
CancelDateTime string `json:"cancelDateTime"` // ISO8601 UTC
RejectRequestDateTime string `json:"rejectRequestDateTime"` // ISO8601 UTC
DocumentStatusReason string `json:"documentStatusReason"`
CreatedByUserID string `json:"createdByUserId"`
Document string `json:"document"`
CancelDateTime time.Time `json:"cancelDateTime"`
CreatedByUserID string `json:"createdByUserId"`
DateTimeIssued time.Time `json:"dateTimeIssued"`
DateTimeReceived time.Time `json:"dateTimeReceived"`
DateTimeValidated time.Time `json:"dateTimeValidated"`
Document string `json:"document"`
DocumentStatusReason string `json:"documentStatusReason"`
InternalID string `json:"internalId"`
IssuerName string `json:"issuerName"`
IssuerTin string `json:"issuerTin"`
LongID string `json:"longID"`
ReceiverID string `json:"receiverId"`
ReceiverName string `json:"receiverName"`
RejectRequestDateTime any `json:"rejectRequestDateTime"`
Status string `json:"status"`
SubmissionUID string `json:"submissionUid"`
TotalDiscount float64 `json:"totalDiscount"`
TotalExcludingTax float64 `json:"totalExcludingTax"`
TotalNetAmount float64 `json:"totalNetAmount"`
TotalPayableAmount float64 `json:"totalPayableAmount"`
TypeName string `json:"typeName"`
TypeVersionName string `json:"typeVersionName"`
UUID string `json:"uuid"`
}
func (docEx ExtendedDocument) String() string {
return fmt.Sprintf(
"{UUID: %s, SubmissionUID: %s, LongID: %s, Status: %s, DocumentStatusReason: %s, TotalPayableAmount: %f}",
docEx.UUID,
docEx.SubmissionUID,
docEx.LongID,
docEx.Status,
docEx.DocumentStatusReason,
docEx.TotalPayableAmount)
}
type UBLInvoiceRoot struct {

View File

@ -36,17 +36,14 @@ func GetDocument(accessToken, docUUID string) (document.ExtendedDocument, error)
}
defer res.Body.Close()
var resBodyUnformatted map[string]interface{}
json.NewDecoder(res.Body).Decode(&resBodyUnformatted)
if core.DEBUG {
b, _ := json.MarshalIndent(resBodyUnformatted, "", " ")
core.LogInfo.Printf("Response (%d):\n%v\n", res.StatusCode, string(b))
}
var respBodyFormatted document.ExtendedDocument
json.NewDecoder(res.Body).Decode(&respBodyFormatted)
if res.StatusCode == 200 {
return respBodyFormatted, nil
var respBody document.ExtendedDocument
json.NewDecoder(res.Body).Decode(&respBody)
if core.DEBUG {
b, _ := json.MarshalIndent(respBody, "", " ")
core.LogInfo.Printf("Response (%d):\n%v\n", res.StatusCode, string(b))
}
return respBody, nil
} else {
core.LogInfo.Printf("ERROR: LHDN GetDocument error response (%d):\n%v\n", res.StatusCode, res.Body)
return document.ExtendedDocument{}, errors.New("LHDN GetDocument failed")

View File

@ -0,0 +1,58 @@
package route
import (
"encoding/json"
"iralex-einvoice/config"
"iralex-einvoice/core"
"net/http"
"net/http/httputil"
"time"
)
func GetDocumentDetails(accessToken, docUUID string) {
core.LogInfo.Printf("INFO: sending request GET %s\n", config.LHDN_BASE_URL+"/api/v1.0/documents/"+docUUID+"/details")
req, err := http.NewRequest(http.MethodGet, config.LHDN_BASE_URL+"/api/v1.0/documents/"+docUUID+"/details", nil)
if err != nil {
core.LogInfo.Printf("ERROR: cannot construct the LHDN GetDocument request: %v", err)
// return document.ExtendedDocument{}, err
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer "+accessToken)
if core.DEBUG {
bytes, _ := httputil.DumpRequestOut(req, true)
core.LogInfo.Printf("Request:\n%s\n", bytes)
}
client := &http.Client{
Timeout: time.Second * config.DEFAULT_TIMEOUT,
}
res, err := client.Do(req)
if err != nil {
core.LogInfo.Printf("ERROR: couldn't send the LHDN GetDocument request: %v", err)
// return document.ExtendedDocument{}, err
}
defer res.Body.Close()
var respBody map[string]interface{}
json.NewDecoder(res.Body).Decode(&respBody)
if core.DEBUG {
b, _ := json.MarshalIndent(respBody, "", " ")
core.LogInfo.Printf("Response (%d):\n%v\n", res.StatusCode, string(b))
}
return
if res.StatusCode == 200 {
// var respBody document.ExtendedDocument
var respBody map[string]interface{}
json.NewDecoder(res.Body).Decode(&respBody)
if core.DEBUG {
b, _ := json.MarshalIndent(respBody, "", " ")
core.LogInfo.Printf("Response (%d):\n%v\n", res.StatusCode, string(b))
}
// return respBody, nil
} else {
core.LogInfo.Printf("ERROR: LHDN GetDocument error response (%d):\n%v\n", res.StatusCode, res.Body)
// return document.ExtendedDocument{}, errors.New("LHDN GetDocument failed")
}
}

View File

@ -8,6 +8,7 @@ import (
"iralex-einvoice/core"
"iralex-einvoice/route"
"net/http"
"net/http/httputil"
"runtime/debug"
"time"
)
@ -25,7 +26,7 @@ type sendQRCodeResponseBody struct {
// JSON body, with a single "doc_id" field that contains the LHDN document UUID.
func HandleQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
fmt.Println("Accepted request at /send-qrcode")
core.LogInfo.Println("Accepted request at /send-qrcode")
core.LogInfo.Println("INFO - Accepted request at /send-qrcode")
// In case of a panic, do not stop the server, but send a 500.
defer func() {
@ -73,12 +74,20 @@ func HandleQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
return
}
var link = "https://myinvois.hasil.gov.my/" + reqBody.DocID + "/share/" + docEx.LongID
if core.DEBUG {
core.LogInfo.Println("INFO - Document formatted", docEx)
}
json.NewEncoder(w).Encode(sendQRCodeResponseBody{
DocID: reqBody.DocID,
QRCode: link,
})
if len(docEx.LongID) > 0 {
var link = "https://myinvois.hasil.gov.my/" + reqBody.DocID + "/share/" + docEx.LongID
json.NewEncoder(w).Encode(sendQRCodeResponseBody{
DocID: reqBody.DocID,
QRCode: link,
})
} else {
w.WriteHeader(528)
fmt.Fprint(w, "528 - document QRCode link is not available")
}
}
func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
@ -93,11 +102,17 @@ func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
// hardcoded for testing
// QR code generation must be parameterized on document UUID
const docUUID = "408BMZ0D2AVGTWHDWAH3GQ0K10"
const docUUID =
// "408BMZ0D2AVGTWHDWAH3GQ0K10"
// "B6CGMPM3BBJFYMQRDYKVF15K10"
"RSS3HQQF0SXFG06AWSTVE15K10"
var longID string = ""
accessToken := route.AuthenticateFormData(core.CLIENT_ID, core.CLIENT_SECRET_1, core.TIN)
route.GetDocumentDetails(accessToken, docUUID)
return
req, err := http.NewRequest(http.MethodGet, config.LHDN_BASE_URL+"/api/v1.0/documents/"+docUUID+"/raw", nil) // GetDocument
if err != nil {
fmt.Printf("Error in constructing the request: %v", err)
@ -105,6 +120,8 @@ func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer "+accessToken)
bytes, _ := httputil.DumpRequestOut(req, true)
fmt.Printf("Request:\n%s\n", bytes)
client := &http.Client{
Timeout: time.Second * config.DEFAULT_TIMEOUT,
@ -118,9 +135,13 @@ func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
var respBody map[string]interface{}
json.NewDecoder(resp.Body).Decode(&respBody)
b, _ := json.MarshalIndent(respBody, "", " ")
fmt.Printf("Response (%d):\n%v\n", resp.StatusCode, string(b))
if resp.StatusCode == 200 {
if _longID, ok := respBody["longID"]; ok {
longID = _longID.(string)
} else {
fmt.Println("key doesn't exist")
}
} else {
fmt.Printf("Error\n")

View File

@ -225,11 +225,21 @@ func HandleSubmit(w http.ResponseWriter, r *http.Request) {
return
}
if core.DEBUG {
core.LogInfo.Println("INFO - Document Details: ", docEx)
}
// If we reached here, everything worked fine.
json.NewEncoder(w).Encode(submitResponseBody{
DocID: docUUID,
QRCode: "https://myinvois.hasil.gov.my/" + docUUID + "/share/" + docEx.LongID,
})
if len(docEx.LongID) > 0 {
var link = "https://myinvois.hasil.gov.my/" + docUUID + "/share/" + docEx.LongID
json.NewEncoder(w).Encode(submitResponseBody{
DocID: docUUID,
QRCode: link,
})
} else {
w.WriteHeader(528)
fmt.Fprint(w, "528 - document QRCode link is not available")
}
}
// =============================================================================