diff --git a/examples/dynamic-checkout/index.html b/examples/dynamic-checkout/index.html index 4e53cfa2..bea7306f 100644 --- a/examples/dynamic-checkout/index.html +++ b/examples/dynamic-checkout/index.html @@ -11,7 +11,7 @@ document.addEventListener("DOMContentLoaded", function () { // You need to replace these values with your own const projectId = "test-proj_qEi1u5BwoYcZb6mOMKDWIm4mpqKCq6bN" - const invoiceId = "iv_39bZyCq6bNxAWSl9DA6PXGt63mEi952M" + const invoiceId = "iv_39vI9Cq6bN1LV3k0Xh7bSIxV0ICdFwHu" const clientSecret = "" const client = new ProcessOut.ProcessOut(projectId) diff --git a/package.json b/package.json index e352790f..0c748a34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "processout.js", - "version": "1.7.3", + "version": "1.7.4", "description": "ProcessOut.js is a JavaScript library for ProcessOut's payment processing API.", "scripts": { "build:processout": "tsc -p src/processout && uglifyjs --compress --keep-fnames --ie8 dist/processout.js -o dist/processout.js", diff --git a/src/dynamic-checkout/config/billing-address.ts b/src/dynamic-checkout/config/billing-address.ts index 92f4d3b5..ab978bf6 100644 --- a/src/dynamic-checkout/config/billing-address.ts +++ b/src/dynamic-checkout/config/billing-address.ts @@ -978,7 +978,7 @@ module ProcessOut { name: "United States", postcodeUnit: "zip", stateUnit: "state", - units: ["state"], + units: ["street1", "street2", "city", "state", "postcode"], states: [ { abbreviation: "AL", diff --git a/src/dynamic-checkout/config/payment-config.ts b/src/dynamic-checkout/config/payment-config.ts index d43e6cbf..8aa9a217 100644 --- a/src/dynamic-checkout/config/payment-config.ts +++ b/src/dynamic-checkout/config/payment-config.ts @@ -9,6 +9,7 @@ module ProcessOut { capturePayments?: boolean allowFallbackToSale?: boolean showStatusMessage?: boolean + payButtonText?: string } export type DynamicCheckoutInternalConfigType = { @@ -26,6 +27,7 @@ module ProcessOut { capturePayments: DynamicCheckoutPublicConfigType["capturePayments"] = false allowFallbackToSale: DynamicCheckoutPublicConfigType["allowFallbackToSale"] = false showStatusMessage: DynamicCheckoutPublicConfigType["showStatusMessage"] = true + payButtonText: DynamicCheckoutPublicConfigType["payButtonText"] = "" invoiceDetails: DynamicCheckoutInternalConfigType["invoiceDetails"] constructor(config: DynamicCheckoutPublicConfigType) { @@ -61,6 +63,7 @@ module ProcessOut { this.locale = config.locale || "en" this.capturePayments = config.capturePayments || false this.allowFallbackToSale = config.allowFallbackToSale || false + this.payButtonText = config.payButtonText || "" if (config.showStatusMessage !== undefined && config.showStatusMessage !== null) { this.showStatusMessage = config.showStatusMessage diff --git a/src/dynamic-checkout/dynamic-checkout.ts b/src/dynamic-checkout/dynamic-checkout.ts index 0a2169a9..273c9338 100644 --- a/src/dynamic-checkout/dynamic-checkout.ts +++ b/src/dynamic-checkout/dynamic-checkout.ts @@ -71,7 +71,7 @@ module ProcessOut { } private onGetInvoiceLoading() { - this.loadView(new DynamicCheckoutInvoiceLoadingView().element) + this.loadView(new DynamicCheckoutInvoiceLoadingView(this.paymentConfig.locale).element) DynamicCheckoutEventsUtils.dispatchWidgetLoadingEvent() } diff --git a/src/dynamic-checkout/elements/payment-method-button.ts b/src/dynamic-checkout/elements/payment-method-button.ts index fb4073ec..e2fa210a 100644 --- a/src/dynamic-checkout/elements/payment-method-button.ts +++ b/src/dynamic-checkout/elements/payment-method-button.ts @@ -14,6 +14,7 @@ module ProcessOut { deleteMode: boolean = false, deletingAllowed: boolean = false, handleDeletePaymentMethod?: () => void, + locale: string = "en", ) { this.processOutInstance = processOutInstance @@ -25,6 +26,7 @@ module ProcessOut { deletingAllowed, rightContent, handleDeletePaymentMethod, + locale, ) } @@ -36,6 +38,7 @@ module ProcessOut { deletingAllowed: boolean, rightContent?: HTMLElement, handleDeletePaymentMethod?: () => void, + locale: string = "en", ) { const [ element, @@ -56,6 +59,7 @@ module ProcessOut { ], attributes: { "data-id": id, + for: `payment-method-${id}`, }, }, { @@ -88,17 +92,23 @@ module ProcessOut { attributes: { type: "radio", name: "payment-method", + id: `payment-method-${id}`, + value: id, }, }, { tagName: "button", classNames: ["dco-delete-payment-method-button"], + attributes: { + "aria-label": Translations.getText("delete-payment-method-label", locale), + }, }, { tagName: "img", classNames: ["dco-delete-payment-method-icon"], attributes: { src: this.processOutInstance.endpoint("js", TRASH_ICON), + alt: "", }, }, ]) diff --git a/src/dynamic-checkout/elements/payment-methods-manager.ts b/src/dynamic-checkout/elements/payment-methods-manager.ts index 0d11bb3e..e815c655 100644 --- a/src/dynamic-checkout/elements/payment-methods-manager.ts +++ b/src/dynamic-checkout/elements/payment-methods-manager.ts @@ -25,12 +25,19 @@ module ProcessOut { { tagName: "button", classNames: ["dco-express-checkout-header-settings-button"], + attributes: { + "aria-label": Translations.getText( + "settings-button-label", + this.paymentConfig.locale, + ), + }, }, { tagName: "img", classNames: ["dco-express-checkout-cog-icon"], attributes: { src: this.processOutInstance.endpoint("js", COG_ICON), + alt: "", }, }, ]) @@ -58,6 +65,9 @@ module ProcessOut { stickyFooter: true, closeMethods: ["overlay", "button", "escape"], closeLabel: closeLabel, + onClose: () => { + this.element.focus() + }, }) : null diff --git a/src/dynamic-checkout/locales/de.ts b/src/dynamic-checkout/locales/de.ts new file mode 100644 index 00000000..5dac6bf7 --- /dev/null +++ b/src/dynamic-checkout/locales/de.ts @@ -0,0 +1,47 @@ +module ProcessOut { + export const de = { + "apm-redirect-message": "Sie werden zur Zahlungsbestätigung weitergeleitet.", + "save-for-future-label": "Für zukünftige Zahlungen speichern", + "continue-with-apm-button": "Weiter mit", + "pay-button-text": "Bezahlen", + "card-details-section-title": "Kartendetails", + "cardholder-name-label": "Name des Karteninhabers", + "billing-address-section-title": "Rechnungsadresse", + "country-label": "Land", + "select-country-placeholder": "Land auswählen", + "select-state-placeholder": "Bundesland auswählen", + "card-number-error-message": "Kartennummer ist ungültig", + "expiry-date-error-message": "Ablaufdatum ist ungültig", + "cvc-error-message": "CVC ist ungültig", + "cardholder-name-error-message": "Name des Karteninhabers ist ungültig", + "payment-error-message": "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "payment-cancelled-message": "Die Zahlung wurde storniert.", + "express-checkout-header": "Express-Checkout", + "other-payment-methods-header": "Weitere Zahlungsmethoden", + "select-payment-method-label": "Zahlungsmethode auswählen", + "payment-success-message": "Diese Zahlung wurde abgeschlossen.", + "payment-info-message": + "Wir verarbeiten Ihre Zahlung. Sie können dieses Fenster jetzt schließen.", + "payment-error-generic-message": "Wir konnten Ihre Zahlung nicht verarbeiten.", + "street1-label": "Adresszeile 1", + "street2-label": "Adresszeile 2", + "city-label": "Stadt", + "postcode-label": "Postleitzahl", + "state-label": "Bundesland", + "payments-manager-header": "Gespeicherte Zahlungsmethoden verwalten", + "payments-manager-close-button": "Schließen", + "no-saved-payment-methods-header": "Keine gespeicherten Zahlungsmethoden", + "no-saved-payment-methods-message": + "Wenn Sie das nächste Mal eine Zahlungsmethode speichern, wird sie hier angezeigt.", + "card-number-label": "Kartennummer", + "expiry-date-label": "Ablaufdatum", + "cvc-label": "CVC", + "card-not-supported-error-message": "Diese Karte wird nicht unterstützt", + "card-label": "Karte", + "delete-payment-method-label": "Zahlungsmethode löschen", + "settings-button-label": "Gespeicherte Zahlungsmethoden verwalten", + "loading-label": "Laden", + "processing-payment-label": "Zahlung wird verarbeitet", + "card-form-label": "Kartenzahlungsformular", + } +} diff --git a/src/dynamic-checkout/locales/en.ts b/src/dynamic-checkout/locales/en.ts index 569c4b67..a9ca100e 100644 --- a/src/dynamic-checkout/locales/en.ts +++ b/src/dynamic-checkout/locales/en.ts @@ -32,7 +32,15 @@ module ProcessOut { "no-saved-payment-methods-header": "No saved payment methods", "no-saved-payment-methods-message": "The next time you save a payment method, it will appear here.", + "card-number-label": "Card number", + "expiry-date-label": "Expiry date", + "cvc-label": "CVC", "card-not-supported-error-message": "This card is not supported", "card-label": "Card", + "delete-payment-method-label": "Delete payment method", + "settings-button-label": "Manage saved payment methods", + "loading-label": "Loading", + "processing-payment-label": "Processing payment", + "card-form-label": "Card payment form", } } diff --git a/src/dynamic-checkout/locales/es.ts b/src/dynamic-checkout/locales/es.ts index 58cfe3a6..022f2b75 100644 --- a/src/dynamic-checkout/locales/es.ts +++ b/src/dynamic-checkout/locales/es.ts @@ -32,7 +32,15 @@ module ProcessOut { "no-saved-payment-methods-header": "No hay métodos de pago guardados", "no-saved-payment-methods-message": "La próxima vez que guardes un método de pago, aparecerá aquí.", + "card-number-label": "Número de tarjeta", + "expiry-date-label": "Fecha de vencimiento", + "cvc-label": "CVC", "card-not-supported-error-message": "Esta tarjeta no es compatible", "card-label": "Tarjeta bancaria", + "delete-payment-method-label": "Eliminar método de pago", + "settings-button-label": "Administrar métodos de pago guardados", + "loading-label": "Cargando", + "processing-payment-label": "Procesando pago", + "card-form-label": "Formulario de pago con tarjeta", } } diff --git a/src/dynamic-checkout/locales/fr.ts b/src/dynamic-checkout/locales/fr.ts index b3945dcc..c303ebd0 100644 --- a/src/dynamic-checkout/locales/fr.ts +++ b/src/dynamic-checkout/locales/fr.ts @@ -33,7 +33,15 @@ module ProcessOut { "no-saved-payment-methods-header": "Aucune méthode de paiement enregistrée", "no-saved-payment-methods-message": "La prochaine fois que vous enregistrez une méthode de paiement, elle apparaîtra ici.", + "card-number-label": "Numéro de carte", + "expiry-date-label": "Date d'expiration", + "cvc-label": "CVC", "card-not-supported-error-message": "Cette carte n'est pas acceptée", "card-label": "Carte", + "delete-payment-method-label": "Supprimer le moyen de paiement", + "settings-button-label": "Gérer les moyens de paiement enregistrés", + "loading-label": "Chargement", + "processing-payment-label": "Traitement du paiement", + "card-form-label": "Formulaire de paiement par carte", } } diff --git a/src/dynamic-checkout/locales/it.ts b/src/dynamic-checkout/locales/it.ts new file mode 100644 index 00000000..6cc82a9d --- /dev/null +++ b/src/dynamic-checkout/locales/it.ts @@ -0,0 +1,47 @@ +module ProcessOut { + export const it = { + "apm-redirect-message": "Verrai reindirizzato per completare il pagamento.", + "save-for-future-label": "Salva per pagamenti futuri", + "continue-with-apm-button": "Continua con", + "pay-button-text": "Paga", + "card-details-section-title": "Dettagli della carta", + "cardholder-name-label": "Nome del titolare della carta", + "billing-address-section-title": "Indirizzo di fatturazione", + "country-label": "Paese", + "select-country-placeholder": "Seleziona paese", + "select-state-placeholder": "Seleziona provincia", + "card-number-error-message": "Il numero della carta non è valido", + "expiry-date-error-message": "La data di scadenza non è valida", + "cvc-error-message": "Il CVC non è valido", + "cardholder-name-error-message": "Il nome del titolare della carta non è valido", + "payment-error-message": "Si è verificato un errore. Riprova.", + "payment-cancelled-message": "Il pagamento è stato annullato.", + "express-checkout-header": "Checkout rapido", + "other-payment-methods-header": "Altri metodi di pagamento", + "select-payment-method-label": "Seleziona metodo di pagamento", + "payment-success-message": "Il pagamento è stato completato.", + "payment-info-message": + "Stiamo elaborando il tuo pagamento. Ora puoi chiudere questa finestra.", + "payment-error-generic-message": "Non è stato possibile elaborare il pagamento.", + "street1-label": "Indirizzo riga 1", + "street2-label": "Indirizzo riga 2", + "city-label": "Città", + "postcode-label": "Codice postale", + "state-label": "Provincia", + "payments-manager-header": "Gestisci metodi di pagamento salvati", + "payments-manager-close-button": "Chiudi", + "no-saved-payment-methods-header": "Nessun metodo di pagamento salvato", + "no-saved-payment-methods-message": + "La prossima volta che salvi un metodo di pagamento, apparirà qui.", + "card-number-label": "Numero della carta", + "expiry-date-label": "Data di scadenza", + "cvc-label": "CVC", + "card-not-supported-error-message": "Questa carta non è supportata", + "card-label": "Carta", + "delete-payment-method-label": "Elimina metodo di pagamento", + "settings-button-label": "Gestisci metodi di pagamento salvati", + "loading-label": "Caricamento", + "processing-payment-label": "Elaborazione del pagamento", + "card-form-label": "Modulo di pagamento con carta", + } +} diff --git a/src/dynamic-checkout/locales/ja.ts b/src/dynamic-checkout/locales/ja.ts new file mode 100644 index 00000000..8a13753c --- /dev/null +++ b/src/dynamic-checkout/locales/ja.ts @@ -0,0 +1,47 @@ +module ProcessOut { + export const ja = { + "apm-redirect-message": "お支払いを完了するためにリダイレクトされます。", + "save-for-future-label": "今後の支払いのために保存", + "continue-with-apm-button": "で続行", + "pay-button-text": "支払う", + "card-details-section-title": "カード情報", + "cardholder-name-label": "カード名義人", + "billing-address-section-title": "請求先住所", + "country-label": "国", + "select-country-placeholder": "国を選択", + "select-state-placeholder": "都道府県を選択", + "card-number-error-message": "カード番号が無効です", + "expiry-date-error-message": "有効期限が無効です", + "cvc-error-message": "CVCが無効です", + "cardholder-name-error-message": "カード名義人が無効です", + "payment-error-message": "エラーが発生しました。もう一度お試しください。", + "payment-cancelled-message": "お支払いがキャンセルされました。", + "express-checkout-header": "エクスプレスチェックアウト", + "other-payment-methods-header": "その他の支払い方法", + "select-payment-method-label": "支払い方法を選択", + "payment-success-message": "お支払いが完了しました。", + "payment-info-message": + "お支払いを処理しています。このウィンドウを閉じていただいて構いません。", + "payment-error-generic-message": "お支払いを処理できませんでした。", + "street1-label": "住所1", + "street2-label": "住所2", + "city-label": "市区町村", + "postcode-label": "郵便番号", + "state-label": "都道府県", + "payments-manager-header": "保存済みの支払い方法を管理", + "payments-manager-close-button": "閉じる", + "no-saved-payment-methods-header": "保存済みの支払い方法はありません", + "no-saved-payment-methods-message": + "次回支払い方法を保存すると、ここに表示されます。", + "card-number-label": "カード番号", + "expiry-date-label": "有効期限", + "cvc-label": "CVC", + "card-not-supported-error-message": "このカードはサポートされていません", + "card-label": "カード", + "delete-payment-method-label": "支払い方法を削除", + "settings-button-label": "保存済みの支払い方法を管理", + "loading-label": "読み込み中", + "processing-payment-label": "支払いを処理中", + "card-form-label": "カード決済フォーム", + } +} diff --git a/src/dynamic-checkout/locales/ko.ts b/src/dynamic-checkout/locales/ko.ts new file mode 100644 index 00000000..064f220e --- /dev/null +++ b/src/dynamic-checkout/locales/ko.ts @@ -0,0 +1,47 @@ +module ProcessOut { + export const ko = { + "apm-redirect-message": "결제를 완료하기 위해 리디렉션됩니다.", + "save-for-future-label": "향후 결제를 위해 저장", + "continue-with-apm-button": "계속하기", + "pay-button-text": "결제", + "card-details-section-title": "카드 정보", + "cardholder-name-label": "카드 소유자 이름", + "billing-address-section-title": "청구지 주소", + "country-label": "국가", + "select-country-placeholder": "국가 선택", + "select-state-placeholder": "시/도 선택", + "card-number-error-message": "카드 번호가 유효하지 않습니다", + "expiry-date-error-message": "만료일이 유효하지 않습니다", + "cvc-error-message": "CVC가 유효하지 않습니다", + "cardholder-name-error-message": "카드 소유자 이름이 유효하지 않습니다", + "payment-error-message": "오류가 발생했습니다. 다시 시도해 주세요.", + "payment-cancelled-message": "결제가 취소되었습니다.", + "express-checkout-header": "빠른 결제", + "other-payment-methods-header": "기타 결제 수단", + "select-payment-method-label": "결제 수단 선택", + "payment-success-message": "결제가 완료되었습니다.", + "payment-info-message": + "결제를 처리하고 있습니다. 이 창을 닫으셔도 됩니다.", + "payment-error-generic-message": "결제를 처리할 수 없습니다.", + "street1-label": "주소 1", + "street2-label": "주소 2", + "city-label": "시/군/구", + "postcode-label": "우편번호", + "state-label": "시/도", + "payments-manager-header": "저장된 결제 수단 관리", + "payments-manager-close-button": "닫기", + "no-saved-payment-methods-header": "저장된 결제 수단 없음", + "no-saved-payment-methods-message": + "다음에 결제 수단을 저장하면 여기에 표시됩니다.", + "card-number-label": "카드 번호", + "expiry-date-label": "만료일", + "cvc-label": "CVC", + "card-not-supported-error-message": "이 카드는 지원되지 않습니다", + "card-label": "카드", + "delete-payment-method-label": "결제 수단 삭제", + "settings-button-label": "저장된 결제 수단 관리", + "loading-label": "로딩 중", + "processing-payment-label": "결제 처리 중", + "card-form-label": "카드 결제 양식", + } +} diff --git a/src/dynamic-checkout/locales/pl.ts b/src/dynamic-checkout/locales/pl.ts index 7d0329e9..abfed255 100644 --- a/src/dynamic-checkout/locales/pl.ts +++ b/src/dynamic-checkout/locales/pl.ts @@ -32,7 +32,15 @@ module ProcessOut { "no-saved-payment-methods-header": "Brak zapisanych metod płatności", "no-saved-payment-methods-message": "Gdy następnym razem zapiszesz metodę płatności, pojawi się tutaj.", + "card-number-label": "Numer karty", + "expiry-date-label": "Data ważności", + "cvc-label": "CVC", "card-not-supported-error-message": "Ta karta nie jest obsługiwana", "card-label": "Karta płatnicza", + "delete-payment-method-label": "Usuń metodę płatności", + "settings-button-label": "Zarządzaj zapisanymi metodami płatności", + "loading-label": "Ładowanie", + "processing-payment-label": "Przetwarzanie płatności", + "card-form-label": "Formularz płatności kartą", } } diff --git a/src/dynamic-checkout/locales/pt.ts b/src/dynamic-checkout/locales/pt.ts index 3c6c8c77..e581918c 100644 --- a/src/dynamic-checkout/locales/pt.ts +++ b/src/dynamic-checkout/locales/pt.ts @@ -33,7 +33,15 @@ module ProcessOut { "no-saved-payment-methods-header": "Nenhum método de pagamento salvo", "no-saved-payment-methods-message": "Depois de guardar uma forma de pagamento, ela aparecerá aqui. ", + "card-number-label": "Número do cartão", + "expiry-date-label": "Data de validade", + "cvc-label": "CVC", "card-not-supported-error-message": "Este cartão não é suportado", "card-label": "Cartão", + "delete-payment-method-label": "Excluir método de pagamento", + "settings-button-label": "Gerenciar métodos de pagamento salvos", + "loading-label": "Carregando", + "processing-payment-label": "Processando pagamento", + "card-form-label": "Formulário de pagamento com cartão", } } diff --git a/src/dynamic-checkout/payment-methods/apm.ts b/src/dynamic-checkout/payment-methods/apm.ts index f85ee139..feb3e57f 100644 --- a/src/dynamic-checkout/payment-methods/apm.ts +++ b/src/dynamic-checkout/payment-methods/apm.ts @@ -94,7 +94,7 @@ module ProcessOut { this.processOutInstance.handleAction( apm.redirect_url, paymentToken => { - this.resetContainerHtml().appendChild(new DynamicCheckoutInvoiceLoadingView().element) + this.resetContainerHtml().appendChild(new DynamicCheckoutInvoiceLoadingView(this.paymentConfig.locale).element) DynamicCheckoutEventsUtils.dispatchPaymentPendingEvent(paymentToken, { payment_method_name: apm.gateway_name, @@ -177,7 +177,7 @@ module ProcessOut { this.processOutInstance.handleAction( data.customer_action.value, paymentToken => { - this.resetContainerHtml().appendChild(new DynamicCheckoutInvoiceLoadingView().element) + this.resetContainerHtml().appendChild(new DynamicCheckoutInvoiceLoadingView(this.paymentConfig.locale).element) this.processOutInstance.makeCardPayment( this.paymentConfig.invoiceId, @@ -302,6 +302,7 @@ module ProcessOut { "js", "/images/dynamic-checkout-assets/apm-redirect-arrow.svg", ), + alt: "", }, }, { @@ -325,7 +326,7 @@ module ProcessOut { tagName: "label", classNames: ["dco-payment-method-button-save-for-future-label"], attributes: { - for: `save-apm-for-future-${this.paymentMethod.display.name}`, + for: `save-apm-for-future-${this.paymentMethod.apm.gateway_name}`, }, textContent: Translations.getText("save-for-future-label", this.paymentConfig.locale), }, diff --git a/src/dynamic-checkout/payment-methods/apple-pay.ts b/src/dynamic-checkout/payment-methods/apple-pay.ts index 5c4e3107..66953aed 100644 --- a/src/dynamic-checkout/payment-methods/apple-pay.ts +++ b/src/dynamic-checkout/payment-methods/apple-pay.ts @@ -21,7 +21,7 @@ module ProcessOut { tagName: "div", classNames: ["dco-wallet-checkout-button"], attributes: { - id: "google-pay-button-container", + id: "apple-pay-button-container", }, }) diff --git a/src/dynamic-checkout/payment-methods/card.ts b/src/dynamic-checkout/payment-methods/card.ts index a2d9b42b..5e40ced1 100644 --- a/src/dynamic-checkout/payment-methods/card.ts +++ b/src/dynamic-checkout/payment-methods/card.ts @@ -224,10 +224,11 @@ module ProcessOut { } private getChildrenElement() { - const payButtonText = `${Translations.getText( - "pay-button-text", - this.paymentConfig.locale, - )} ${this.paymentConfig.invoiceDetails.amount} ${this.paymentConfig.invoiceDetails.currency}` + const payButtonText = this.paymentConfig.payButtonText + || `${Translations.getText( + "pay-button-text", + this.paymentConfig.locale, + )} ${this.paymentConfig.invoiceDetails.amount} ${this.paymentConfig.invoiceDetails.currency}` const [ cardFormWrapper, @@ -242,6 +243,7 @@ module ProcessOut { classNames: ["dco-payment-method-card-form-wrapper"], attributes: { id: "card-form", + "aria-label": Translations.getText("card-form-label", this.paymentConfig.locale), }, }, { @@ -313,16 +315,20 @@ module ProcessOut { cardDetailsSectionTitle, cardDetailsSectionInputsWrapper, cardNumberInputWrapper, + cardNumberLabel, cardNumberInput, cardNumberInputErrorMessage, splitCardInputRow, expiryDateInputWrapper, + expiryDateLabel, expiryDateInput, expiryDateInputErrorMessage, cvcInputWrapper, + cvcLabel, cvcInput, cvcInputErrorMessage, cardHolderNameInputWrapper, + cardHolderNameLabel, cardHolderNameInput, cardHolderNameInputErrorMessage, cardSchemeLogo, @@ -347,6 +353,11 @@ module ProcessOut { tagName: "div", classNames: ["dco-payment-method-card-form-input-wrapper"], }, + { + tagName: "label", + classNames: ["dco-input-label"], + textContent: Translations.getText("card-number-label", this.paymentConfig.locale), + }, { tagName: "div", classNames: ["dco-payment-method-card-form-input"], @@ -360,6 +371,7 @@ module ProcessOut { classNames: ["dco-payment-method-card-form-input-error-message"], attributes: { id: "card-number-error-message", + role: "alert", }, }, { @@ -370,6 +382,11 @@ module ProcessOut { tagName: "div", classNames: ["dco-payment-method-card-form-input-wrapper"], }, + { + tagName: "label", + classNames: ["dco-input-label"], + textContent: Translations.getText("expiry-date-label", this.paymentConfig.locale), + }, { tagName: "div", classNames: ["dco-payment-method-card-form-input"], @@ -383,12 +400,18 @@ module ProcessOut { classNames: ["dco-payment-method-card-form-input-error-message"], attributes: { id: "expiry-date-error-message", + role: "alert", }, }, { tagName: "div", classNames: ["dco-payment-method-card-form-input-wrapper"], }, + { + tagName: "label", + classNames: ["dco-input-label"], + textContent: Translations.getText("cvc-label", this.paymentConfig.locale), + }, { tagName: "div", classNames: [ @@ -405,12 +428,21 @@ module ProcessOut { classNames: ["dco-payment-method-card-form-input-error-message"], attributes: { id: "cvc-error-message", + role: "alert", }, }, { tagName: "div", classNames: ["dco-payment-method-card-form-input-wrapper"], }, + { + tagName: "label", + classNames: ["dco-input-label"], + attributes: { + for: "cardholder-name-input", + }, + textContent: Translations.getText("cardholder-name-label", this.paymentConfig.locale), + }, { tagName: "input", classNames: [ @@ -418,8 +450,10 @@ module ProcessOut { "dco-payment-method-card-form-input-cardholder-name", ], attributes: { + id: "cardholder-name-input", name: "cardholder-name", placeholder: Translations.getText("cardholder-name-label", this.paymentConfig.locale), + autocomplete: "cc-name", }, }, { @@ -427,6 +461,7 @@ module ProcessOut { classNames: ["dco-payment-method-card-form-input-error-message"], attributes: { id: "cardholder-name-error-message", + role: "alert", }, }, { @@ -444,6 +479,7 @@ module ProcessOut { } HTMLElements.appendChildren(cardNumberInputWrapper, [ + cardNumberLabel, cardNumberInput, cardNumberInputErrorMessage, ]) @@ -454,15 +490,17 @@ module ProcessOut { ]) HTMLElements.appendChildren(expiryDateInputWrapper, [ + expiryDateLabel, expiryDateInput, expiryDateInputErrorMessage, ]) - HTMLElements.appendChildren(cvcInputWrapper, [cvcInput, cvcInputErrorMessage]) + HTMLElements.appendChildren(cvcInputWrapper, [cvcLabel, cvcInput, cvcInputErrorMessage]) HTMLElements.appendChildren(splitCardInputRow, [expiryDateInputWrapper, cvcInputWrapper]) HTMLElements.appendChildren(cardHolderNameInputWrapper, [ + cardHolderNameLabel, cardHolderNameInput, cardHolderNameInputErrorMessage, ]) @@ -555,10 +593,17 @@ module ProcessOut { payButton.disabled = true payButton.textContent = "" + payButton.setAttribute( + "aria-label", + Translations.getText("processing-payment-label", this.paymentConfig.locale), + ) const spinner = HTMLElements.createElement({ tagName: "span", classNames: ["dco-payment-method-button-pay-button-spinner"], + attributes: { + "aria-hidden": "true", + }, }) HTMLElements.appendChildren(payButton, [spinner]) @@ -569,8 +614,10 @@ module ProcessOut { tagName: "select", classNames: ["dco-payment-method-card-form-input"], attributes: { + id: "country-select", name: "country", placeholder: Translations.getText("country-label", this.paymentConfig.locale), + "aria-label": Translations.getText("country-label", this.paymentConfig.locale), }, }) @@ -634,13 +681,16 @@ module ProcessOut { } if (automaticMode && shouldShowPostcodeForAutomaticMode) { + const postcodeLabel = billingAddressUnitsData(this.paymentConfig).postcode.placeholder + return [ HTMLElements.createElement({ tagName: "input", classNames: ["dco-payment-method-card-form-input"], attributes: { name: "postcode", - placeholder: billingAddressUnitsData(this.paymentConfig).postcode.placeholder, + placeholder: postcodeLabel, + "aria-label": postcodeLabel, }, }), ] @@ -657,6 +707,7 @@ module ProcessOut { classNames: ["dco-payment-method-card-form-input"], attributes: { name: unit, + "aria-label": Translations.getText("state-label", this.paymentConfig.locale), }, }) @@ -678,12 +729,15 @@ module ProcessOut { HTMLElements.appendChildren(input, [stateOption]) }) } else { + const unitLabel = billingAddressUnitsData(this.paymentConfig)[unit].placeholder + input = HTMLElements.createElement({ tagName: "input", classNames: ["dco-payment-method-card-form-input"], attributes: { name: unit, - placeholder: billingAddressUnitsData(this.paymentConfig)[unit].placeholder, + placeholder: unitLabel, + "aria-label": unitLabel, }, }) } diff --git a/src/dynamic-checkout/payment-methods/saved-apm.ts b/src/dynamic-checkout/payment-methods/saved-apm.ts index 29c0f26f..86df63ca 100644 --- a/src/dynamic-checkout/payment-methods/saved-apm.ts +++ b/src/dynamic-checkout/payment-methods/saved-apm.ts @@ -32,6 +32,7 @@ module ProcessOut { deleteMode, paymentMethod.apm_customer_token.deleting_allowed, handleDeletePaymentMethod, + paymentConfig.locale, ) this.processOutInstance = processOutInstance @@ -44,10 +45,11 @@ module ProcessOut { } private getChildrenElement(deleteMode?: boolean) { - const payButtonText = `${Translations.getText( - "pay-button-text", - this.paymentConfig.locale, - )} ${this.paymentConfig.invoiceDetails.amount} ${this.paymentConfig.invoiceDetails.currency}` + const payButtonText = this.paymentConfig.payButtonText + || `${Translations.getText( + "pay-button-text", + this.paymentConfig.locale, + )} ${this.paymentConfig.invoiceDetails.amount} ${this.paymentConfig.invoiceDetails.currency}` const [wrapper, payButton] = HTMLElements.createMultipleElements([ { @@ -214,10 +216,17 @@ module ProcessOut { payButton.disabled = true payButton.textContent = "" + payButton.setAttribute( + "aria-label", + Translations.getText("processing-payment-label", this.paymentConfig.locale), + ) const spinner = HTMLElements.createElement({ tagName: "span", classNames: ["dco-payment-method-button-pay-button-spinner"], + attributes: { + "aria-hidden": "true", + }, }) HTMLElements.appendChildren(payButton, [spinner]) diff --git a/src/dynamic-checkout/payment-methods/saved-card.ts b/src/dynamic-checkout/payment-methods/saved-card.ts index f98751ad..f4dd9409 100644 --- a/src/dynamic-checkout/payment-methods/saved-card.ts +++ b/src/dynamic-checkout/payment-methods/saved-card.ts @@ -33,6 +33,7 @@ module ProcessOut { deleteMode, paymentMethod.card_customer_token.deleting_allowed, handleDeletePaymentMethod, + paymentConfig.locale, ) this.processOutInstance = processOutInstance @@ -45,12 +46,11 @@ module ProcessOut { } private getChildrenElement(deleteMode?: boolean) { - const payButtonText = `${Translations.getText( - "pay-button-text", - this.paymentConfig.locale, - )} ${this.paymentConfig.invoiceDetails.amount} ${this.paymentConfig.invoiceDetails.currency}` - - console.log(this.paymentMethod) + const payButtonText = this.paymentConfig.payButtonText + || `${Translations.getText( + "pay-button-text", + this.paymentConfig.locale, + )} ${this.paymentConfig.invoiceDetails.amount} ${this.paymentConfig.invoiceDetails.currency}` const [wrapper, payButton] = HTMLElements.createMultipleElements([ { @@ -148,10 +148,17 @@ module ProcessOut { payButton.disabled = true payButton.textContent = "" + payButton.setAttribute( + "aria-label", + Translations.getText("processing-payment-label", this.paymentConfig.locale), + ) const spinner = HTMLElements.createElement({ tagName: "span", classNames: ["dco-payment-method-button-pay-button-spinner"], + attributes: { + "aria-hidden": "true", + }, }) HTMLElements.appendChildren(payButton, [spinner]) diff --git a/src/dynamic-checkout/references.ts b/src/dynamic-checkout/references.ts index 7c7db413..5eeef502 100644 --- a/src/dynamic-checkout/references.ts +++ b/src/dynamic-checkout/references.ts @@ -22,10 +22,14 @@ /// /// /// -/// +/// /// /// /// +/// +/// +/// +/// /// /// /// diff --git a/src/dynamic-checkout/styles/default.ts b/src/dynamic-checkout/styles/default.ts index e78c01ad..280d4f89 100644 --- a/src/dynamic-checkout/styles/default.ts +++ b/src/dynamic-checkout/styles/default.ts @@ -500,8 +500,8 @@ const defaultStyles = ` } .dco-express-checkout-header-settings-button { - width: 32px; - height: 32px; + min-width: 44px; + min-height: 44px; padding: 0; display: flex; justify-content: center; @@ -509,14 +509,13 @@ const defaultStyles = ` background-color: transparent; border: none; cursor: pointer; - padding: 0; transition: all .4s; border-radius: 4px; } .dco-delete-payment-method-button { - width: 32px; - height: 32px; + min-width: 44px; + min-height: 44px; display: flex; justify-content: center; align-items: center; @@ -811,4 +810,58 @@ const defaultStyles = ` padding: 12px; background-color: #1213140a; } + +.dco-input-label { + display: block; + font-size: 12px; + font-weight: 500; + color: #696F79; + margin-bottom: 4px; +} + + .dco-payment-method-button-radio-button:focus-visible { + outline: 2px solid #242C38; + outline-offset: 2px; + } + + .dco-payment-method-button-pay-button:focus-visible { + outline: 2px solid #242C38; + outline-offset: 2px; + } + + .dco-delete-payment-method-button:focus-visible, + .dco-express-checkout-header-settings-button:focus-visible { + outline: 2px solid #242C38; + outline-offset: 2px; + } + + .dco-payment-method-card-form-input:focus-visible, + .dco-payment-method-card-form-input-cardholder-name:focus-visible { + outline: 2px solid #242C38; + outline-offset: -1px; + border-color: #242C38; + } + + select.dco-payment-method-card-form-input:focus-visible { + outline: 2px solid #242C38; + outline-offset: -1px; + border-color: #242C38; + } + + .close-modal-btn:focus-visible { + outline: 2px solid #242C38; + outline-offset: 2px; + } + + .dco-payment-method-button-save-for-future-checkbox:focus-visible { + outline: 2px solid #242C38; + outline-offset: 2px; + } + + @media (prefers-reduced-motion: reduce) { + .dco-invoice-loading, + .dco-payment-method-button-pay-button-spinner { + animation: none; + } + } ` diff --git a/src/dynamic-checkout/utils/translations.ts b/src/dynamic-checkout/utils/translations.ts index e1a3e6f1..9fc101dc 100644 --- a/src/dynamic-checkout/utils/translations.ts +++ b/src/dynamic-checkout/utils/translations.ts @@ -3,9 +3,13 @@ module ProcessOut { export class Translations { static localeTranslationsMap = { + de: de, en: en, es: es, fr: fr, + it: it, + ja: ja, + ko: ko, pl: pl, pt: pt, } diff --git a/src/dynamic-checkout/views/invoice-loading.ts b/src/dynamic-checkout/views/invoice-loading.ts index dbb9e494..fc4bab97 100644 --- a/src/dynamic-checkout/views/invoice-loading.ts +++ b/src/dynamic-checkout/views/invoice-loading.ts @@ -4,15 +4,22 @@ module ProcessOut { export class DynamicCheckoutInvoiceLoadingView { public element: Element - constructor() { + constructor(locale: string = "en") { const [element, spinner] = HTMLElements.createMultipleElements([ { tagName: "div", classNames: ["dco-invoice-loading-container"], + attributes: { + role: "status", + "aria-label": Translations.getText("loading-label", locale), + }, }, { tagName: "div", classNames: ["dco-invoice-loading"], + attributes: { + "aria-hidden": "true", + }, }, ]) diff --git a/src/dynamic-checkout/views/payment-cancelled.ts b/src/dynamic-checkout/views/payment-cancelled.ts index 946f7390..66547ec9 100644 --- a/src/dynamic-checkout/views/payment-cancelled.ts +++ b/src/dynamic-checkout/views/payment-cancelled.ts @@ -9,12 +9,16 @@ module ProcessOut { { tagName: "div", classNames: ["dco-card-payment-success"], + attributes: { + role: "alert", + }, }, { tagName: "img", classNames: ["dco-card-payment-success-image"], attributes: { src: processOutInstance.endpoint("js", PAYMENT_ERROR_IMAGE_ASSET), + alt: "", }, }, { diff --git a/src/dynamic-checkout/views/payment-error.ts b/src/dynamic-checkout/views/payment-error.ts index 244c590e..ef7b4ae8 100644 --- a/src/dynamic-checkout/views/payment-error.ts +++ b/src/dynamic-checkout/views/payment-error.ts @@ -13,12 +13,16 @@ module ProcessOut { { tagName: "div", classNames: ["dco-card-payment-success"], + attributes: { + role: "alert", + }, }, { tagName: "img", classNames: ["dco-card-payment-success-image"], attributes: { src: processOutInstance.endpoint("js", PAYMENT_ERROR_IMAGE_ASSET), + alt: "", }, }, { diff --git a/src/dynamic-checkout/views/payment-info.ts b/src/dynamic-checkout/views/payment-info.ts index fd48b3ac..2b64abad 100644 --- a/src/dynamic-checkout/views/payment-info.ts +++ b/src/dynamic-checkout/views/payment-info.ts @@ -9,6 +9,9 @@ module ProcessOut { { tagName: "div", classNames: ["dco-card-payment-success"], + attributes: { + role: "status", + }, }, { tagName: "p", diff --git a/src/dynamic-checkout/views/payment-methods.ts b/src/dynamic-checkout/views/payment-methods.ts index 813b8bad..8c07191c 100644 --- a/src/dynamic-checkout/views/payment-methods.ts +++ b/src/dynamic-checkout/views/payment-methods.ts @@ -68,6 +68,30 @@ module ProcessOut { wrapper.appendChild(regularPaymentMethodsSectionWrapper) } + const hasExpressPaymentMethods = + walletPaymentMethods.length > 0 || expressPaymentMethods.length > 0 + + if (regularPaymentMethods.length === 1) { + const radioButton = regularPaymentMethods[0].element.querySelector( + ".dco-payment-method-button-radio-button", + ) as HTMLInputElement + + if (radioButton) { + radioButton.checked = true + radioButton.style.display = "none" + } + + if (!hasExpressPaymentMethods) { + const header = regularPaymentMethodsSectionWrapper.querySelector( + ".dco-regular-payment-methods-section-header", + ) as HTMLElement + + if (header) { + header.style.display = "none" + } + } + } + return wrapper } @@ -127,6 +151,10 @@ module ProcessOut { { tagName: "div", classNames: ["dco-express-checkout-payment-methods-wrapper"], + attributes: { + role: "radiogroup", + "aria-label": Translations.getText("express-checkout-header", this.paymentConfig.locale), + }, }, ]) @@ -189,6 +217,15 @@ module ProcessOut { { tagName: "div", classNames: ["dco-regular-payment-methods-list-wrapper"], + attributes: { + role: "radiogroup", + "aria-label": Translations.getText( + expressPaymentMethods.length > 0 + ? "other-payment-methods-header" + : "select-payment-method-label", + this.paymentConfig.locale, + ), + }, }, ]) @@ -387,6 +424,14 @@ module ProcessOut { } this.createPaymentMethodsManager(expressCheckoutHeader) + + const nextFocusTarget = + (paymentManagerMethodsList.querySelector(".dco-delete-payment-method-button") as HTMLElement) || + (document.querySelector(".close-modal-btn") as HTMLElement) + + if (nextFocusTarget) { + nextFocusTarget.focus() + } } private createPaymentsManagerEmptyState(paymentsManagerMethodsList: Element) { diff --git a/src/dynamic-checkout/views/payment-success.ts b/src/dynamic-checkout/views/payment-success.ts index b7ac206d..8318cc40 100644 --- a/src/dynamic-checkout/views/payment-success.ts +++ b/src/dynamic-checkout/views/payment-success.ts @@ -9,12 +9,16 @@ module ProcessOut { { tagName: "div", classNames: ["dco-card-payment-success"], + attributes: { + role: "status", + }, }, { tagName: "img", classNames: ["dco-card-payment-success-image"], attributes: { src: processOutInstance.endpoint("js", PAYMENT_SUCCESS_IMAGE_ASSET), + alt: "", }, }, {