diff --git a/src/Resources/dev/images/integrated/account.svg b/src/Resources/dev/images/integrated/account.svg new file mode 100644 index 00000000..297efca7 --- /dev/null +++ b/src/Resources/dev/images/integrated/account.svg @@ -0,0 +1,9 @@ + + + General/Icons/Icon 24x24 - Account + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/calendar.svg b/src/Resources/dev/images/integrated/calendar.svg new file mode 100644 index 00000000..82fb3686 --- /dev/null +++ b/src/Resources/dev/images/integrated/calendar.svg @@ -0,0 +1,13 @@ + + + General/Icons/Icon 24x24 - Calendar + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/card.svg b/src/Resources/dev/images/integrated/card.svg new file mode 100644 index 00000000..5c08a102 --- /dev/null +++ b/src/Resources/dev/images/integrated/card.svg @@ -0,0 +1,10 @@ + + + General/Icons/Icon 24x24 - Card + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/cb-dark.svg b/src/Resources/dev/images/integrated/cb-dark.svg new file mode 100644 index 00000000..5281a653 --- /dev/null +++ b/src/Resources/dev/images/integrated/cb-dark.svg @@ -0,0 +1,33 @@ + + + CB dark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/cb.svg b/src/Resources/dev/images/integrated/cb.svg new file mode 100644 index 00000000..51c1cad5 --- /dev/null +++ b/src/Resources/dev/images/integrated/cb.svg @@ -0,0 +1,33 @@ + + + CB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/lock.svg b/src/Resources/dev/images/integrated/lock.svg new file mode 100644 index 00000000..316e1f5e --- /dev/null +++ b/src/Resources/dev/images/integrated/lock.svg @@ -0,0 +1,9 @@ + + + General/Icons/Icon 24x24 - Lock + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/logo-payplug.png b/src/Resources/dev/images/integrated/logo-payplug.png new file mode 100644 index 00000000..b67d7cce Binary files /dev/null and b/src/Resources/dev/images/integrated/logo-payplug.png differ diff --git a/src/Resources/dev/images/integrated/mastercard-dark.svg b/src/Resources/dev/images/integrated/mastercard-dark.svg new file mode 100644 index 00000000..652fb0ae --- /dev/null +++ b/src/Resources/dev/images/integrated/mastercard-dark.svg @@ -0,0 +1,19 @@ + + + Mastercard dark + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/mastercard.svg b/src/Resources/dev/images/integrated/mastercard.svg new file mode 100644 index 00000000..337ff9fb --- /dev/null +++ b/src/Resources/dev/images/integrated/mastercard.svg @@ -0,0 +1,18 @@ + + + Mastercard + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/visa-dark.svg b/src/Resources/dev/images/integrated/visa-dark.svg new file mode 100644 index 00000000..9de9cde7 --- /dev/null +++ b/src/Resources/dev/images/integrated/visa-dark.svg @@ -0,0 +1,19 @@ + + + General/Icons/Logos cb/Visa dark + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/images/integrated/visa.svg b/src/Resources/dev/images/integrated/visa.svg new file mode 100644 index 00000000..5c488458 --- /dev/null +++ b/src/Resources/dev/images/integrated/visa.svg @@ -0,0 +1,16 @@ + + + General/Icons/Logos cb/Visa + + + + + + + \ No newline at end of file diff --git a/src/Resources/dev/shop/payment/integrated.js b/src/Resources/dev/shop/payment/integrated.js new file mode 100644 index 00000000..2953fa83 --- /dev/null +++ b/src/Resources/dev/shop/payment/integrated.js @@ -0,0 +1,178 @@ +const IntegratedPayment = { + options: { + api: null, + cartId: null, + paymentId: null, + paymentOptionId: null, + form: {}, + checkoutForm: null, + token: null, + notValid: true, + fieldsValid: { + cardHolder: false, + pan: false, + cvv: false, + exp: false, + }, + fieldsEmpty: { + cardHolder: true, + pan: true, + cvv: true, + exp: true, + }, + inputStyle: { + default: { + color: '#2B343D', + fontFamily: 'Poppins, Arial, sans-serif', + fontSize: '14px', + textAlign: 'left', + '::placeholder': { + color: '#969a9f', + }, + ':focus': { + color: '#2B343D', + } + }, + invalid: { + color: '#E91932' + } + }, + save_card: false, + schemes: null, + scheme: null, + query: null, + submit: null, + order_review: false, + return_url: null + }, + init() { + if (payplug_integrated_payment_params === undefined) { + return; + } + if (payplug_integrated_payment_params.has_saved_cards) { + document.querySelectorAll('.payment-choice__input').forEach((element) => { + element.addEventListener('change', (e) => { + if ('payplug_choice_card_other' === e.currentTarget.id && e.currentTarget.checked) { + IntegratedPayment.openFields(); + return; + } + IntegratedPayment.closeFields(); + }) + }) + return; + } + if (document.querySelector('[id*=sylius_checkout_select_payment_payments][value=payplug]:checked')) { + IntegratedPayment.openFields(); + } + document.querySelectorAll('[id*=sylius_checkout_select_payment_payments]').forEach((element) => { + element.addEventListener('change', (e) => { + if ('payplug' === e.currentTarget.value && e.currentTarget.checked) { + IntegratedPayment.openFields(); + return; + } + IntegratedPayment.closeFields(); + }) + }); + }, + openFields() { + document.querySelector('.payplugIntegratedPayment').classList.add('payplugIntegratedPayment--loaded'); + document.querySelector('#next-step').classList.add('disabled'); + if (null === IntegratedPayment.options.api) { + IntegratedPayment.load(); + } + }, + closeFields() { + document.querySelector('.payplugIntegratedPayment').classList.remove('payplugIntegratedPayment--loaded'); + document.querySelector('#next-step').classList.remove('disabled'); + }, + load() { + IntegratedPayment.options.api = integratedPaymentApi = new Payplug.IntegratedPayment(payplug_integrated_payment_params.is_test_mode); + integratedPaymentApi.setDisplayMode3ds(Payplug.DisplayMode3ds.LIGHTBOX); + IntegratedPayment.options.form.cardHolder = integratedPaymentApi.cardHolder( + document.querySelector('.cardHolder-input-container'), + { + default: IntegratedPayment.options.inputStyle.default, + placeholder: payplug_integrated_payment_params.cardholder + } + ); + IntegratedPayment.options.form.pan = integratedPaymentApi.cardNumber( + document.querySelector('.pan-input-container'), + { + default: IntegratedPayment.options.inputStyle.default, + placeholder: payplug_integrated_payment_params.pan + } + ); + IntegratedPayment.options.form.cvv = integratedPaymentApi.cvv( + document.querySelector('.cvv-input-container'), + { + default: IntegratedPayment.options.inputStyle.default, + placeholder: payplug_integrated_payment_params.cvv + } + ); + IntegratedPayment.options.form.exp = integratedPaymentApi.expiration( + document.querySelector('.exp-input-container'), + { + default: IntegratedPayment.options.inputStyle.default, + placeholder: payplug_integrated_payment_params.exp + } + ); + IntegratedPayment.options.schemes = integratedPaymentApi.getSupportedSchemes(); + IntegratedPayment.bindEvents(); + IntegratedPayment.fieldValidation(); + }, + bindEvents() { + document.querySelector('#paid').addEventListener('click', (event) => { + event.preventDefault(); + integratedPaymentApi.validateForm(); + }); + integratedPaymentApi.onValidateForm(async ({isFormValid}) => { + if (isFormValid) { + if (payplug_integrated_payment_params.payment_id !== undefined) { + integratedPaymentApi.pay(payplug_integrated_payment_params.payment_id, 'visa'); + return; + } + IntegratedPayment.options.save_card = document.querySelector('[name=schemeOptions]:checked').value; + IntegratedPayment.options.scheme = document.querySelector('#savecard').checked; + + const response = await fetch(payplug_integrated_payment_params.routes.init_payment, {method: 'POST'}); + const data = await response.json(); + integratedPaymentApi.pay(data.payment_id, IntegratedPayment.options.scheme, {save_card: IntegratedPayment.options.save_card}); + } + }); + integratedPaymentApi.onCompleted((event) => { + if (event.error) { + console.error(event.error); + return; + } + document.querySelector('input[name=payplug_integrated_payment_token]').value = event.token; + document.querySelector('form[name=sylius_checkout_select_payment]').submit(); + }); + }, + fieldValidation () { + $.each(IntegratedPayment.options.form, function (key, field) { + field.onChange(function(err) { + if (err.error) { + document.querySelector(`.payplugIntegratedPayment__error--${key}`).classList.remove('payplugIntegratedPayment__error--hide'); + document.querySelector(`.${key}-input-container`).classList.add('payplugIntegratedPayment__container--invalid'); + + if (err.error.name === "FIELD_EMPTY") { + document.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".emptyField").classList.remove('payplugIntegratedPayment__error--hide'); + document.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".invalidField").classList.add('payplugIntegratedPayment__error--hide'); + } else { + document.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".invalidField").classList.remove('payplugIntegratedPayment__error--hide'); + document.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".emptyField").classList.add('payplugIntegratedPayment__error--hide'); + } + } else { + document.querySelector(`.payplugIntegratedPayment__error--${key}`).classList.add('payplugIntegratedPayment__error--hide'); + document.querySelector(`.${key}-input-container`).classList.remove('payplugIntegratedPayment__container--invalid'); + document.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".invalidField").classList.add('payplugIntegratedPayment__error--hide'); + document.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".emptyField").classList.add('payplugIntegratedPayment__error--hide'); + IntegratedPayment.options.fieldsValid[key] = true; + IntegratedPayment.options.fieldsEmpty[key] = false; + } + }); + }); + } +}; + +document.addEventListener("DOMContentLoaded", IntegratedPayment.init, false); diff --git a/src/Resources/dev/shop/payment/integrated.scss b/src/Resources/dev/shop/payment/integrated.scss new file mode 100644 index 00000000..dff7e65b --- /dev/null +++ b/src/Resources/dev/shop/payment/integrated.scss @@ -0,0 +1,299 @@ +.payplugIntegratedPayment { + display: none; + justify-self: center; + + * { + font-family: Poppins, Arial, sans-serif !important; + } + + &--loaded { + display: flex; + margin-top: 20px; + width: 100%; + max-width: 400px; + flex-wrap: wrap; + justify-content: space-between; + margin-bottom: 0; + } + + &__select { + border: 1px solid #ccc; + border-radius: 5px; + padding: 0 8px; + margin: 0 0 10px; + height: 36px; + width: 100%; + } + + &__container { + display: flex; + padding: 0; + margin: 0 0 10px; + width: 100%; + position: relative; + + &--cardHolder, + &--pan, + &--exp, + &--cvv { + height: 40px; + line-height: 40px; + border-radius: 2px; + padding: 0 16px 0 50px; + cursor: text; + border: solid 1px #d5d6d8; + + &:before { + content: ""; + position: absolute; + top: 20%; + left: 16px; + width: 24px; + height: 24px; + background: #95999e 50% no-repeat; + background-size: 100% auto; + } + + &:focus { + border-color: #2b343d; + } + + &--invalid { + border-color: #E91932; + } + } + + &--cardHolder { + &:before { + mask-image: url(../../images/integrated/account.svg); + } + } + + &--pan { + &:before { + mask-image: url(../../images/integrated/card.svg); + } + } + + &--exp { + &:before { + mask-image: url(../../images/integrated/calendar.svg); + } + } + + &--cvv { + &:before { + mask-image: url(../../images/integrated/lock.svg); + } + } + + &--exp, + &--cvv { + max-width: calc(50% - 2px); + display: inline-block; + } + + &--scheme { + justify-content: space-between; + align-items: center; + font-size: 14px; + font-weight: 700; + text-transform: uppercase; + height: 22px; + margin: 10px 0; + } + + &--saveCard { + display: flex; + height: auto; + align-items: center; + padding: 10px 0 0; + + input { + display: none; + } + + input:checked + label span:before { + opacity: 1; + } + + label { + margin: 0; + cursor: pointer; + font-size: 12px; + color: #918f8f; + + span { + border: 1px solid #d5d6d8; + border-radius: 2px; + cursor: pointer; + display: inline-block; + height: 16px; + margin: 0 10px -3px 0; + position: relative; + -webkit-transition: border .4s; + -moz-transition: border .4s; + -ms-transition: border .4s; + -o-transition: border .4s; + transition: border .4s; + width: 16px; + + &:before { + border: 2.5px solid #2b343d; + border-radius: 1px; + border-right: none; + border-top: none; + content: ""; + display: block; + height: 5px; + left: 50%; + opacity: 0; + position: absolute; + top: 50%; + transform: translate(-50%,-55%) rotate(-48deg); + transition: opacity .4s; + width: 10px; + } + } + + &:hover { + color: #2b343d; + transition: 0.1s; + + span { + border-color: #2b343d; + transition: 0.1s; + } + } + } + } + + &--transaction { + align-items: center; + margin-top: 10px; + + .transaction-label{ + font-size: 12px; + margin-left: 5px; + vertical-align: super; + } + } + + img.lock-icon { + width: 18px; + float: left !important; + } + + img.payplug-logo { + width: 80px; + height: auto; + display: inline-block; + float: inherit !important; + margin-left: 6px; + vertical-align: text-top; + } + + &--privacy-policy { + display: inline-block; + text-align: center; + + a { + color: #918f8f; + font-size: 14px; + } + } + } + + &__schemes { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + width: 115px; + + label { + display: table-cell; + } + } + + &__scheme { + margin: 0; + + span { + cursor: pointer; + display: block; + width: 33px; + height: 22px; + background: 50% no-repeat; + background-size: 100% auto; + + &:before { + display: block; + width: 100%; + height: 100%; + content: ""; + background: 50% no-repeat; + background-size: 100% auto; + opacity: 0; + } + } + + input { + display: none; + + &:checked + span:before { + opacity: 1; + } + } + + &--visa span { + background-image: url(../../images/integrated/visa-dark.svg); + + &:before { + background-image: url(../../images/integrated/visa.svg); + } + } + &--mastercard span { + background-image: url(../../images/integrated/mastercard-dark.svg); + + &:before { + background-image: url(../../images/integrated/mastercard.svg); + } + } + &--cb span { + background-image: url(../../images/integrated/cb-dark.svg); + + &:before { + background-image: url(../../images/integrated/cb.svg); + } + } + } + + &__error { + color: #E91932; + font-size: 12px; + margin: -10px 0 10px; + width: 100%; + line-height: 18px; + padding-left: 4px; + + &--cardHolder { + margin: -10px 0 0; + } + + &--cvv { + justify-self: flex-end; + margin: -10px 0 10px auto; + } + + &--exp, + &--cvv { + width: 100%; + max-width: calc(49%); + } + + &--hide { + display: none; + } + } +} diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index d816a39d..a95cc42e 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -92,6 +92,20 @@ payplug_sylius_payplug_plugin: title: 'The fees are:' client: Split between you and your customers merchant: For you + integrated_payment: + card_holder.title: 'Cardholder name' + card_holder.error: 'Invalid Name and/or Last Name.' + pan.title: 'Card number' + pan.error: 'Invalid card number.' + cvv.title: '123' + cvv.error: 'Invalid CVV.' + exp.title: 'MM/YY' + exp.error: 'Invalid expiration date.' + empty: 'Mandatory field.' + scheme.label: 'Your card' + place_order.label: 'Place order' + transaction_secure.label: 'Transaction secured by' + privacy_policy.label: 'Privacy Policy' form: oney_error: Some missing information is required to pay using Oney by Payplug complete_info: diff --git a/src/Resources/translations/messages.fr.yml b/src/Resources/translations/messages.fr.yml index a0e35e54..4830f3e4 100644 --- a/src/Resources/translations/messages.fr.yml +++ b/src/Resources/translations/messages.fr.yml @@ -110,6 +110,21 @@ payplug_sylius_payplug_plugin: title: 'Les frais sont :' client: Répartis entre vous et vos clients merchant: À votre charge + integrated_payment: + card_holder.title: 'Nom du titulaire de la carte' + card_holder.error: 'Nom et/ou prénom invalide(s).' + pan.title: 'Numero de carte' + pan.error: 'Numéro de carte invalide.' + cvv.title: '123' + cvv.error: 'CVV invalide.' + exp.title: "MM/AA" + exp.error: "Date d'expiration invalide." + empty: 'Champ obligatoire.' + scheme.label: 'Votre carte' + save_card.label: 'Enregistrer ma carte bancaire' + place_order.label: 'Confirmer le paiement' + transaction_secure.label: 'Transaction sécurisée par' + privacy_policy.label: 'Politique de confidentialité' form: oney_error: Il y a des informations manquantes pour pouvoir payer en utilisant Oney by Payplug complete_info: diff --git a/src/Resources/translations/messages.it.yml b/src/Resources/translations/messages.it.yml index e83d2456..b06fa4d0 100644 --- a/src/Resources/translations/messages.it.yml +++ b/src/Resources/translations/messages.it.yml @@ -92,6 +92,20 @@ payplug_sylius_payplug_plugin: title: 'Le spese sono:' client: Ripartite tra te e i tuoi clienti merchant: A tuo carico + integrated_payment: + card_holder.title: 'Titolare della carta' + card_holder.error: 'Nome e/o Cognome non valido(i).' + pan.title: 'Numero di carta' + pan.error: 'Numero di carta non valido.' + cvv.title: '123' + cvv.error: 'CVV non valido.' + exp.title: "MM/AA" + exp.error: "Data di scadenza non valida." + empty: 'Campo obbligatorio.' + scheme.label: 'La tua carta' + place_order.label: 'Ordine' + transaction_secure.label: 'Transazione protetta da' + privacy_policy.label: 'Politica di confidenzialità' form: oney_error: Mancano alcune informazioni per poter pagare con “Oney by Payplug” complete_info: diff --git a/src/Resources/views/SyliusShopBundle/Checkout/SelectPayment/_choice.html.twig b/src/Resources/views/SyliusShopBundle/Checkout/SelectPayment/_choice.html.twig index 1b3d86f3..f348b41c 100644 --- a/src/Resources/views/SyliusShopBundle/Checkout/SelectPayment/_choice.html.twig +++ b/src/Resources/views/SyliusShopBundle/Checkout/SelectPayment/_choice.html.twig @@ -92,7 +92,13 @@ {{ form_row(form.parent.parent.payplug_card_choice) }} {% endif %} - + {% if method.gatewayConfig.factoryName is same as payplugFactoryName and method.gatewayConfig.config.integratedPayment is defined and method.gatewayConfig.config.integratedPayment is same as true %} + {% include '@PayPlugSyliusPayPlugPlugin/form/integrated.html.twig' with { + 'paymentMethod': method, + 'payment': order.getLastPayment('cart'), + 'hasSavedCards': hasSavedCards + } %} + {% endif %} {% if method.gatewayConfig.factoryName == applePayFactoryName %} + + +
+
+
+ + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.card_holder.error'|trans }} + + + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.empty'|trans }} + +
+
+
{{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.scheme.label'|trans }}
+
+ + + +
+
+
+
+ + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.pan.error'|trans }} + + + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.empty'|trans }} + +
+
+
+
+ + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.exp.error'|trans }} + + + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.empty'|trans }} + +
+
+ + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.cvv.error'|trans }} + + + {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.empty'|trans }} + +
+ {% if is_save_card_enabled(paymentMethod) %} +
+ + +
+ {% endif %} + +
+ +
+ {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.transaction_secure.label'|trans }} +
+ +
+
+ {{ 'payplug_sylius_payplug_plugin.ui.integrated_payment.privacy_policy.label'|trans }} +
+
+ + + +{% include '@SyliusUi/_javascripts.html.twig' with {'path': 'bundles/payplugsyliuspayplugplugin/assets/shop/payment/integrated.js'} %} +{% include '@SyliusUi/_stylesheets.html.twig' with {'path': 'bundles/payplugsyliuspayplugplugin/assets/shop/payment/integrated.css'} %}