зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1490805 - Add a required CSC/CVV field to the add card page. r=jaws
Depends on D6882 Differential Revision: https://phabricator.services.mozilla.com/D6883 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
b3b9e414bc
Коммит
05d5fb0939
|
@ -128,7 +128,7 @@ var paymentDialogWrapper = {
|
|||
let addressData = this.temporaryStore.addresses.get(guid) ||
|
||||
await formAutofillStorage.addresses.get(guid);
|
||||
if (!addressData) {
|
||||
throw new Error(`Shipping address not found: ${guid}`);
|
||||
throw new Error(`Address not found: ${guid}`);
|
||||
}
|
||||
|
||||
let address = this.createPaymentAddress({
|
||||
|
|
|
@ -313,6 +313,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
|||
if (page.onboardingWizard && !Object.keys(savedBasicCards).length) {
|
||||
successStateChange = {
|
||||
"basic-card-page": {
|
||||
selectedStateKey: "selectedPaymentCard",
|
||||
// Preserve field values as the user may have already edited the card
|
||||
// page and went back to the address page to make a correction.
|
||||
preserveFieldValues: true,
|
||||
|
|
|
@ -169,6 +169,10 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
|
||||
this.form.querySelector("#cc-number").disabled = editing;
|
||||
|
||||
// The CVV fields should be hidden and disabled when editing.
|
||||
this.form.querySelector("#cc-csc-container").hidden = editing;
|
||||
this.form.querySelector("#cc-csc").disabled = editing;
|
||||
|
||||
// If a card is selected we want to edit it.
|
||||
if (editing) {
|
||||
this.pageTitleHeading.textContent = this.dataset.editBasicCardTitle;
|
||||
|
@ -284,6 +288,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
preserveFieldValues: true,
|
||||
guid: basicCardPage.guid,
|
||||
persistCheckboxValue: this.persistCheckbox.checked,
|
||||
selectedStateKey: basicCardPage.selectedStateKey,
|
||||
},
|
||||
};
|
||||
let billingAddressGUID = this.form.querySelector("#billingAddressGUID");
|
||||
|
@ -400,7 +405,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
record.isTemporary = true;
|
||||
}
|
||||
|
||||
for (let editableFieldName of ["cc-name", "cc-exp-month", "cc-exp-year"]) {
|
||||
for (let editableFieldName of ["cc-name", "cc-exp-month", "cc-exp-year", "cc-type"]) {
|
||||
record[editableFieldName] = record[editableFieldName] || "";
|
||||
}
|
||||
|
||||
|
@ -410,14 +415,23 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
record["cc-number"] = record["cc-number"] || "";
|
||||
}
|
||||
|
||||
// Never save the CSC in storage. Storage will throw and not save the record
|
||||
// if it is passed.
|
||||
delete record["cc-csc"];
|
||||
|
||||
try {
|
||||
let {guid} = await paymentRequest.updateAutofillRecord("creditCards", record,
|
||||
basicCardPage.guid);
|
||||
let {selectedStateKey} = currentState["basic-card-page"];
|
||||
if (!selectedStateKey) {
|
||||
throw new Error(`state["basic-card-page"].selectedStateKey is required`);
|
||||
}
|
||||
this.requestStore.setState({
|
||||
page: {
|
||||
id: "payment-summary",
|
||||
},
|
||||
selectedPaymentCard: guid,
|
||||
[selectedStateKey]: guid,
|
||||
[selectedStateKey + "SecurityCode"]: this.form.querySelector("#cc-csc").value,
|
||||
});
|
||||
} catch (ex) {
|
||||
log.warn("saveRecord: error:", ex);
|
||||
|
|
|
@ -75,6 +75,11 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
`does not exist in the payment method picker`);
|
||||
}
|
||||
|
||||
let securityCodeState = state[this.selectedStateKey + "SecurityCode"];
|
||||
if (securityCodeState && securityCodeState != this.securityCodeInput.value) {
|
||||
this.securityCodeInput.defaultValue = securityCodeState;
|
||||
}
|
||||
|
||||
super.render(state);
|
||||
}
|
||||
|
||||
|
@ -143,7 +148,9 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
page: {
|
||||
id: "basic-card-page",
|
||||
},
|
||||
"basic-card-page": {},
|
||||
"basic-card-page": {
|
||||
selectedStateKey: this.selectedStateKey,
|
||||
},
|
||||
};
|
||||
|
||||
switch (target) {
|
||||
|
|
|
@ -17,6 +17,7 @@ export let requestStore = new PaymentsStore({
|
|||
"basic-card-page": {
|
||||
guid: null,
|
||||
// preserveFieldValues: true,
|
||||
selectedStateKey: null,
|
||||
},
|
||||
"address-page": {
|
||||
guid: null,
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<!ENTITY basicCardPage.addButton.label "Add">
|
||||
<!ENTITY basicCardPage.nextButton.label "Next">
|
||||
<!ENTITY basicCardPage.updateButton.label "Update">
|
||||
<!ENTITY basicCardPage.persistCheckbox.label "Save credit card to &brandShortName; (Security code will not be saved)">
|
||||
<!ENTITY basicCardPage.persistCheckbox.label "Save credit card to &brandShortName; (CVV will not be saved)">
|
||||
<!ENTITY basicCardPage.persistCheckbox.infoTooltip "&brandShortName; can securely store your credit card information to use in forms like this, so you don’t have to enter it every time.">
|
||||
<!ENTITY addressPage.error.genericSave "There was an error saving the address.">
|
||||
<!ENTITY addressPage.cancelButton.label "Cancel">
|
||||
|
|
|
@ -61,7 +61,10 @@ async function add_link(aOptions = {}) {
|
|||
if (aOptions.hasOwnProperty("setCardPersistCheckedValue")) {
|
||||
cardOptions.setPersistCheckedValue = aOptions.setCardPersistCheckedValue;
|
||||
}
|
||||
await fillInCardForm(frame, PTU.BasicCards.JaneMasterCard, cardOptions);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: 123,
|
||||
...PTU.BasicCards.JaneMasterCard,
|
||||
}, cardOptions);
|
||||
|
||||
await verifyCardNetwork(frame, cardOptions);
|
||||
await verifyPersistCheckbox(frame, cardOptions);
|
||||
|
@ -650,7 +653,10 @@ add_task(async function test_private_card_adding() {
|
|||
"Check card page state");
|
||||
});
|
||||
|
||||
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: "999",
|
||||
...PTU.BasicCards.JohnDoe,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, async function() {
|
||||
let {
|
||||
|
|
|
@ -91,7 +91,10 @@ add_task(async function test_onboarding_wizard_without_saved_addresses_and_saved
|
|||
}, "Shipping address is selected as the billing address");
|
||||
});
|
||||
|
||||
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: "123",
|
||||
...PTU.BasicCards.JohnDoe,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
|
||||
|
||||
|
@ -362,7 +365,10 @@ add_task(async function test_onboarding_wizard_with_requestShipping_turned_off()
|
|||
}, "Billing Address is correctly shown");
|
||||
});
|
||||
|
||||
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: "123",
|
||||
...PTU.BasicCards.JohnDoe,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
|
||||
|
||||
|
|
|
@ -138,6 +138,7 @@ add_task(async function test_saveButton() {
|
|||
let year = (new Date()).getFullYear().toString();
|
||||
fillField(form.form.querySelector("#cc-exp-year"), year);
|
||||
fillField(form.form.querySelector("#cc-type"), "visa");
|
||||
fillField(form.form.querySelector("#cc-csc"), "123");
|
||||
isnot(form.form.querySelector("#billingAddressGUID").value, address2.guid,
|
||||
"Check initial billing address");
|
||||
fillField(form.form.querySelector("#billingAddressGUID"), address2.guid);
|
||||
|
@ -203,7 +204,7 @@ add_task(async function test_requiredAttributePropagated() {
|
|||
await asyncElementRendered();
|
||||
|
||||
let requiredElements = [...form.form.elements].filter(e => e.required && !e.disabled);
|
||||
is(requiredElements.length, 6, "Number of required elements");
|
||||
is(requiredElements.length, 7, "Number of required elements");
|
||||
for (let element of requiredElements) {
|
||||
if (element.id == "billingAddressGUID") {
|
||||
// The billing address has a different layout.
|
||||
|
@ -445,6 +446,7 @@ add_task(async function test_field_validity_updates() {
|
|||
let ccNumber = form.form.querySelector("#cc-number");
|
||||
let nameInput = form.form.querySelector("#cc-name");
|
||||
let typeInput = form.form.querySelector("#cc-type");
|
||||
let cscInput = form.form.querySelector("#cc-csc");
|
||||
let monthInput = form.form.querySelector("#cc-exp-month");
|
||||
let yearInput = form.form.querySelector("#cc-exp-year");
|
||||
|
||||
|
@ -460,6 +462,7 @@ add_task(async function test_field_validity_updates() {
|
|||
let year = (new Date()).getFullYear().toString();
|
||||
fillField(yearInput, year);
|
||||
fillField(typeInput, "visa");
|
||||
fillField(cscInput, "456");
|
||||
ok(ccNumber.checkValidity(), "cc-number field is valid with good input");
|
||||
ok(nameInput.checkValidity(), "cc-name field is valid with a value");
|
||||
ok(monthInput.checkValidity(), "cc-exp-month field is valid with a value");
|
||||
|
|
|
@ -60,6 +60,17 @@
|
|||
</select>
|
||||
<span data-localization="cardNetwork" class="label-text"/>
|
||||
</label>
|
||||
<label id="cc-csc-container" class="container" hidden="hidden">
|
||||
<!-- Keep these attributes in-sync with securityCodeInput in payment-method-picker.js -->
|
||||
<input id="cc-csc"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
size="3"
|
||||
required="required"
|
||||
pattern="[0-9]{3,}"
|
||||
disabled="disabled"/>
|
||||
<span data-localization="cardCVV" class="label-text"/>
|
||||
</label>
|
||||
<div id="billingAddressGUID-container" class="billingAddressRow container rich-picker">
|
||||
<select id="billingAddressGUID" required="required">
|
||||
</select>
|
||||
|
|
|
@ -185,6 +185,8 @@ cardExpiresMonth = Exp. Month
|
|||
cardExpiresYear = Exp. Year
|
||||
billingAddress = Billing Address
|
||||
cardNetwork = Card Type
|
||||
# LOCALIZATION NOTE (cardCVV): Credit card security code https://en.wikipedia.org/wiki/Card_security_code
|
||||
cardCVV = CVV
|
||||
|
||||
# LOCALIZATION NOTE: (cardNetwork.*): These are brand names and should only be translated when a locale-specific name for that brand is in common use
|
||||
cardNetwork.amex = American Express
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
grid-area: cc-type;
|
||||
}
|
||||
|
||||
#cc-csc-container {
|
||||
grid-area: cc-csc;
|
||||
}
|
||||
|
||||
#billingAddressGUID-container {
|
||||
grid-area: billingAddressGUID;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче