mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
fix(whatsapp): allow connecting Meta test numbers without a PIN
The WhatsApp config POST handler hard-failed with a 400 whenever a number needed registration (first save or number change) and no 6-digit two-step-verification PIN was supplied. Meta test numbers (Developer Console) are pre-registered by Meta and expose no PIN to set, so test-number users could never complete setup (issue #242). The PIN-based /{phone_number_id}/register call only governs inbound webhook routing for production numbers under a shared WABA (issue #136) — it has nothing to do with credential validity or outbound sends. So treat it as best-effort: when no PIN is supplied, skip /register, save the (already Meta-verified) credentials as connected, and leave registered_at null. The response now reports `registered` accurately and adds `registration_skipped` so the UI can react. UI: the save handler no longer falsely claims the number is "Live" on a skipped registration — it points at the existing "Not registered" banner, which already offers a PIN-retry path. The PIN field is relabelled (optional) with copy explaining it's only for production inbound routing and that test numbers have no PIN. Fixes #242 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5b6fe3d677
commit
3b924c85bc
@ -284,26 +284,32 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
// Step 1: register the phone number for inbound webhooks.
|
// Step 1: register the phone number for inbound webhooks.
|
||||||
//
|
//
|
||||||
// Required on first save AND whenever the user supplies a fresh
|
// Attempted on first save AND whenever the user supplies a fresh
|
||||||
// PIN (e.g. they rotated the 2FA PIN in Meta Manager). Skipped
|
// PIN (e.g. they rotated the 2FA PIN in Meta Manager). Skipped
|
||||||
// when the same number is already registered and no PIN was
|
// when the same number is already registered and no PIN was
|
||||||
// supplied — re-registering an already-active number with a
|
// supplied — re-registering an already-active number with a
|
||||||
// stale PIN would actually fail and undo the active subscription.
|
// stale PIN would actually fail and undo the active subscription.
|
||||||
let registeredAt: string | null = existing?.registered_at ?? null
|
let registeredAt: string | null = existing?.registered_at ?? null
|
||||||
let registrationError: string | null = null
|
let registrationError: string | null = null
|
||||||
|
// True when registration was deliberately skipped because no PIN
|
||||||
|
// was supplied (see below). Distinct from registrationError — this
|
||||||
|
// is not a failure, just an incomplete-but-valid save.
|
||||||
|
let registrationSkipped = false
|
||||||
|
|
||||||
const needsRegistration = !sameNumber || (typeof pin === 'string' && pin.length > 0)
|
const needsRegistration = !sameNumber || (typeof pin === 'string' && pin.length > 0)
|
||||||
if (needsRegistration) {
|
if (needsRegistration) {
|
||||||
if (!pin) {
|
if (!pin) {
|
||||||
return NextResponse.json(
|
// No PIN provided. Meta TEST numbers (Developer Console) are
|
||||||
{
|
// pre-registered by Meta and expose no two-step verification
|
||||||
error:
|
// PIN to set, so requiring one made them impossible to connect
|
||||||
'Two-step verification PIN is required to subscribe this number to wacrm. ' +
|
// (issue #242). The /register + PIN step only matters for
|
||||||
'Set a 6-digit PIN in Meta WhatsApp Manager → Phone Numbers → Two-step verification, then paste it below.',
|
// production numbers under a shared WABA (issue #136), so treat
|
||||||
},
|
// it as best-effort: skip it, save the (already Meta-verified)
|
||||||
{ status: 400 }
|
// credentials as connected, and leave registered_at null. The
|
||||||
)
|
// UI surfaces a separate "Not registered" banner with a path to
|
||||||
}
|
// add a PIN later for users who do need inbound webhook routing.
|
||||||
|
registrationSkipped = true
|
||||||
|
} else {
|
||||||
try {
|
try {
|
||||||
await registerPhoneNumber({
|
await registerPhoneNumber({
|
||||||
phoneNumberId: phone_number_id,
|
phoneNumberId: phone_number_id,
|
||||||
@ -321,6 +327,7 @@ export async function POST(request: Request) {
|
|||||||
// not actually live yet.
|
// not actually live yet.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 2: subscribe the WABA to this app. Idempotent on Meta's
|
// Step 2: subscribe the WABA to this app. Idempotent on Meta's
|
||||||
// side, so we call on every save and persist the timestamp.
|
// side, so we call on every save and persist the timestamp.
|
||||||
@ -410,7 +417,12 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
saved: true,
|
saved: true,
|
||||||
registered: true,
|
registered: registeredAt != null,
|
||||||
|
// Credentials are valid and saved, but inbound webhook
|
||||||
|
// registration was skipped because no PIN was supplied (e.g. a
|
||||||
|
// Meta test number). The UI shows the "Not registered" banner
|
||||||
|
// rather than claiming the number is fully live.
|
||||||
|
registration_skipped: registrationSkipped,
|
||||||
phone_info: phoneInfo,
|
phone_info: phoneInfo,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -233,6 +233,16 @@ export function WhatsAppConfig() {
|
|||||||
`Saved, but Meta couldn't register the number: ${data.registration_error}`,
|
`Saved, but Meta couldn't register the number: ${data.registration_error}`,
|
||||||
{ duration: 12000 },
|
{ duration: 12000 },
|
||||||
);
|
);
|
||||||
|
} else if (data.registration_skipped) {
|
||||||
|
// Credentials saved + verified, but /register was skipped
|
||||||
|
// because no PIN was supplied (e.g. a Meta test number).
|
||||||
|
// Don't claim the number is "Live" — point at the
|
||||||
|
// Registration status banner instead.
|
||||||
|
toast.success(
|
||||||
|
'Credentials saved and verified. Inbound registration was skipped (no PIN) — see Registration status below.',
|
||||||
|
{ duration: 10000 },
|
||||||
|
);
|
||||||
|
setPin('');
|
||||||
} else {
|
} else {
|
||||||
toast.success(
|
toast.success(
|
||||||
data.phone_info?.verified_name
|
data.phone_info?.verified_name
|
||||||
@ -604,9 +614,7 @@ export function WhatsAppConfig() {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-slate-300">
|
<Label className="text-slate-300">
|
||||||
Two-step verification PIN
|
Two-step verification PIN
|
||||||
{!isRegistered && (
|
<span className="ml-1 text-slate-500">(optional)</span>
|
||||||
<span className="ml-1 text-red-400">*</span>
|
|
||||||
)}
|
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
@ -620,16 +628,19 @@ export function WhatsAppConfig() {
|
|||||||
className="bg-slate-800 border-slate-700 text-white placeholder:text-slate-500 tracking-widest"
|
className="bg-slate-800 border-slate-700 text-white placeholder:text-slate-500 tracking-widest"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-slate-500 leading-relaxed">
|
<p className="text-xs text-slate-500 leading-relaxed">
|
||||||
Required the first time you connect a number, and any
|
Needed only to wire <strong className="text-slate-300">inbound</strong> messages
|
||||||
time you swap to a different number. Set it in{' '}
|
for a <strong className="text-slate-300">production</strong> number. Set it in{' '}
|
||||||
<strong className="text-slate-300">
|
<strong className="text-slate-300">
|
||||||
Meta Business Manager → WhatsApp Accounts → Phone
|
Meta Business Manager → WhatsApp Accounts → Phone
|
||||||
Numbers → Two-step verification
|
Numbers → Two-step verification
|
||||||
</strong>
|
</strong>
|
||||||
. Without this PIN, Meta saves your credentials but
|
, then paste it here so wacrm can subscribe the number —
|
||||||
won't actually route inbound messages to wacrm —
|
otherwise Meta routes inbound events to whichever app
|
||||||
the symptom that hits second numbers under a shared
|
last claimed it (the symptom that hits second numbers
|
||||||
WABA. Leave blank to keep an existing registration
|
under a shared WABA).{' '}
|
||||||
|
<strong className="text-slate-300">Meta test numbers</strong> have no
|
||||||
|
PIN and are pre-registered — leave this blank for them.
|
||||||
|
Leaving it blank also keeps an existing registration
|
||||||
untouched.
|
untouched.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user