Phone-based registration is extremely common on commercial websites in China. Many sites require users to register or log in with a phone number, or at least offer phone login as an option. This article focuses only on the technical implementation of SMS-based registration and login in WordPress.
The overall flow is not actually that complicated. The most annoying part is usually integrating the SMS service itself. Mainstream providers often supply SDKs and sample code, and libraries such as PhpSms can make the job easier. Less polished providers can be painful to integrate, but once the SMS interface is working, the rest of the logic is manageable.
First, choose and verify the SMS service interface
An SMS provider is the one truly required external dependency. Which one you choose depends on what matters most to the project: API stability, flexibility, pricing, and so on. The original article refers readers to a comparison table maintained by the PhpSms project, which is a well-known PHP SMS library in the Chinese ecosystem.
Back-end code for sending and verifying SMS codes
Before sending an SMS, we obviously need to generate a random verification code. At the same time, we need to save that code in the database so we can compare it later when the user submits it for verification. The following example shows the basic sending logic used in a real registration/login project.
There are two details worth paying close attention to. First, save the code to the database before sending the SMS. Otherwise the SMS might go out successfully but the database save might fail, and then the user would receive a code that can never be verified. Second, if the user requests another code because the original message did not arrive, update the existing database record instead of inserting a duplicate one. If duplicate records are created, the verification logic can easily read the wrong code and reject the valid one the user just received.
The original article notes that one extra validation rule is omitted from the sample below: validating whether the submitted phone number itself is well formed.
/**
* Send an SMS verification code
*/
new Dispatch( [
'wizhi/get_phone_code' => function ( $request ) {
$phone = isset( $_POST[ 'phone' ] ) ? $_POST[ 'phone' ] : false;
if ( $phone ) {
// Generate a random verification code
$phone_code = str_pad( mt_rand( 1, 99999 ), 6, '0', STR_PAD_LEFT );
// Create or update the database record before sending the SMS
$code = R::findOrCreate( CODES, [ 'phone' => $phone ] );
$code->code = $phone_code;
$success = R::store( $code );
// Only send the SMS if the database write succeeds
$sms = false;
if ( $success ) {
$sms = send_sms( $phone, $phone_code );
} else {
$msg = [
'success' => 1,
'message' => '验证短信发送失败,请重试',
];
}
if ( $sms ) {
$msg = [
'success' => 1,
'message' => '验证短信已发送',
];
} else {
$msg = [
'success' => 1,
'message' => '验证短信发送失败,请重试',
];
}
} else {
$msg = [
'success' => 0,
'message' => '请输入正确的手机号码',
];
}
wp_send_json( $msg );
},
] );
Once the request succeeds, the route returns a JSON response. The front end can then use that response to decide how to update the UI.
Front-end code for sending the SMS verification code
The following code implements a button that sends the SMS verification request to the back end. After a successful send, the button enters a one-minute countdown before the user can request another code. The timing detail matters: the countdown should only start after the SMS has actually been sent successfully. Otherwise the user could mistype the phone number, fail to send the SMS, and still be forced to wait a full minute before trying again.
// Timer-related variables
var InterValObj; // timer variable
var count = 60; // interval in seconds
var curCount; // current remaining seconds
/**
* Update the countdown
*/
function SetRemainTime() {
if (curCount == 0) {
window.clearInterval(InterValObj);
$("#get_code").removeAttr("disabled");
$("#get_code").val("重新发送");
}
else {
curCount--;
$("#get_code").val(curCount + "后重新获取");
}
}
/**
* Send the SMS verification code
*/
$('#get_code').on('click', function (event, element) {
event.preventDefault();
$.ajax({
method : 'POST',
url : WizUrls.get_phone_code,
dataType: "json",
data : {
'phone': $('form#modal-register #user_login').val()
},
success : function (data) {
if (data.success === 0) {
$('form#modal-register div.status').removeClass('c-alert-success').addClass('c-alert c-alert-danger').html(data.message);
return false;
} else {
curCount = count;
$(this).attr("disabled", "true");
$(this).val(curCount + "后重新获取");
InterValObj = window.setInterval(SetRemainTime, 1000);
$('form#modal-register div.status').removeClass('c-alert-danger').addClass('c-alert-success').html(data.message);
}
}
});
});
Once both the front-end and back-end parts are in place, the final step is the actual registration or login logic. At that point the workflow is fairly standard: fetch the saved code from the database for the submitted phone number, compare it with the code the user provided, and if they match, continue to registration or login. If they do not match, show an error and let the user correct the code or request another one.
