Returns qr_code and doc_id upon sumbission, added a new api handler as fallback for qr_code link generation.
This commit is contained in:
parent
1710ce35d2
commit
274f96df3e
|
|
@ -1,5 +1,6 @@
|
|||
.git/
|
||||
.vscode/
|
||||
storage/
|
||||
.env
|
||||
cache.txt
|
||||
logs.txt
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@ Listening on http://0.0.0.0:8080
|
|||
|
||||
Note that `-port` (command-line argument) has a higher priority than `PORT` (environment variable). So specifying `-port` overwrites everything else. If nothing is specified, a default port will be opened.
|
||||
|
||||
## Package Structure
|
||||
## Module Structure
|
||||
|
||||
- `common`: Contains common types for use between packages in order to avoid cyclic dependencies. This package should not depend on any other package except those from the standard library or third-party libraries.
|
||||
- `config`: Some configurations for the application, such as default timeout, default port, debug mode, etc.
|
||||
- `core`: Core functionalities, including logging, environment variables, command-line arguments, and a very simple file-based caching system.
|
||||
- `core`: Core functionalities, including logging, environment variables, command-line arguments, disk operations, and a very simple file-based caching system.
|
||||
- `document`: Document generation, signing, etc.
|
||||
- `route`: The third-party API routes the application calls into, such as those of Iralex and LHDN.
|
||||
- `server`: A simple HTTP server.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// Converts a bill object to a document (UBL invoice) object.
|
||||
// Converts an Iralex bill object to a document (UBL invoice) object.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// The following pre-conditions on the bill parameter must be satisified by the
|
||||
|
|
|
|||
|
|
@ -4,6 +4,33 @@ import (
|
|||
"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"`
|
||||
}
|
||||
|
||||
type UBLInvoiceRoot struct {
|
||||
D string `json:"_D"`
|
||||
A string `json:"_A"`
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ package route
|
|||
// This file contains error types that different routes use to communicate
|
||||
// failure states with the rest of the application.
|
||||
|
||||
/*
|
||||
| -----------------------------------------------------------------------------
|
||||
| Document Submission Rejection Error
|
||||
| -----------------------------------------------------------------------------
|
||||
| Error returned in case the document was rejected by LHDN, along with the list
|
||||
| of rejection reasons.
|
||||
*/
|
||||
type SubmitDocRejectionError struct {
|
||||
RejectedDocuments []string
|
||||
}
|
||||
|
|
@ -15,6 +22,14 @@ func (err SubmitDocRejectionError) Error() string {
|
|||
return result[:len(result)-2]
|
||||
}
|
||||
|
||||
/*
|
||||
| -----------------------------------------------------------------------------
|
||||
| Document Submission Bad Structure Error
|
||||
| -----------------------------------------------------------------------------
|
||||
| Error returned in case the generated UBL invoice does not have a correct
|
||||
| format. Incorrect JSON format, incorrect field names, missing required fields,
|
||||
| etc.
|
||||
*/
|
||||
type SubmitDocBadStructureError struct {
|
||||
}
|
||||
|
||||
|
|
@ -22,6 +37,13 @@ func (err SubmitDocBadStructureError) Error() string {
|
|||
return "the submitted document was ill-formed"
|
||||
}
|
||||
|
||||
/*
|
||||
| -----------------------------------------------------------------------------
|
||||
| Document Submission Maximum Size Error
|
||||
| -----------------------------------------------------------------------------
|
||||
| Error returned in case the UBL invoice was too large. The LHDN API has a
|
||||
| limitation where the sent document cannot exceed 5 MB in size.
|
||||
*/
|
||||
type SubmitDocMaxSizeError struct {
|
||||
}
|
||||
|
||||
|
|
@ -29,9 +51,32 @@ func (err SubmitDocMaxSizeError) Error() string {
|
|||
return "maximum size exceeded: 5 MB maximum submission size, 100 maximum e-Invoices per submission, and 300 KB maximum per e-Invoice"
|
||||
}
|
||||
|
||||
/*
|
||||
| -----------------------------------------------------------------------------
|
||||
| Document Submission Duplication Error
|
||||
| -----------------------------------------------------------------------------
|
||||
| Error returned in case the exact same UBL invoice was sent consecutively. The
|
||||
| LHDN API has a limitation where the exact same document cannot be sent again
|
||||
| until after 10 minutes has passed.
|
||||
*/
|
||||
type SubmitDocDuplicateError struct {
|
||||
}
|
||||
|
||||
func (err SubmitDocDuplicateError) Error() string {
|
||||
return "duplicate submission was sent not so long ago"
|
||||
}
|
||||
|
||||
/*
|
||||
| -----------------------------------------------------------------------------
|
||||
| Document Submission Not Found Error
|
||||
| -----------------------------------------------------------------------------
|
||||
| Error returned in case the document submission was successful, but no
|
||||
| acceptedDocuments fields was returned, or it was present but had no elements.
|
||||
| This error should rarely happen, if any at all.
|
||||
*/
|
||||
type SubmitDocNotFound struct {
|
||||
}
|
||||
|
||||
func (err SubmitDocNotFound) Error() string {
|
||||
return "document could not but found"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package route
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"iralex-einvoice/config"
|
||||
"iralex-einvoice/core"
|
||||
"iralex-einvoice/document"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetDocument(accessToken, docUUID string) (document.ExtendedDocument, error) {
|
||||
core.LogInfo.Printf("INFO: sending request GET %s\n", config.LHDN_BASE_URL+"/api/v1.0/documents/"+docUUID+"/raw")
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, config.LHDN_BASE_URL+"/api/v1.0/documents/"+docUUID+"/raw", nil)
|
||||
if err != nil {
|
||||
core.LogInfo.Printf("ERROR: in constructing the LHDN GetDocument request: %v", err)
|
||||
return document.ExtendedDocument{}, err
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * config.DEFAULT_TIMEOUT,
|
||||
}
|
||||
resp, 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 resp.Body.Close()
|
||||
|
||||
var respBody document.ExtendedDocument
|
||||
json.NewDecoder(resp.Body).Decode(&respBody)
|
||||
if resp.StatusCode == 200 {
|
||||
return respBody, nil
|
||||
} else {
|
||||
core.LogInfo.Printf("ERROR: LHDN GetDocument error response (%d):\n%v\n", resp.StatusCode, resp.Body)
|
||||
return document.ExtendedDocument{}, errors.New("LHDN GetDocument failed")
|
||||
}
|
||||
}
|
||||
|
|
@ -51,8 +51,8 @@ type responseSuccess struct {
|
|||
} `json:"rejectedDocuments"`
|
||||
}
|
||||
|
||||
// Submits a document to LHDN.
|
||||
func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) error {
|
||||
// Submits a document to LHDN. Returns the document UUID on success.
|
||||
func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) (string, error) {
|
||||
core.LogInfo.Println("INFO: Sending Request (/submit-document):")
|
||||
|
||||
// Minify and convert the document to base64.
|
||||
|
|
@ -83,7 +83,7 @@ func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) error {
|
|||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't marshal the request body: %w", err)
|
||||
return "", fmt.Errorf("couldn't marshal the request body: %w", err)
|
||||
}
|
||||
|
||||
// Pretty print the document/body.
|
||||
|
|
@ -99,7 +99,7 @@ func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) error {
|
|||
|
||||
req, err := http.NewRequest(http.MethodPost, config.LHDN_BASE_URL+"/api/v1.0/documentsubmissions/", bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't construct the /submit-document request: %w", err)
|
||||
return "", fmt.Errorf("couldn't construct the /submit-document request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
|
@ -117,7 +117,7 @@ func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) error {
|
|||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't send the request: %w", err)
|
||||
return "", fmt.Errorf("couldn't send the request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -135,13 +135,22 @@ func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) error {
|
|||
for _, value := range rejectedDocs {
|
||||
result = append(result, value.(map[string]interface{})["invoiceCodeNumber"].(string))
|
||||
}
|
||||
return SubmitDocRejectionError{
|
||||
return "", SubmitDocRejectionError{
|
||||
RejectedDocuments: result,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if acceptedDocs, ok := res["acceptedDocuments"]; ok {
|
||||
if acceptedDocs, ok := acceptedDocs.([]interface{}); ok {
|
||||
if len(acceptedDocs) > 0 {
|
||||
var docUUID string
|
||||
for _, value := range acceptedDocs {
|
||||
docUUID = value.(map[string]interface{})["uuid"].(string)
|
||||
return docUUID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
var res lhdnStdError
|
||||
json.NewDecoder(resp.Body).Decode(&res)
|
||||
|
|
@ -151,15 +160,15 @@ func SubmitDocument(accessToken string, doc document.UBLInvoiceRoot) error {
|
|||
}
|
||||
|
||||
if resp.StatusCode == 400 && *res.ErrorCode == "BadStructure" {
|
||||
return SubmitDocBadStructureError{}
|
||||
return "", SubmitDocBadStructureError{}
|
||||
} else if resp.StatusCode == 400 && *res.ErrorCode == "MaximumSizeExceeded" {
|
||||
return SubmitDocMaxSizeError{}
|
||||
return "", SubmitDocMaxSizeError{}
|
||||
} else if resp.StatusCode == 422 {
|
||||
return SubmitDocDuplicateError{}
|
||||
return "", SubmitDocDuplicateError{}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return "", SubmitDocNotFound{}
|
||||
|
||||
// var bill = database.Bill{}
|
||||
// document.SubmitDocument("aasdasd")
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"iralex-einvoice/config"
|
||||
"iralex-einvoice/core"
|
||||
"iralex-einvoice/route"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
|
||||
// filepath := "N:\\Pooya\\Projects\\iralex einvoice\\storage\\2025\\8-August\\qrcode\\408BMZ0D2AVGTWHDWAH3GQ0K10.png"
|
||||
// content, err := os.ReadFile(filepath)
|
||||
// if err != nil {
|
||||
// fmt.Println("failed to read file %v", err)
|
||||
// }
|
||||
// b := b64.StdEncoding.EncodeToString(content)
|
||||
// fmt.Println(b)
|
||||
// return
|
||||
|
||||
// hardcoded for testing
|
||||
// QR code generation must be parameterized on document UUID
|
||||
const docUUID = "408BMZ0D2AVGTWHDWAH3GQ0K10"
|
||||
var longID string = ""
|
||||
|
||||
accessToken := route.AuthenticateFormData(core.CLIENT_ID, core.CLIENT_SECRET_1, core.TIN)
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * config.DEFAULT_TIMEOUT,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Printf("couldn't send the request: %v", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var respBody map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&respBody)
|
||||
if resp.StatusCode == 200 {
|
||||
if _longID, ok := respBody["longID"]; ok {
|
||||
longID = _longID.(string)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error\n")
|
||||
return
|
||||
}
|
||||
|
||||
var link = "https://myinvois.hasil.gov.my/" + docUUID + "/share/" + longID
|
||||
fmt.Println("Link")
|
||||
fmt.Println(link)
|
||||
fmt.Fprintf(w, "Link: %s\n", link)
|
||||
|
||||
err = core.CreateQRCodeImage(link, docUUID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "ERROR: Couldn't create the QRCode image: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"iralex-einvoice/config"
|
||||
"iralex-einvoice/core"
|
||||
"iralex-einvoice/route"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
type sendQRCodeRequestBody struct {
|
||||
DocID string `json:"doc_id"`
|
||||
}
|
||||
|
||||
type sendQRCodeResponseBody struct {
|
||||
DocID string `json:"doc_id"`
|
||||
QRCode string `json:"qr_code"`
|
||||
}
|
||||
|
||||
// This route generates the QR code link of the specified bill. It expects a
|
||||
// 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")
|
||||
|
||||
// In case of a panic, do not stop the server, but send a 500.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
core.LogInfo.Printf("ERROR - recovered a panic: %v\n", r)
|
||||
core.LogInfo.Println("STACK TRACE:")
|
||||
core.LogInfo.Println(string(debug.Stack()))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
}()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
fmt.Fprintf(w, "The %s method is not allowed.", r.Method)
|
||||
return
|
||||
}
|
||||
|
||||
reqBody, err := decodeJSONBody[sendQRCodeRequestBody](w, r)
|
||||
if err != nil {
|
||||
core.LogInfo.Printf("ERROR - Cannot decode request JSON body: %s\n", err.Error())
|
||||
var reqError *malformedRequestError
|
||||
if errors.As(err, &reqError) {
|
||||
w.WriteHeader(reqError.status)
|
||||
fmt.Fprint(w, reqError.msg)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Retreive long ID and generate and return the link.
|
||||
|
||||
accessToken := route.AuthenticateFormData(core.CLIENT_ID, core.CLIENT_SECRET_1, core.TIN)
|
||||
|
||||
docEx, err := route.GetDocument(accessToken, reqBody.DocID)
|
||||
if err != nil {
|
||||
core.LogInfo.Println("ERROR - Failed to get the document")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
|
||||
var link = "https://myinvois.hasil.gov.my/" + reqBody.DocID + "/share/" + docEx.LongID
|
||||
|
||||
json.NewEncoder(w).Encode(sendQRCodeResponseBody{
|
||||
DocID: reqBody.DocID,
|
||||
QRCode: link,
|
||||
})
|
||||
}
|
||||
|
||||
func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
|
||||
// filepath := "N:\\Pooya\\Projects\\iralex einvoice\\storage\\2025\\8-August\\qrcode\\408BMZ0D2AVGTWHDWAH3GQ0K10.png"
|
||||
// content, err := os.ReadFile(filepath)
|
||||
// if err != nil {
|
||||
// fmt.Println("failed to read file %v", err)
|
||||
// }
|
||||
// b := b64.StdEncoding.EncodeToString(content)
|
||||
// fmt.Println(b)
|
||||
// return
|
||||
|
||||
// hardcoded for testing
|
||||
// QR code generation must be parameterized on document UUID
|
||||
const docUUID = "408BMZ0D2AVGTWHDWAH3GQ0K10"
|
||||
var longID string = ""
|
||||
|
||||
accessToken := route.AuthenticateFormData(core.CLIENT_ID, core.CLIENT_SECRET_1, core.TIN)
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * config.DEFAULT_TIMEOUT,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Printf("couldn't send the request: %v", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var respBody map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&respBody)
|
||||
if resp.StatusCode == 200 {
|
||||
if _longID, ok := respBody["longID"]; ok {
|
||||
longID = _longID.(string)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error\n")
|
||||
return
|
||||
}
|
||||
|
||||
var link = "https://myinvois.hasil.gov.my/" + docUUID + "/share/" + longID
|
||||
fmt.Println("Link")
|
||||
fmt.Println(link)
|
||||
fmt.Fprintf(w, "Link: %s\n", link)
|
||||
|
||||
err = core.CreateQRCodeImage(link, docUUID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "ERROR: Couldn't create the QRCode image: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,15 @@ type billValidationError struct {
|
|||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type submitResponseBody struct {
|
||||
DocID string `json:"doc_id"`
|
||||
QRCode string `json:"qr_code"`
|
||||
}
|
||||
|
||||
type submitDamagedResponseBody struct {
|
||||
DocID string `json:"doc_id"`
|
||||
}
|
||||
|
||||
// the following operations need to happen in order to submit a document:
|
||||
// 1. log into Iralex and retrieve access token
|
||||
// 2. get data for a specific document
|
||||
|
|
@ -42,7 +51,7 @@ type billValidationError struct {
|
|||
// • In case of a logical error in the bill, sends a 422.
|
||||
|
||||
func HandleSubmitDebug(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("Accepted request at /submit")
|
||||
fmt.Println("Accepted request at /submit (debug)")
|
||||
core.LogInfo.Println("Accepted request at /submit (debug)")
|
||||
|
||||
// In case of a panic, do not stop the server, but send a 500.
|
||||
|
|
@ -146,7 +155,7 @@ func HandleSubmit(w http.ResponseWriter, r *http.Request) {
|
|||
// In case of a panic, do not stop the server, but send a 500.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
core.LogInfo.Printf("ERROR: recovered a panic: %v\n", r)
|
||||
core.LogInfo.Printf("ERROR - recovered a panic: %v\n", r)
|
||||
core.LogInfo.Println("STACK TRACE:")
|
||||
core.LogInfo.Println(string(debug.Stack()))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
|
@ -196,7 +205,7 @@ func HandleSubmit(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
accessToken := route.AuthenticateFormData(core.CLIENT_ID, core.CLIENT_SECRET_1, core.TIN)
|
||||
err = route.SubmitDocument(accessToken, doc)
|
||||
docUUID, err := route.SubmitDocument(accessToken, doc)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, err)
|
||||
|
|
@ -204,9 +213,22 @@ func HandleSubmit(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// If we reached here, everything worked fine.
|
||||
// Generate QRCode Link
|
||||
|
||||
fmt.Fprint(w, "success")
|
||||
docEx, err := route.GetDocument(accessToken, docUUID)
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode(submitResponseBody{
|
||||
DocID: docUUID,
|
||||
QRCode: "",
|
||||
})
|
||||
core.LogInfo.Printf("ERROR - Could not get the document details upon submission: %v", err)
|
||||
}
|
||||
|
||||
// If we reached here, everything worked fine.
|
||||
json.NewEncoder(w).Encode(submitResponseBody{
|
||||
DocID: docUUID,
|
||||
QRCode: "https://myinvois.hasil.gov.my/" + docUUID + "/share/" + docEx.LongID,
|
||||
})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ func Run() {
|
|||
// Register routes.
|
||||
|
||||
http.HandleFunc("/ping", HandlePing)
|
||||
http.HandleFunc("/send-qrcode", HandleQRCodeGenerate)
|
||||
|
||||
if core.DEV {
|
||||
http.HandleFunc("/submit", HandleSubmitDebug)
|
||||
|
|
@ -47,7 +48,5 @@ func Run() {
|
|||
log.Fatal(http.ListenAndServe(":"+core.PORT, nil))
|
||||
|
||||
// db.New2(DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME)
|
||||
|
||||
// db.GetBill(1158)
|
||||
// route.SubmitDocument("sadasd")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Decoding Configurations
|
||||
// Configure how the decoding logic should behave.
|
||||
const (
|
||||
// When parsing the request JSON body, whether to send an error or not when
|
||||
// extra fields not specified in route.IralexBill are present.
|
||||
|
|
@ -26,6 +28,8 @@ const (
|
|||
_CHECK_CONTENT_TYPE = true
|
||||
)
|
||||
|
||||
// Custom Error types returned from the decoder. Each different type represents
|
||||
// a different error.
|
||||
type malformedRequestError struct {
|
||||
status int
|
||||
msg string
|
||||
|
|
|
|||
Loading…
Reference in New Issue