Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refacto/payment method token #1181

Merged
merged 60 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
037b24b
Replace client token by user id token
Matt75 Jan 15, 2024
f5ef9ad
Implement payment method token
Matt75 Jan 15, 2024
3680039
Adding payment method token events for each webhook
btafforeau Mar 6, 2024
fa194cb
Added is_favorite column to token table
L3RAZ Mar 8, 2024
6ded1db
PaymentToken entity and repository
L3RAZ Mar 8, 2024
72ee1df
Added new JS component for vaulted payment methods
L3RAZ Mar 13, 2024
0b15f00
Added correct payment logo display
L3RAZ Mar 13, 2024
feaf7ad
Added custom marks for vaulted payment methods
L3RAZ Mar 13, 2024
6eb6b1e
Moved vaulting config check to fundingSourceProvider
L3RAZ Mar 13, 2024
646f7d4
CS fix
L3RAZ Mar 13, 2024
d1654f7
Added forms for vaulting payments and setting as favorite
L3RAZ Mar 14, 2024
b5f7431
Renamed smarty template variables
L3RAZ Mar 14, 2024
4f89ef0
Added token delete handler and repository functions
L3RAZ Mar 15, 2024
6235c54
Temp fix for token events
L3RAZ Mar 15, 2024
30c5c0f
Changed delete function argument type
L3RAZ Mar 15, 2024
f305590
Undid database table changes
L3RAZ Mar 15, 2024
f7845fc
Added event subscriber implementation
L3RAZ Mar 15, 2024
8978e56
CS fix
L3RAZ Mar 15, 2024
753a4fb
Fixed OrderDispatcher
L3RAZ Mar 15, 2024
fa9cf77
PHPStan fixes
L3RAZ Mar 15, 2024
d68fd96
Added PayPal customer saving
L3RAZ Mar 18, 2024
edc5b29
Added logic to set token as favorite after receiving webhook
L3RAZ Mar 18, 2024
f402bf4
CS fix
L3RAZ Mar 18, 2024
c9e1e95
phpStan fix
L3RAZ Mar 18, 2024
5a8e626
Remove final_capture from pscheckout_order table
Matt75 Mar 19, 2024
f28596c
Added payment token delete without confirmation
L3RAZ Mar 19, 2024
5cce7d2
Added modal component and confirmation for payment token delete
L3RAZ Mar 19, 2024
fbb9416
Added config for token event subscriber
L3RAZ Mar 20, 2024
04f875b
Added token deletion
L3RAZ Mar 20, 2024
c3cf3ca
Added total count function for admin ajax controller
L3RAZ Mar 20, 2024
ff1ba51
Added setting payment token as favorite on order create
L3RAZ Mar 20, 2024
9888ab1
Added userIdToken loading
L3RAZ Mar 20, 2024
61cf35d
Added payment token status to Db table, entity and repository
L3RAZ Mar 21, 2024
89da15f
Added API call to get userIdToken
L3RAZ Mar 21, 2024
8a313c1
added payment token status fetching from webhook
L3RAZ Mar 21, 2024
67a824d
Added token filtering by status
L3RAZ Mar 21, 2024
543c58e
Added Payment controller for validating orders with payment token
L3RAZ Mar 22, 2024
92b3d0c
Added check if payment token belongs to customer
L3RAZ Mar 22, 2024
02ebb46
Replaced new order payload builder with old one in create order comma…
L3RAZ Mar 22, 2024
d4de4b7
Changed token and oauth functions to match API specifications
L3RAZ Mar 25, 2024
fde9181
Added new handler to save paypal order and related entities to database
L3RAZ Mar 25, 2024
cd1e08c
CS fix
L3RAZ Mar 25, 2024
57c15d0
Added template for 3DS error
L3RAZ Mar 25, 2024
8442ed9
Removed OauthHttpClient and added configuration for CheckoutHttpCLien…
L3RAZ Mar 26, 2024
54750b9
CS fix
L3RAZ Mar 26, 2024
79c6aa5
Add vault to legacy OrderPayloadBuilder
Matt75 Mar 26, 2024
a64c088
Added 3DS logic to payment controller and moved order entities
L3RAZ Mar 26, 2024
d577d95
Fix ExpressCheckout product data
Matt75 Mar 22, 2024
b5fcc71
Fix create order payload address
Matt75 Mar 21, 2024
f16007a
Fix json array for card brands
Matt75 Mar 13, 2024
e5a2921
Added cancel_url to order payload
L3RAZ Mar 26, 2024
bd75d0d
Changed PayPal Order update functions and added Order update on PayPa…
L3RAZ Mar 27, 2024
806e55f
Fixed total token count query
L3RAZ Mar 28, 2024
4e99daf
PHPStan fixes
L3RAZ Mar 29, 2024
419fbb9
CS fix
L3RAZ Mar 29, 2024
68296e8
Fixes payment token saving during order capture
L3RAZ Mar 29, 2024
46139a9
Fixes for vaulting order creation and capture
L3RAZ Apr 2, 2024
697e065
Replaced returl_url in order create payload with redirect_uri in 3DS url
L3RAZ Apr 2, 2024
39d6bec
Remove deprecated PayPal Client Token
Matt75 Apr 2, 2024
236427f
CS fixes
L3RAZ Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions _dev/js/front/src/api/ps-checkout.api.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,35 @@ export class PsCheckoutApi extends BaseClass {
})
);
}

postDeleteVaultedToken(data) {
return fetch(this.config.vaultUrl, {
method: 'post',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
...(data ? { body: JSON.stringify({action: 'deleteToken', ...data}) } : {})
})
.then((response) => {
const contentType = response.headers.get('content-type');
const isJsonResponse =
contentType && contentType.indexOf('application/json') !== -1;

if (isJsonResponse) {
if (false === response.ok || response.status >= 400) {
return response.json().then((response) => {
throw response.body && response.body.error
? response.body.error
: { message: this.$('checkout.form.error.label') };
});
}

return response.json();
}

throw new Error(this.$('checkout.form.error.label'));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,11 @@ export class ExpressButtonProductComponent extends BaseComponent {

buttonContainer.append(this.checkoutExpressButton);

const {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
} = this.prestashopService.getProductDetails();

this.children.expressCheckoutButton = new ExpressCheckoutButtonComponent(
this.app,
{
fundingSource: 'paypal',
querySelector: '#ps-checkout-express-button',
data: {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
}
querySelector: '#ps-checkout-express-button'
}
).render();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,11 @@ export class PayLaterButtonProductComponent extends BaseComponent {
buttonContainer.append(this.checkoutExpressButton);
}

const {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
} = this.prestashopService.getProductDetails();

this.children.expressCheckoutButton = new ExpressCheckoutButtonComponent(
this.app,
{
fundingSource: 'paylater',
querySelector: '#ps-checkout-express-button',
data: {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
}
querySelector: '#ps-checkout-express-button'
}
).render();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ export class PaymentOptionsComponent extends BaseComponent {
if (
this.config.expressCheckout.active &&
'ps_checkout-' + this.payPalService.getFundingSource() !==
HTMLListenerElements[index].button.dataset.moduleName
HTMLListenerElements[index].button.dataset.moduleName &&
this.payPalService.getOrderId()
) {
this.psCheckoutApi
.postCancelOrder({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,11 @@ export class ExpressButtonProductComponent extends BaseComponent {
this.updateButtonContainerVisibility();
});

const {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
} = this.prestashopService.getProductDetails();

this.children.expressCheckoutButton = new ExpressCheckoutButtonComponent(
this.app,
{
fundingSource: 'paypal',
querySelector: `#${BUTTON_CONTAINER_SELECTOR}`,
data: {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
}
querySelector: `#${BUTTON_CONTAINER_SELECTOR}`
}
).render();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,11 @@ export class PayLaterButtonProductComponent extends BaseComponent {
this.updateButtonContainerVisibility();
});

const {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
} = this.prestashopService.getProductDetails();

this.children.expressCheckoutButton = new ExpressCheckoutButtonComponent(
this.app,
{
fundingSource: 'paylater',
querySelector: `#${BUTTON_CONTAINER_SELECTOR}`,
data: {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
}
querySelector: `#${BUTTON_CONTAINER_SELECTOR}`
}
).render();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export class PaymentOptionsComponent extends BaseComponent {
if (
this.config.expressCheckout.active &&
'ps_checkout-' + this.payPalService.getFundingSource() !==
radio.dataset.moduleName
radio.dataset.moduleName &&
this.payPalService.getOrderId()
) {
this.psCheckoutApi
.postCancelOrder({
Expand Down
16 changes: 14 additions & 2 deletions _dev/js/front/src/components/common/card-fields.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ export class CardFieldsComponent extends BaseComponent {
this.toggleCardFieldsErrors();
}

getVaultFormData() {
if (this.data.HTMLElementCardForm) {
const formData = new FormData(this.data.HTMLElementCardForm);
return {
vault: formData.get(`ps_checkout-vault-payment-${this.data.name}`) === '1',
favorite: formData.get(`ps_checkout-favorite-payment-${this.data.name}`) === '1'
};

}
return {};
}

renderPayPalCardFields() {
this.data.HTMLElementCardForm.classList.toggle('loading', true);

Expand Down Expand Up @@ -223,10 +235,10 @@ export class CardFieldsComponent extends BaseComponent {

return this.psCheckoutApi
.postCreateOrder({
...this.getVaultFormData(),
...data,
fundingSource: this.data.name,
isHostedFields: true
// vault: storeCardInVault
isCardFields: true
})
.then((data) => {
this.data.orderId = data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class ExpressCheckoutButtonComponent extends BaseComponent {
static Inject = {
payPalService: 'PayPalService',
psCheckoutApi: 'PsCheckoutApi',
prestashopService: 'PrestashopService',
$: '$'
};

Expand Down Expand Up @@ -88,7 +89,22 @@ export class ExpressCheckoutButtonComponent extends BaseComponent {
}

createOrder(data) {
const extraData = this.props?.data ? this.props.data : {};
let extraData = {};

if (this.prestashopService.isProductPage()) {
let {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
} = this.prestashopService.getProductDetails();
extraData = {
id_product,
id_product_attribute,
id_customization,
quantity_wanted
};
}

return this.psCheckoutApi
.postCreateOrder({
Expand Down
2 changes: 1 addition & 1 deletion _dev/js/front/src/components/common/marker.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class MarkComponent extends BaseComponent {
const src = this.config.customMark[this.data.name];
let logoList = [];

if (this.config.cardSupportedBrands && this.config.cardLogoBrands) {
if (this.data.name === 'card' && this.config.cardSupportedBrands && this.config.cardLogoBrands) {
this.config.cardSupportedBrands.forEach(brand => {
if (this.config.cardLogoBrands[brand]) {
let customMarkImg = document.createElement('img');
Expand Down
109 changes: 109 additions & 0 deletions _dev/js/front/src/components/common/modal.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* @author PrestaShop SA <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
import { BaseComponent } from '../../core/dependency-injection/base.component';

export class ModalComponent extends BaseComponent {
static Inject = {
querySelectorService: 'QuerySelectorService',
config: 'PsCheckoutConfig',
$: '$'
};

created() {
this.data.parent = this.querySelectorService.getLoaderParent();
this.data.header = this.props.header || null;
this.data.content = this.props.content || null;
this.data.confirmText = this.props.confirmText || this.$('ok');
this.data.confirmType = this.props.confirmType || 'primary';
this.data.cancelText = this.props.cancelText || this.$('cancel');
this.data.cancelType = this.props.cancelType || 'primary';
this.data.onConfirm = this.props.onConfirm || (() => {});
this.data.onClose = this.props.onClose || (() => {});
}

render() {
this.overlay = document.createElement('div');
this.overlay.classList.add('ps-checkout', 'overlay');

this.overlay.addEventListener('click', (event) => {
if (event.target === this.overlay) {
this.data.onClose();
this.hide();
}
})

this.modal = document.createElement('div');
this.modal.classList.add('ps-checkout', 'ps-checkout-modal');

if (this.data.header) {
this.header = document.createElement('h1');
this.header.classList.add('ps-checkout', 'text');
this.header.innerHTML = this.data.header;

this.modal.append(this.header);
}

if (this.data.content) {
this.contentContainer = document.createElement('div');
this.contentContainer.classList.add('ps-checkout', 'content');
this.contentContainer.append(this.data.content);
this.modal.append(this.contentContainer);
}

this.footer = document.createElement('div');
this.footer.classList.add('ps-checkout', 'footer');

this.cancelButton = document.createElement('button');
this.cancelButton.innerHTML = this.data.cancelText;
this.cancelButton.classList.add('ps-checkout', 'btn', this.data.cancelType);

this.cancelButton.addEventListener('click', (event) => {
this.data.onClose();
this.hide();
})

this.confirmButton = document.createElement('button');
this.confirmButton.innerHTML = this.data.confirmText;
this.confirmButton.classList.add('ps-checkout', 'btn', this.data.confirmType);

this.confirmButton.addEventListener('click', (event) => {
this.data.onConfirm();
this.hide();
})

this.footer.append(this.cancelButton, this.confirmButton);

this.modal.append(this.footer);

this.overlay.append(this.modal);
this.data.parent.append(this.overlay);

return this;
}

show() {
this.overlay.classList.add('visible');
document.body.style.overflow = 'hidden';
}

hide() {
this.overlay.classList.remove('visible');
document.body.style.overflow = '';
}
}
15 changes: 14 additions & 1 deletion _dev/js/front/src/components/common/payment-option.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SmartButtonComponent } from './smart-button.component';
import { PaymentFieldsComponent } from "./payment-fields.component";
import {CardFieldsComponent} from "./card-fields.component";
import {PS_VERSION_1_6} from "../../constants/ps-version.constants";
import {PaymentTokenComponent} from "./payment-token.component";

/**
* @typedef PaymentOptionComponentProps
Expand Down Expand Up @@ -74,6 +75,11 @@ export class PaymentOptionComponent extends BaseComponent {
);
}

getPaymentForm() {
const container = `pay-with-${this.data.HTMLElement.id}-form`;
return document.getElementById(container);
}

getLabel() {
const translationKey = `funding-source.name.${this.data.name}`;
const label =
Expand Down Expand Up @@ -170,7 +176,14 @@ export class PaymentOptionComponent extends BaseComponent {
this.data.HTMLElementCardFields.style.display = 'none';
}

if (this.data.HTMLElementCardFields && isCardFieldsEligible && isCardFieldsAvailable) {
if (this.props.fundingSource.name.includes('token')) {
this.children.paymentToken = new PaymentTokenComponent(this.app, {
fundingSource: this.props.fundingSource,
HTMLElementRadio: this.data.HTMLElement,
HTMLElementContainer: this.data.HTMLElementContainer,
HTMLElementForm: this.getPaymentForm()
}).render();
} else if (this.data.HTMLElementCardFields && isCardFieldsEligible && isCardFieldsAvailable) {
this.data.HTMLElementCardFields.style.display = '';
this.children.cardFields = new CardFieldsComponent(this.app, {
fundingSource: this.props.fundingSource,
Expand Down
Loading
Loading