start of preauth

This commit is contained in:
yurii 2025-12-02 17:03:30 +00:00
parent bb526a248d
commit 56d33b167a
2 changed files with 130 additions and 2 deletions

View File

@ -43,6 +43,7 @@ func (app *App) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("/issuedoorcard", app.issueDoorCard)
mux.HandleFunc("/printroomticket", app.printRoomTicket)
mux.HandleFunc("/starttransaction", app.startTransaction)
mux.HandleFunc("/startandconfirmtransaction", app.startAndConfirmTransaction)
}
func (app *App) startTransaction(w http.ResponseWriter, r *http.Request) {
@ -95,7 +96,106 @@ func (app *App) startTransaction(w http.ResponseWriter, r *http.Request) {
writeTransactionResult(w, http.StatusBadRequest, theResponse)
return
}
log.Printf("Start trnasaction payload: Amount=%s, Type=%s", theRequest.AmountMinorUnits, theRequest.TransactionType)
log.Printf("Transaction payload: Amount=%s, Type=%s", theRequest.AmountMinorUnits, theRequest.TransactionType)
client := &http.Client{Timeout: 300 * time.Second}
response, err := client.Post(transactionUrl, "text/xml", bytes.NewBuffer(body))
if err != nil {
logging.Error(serviceName, err.Error(), "Payment processing error", string(op), "", "", 0)
theResponse.Data = payment.BuildFailureURL(payment.ResultError, "No response from payment processor")
writeTransactionResult(w, http.StatusBadGateway, theResponse)
return
}
defer response.Body.Close()
body, err = io.ReadAll(response.Body)
if err != nil {
logging.Error(serviceName, err.Error(), "Read response body error", string(op), "", "", 0)
theResponse.Data = payment.BuildFailureURL(payment.ResultError, "Failed to read response body")
writeTransactionResult(w, http.StatusInternalServerError, theResponse)
return
}
if err := trResult.ParseTransactionResult(body); err != nil {
logging.Error(serviceName, err.Error(), "Parse transaction result error", string(op), "", "", 0)
}
// Compose JSON from responseEntries
result := make(map[string]string)
for _, e := range trResult.Entries {
switch e.Key {
case payment.ReceiptData, payment.ReceiptDataMerchant:
// ignore these
case payment.ReceiptDataCardholder:
cardholderReceipt = e.Value
case payment.TransactionResult:
theResponse.Status.Message = e.Value
theResponse.Status.Code = http.StatusOK
result[e.Key] = e.Value
default:
result[e.Key] = e.Value
}
}
if err := printer.PrintCardholderReceipt(cardholderReceipt); err != nil {
log.Errorf("PrintCardholderReceipt error: %v", err)
}
theResponse.Data = payment.BuildPaymentRedirectURL(result)
writeTransactionResult(w, http.StatusOK, theResponse)
}
func (app *App) startAndConfirmTransaction(w http.ResponseWriter, r *http.Request) {
const op = logging.Op("startAndConfirmTransaction")
var (
theResponse cmstypes.ResponseRec
cardholderReceipt string
theRequest cmstypes.TransactionRec
trResult payment.TransactionResultXML
)
theResponse.Status.Code = http.StatusInternalServerError
theResponse.Status.Message = "500 Internal server error"
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Content-Type", "application/json")
if !app.isPayment {
theResponse.Data = payment.BuildFailureURL(payment.ResultError, "Payment processing is disabled")
writeTransactionResult(w, http.StatusServiceUnavailable, theResponse)
return
}
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
log.Println("startAndConfirmTransaction called")
if r.Method != http.MethodPost {
theResponse.Data = payment.BuildFailureURL(payment.ResultError, "Method not allowed; use POST")
writeTransactionResult(w, http.StatusMethodNotAllowed, theResponse)
return
}
defer r.Body.Close()
if ct := r.Header.Get("Content-Type"); ct != "text/xml" {
theResponse.Data = payment.BuildFailureURL(payment.ResultError, "Content-Type must be text/xml")
writeTransactionResult(w, http.StatusUnsupportedMediaType, theResponse)
return
}
body, _ := io.ReadAll(r.Body)
err := xml.Unmarshal(body, &theRequest)
if err != nil {
logging.Error(serviceName, err.Error(), "ReadXML", string(op), "", "", 0)
theResponse.Data = payment.BuildFailureURL(payment.ResultError, "Invalid XML payload")
writeTransactionResult(w, http.StatusBadRequest, theResponse)
return
}
log.Printf("Transaction payload: Amount=%s, Type=%s", theRequest.AmountMinorUnits, theRequest.TransactionType)
client := &http.Client{Timeout: 300 * time.Second}
response, err := client.Post(transactionUrl, "text/xml", bytes.NewBuffer(body))

View File

@ -248,7 +248,7 @@ func nullableFloatArg(nf sql.NullFloat64) interface{} {
}
// BuildRedirectURL builds the redirect URL to send the guest to after payment.
func BuildRedirectURL(result map[string]string) string {
func BuildPaymentRedirectURL(result map[string]string) string {
res := result[TransactionResult]
tType := result[TransactionType]
@ -284,6 +284,34 @@ func BuildRedirectURL(result map[string]string) string {
return BuildFailureURL(res, result[Errors])
}
func BuildPreauthRedirectURL(result map[string]string) string {
res := result[TransactionResult]
tType := result[TransactionType]
// Transaction approved?
if strings.EqualFold(res, ResultApproved) {
switch {
// Transaction type AccountVerification?
case strings.EqualFold(tType, AccountVerificationType):
log.WithField(LogResult, result[TransactionResult]).
Info("Account verification approved")
return buildSuccessURL(result)
// Transaction type Sale?
case strings.EqualFold(tType, SaleTransactionType):
// Transaction confirmed?
log.WithField(LogResult, result[ConfirmResult]).
Info("Transaction approved and confirmed")
return buildSuccessURL(result)
}
}
// Not approved
return BuildFailureURL(res, result[Errors])
}
func buildSuccessURL(result map[string]string) string {
q := url.Values{}
q.Set("TxnReference", result[Reference])