mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
use localPaymentModal in cart directive
This commit is contained in:
parent
d43f719038
commit
19717d1351
@ -30,7 +30,7 @@ class API::StripeController < API::PaymentsController
|
|||||||
currency: Setting.get('stripe_currency'),
|
currency: Setting.get('stripe_currency'),
|
||||||
confirmation_method: 'manual',
|
confirmation_method: 'manual',
|
||||||
confirm: true,
|
confirm: true,
|
||||||
customer: current_user.payment_gateway_object.gateway_object_id
|
customer: cart.customer.payment_gateway_object.gateway_object_id
|
||||||
}, { api_key: Setting.get('stripe_secret_key') }
|
}, { api_key: Setting.get('stripe_secret_key') }
|
||||||
)
|
)
|
||||||
elsif params[:payment_intent_id].present?
|
elsif params[:payment_intent_id].present?
|
||||||
|
@ -46,6 +46,7 @@ interface AbstractPaymentModalProps {
|
|||||||
formClassName?: string,
|
formClassName?: string,
|
||||||
title?: string,
|
title?: string,
|
||||||
preventCgv?: boolean,
|
preventCgv?: boolean,
|
||||||
|
preventScheduleInfo?: boolean,
|
||||||
modalSize?: ModalSize,
|
modalSize?: ModalSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ interface AbstractPaymentModalProps {
|
|||||||
* This component must not be called directly but must be extended for each implemented payment gateway
|
* This component must not be called directly but must be extended for each implemented payment gateway
|
||||||
* @see https://reactjs.org/docs/composition-vs-inheritance.html
|
* @see https://reactjs.org/docs/composition-vs-inheritance.html
|
||||||
*/
|
*/
|
||||||
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName, title, preventCgv, modalSize }) => {
|
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName, title, preventCgv, preventScheduleInfo, modalSize }) => {
|
||||||
// customer's wallet
|
// customer's wallet
|
||||||
const [wallet, setWallet] = useState<Wallet>(null);
|
const [wallet, setWallet] = useState<Wallet>(null);
|
||||||
// server-computed price with all details
|
// server-computed price with all details
|
||||||
@ -129,10 +130,10 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we are currently creating a payment schedule
|
* Check if we must display the info box about the payment schedule
|
||||||
*/
|
*/
|
||||||
const isPaymentSchedule = (): boolean => {
|
const hasPaymentScheduleInfo = (): boolean => {
|
||||||
return schedule !== undefined;
|
return schedule !== undefined && !preventScheduleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +203,7 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
|||||||
{hasErrors() && <div className="payment-errors">
|
{hasErrors() && <div className="payment-errors">
|
||||||
{errors}
|
{errors}
|
||||||
</div>}
|
</div>}
|
||||||
{isPaymentSchedule() && <div className="payment-schedule-info">
|
{hasPaymentScheduleInfo() && <div className="payment-schedule-info">
|
||||||
<HtmlTranslate trKey="app.shared.payment.payment_schedule_html" options={{ DEADLINES: schedule.items.length, GATEWAY: gateway }} />
|
<HtmlTranslate trKey="app.shared.payment.payment_schedule_html" options={{ DEADLINES: schedule.items.length, GATEWAY: gateway }} />
|
||||||
</div>}
|
</div>}
|
||||||
{hasCgv() && <div className="terms-of-sales">
|
{hasCgv() && <div className="terms-of-sales">
|
||||||
@ -233,5 +234,6 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
|||||||
AbstractPaymentModal.defaultProps = {
|
AbstractPaymentModal.defaultProps = {
|
||||||
title: 'app.shared.payment.online_payment',
|
title: 'app.shared.payment.online_payment',
|
||||||
preventCgv: false,
|
preventCgv: false,
|
||||||
|
preventScheduleInfo: false,
|
||||||
modalSize: ModalSize.medium
|
modalSize: ModalSize.medium
|
||||||
};
|
};
|
||||||
|
@ -103,10 +103,12 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
|
|||||||
{!paymentSchedule && <p className="payment">{t('app.admin.local_payment.about_to_cash')}</p>}
|
{!paymentSchedule && <p className="payment">{t('app.admin.local_payment.about_to_cash')}</p>}
|
||||||
{paymentSchedule && <div className="payment-schedule">
|
{paymentSchedule && <div className="payment-schedule">
|
||||||
<div className="schedule-method">
|
<div className="schedule-method">
|
||||||
|
<label htmlFor="payment-method">{t('app.admin.local_payment.payment_method')}</label>
|
||||||
<Select placeholder={ t('app.admin.local_payment.payment_method') }
|
<Select placeholder={ t('app.admin.local_payment.payment_method') }
|
||||||
|
id="payment-method"
|
||||||
className="method-select"
|
className="method-select"
|
||||||
onChange={handleUpdateMethod}
|
onChange={handleUpdateMethod}
|
||||||
options={buildMethodOptions}
|
options={buildMethodOptions()}
|
||||||
defaultValue={methodToOption(method)} />
|
defaultValue={methodToOption(method)} />
|
||||||
{method === 'card' && <p>{t('app.admin.local_payment.card_collection_info')}</p>}
|
{method === 'card' && <p>{t('app.admin.local_payment.card_collection_info')}</p>}
|
||||||
{method === 'check' && <p>{t('app.admin.local_payment.check_collection_info', { DEADLINES: paymentSchedule.items.length })}</p>}
|
{method === 'check' && <p>{t('app.admin.local_payment.check_collection_info', { DEADLINES: paymentSchedule.items.length })}</p>}
|
||||||
@ -115,7 +117,7 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
|
|||||||
<ul>
|
<ul>
|
||||||
{paymentSchedule.items.map(item => {
|
{paymentSchedule.items.map(item => {
|
||||||
return (
|
return (
|
||||||
<li key={item.id}>
|
<li key={`${item.due_date}`}>
|
||||||
<span className="schedule-item-date">{FormatLib.date(item.due_date)}</span>
|
<span className="schedule-item-date">{FormatLib.date(item.due_date)}</span>
|
||||||
<span> </span>
|
<span> </span>
|
||||||
<span className="schedule-item-price">{FormatLib.price(item.amount)}</span>
|
<span className="schedule-item-price">{FormatLib.price(item.amount)}</span>
|
||||||
|
@ -7,7 +7,11 @@ import { User } from '../../../models/user';
|
|||||||
import { Invoice } from '../../../models/invoice';
|
import { Invoice } from '../../../models/invoice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ModalSize } from '../../base/fab-modal';
|
import { ModalSize } from '../../base/fab-modal';
|
||||||
|
import { Loader } from '../../base/loader';
|
||||||
|
import { react2angular } from 'react2angular';
|
||||||
|
import { IApplication } from '../../../models/application';
|
||||||
|
|
||||||
|
declare var Application: IApplication;
|
||||||
|
|
||||||
interface LocalPaymentModalProps {
|
interface LocalPaymentModalProps {
|
||||||
isOpen: boolean,
|
isOpen: boolean,
|
||||||
@ -22,7 +26,7 @@ interface LocalPaymentModalProps {
|
|||||||
/**
|
/**
|
||||||
* This component enables a privileged user to confirm a local payments.
|
* This component enables a privileged user to confirm a local payments.
|
||||||
*/
|
*/
|
||||||
export const LocalPaymentModal: React.FC<LocalPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer }) => {
|
const LocalPaymentModalComponent: React.FC<LocalPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer }) => {
|
||||||
|
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
@ -71,6 +75,17 @@ export const LocalPaymentModal: React.FC<LocalPaymentModalProps> = ({ isOpen, to
|
|||||||
schedule={schedule}
|
schedule={schedule}
|
||||||
GatewayForm={renderForm}
|
GatewayForm={renderForm}
|
||||||
modalSize={schedule ? ModalSize.large : ModalSize.medium}
|
modalSize={schedule ? ModalSize.large : ModalSize.medium}
|
||||||
preventCgv />
|
preventCgv
|
||||||
|
preventScheduleInfo />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const LocalPaymentModal: React.FC<LocalPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, currentUser, schedule , cart, customer }) => {
|
||||||
|
return (
|
||||||
|
<Loader>
|
||||||
|
<LocalPaymentModalComponent isOpen={isOpen} toggleModal={toggleModal} afterSuccess={afterSuccess} currentUser={currentUser} schedule={schedule} cart={cart} customer={customer} />
|
||||||
|
</Loader>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Components.component('localPaymentModal', react2angular(LocalPaymentModal, ['isOpen', 'toggleModal', 'afterSuccess', 'currentUser', 'schedule', 'cart', 'customer']));
|
||||||
|
@ -79,6 +79,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
|||||||
cartItems: undefined
|
cartItems: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// offline payments (at the fablab's reception)
|
||||||
|
$scope.localPayment = {
|
||||||
|
showModal: false,
|
||||||
|
cartItems: undefined
|
||||||
|
};
|
||||||
|
|
||||||
// currently logged-in user
|
// currently logged-in user
|
||||||
$scope.currentUser = $rootScope.currentUser;
|
$scope.currentUser = $rootScope.currentUser;
|
||||||
|
|
||||||
@ -325,6 +331,19 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
|||||||
}, 50);
|
}, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will open/close the local payment modal
|
||||||
|
*/
|
||||||
|
$scope.toggleLocalPaymentModal = (beforeApply) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
$scope.localPayment.showModal = !$scope.localPayment.showModal;
|
||||||
|
if (typeof beforeApply === 'function') {
|
||||||
|
beforeApply();
|
||||||
|
}
|
||||||
|
$scope.$apply();
|
||||||
|
}, 50);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked atfer a successful card payment
|
* Invoked atfer a successful card payment
|
||||||
* @param invoice {*} may be an Invoice or a paymentSchedule
|
* @param invoice {*} may be an Invoice or a paymentSchedule
|
||||||
@ -334,6 +353,15 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
|||||||
afterPayment(invoice);
|
afterPayment(invoice);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked atfer a successful offline payment
|
||||||
|
* @param invoice {*} may be an Invoice or a paymentSchedule
|
||||||
|
*/
|
||||||
|
$scope.afterLocalPaymentSuccess = (invoice) => {
|
||||||
|
$scope.toggleLocalPaymentModal();
|
||||||
|
afterPayment(invoice);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when something wrong occurred during the payment dialog initialization
|
* Invoked when something wrong occurred during the payment dialog initialization
|
||||||
* @param message {string}
|
* @param message {string}
|
||||||
@ -717,195 +745,14 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
|
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
|
||||||
*/
|
*/
|
||||||
const payOnSite = function (items) {
|
const payOnSite = function (items) {
|
||||||
$uibModal.open({
|
$scope.toggleLocalPaymentModal(() => {
|
||||||
templateUrl: '/shared/valid_reservation_modal.html',
|
$scope.localPayment.cartItems = mkCartItems(items);
|
||||||
size: $scope.schedule.payment_schedule ? 'lg' : 'sm',
|
});
|
||||||
resolve: {
|
|
||||||
price () {
|
|
||||||
return Price.compute(mkCartItems(items, '')).$promise;
|
|
||||||
},
|
|
||||||
cartItems () {
|
|
||||||
return mkCartItems(items, '');
|
|
||||||
},
|
|
||||||
wallet () {
|
|
||||||
return Wallet.getWalletByUser({ user_id: $scope.user.id }).$promise;
|
|
||||||
},
|
|
||||||
coupon () {
|
|
||||||
return $scope.coupon.applied;
|
|
||||||
},
|
|
||||||
selectedPlan () {
|
|
||||||
return $scope.selectedPlan;
|
|
||||||
},
|
|
||||||
schedule () {
|
|
||||||
return $scope.schedule;
|
|
||||||
},
|
|
||||||
user () {
|
|
||||||
return $scope.user;
|
|
||||||
},
|
|
||||||
settings () {
|
|
||||||
return $scope.settings;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
controller: ['$scope', '$uibModalInstance', '$state', 'price', 'Auth', 'LocalPayment', 'wallet', 'helpers', '$filter', 'coupon', 'selectedPlan', 'schedule', 'cartItems', 'user', 'settings',
|
|
||||||
function ($scope, $uibModalInstance, $state, price, Auth, LocalPayment, wallet, helpers, $filter, coupon, selectedPlan, schedule, cartItems, user, settings) {
|
|
||||||
// user wallet amount
|
|
||||||
$scope.wallet = wallet;
|
|
||||||
|
|
||||||
// Global price (total of all items)
|
|
||||||
$scope.price = price.price;
|
|
||||||
|
|
||||||
// Price to pay (wallet deducted)
|
|
||||||
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
|
|
||||||
|
|
||||||
// Reservation &| subscription
|
|
||||||
$scope.cartItems = cartItems;
|
|
||||||
|
|
||||||
// Subscription
|
|
||||||
$scope.plan = selectedPlan;
|
|
||||||
|
|
||||||
// Used in wallet info template to interpolate some translations
|
|
||||||
$scope.numberFilter = $filter('number');
|
|
||||||
|
|
||||||
// Shows the schedule info in the modal
|
|
||||||
$scope.schedule = schedule.payment_schedule;
|
|
||||||
|
|
||||||
// how should we collect payments for the payment schedule
|
|
||||||
$scope.method = {
|
|
||||||
payment_method: 'card'
|
|
||||||
};
|
|
||||||
|
|
||||||
// "valid" Button label
|
|
||||||
$scope.validButtonName = '';
|
|
||||||
|
|
||||||
// online payment modal state
|
|
||||||
// this is used to collect card data when a payment-schedule was selected, and paid with a card
|
|
||||||
$scope.isOpenOnlinePaymentModal = false;
|
|
||||||
|
|
||||||
// the customer
|
|
||||||
$scope.user = user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the shopping cart contains a reservation
|
|
||||||
* @return {Reservation|boolean}
|
|
||||||
*/
|
|
||||||
$scope.reservation = (function () {
|
|
||||||
const item = cartItems.items.find(i => i.reservation);
|
|
||||||
if (item && item.reservation.slots_attributes.length > 0) {
|
|
||||||
return item.reservation;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the shopping cart contains a subscription
|
|
||||||
* @return {Subscription|boolean}
|
|
||||||
*/
|
|
||||||
$scope.subscription = (function () {
|
|
||||||
const item = cartItems.items.find(i => i.subscription);
|
|
||||||
if (item && item.subscription.plan_id) {
|
|
||||||
return item.subscription;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback to process the local payment, triggered on button click
|
|
||||||
*/
|
|
||||||
$scope.ok = function () {
|
|
||||||
if ($scope.schedule && $scope.method.payment_method === 'card') {
|
|
||||||
// check that the online payment is enabled
|
|
||||||
if (settings.online_payment_module !== 'true') {
|
|
||||||
return growl.error(_t('app.shared.cart.online_payment_disabled'));
|
|
||||||
} else {
|
|
||||||
return $scope.toggleOnlinePaymentModal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$scope.attempting = true;
|
|
||||||
LocalPayment.confirm(cartItems, function (reservation) {
|
|
||||||
$uibModalInstance.close(reservation);
|
|
||||||
$scope.attempting = true;
|
|
||||||
}, function (response) {
|
|
||||||
$scope.alerts = [];
|
|
||||||
$scope.alerts.push({ msg: _t('app.shared.cart.a_problem_occurred_during_the_payment_process_please_try_again_later'), type: 'danger' });
|
|
||||||
$scope.attempting = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Callback to close the modal without processing the payment
|
|
||||||
*/
|
|
||||||
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously updates the status of the online payment modal
|
|
||||||
*/
|
|
||||||
$scope.toggleOnlinePaymentModal = function () {
|
|
||||||
setTimeout(() => {
|
|
||||||
$scope.isOpenOnlinePaymentModal = !$scope.isOpenOnlinePaymentModal;
|
|
||||||
$scope.$apply();
|
|
||||||
}, 50);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* After creating a payment schedule by card, from an administrator.
|
|
||||||
* @param result {*} PaymentSchedule
|
|
||||||
*/
|
|
||||||
$scope.afterCreatePaymentSchedule = function (result) {
|
|
||||||
$scope.toggleOnlinePaymentModal();
|
|
||||||
$uibModalInstance.close(result);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when something wrong occurred during the payment dialog initialization
|
|
||||||
* @param message {string}
|
|
||||||
*/
|
|
||||||
$scope.onCreatePaymentScheduleError = (message) => {
|
|
||||||
growl.error(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* PRIVATE SCOPE */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kind of constructor: these actions will be realized first when the directive is loaded
|
|
||||||
*/
|
|
||||||
const initialize = function () {
|
|
||||||
$scope.$watch('method.payment_method', function (newValue) {
|
|
||||||
$scope.validButtonName = computeValidButtonName();
|
|
||||||
$scope.cartItems.payment_method = newValue;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the Label of the confirmation button
|
|
||||||
*/
|
|
||||||
const computeValidButtonName = function () {
|
|
||||||
let method = '';
|
|
||||||
if ($scope.schedule) {
|
|
||||||
if (AuthService.isAuthorized(['admin', 'manager']) && $rootScope.currentUser.id !== cartItems.customer_id) {
|
|
||||||
method = $scope.method.payment_method;
|
|
||||||
} else {
|
|
||||||
method = 'card';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($scope.amount > 0) {
|
|
||||||
return _t('app.shared.cart.confirm_payment_of_html', { METHOD: method, AMOUNT: $filter('currency')($scope.amount) });
|
|
||||||
} else {
|
|
||||||
if ((price.price > 0) && ($scope.wallet.amount === 0)) {
|
|
||||||
return _t('app.shared.cart.confirm_payment_of_html', { METHOD: method, AMOUNT: $filter('currency')(price.price) });
|
|
||||||
} else {
|
|
||||||
return _t('app.shared.buttons.confirm');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// # !!! MUST BE CALLED AT THE END of the controller
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}).result.finally(null).then(function (paymentSchedule) { afterPayment(paymentSchedule); });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,28 @@
|
|||||||
.local-payment-modal {
|
.local-payment-modal {
|
||||||
|
|
||||||
|
.local-payment-form {
|
||||||
|
.payment-schedule {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.schedule-method,
|
||||||
|
.full-schedule {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-method {
|
||||||
|
p {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-schedule {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.local-modal-icons {
|
.local-modal-icons {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
@ -209,3 +209,13 @@
|
|||||||
customer="user"
|
customer="user"
|
||||||
schedule="schedule.payment_schedule"/>
|
schedule="schedule.payment_schedule"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="localPayment.showModal">
|
||||||
|
<local-payment-modal is-open="localPayment.showModal"
|
||||||
|
toggle-modal="toggleLocalPaymentModal"
|
||||||
|
after-success="afterLocalPaymentSuccess"
|
||||||
|
cart="localPayment.cartItems"
|
||||||
|
current-user="currentUser"
|
||||||
|
customer="user"
|
||||||
|
schedule="schedule.payment_schedule"/>
|
||||||
|
</div>
|
||||||
|
@ -1360,7 +1360,7 @@ en:
|
|||||||
category_deleted: "The category was successfully deleted"
|
category_deleted: "The category was successfully deleted"
|
||||||
unable_to_delete: "Unable to delete the category: "
|
unable_to_delete: "Unable to delete the category: "
|
||||||
local_payment:
|
local_payment:
|
||||||
offline_payment: "Offline payment"
|
offline_payment: "Payment on site"
|
||||||
about_to_cash: "You're about to confirm the cashing by an external payment mean. Please do not click on the button below until you have fully cashed the requested payment."
|
about_to_cash: "You're about to confirm the cashing by an external payment mean. Please do not click on the button below until you have fully cashed the requested payment."
|
||||||
payment_method: "Payment method"
|
payment_method: "Payment method"
|
||||||
method_card: "Online by card"
|
method_card: "Online by card"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user