Sanitize Chapa checkout customization text for initialize API.

Strip disallowed characters from customization title and description so subscription payments pass Chapa validation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yared Yemane 2026-05-29 04:38:55 -07:00
parent d3bbd8c95a
commit 6423bb261e
2 changed files with 55 additions and 2 deletions

View File

@ -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)
}
}

View File

@ -141,8 +141,8 @@ func (s *Service) InitiateSubscriptionPayment(ctx context.Context, userID int64,
CallbackURL: s.cfg.CHAPA_CALLBACK_URL, CallbackURL: s.cfg.CHAPA_CALLBACK_URL,
ReturnURL: s.cfg.CHAPA_RETURN_URL, ReturnURL: s.cfg.CHAPA_RETURN_URL,
} }
initReq.Customization.Title = "Yimaru LMS" initReq.Customization.Title = sanitizeChapaCustomization("Yimaru LMS")
initReq.Customization.Description = fmt.Sprintf("Subscription: %s", plan.Name) initReq.Customization.Description = chapaSubscriptionDescription(plan.Name)
checkoutURL, err := s.initializeTransaction(ctx, initReq) checkoutURL, err := s.initializeTransaction(ctx, initReq)
if err != nil { if err != nil {
@ -453,6 +453,31 @@ func formatAmount(amount float64) string {
return strconv.FormatFloat(math.Round(amount*100)/100, 'f', 2, 64) 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 { func normalizeCurrency(currency string) string {
c := strings.TrimSpace(strings.ToUpper(currency)) c := strings.TrimSpace(strings.ToUpper(currency))
if c == "" { if c == "" {