/** * Send OTP to phone number for LOGIN (existing verified accounts) * * POST: { "phone": "5551234567" } * * Returns: { OK: true, UUID: "..." } or { OK: false, ERROR: "..." } * * Only works for verified accounts. Returns error if no account found. */ function apiAbort(required struct payload) { writeOutput(serializeJSON(payload)); abort; } function readJsonBody() { var raw = getHttpRequestData().content; if (isNull(raw)) raw = ""; if (!len(trim(raw))) return {}; try { var data = deserializeJSON(raw); if (isStruct(data)) return data; } catch (any e) {} return {}; } function normalizePhone(required string p) { var x = trim(arguments.p); x = reReplace(x, "[^0-9]", "", "all"); if (len(x) == 11 && left(x, 1) == "1") { x = right(x, 10); } return x; } function generateOTP() { return randRange(100000, 999999); } try { data = readJsonBody(); phone = structKeyExists(data, "phone") ? normalizePhone(data.phone) : ""; if (len(phone) != 10) { apiAbort({ "OK": false, "ERROR": "invalid_phone", "MESSAGE": "Please enter a valid 10-digit phone number" }); } // Find verified account with this phone qUser = queryExecute(" SELECT UserID, UserUUID FROM Users WHERE UserContactNumber = :phone AND UserIsContactVerified = 1 LIMIT 1 ", { phone: { value: phone, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" }); if (qUser.recordCount == 0) { apiAbort({ "OK": false, "ERROR": "no_account", "MESSAGE": "No account found with this phone number. Please sign up first." }); } // Generate and save OTP otp = generateOTP(); queryExecute(" UPDATE Users SET UserMobileVerifyCode = :otp WHERE UserID = :userId ", { otp: { value: otp, cfsqltype: "cf_sql_varchar" }, userId: { value: qUser.UserID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); // Send OTP via Twilio (if available) smsMessage = "Code saved (SMS skipped in dev)"; if (structKeyExists(application, "twilioObj")) { try { smsResult = application.twilioObj.sendSMS( recipientNumber: "+1" & phone, messageBody: "Your Payfrit login code is: " & otp ); smsMessage = smsResult.success ? "Login code sent" : "SMS failed - please try again"; } catch (any smsErr) { smsMessage = "SMS error: " & smsErr.message; } } writeOutput(serializeJSON({ "OK": true, "UUID": qUser.UserUUID, "MESSAGE": smsMessage, "DEV_OTP": otp })); } catch (any e) { apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": e.message }); }