I finally figured out the problem with my implementation. Hopefully this will help others having similar problems.
First, my form has an <input type="submit"> tag for the submit button. The form had an explicit name attribute for the input (name="submit"). This was conflicting with the submit() method on the form. I tried removing the name attribute completely, but I think the type="submit" must be making the name default to "submit" as before. So I just gave the tag an explicit name attribute with a name other than "submit". After that, the form submission started working.
Second, the callback specified on the submit button will run
after the reCAPTCHA is successfully solved, so if you depend on the callback to do any validation, you must call
if the validation fails. That will make the user solve the reCAPTCHA again, but otherwise the user would never be able to submit the form after making corrections.