Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/api/domain/whatsapp_number.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ func NormalizeWhatsappNumber(raw string) (string, *Error) {
stripped := whatsappNumberSeparators.Replace(raw)

var normalized string
hasExplicitCountryCode := false
switch {
case strings.HasPrefix(stripped, "0"):
normalized = "62" + stripped[1:]
case strings.HasPrefix(stripped, "+"):
normalized = strings.TrimPrefix(stripped, "+")
hasExplicitCountryCode = true
default:
normalized = stripped
}
Expand All @@ -33,6 +35,10 @@ func NormalizeWhatsappNumber(raw string) (string, *Error) {
return normalized, nil
}

if !hasExplicitCountryCode {
return "", invalidWhatsappNumberError()
}

if len(normalized) < 8 || len(normalized) > 15 {
return "", invalidWhatsappNumberError()
}
Expand Down
5 changes: 5 additions & 0 deletions apps/api/domain/whatsapp_number_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ func TestNormalizeWhatsappNumber(t *testing.T) {
}{
{name: "leading 0 becomes 62", raw: "0812-3456-7890", expected: "6281234567890"},
{name: "628 prefix is unchanged", raw: "6281234567890", expected: "6281234567890"},
{name: "62 prefix without a plus is accepted", raw: "6285335473895", expected: "6285335473895"},
{name: "0 prefix converts to the same number", raw: "085335473895", expected: "6285335473895"},
{name: "+628 drops the plus", raw: "+62 812 3456 7890", expected: "6281234567890"},
{name: "other country plus is dropped, kept if 8-15 digits", raw: "+6591234567", expected: "6591234567"},
{name: "dots and parentheses are stripped", raw: "(0812).3456.7890", expected: "6281234567890"},
Expand All @@ -33,6 +35,9 @@ func TestNormalizeWhatsappNumber(t *testing.T) {
{name: "non-62 number at the 15 digit ceiling is accepted", raw: "+123456789012345", expected: "123456789012345"},
{name: "non-62 number over 15 digits is rejected", raw: "+1234567890123456", expectsErr: true},
{name: "bare plus sign is rejected", raw: "+", expectsErr: true},
{name: "number without 0 or 62 prefix is rejected", raw: "85335473895", expectsErr: true},
{name: "number without prefix and with separators is rejected", raw: "853-3547-3895", expectsErr: true},
{name: "non-62 number without a plus is rejected", raw: "6591234567", expectsErr: true},
}

for _, tt := range tests {
Expand Down
9 changes: 7 additions & 2 deletions libs/ui/src/domain/entities/Customer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ describe('normalizeWhatsappNumber', () => {
}[] = [
{ name: 'leading 0 becomes 62', raw: '0812-3456-7890', expected: '6281234567890' },
{ name: '628 prefix is unchanged', raw: '6281234567890', expected: '6281234567890' },
{ name: '62 prefix without a plus is accepted', raw: '6285335473895', expected: '6285335473895' },
{ name: '0 prefix converts to the same number', raw: '085335473895', expected: '6285335473895' },
{ name: '+628 drops the plus', raw: '+62 812 3456 7890', expected: '6281234567890' },
{ name: 'other country plus is dropped, kept if 8-15 digits', raw: '+6591234567', expected: '6591234567' },
{ name: 'dots and parentheses are stripped', raw: '(0812).3456.7890', expected: '6281234567890' },
Expand All @@ -28,6 +30,9 @@ describe('normalizeWhatsappNumber', () => {
{ name: 'non-62 number at the 15 digit ceiling is accepted', raw: '+123456789012345', expected: '123456789012345' },
{ name: 'non-62 number over 15 digits is rejected', raw: '+1234567890123456', expected: null },
{ name: 'bare plus sign is rejected', raw: '+', expected: null },
{ name: 'number without 0 or 62 prefix is rejected', raw: '85335473895', expected: null },
{ name: 'number without prefix and with separators is rejected', raw: '853-3547-3895', expected: null },
{ name: 'non-62 number without a plus is rejected', raw: '6591234567', expected: null },
];

it.each(cases)('$name', ({ raw, expected }) => {
Expand All @@ -40,8 +45,8 @@ describe('formatWhatsappNumberForInput', () => {
expect(formatWhatsappNumberForInput('6281234567890')).toBe('081234567890');
});

it('leaves a non-Indonesian number unchanged', () => {
expect(formatWhatsappNumberForInput('6591234567')).toBe('6591234567');
it('shows a non-Indonesian number with a plus so it stays valid', () => {
expect(formatWhatsappNumberForInput('6591234567')).toBe('+6591234567');
});

it('leaves an empty string unchanged', () => {
Expand Down
7 changes: 6 additions & 1 deletion libs/ui/src/domain/entities/Customer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ export function normalizeWhatsappNumber(raw: string): string | null {
const stripped = raw.replace(WHATSAPP_NUMBER_SEPARATORS, '');

let normalized: string;
let hasExplicitCountryCode = false;
if (stripped.startsWith('0')) {
normalized = '62' + stripped.slice(1);
} else if (stripped.startsWith('+')) {
normalized = stripped.slice(1);
hasExplicitCountryCode = true;
} else {
normalized = stripped;
}
Expand All @@ -25,12 +27,15 @@ export function normalizeWhatsappNumber(raw: string): string | null {
return normalized;
}

if (!hasExplicitCountryCode) return null;

if (normalized.length < 8 || normalized.length > 15) return null;

return normalized;
}

export function formatWhatsappNumberForInput(normalized: string): string {
if (normalized === '') return normalized;
if (normalized.startsWith('62')) return '0' + normalized.slice(2);
return normalized;
return '+' + normalized;
}
Loading