diff --git a/internal/services/chapa/customization_test.go b/internal/services/chapa/customization_test.go new file mode 100644 index 0000000..1a39760 --- /dev/null +++ b/internal/services/chapa/customization_test.go @@ -0,0 +1,28 @@ +package chapa + +import "testing" + +func TestSanitizeChapaCustomization(t *testing.T) { + tests := []struct { + in string + want string + }{ + {"Subscription: Premium", "Subscription Premium"}, + {"New Test Monthly Premium", "New Test Monthly Premium"}, + {"IELTS (Prep) / Monthly", "IELTS Prep Monthly"}, + {"", "Yimaru subscription"}, + } + for _, tc := range tests { + if got := sanitizeChapaCustomization(tc.in); got != tc.want { + t.Fatalf("sanitizeChapaCustomization(%q) = %q, want %q", tc.in, got, tc.want) + } + } +} + +func TestChapaSubscriptionDescription(t *testing.T) { + got := chapaSubscriptionDescription("New Test Monthly Premium") + want := "Subscription New Test Monthly Premium" + if got != want { + t.Fatalf("chapaSubscriptionDescription() = %q, want %q", got, want) + } +} diff --git a/internal/services/chapa/service.go b/internal/services/chapa/service.go index cdddf3f..6d983c9 100644 --- a/internal/services/chapa/service.go +++ b/internal/services/chapa/service.go @@ -141,8 +141,8 @@ func (s *Service) InitiateSubscriptionPayment(ctx context.Context, userID int64, CallbackURL: s.cfg.CHAPA_CALLBACK_URL, ReturnURL: s.cfg.CHAPA_RETURN_URL, } - initReq.Customization.Title = "Yimaru LMS" - initReq.Customization.Description = fmt.Sprintf("Subscription: %s", plan.Name) + initReq.Customization.Title = sanitizeChapaCustomization("Yimaru LMS") + initReq.Customization.Description = chapaSubscriptionDescription(plan.Name) checkoutURL, err := s.initializeTransaction(ctx, initReq) if err != nil { @@ -453,6 +453,31 @@ func formatAmount(amount float64) string { return strconv.FormatFloat(math.Round(amount*100)/100, 'f', 2, 64) } +// sanitizeChapaCustomization keeps only characters allowed by Chapa customization fields. +func sanitizeChapaCustomization(value string) string { + var b strings.Builder + b.Grow(len(value)) + for _, r := range value { + switch { + case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9': + b.WriteRune(r) + case r == '-', r == '_', r == ' ', r == '.': + b.WriteRune(r) + default: + b.WriteRune(' ') + } + } + cleaned := strings.Join(strings.Fields(b.String()), " ") + if cleaned == "" { + return "Yimaru subscription" + } + return cleaned +} + +func chapaSubscriptionDescription(planName string) string { + return sanitizeChapaCustomization("Subscription " + strings.TrimSpace(planName)) +} + func normalizeCurrency(currency string) string { c := strings.TrimSpace(strings.ToUpper(currency)) if c == "" {