I have a form with credit card fields and billing address fields. It's bound to some angular models and works beautifully, until I try to fill in the form fields with password managers (I tried 1Password and RoboForm).
With the form below, I see the following behavior:
- 1Password: credit card info is entered correctly, but it also puts in the credit card number in the name, company, and street address fields, and the expiration month in city and state.
- RoboForm: fills the CVC field with the expiration year and the zip code field with the state
Here is the form:
<form class="form" rc-submit="submitCC()" name="ccForm" id="ccForm" novalidate>
<div class="cardback">
<div class="title">Credit Card Details</div>
<div class="form-row with-label" ng-class="{'has-error': numberInvalid || (ccForm.ccNumber.$invalid && rc.ccForm.attempted)}">
<input id="cc-number" class="form-control" type="text" name="ccNumber" ng-model="card.number" data-stripe="number" required size="20" autocomplete="cc-number" />
<label class="form-label">card number</label>
<div class="validation-error" ng-show="numberInvalid">the credit card number is invalid</div>
</div>
<div class="form-row for-inline">
<div class="form-row inline with-label month" ng-class="{'has-error': ccForm.ccExpMonth.$invalid && rc.ccForm.attempted}">
<select id="cc-exp-month" class="form-control" ng-model="card.expMonth" name="ccExpMonth" data-stripe="exp-month" ng-options="m for m in expMonths" required autocomplete="cc-exp-month">
<option value="">MM</option>
</select>
<label class="form-label">exp month</label>
<div class="select-arrow"></div>
</div>
<div class="form-row inline with-label year" ng-class="{'has-error': ccForm.ccExpYear.$invalid && rc.ccForm.attempted}">
<select id="cc-exp-year" class="form-control" ng-model="card.expYear" name="ccExpYear" data-stripe="exp-year" ng-options="y for y in expYears" required autocomplete="cc-exp-year">
<option value="">YYYY</option>
</select>
<label class="form-label">exp year</label>
<div class="select-arrow"></div>
</div>
<div class="form-row inline with-label cvc" ng-class="{'has-error': ccForm.ccCsc.$invalid && rc.ccForm.attempted}">
<input id="csc" class="form-control" type="text" name="csc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
<label class="form-label">cvc</label>
</div>
<!--
<input id="cc-csc" class="form-control" type="text" name="ccCsc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
<input id="securityCode" class="form-control" type="text" name="securityCode" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
<input id="csc" class="form-control" type="text" name="csc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
<input id="cvc" class="form-control" type="text" name="cvc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
<input id="cardCode" class="form-control" type="text" name="cardCode" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
<input id="code" class="form-control" type="text" name="code" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
-->
</div>
</div>
<div class="billing-title">Billing Address</div>
<div class="form-row with-label" ng-class="{'has-error': ccForm.name.$invalid && rc.ccForm.attempted}">
<input id="name" class="form-control" type="text" name="name" required ng-model="card.billingName" autocomplete="name"/>
<label class="form-label">name</label>
<div class="validation-error" ng-show="ccForm.name.$invalid && rc.ccForm.attempted">name required</div>
</div>
<div class="form-row with-label" ng-class="{'has-error': ccForm.organization.$invalid && rc.ccForm.attempted}">
<input id="organization" class="form-control" type="text" name="organization" ng-model="card.billingCompany" autocomplete="organization"/>
<label class="form-label">company (optional)</label>
</div>
<div class="form-row with-label" ng-class="{'has-error': ccForm.addressLine1.$invalid && rc.ccForm.attempted}">
<input id="address-line1" class="form-control" type="text" name="addressLine1" required ng-model="card.billingAddress" autocomplete="address-line1"/>
<label class="form-label">street address</label>
<div class="validation-error" ng-show="ccForm.addressLine1.$invalid && rc.ccForm.attempted">address required</div>
</div>
<div class="form-row with-label" ng-class="{'has-error': ccForm.addressCity.$invalid && rc.ccForm.attempted}">
<input id="address-level2" class="form-control" type="text" name="addressCity" required ng-model="card.billingCity" autocomplete="address-level2"/>
<label class="form-label">city</label>
<div class="validation-error" ng-show="ccForm.addressCity.$invalid && rc.ccForm.attempted">city required</div>
</div>
<div class="form-row for-inline">
<div class="form-row inline with-label state" ng-class="{'has-error': ccForm.state.$invalid && rc.ccForm.attempted}">
<input id="address-level1" class="form-control" type="text" name="state" ng-model="card.billingState" autocomplete="address-level1"/>
<label class="form-label">state</label>
</div>
<div class="form-row inline with-label zip" ng-class="{'has-error': ccForm.postalCode.$invalid && rc.ccForm.attempted}">
<input id="zip" class="form-control" type="text" name="zip" required ng-model="card.billingZip" autocomplete="postal-code"/>
<label class="form-label">zip</label>
<div class="validation-error" ng-show="ccForm.postalCode.$invalid && rc.ccForm.attempted">zip required</div>
</div>
<!--
<input id="postal-code" class="form-control" type="text" name="postalCode" required ng-model="card.billingZip" autocomplete="postal-code"/>
<input id="zipCode" class="form-control" type="text" name="zipCode" required ng-model="card.billingZip" autocomplete="postal-code"/>
<input id="zip" class="form-control" type="text" name="zip" required ng-model="card.billingZip" autocomplete="postal-code"/>
-->
<div class="form-row inline with-label country" ng-class="{'has-error': ccForm.countryName.$invalid && rc.ccForm.attempted}">
<input id="country-name" class="form-control" type="text" name="countryName" value="USA" required ng-model="card.billingCountry" autocomplete="country-name"/>
<label class="form-label">country</label>
<div class="validation-error" ng-show="ccForm.countryName.$invalid && rc.ccForm.attempted">country required</div>
</div>
</div>
<div class="form-row spaced">
<button class="constructive" type="submit" ng-disabled="ccInProgress">confirm payment</button>
</div>
</form>
I tried several different ways to format the fields, including various name
and id
attributes, as well as the autocomplete
field as defined here: https://html.spec.whatwg.org/multipage/forms.html#autofill. Chrome seems to understand the autocomplete
attribute pretty well.
I also tried adding multiple variations of the CVC and zip code fields for testing, and if I have a multitude of these fields, RoboForm works correctly. But as soon as I remove all but any one of those fields, it stops working.
Any experience with that or links to documentation would be greatly appreciated.