package paymentkit import ( "encoding/json" "fmt" "net/url" "strings" ) func NormalizeStatus(value, fallback string) string { switch strings.ToUpper(strings.TrimSpace(value)) { case "SUCCESS", "OK", "PAID", "FULFILLED", "TRADE_SUCCESS", "TRADE_FINISHED", "COMPLETED", "PAY_SUCCESS", "REFUND_SUCCESS", "REFUNDED": return "success" case "WAIT_BUYER_PAY", "USERPAYING", "NOTPAY", "PROCESSING", "PENDING", "CREATED", "CLIENT_PENDING", "ACCEPT", "ACCEPTED", "PAYING": return "pending" case "CLOSED", "TRADE_CLOSED", "CANCELLED", "CANCELED", "REVOKED", "FAILED", "FAIL", "REJECTED", "DENIED", "DECLINED", "ERROR", "PAYERROR": return "failed" default: return fallback } } func NormalizeRefundStatus(value, fallback string) string { switch strings.ToUpper(strings.TrimSpace(value)) { case "SUCCESS", "OK", "PAID", "REFUND_SUCCESS", "REFUNDED", "COMPLETED", "FINISHED", "TRADE_SUCCESS": return "success" case "WAIT_BUYER_PAY", "USERPAYING", "NOTPAY", "PROCESSING", "PENDING", "CREATED", "ACCEPT", "ACCEPTED", "PAYING": return "pending" case "CLOSED", "TRADE_CLOSED", "CANCELLED", "CANCELED", "REVOKED", "REFUND", "FAILED", "FAIL", "REJECTED", "DENIED", "DECLINED", "ERROR", "PAYERROR": return "failed" default: return fallback } } // NormalizePaymentMethod canonicalizes provider method identifiers while // leaving provider-specific classification to the integration adapter. func NormalizePaymentMethod(value string) string { return strings.NewReplacer(".", "_", "-", "_", " ", "_").Replace(strings.ToLower(strings.TrimSpace(value))) } func ConfiguredInt64(values map[string]any, key string, fallback int64) int64 { value, ok := values[key] if !ok { return fallback } parsed, err := ParseIntegerAmount(strings.TrimSpace(toString(value))) if err != nil || parsed <= 0 { return fallback } return parsed } func ConfiguredValues(values map[string]any, key string) []string { result := []string{} for _, item := range strings.Split(toString(values[key]), ",") { item = strings.ToUpper(strings.TrimSpace(item)) if item != "" { result = append(result, item) } } return result } func ContainsFold(values []string, value string) bool { value = strings.ToUpper(strings.TrimSpace(value)) for _, item := range values { if item == value { return true } } return false } func Text(values map[string]any, key string) string { value, _ := values[key].(string) return value } func FirstText(values map[string]any, keys ...string) string { for _, key := range keys { if value := Text(values, key); value != "" { return value } } return "" } func FirstString(values map[string]string, keys ...string) string { for _, key := range keys { if values[key] != "" { return values[key] } } return "" } // CallbackFields merges query parameters, form data, and top-level JSON // strings using the same precedence for every payment boundary. func CallbackFields(query, headers map[string]string, body []byte) map[string]string { fields := make(map[string]string, len(query)) for key, value := range query { fields[key] = value } contentType := "" for key, value := range headers { if strings.EqualFold(key, "Content-Type") { contentType = strings.ToLower(value) break } } if strings.Contains(contentType, "application/x-www-form-urlencoded") { if values, err := url.ParseQuery(string(body)); err == nil { for key, value := range values { if len(value) > 0 { fields[key] = value[0] } } } } var object map[string]any if json.Unmarshal(body, &object) == nil { for key, value := range object { if text, ok := value.(string); ok { fields[key] = text } } } return fields } func toString(value any) string { if value == nil { return "" } return strings.TrimSpace(strings.ReplaceAll(strings.TrimSpace(stringify(value)), "", "")) } func stringify(value any) string { return fmt.Sprint(value) }