mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-12-02 13:24:20 +01:00
Merge branch 'product_store-payment' into product_store-store
This commit is contained in:
commit
6d39e4a6ee
@ -9,22 +9,7 @@ class API::CartController < API::ApiController
|
|||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :cart, :create?
|
authorize :cart, :create?
|
||||||
@order = Order.find_by(token: order_token, state: 'cart')
|
@order ||= Cart::FindOrCreateService.new.call(order_token, current_user)
|
||||||
if @order.nil?
|
|
||||||
if current_user&.member?
|
|
||||||
@order = Order.where(statistic_profile_id: current_user.statistic_profile.id,
|
|
||||||
state: 'cart').last
|
|
||||||
end
|
|
||||||
if current_user&.privileged?
|
|
||||||
@order = Order.where(operator_profile_id: current_user.invoicing_profile.id,
|
|
||||||
state: 'cart').last
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if @order
|
|
||||||
@order.update(statistic_profile_id: current_user.statistic_profile.id) if @order.statistic_profile_id.nil? && current_user&.member?
|
|
||||||
@order.update(operator_profile_id: current_user.invoicing_profile.id) if @order.operator_profile_id.nil? && current_user&.privileged?
|
|
||||||
end
|
|
||||||
@order ||= Cart::CreateService.new.call(current_user)
|
|
||||||
render 'api/orders/show'
|
render 'api/orders/show'
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -46,12 +31,6 @@ class API::CartController < API::ApiController
|
|||||||
render 'api/orders/show'
|
render 'api/orders/show'
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_customer
|
|
||||||
authorize @current_order, policy_class: CartPolicy
|
|
||||||
@order = Cart::SetCustomerService.new.call(@current_order, cart_params[:user_id])
|
|
||||||
render 'api/orders/show'
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def orderable
|
def orderable
|
||||||
|
@ -8,14 +8,21 @@ class API::CheckoutController < API::ApiController
|
|||||||
before_action :ensure_order
|
before_action :ensure_order
|
||||||
|
|
||||||
def payment
|
def payment
|
||||||
res = Checkout::PaymentService.new.payment(@current_order, current_user, params[:payment_id])
|
authorize @current_order, policy_class: CheckoutPolicy
|
||||||
|
if @current_order.statistic_profile_id.nil? && current_user.privileged?
|
||||||
|
user = User.find(params[:customer_id])
|
||||||
|
@current_order.statistic_profile = user.statistic_profile
|
||||||
|
end
|
||||||
|
res = Checkout::PaymentService.new.payment(@current_order, current_user, params[:coupon_code],
|
||||||
|
params[:payment_id])
|
||||||
render json: res
|
render json: res
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
render json: e, status: :unprocessable_entity
|
render json: e, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_payment
|
def confirm_payment
|
||||||
res = Checkout::PaymentService.new.confirm_payment(@current_order, current_user, params[:payment_id])
|
authorize @current_order, policy_class: CheckoutPolicy
|
||||||
|
res = Checkout::PaymentService.new.confirm_payment(@current_order, current_user, params[:coupon_code], params[:payment_id])
|
||||||
render json: res
|
render json: res
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
render json: e, status: :unprocessable_entity
|
render json: e, status: :unprocessable_entity
|
||||||
|
@ -22,9 +22,4 @@ export default class CartAPI {
|
|||||||
const res: AxiosResponse<Order> = await apiClient.put('/api/cart/set_quantity', { order_token: order.token, orderable_id: orderableId, quantity });
|
const res: AxiosResponse<Order> = await apiClient.put('/api/cart/set_quantity', { order_token: order.token, orderable_id: orderableId, quantity });
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setCustomer (order: Order, userId: number): Promise<Order> {
|
|
||||||
const res: AxiosResponse<Order> = await apiClient.put('/api/cart/set_customer', { order_token: order.token, user_id: userId });
|
|
||||||
return res?.data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
import apiClient from './clients/api-client';
|
import apiClient from './clients/api-client';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { OrderPayment } from '../models/order';
|
import { OrderPayment, Order } from '../models/order';
|
||||||
|
|
||||||
export default class CheckoutAPI {
|
export default class CheckoutAPI {
|
||||||
static async payment (token: string, paymentId?: string): Promise<OrderPayment> {
|
static async payment (order: Order, paymentId?: string): Promise<OrderPayment> {
|
||||||
const res: AxiosResponse<OrderPayment> = await apiClient.post('/api/checkout/payment', {
|
const res: AxiosResponse<OrderPayment> = await apiClient.post('/api/checkout/payment', {
|
||||||
order_token: token,
|
order_token: order.token,
|
||||||
payment_id: paymentId
|
coupon_code: order.coupon?.code,
|
||||||
|
payment_id: paymentId,
|
||||||
|
customer_id: order.user.id
|
||||||
});
|
});
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async confirmPayment (token: string, paymentId: string): Promise<OrderPayment> {
|
static async confirmPayment (order: Order, paymentId: string): Promise<OrderPayment> {
|
||||||
const res: AxiosResponse<OrderPayment> = await apiClient.post('/api/checkout/confirm_payment', {
|
const res: AxiosResponse<OrderPayment> = await apiClient.post('/api/checkout/confirm_payment', {
|
||||||
order_token: token,
|
order_token: order.token,
|
||||||
payment_id: paymentId
|
coupon_code: order.coupon?.code,
|
||||||
|
payment_id: paymentId,
|
||||||
|
customer_id: order.user.id
|
||||||
});
|
});
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
@ -13,18 +13,21 @@ import { PaymentMethod } from '../../models/payment';
|
|||||||
import { Order } from '../../models/order';
|
import { Order } from '../../models/order';
|
||||||
import { MemberSelect } from '../user/member-select';
|
import { MemberSelect } from '../user/member-select';
|
||||||
import { CouponInput } from '../coupon/coupon-input';
|
import { CouponInput } from '../coupon/coupon-input';
|
||||||
|
import { Coupon } from '../../models/coupon';
|
||||||
|
import { computePriceWithCoupon } from '../../lib/coupon';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
interface StoreCartProps {
|
interface StoreCartProps {
|
||||||
onError: (message: string) => void,
|
onError: (message: string) => void,
|
||||||
currentUser?: User,
|
userLogin: () => void,
|
||||||
|
currentUser?: User
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component shows user's cart
|
* This component shows user's cart
|
||||||
*/
|
*/
|
||||||
const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser }) => {
|
const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser, userLogin }) => {
|
||||||
const { t } = useTranslation('public');
|
const { t } = useTranslation('public');
|
||||||
|
|
||||||
const { cart, setCart } = useCart(currentUser);
|
const { cart, setCart } = useCart(currentUser);
|
||||||
@ -58,7 +61,11 @@ const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser }) => {
|
|||||||
* Checkout cart
|
* Checkout cart
|
||||||
*/
|
*/
|
||||||
const checkout = () => {
|
const checkout = () => {
|
||||||
setPaymentModal(true);
|
if (!currentUser) {
|
||||||
|
userLogin();
|
||||||
|
} else {
|
||||||
|
setPaymentModal(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,9 +91,7 @@ const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser }) => {
|
|||||||
* Change cart's customer by admin/manger
|
* Change cart's customer by admin/manger
|
||||||
*/
|
*/
|
||||||
const handleChangeMember = (userId: number): void => {
|
const handleChangeMember = (userId: number): void => {
|
||||||
CartAPI.setCustomer(cart, userId).then(data => {
|
setCart({ ...cart, user: { id: userId, role: 'member' } });
|
||||||
setCart(data);
|
|
||||||
}).catch(onError);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,6 +108,15 @@ const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser }) => {
|
|||||||
return cart && cart.order_items_attributes.length === 0;
|
return cart && cart.order_items_attributes.length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply coupon to current cart
|
||||||
|
*/
|
||||||
|
const applyCoupon = (coupon?: Coupon): void => {
|
||||||
|
if (coupon !== cart.coupon) {
|
||||||
|
setCart({ ...cart, coupon });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="store-cart">
|
<div className="store-cart">
|
||||||
{cart && cartIsEmpty() && <p>{t('app.public.store_cart.cart_is_empty')}</p>}
|
{cart && cartIsEmpty() && <p>{t('app.public.store_cart.cart_is_empty')}</p>}
|
||||||
@ -122,11 +136,13 @@ const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser }) => {
|
|||||||
</FabButton>
|
</FabButton>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{cart && !cartIsEmpty() && <CouponInput user={cart.user} amount={cart.total} />}
|
{cart && !cartIsEmpty() && cart.user && <CouponInput user={cart.user as User} amount={cart.total} onChange={applyCoupon} />}
|
||||||
{cart && !cartIsEmpty() && <p>Totale: {FormatLib.price(cart.total)}</p>}
|
{cart && !cartIsEmpty() && <p>Total produits: {FormatLib.price(cart.total)}</p>}
|
||||||
{cart && !cartIsEmpty() && isPrivileged() && <MemberSelect defaultUser={cart.user} onSelected={handleChangeMember} />}
|
{cart && !cartIsEmpty() && cart.coupon && computePriceWithCoupon(cart.total, cart.coupon) !== cart.total && <p>Coupon réduction: {FormatLib.price(-(cart.total - computePriceWithCoupon(cart.total, cart.coupon)))}</p>}
|
||||||
|
{cart && !cartIsEmpty() && <p>Total panier: {FormatLib.price(computePriceWithCoupon(cart.total, cart.coupon))}</p>}
|
||||||
|
{cart && !cartIsEmpty() && isPrivileged() && <MemberSelect onSelected={handleChangeMember} />}
|
||||||
{cart && !cartIsEmpty() &&
|
{cart && !cartIsEmpty() &&
|
||||||
<FabButton className="checkout-btn" onClick={checkout} disabled={!cart.user || cart.order_items_attributes.length === 0}>
|
<FabButton className="checkout-btn" onClick={checkout}>
|
||||||
{t('app.public.store_cart.checkout')}
|
{t('app.public.store_cart.checkout')}
|
||||||
</FabButton>
|
</FabButton>
|
||||||
}
|
}
|
||||||
@ -138,7 +154,7 @@ const StoreCart: React.FC<StoreCartProps> = ({ onError, currentUser }) => {
|
|||||||
cart={{ customer_id: cart.user.id, items: [], payment_method: PaymentMethod.Card }}
|
cart={{ customer_id: cart.user.id, items: [], payment_method: PaymentMethod.Card }}
|
||||||
order={cart}
|
order={cart}
|
||||||
operator={currentUser}
|
operator={currentUser}
|
||||||
customer={cart.user}
|
customer={cart.user as User}
|
||||||
updateCart={() => 'dont need update shopping cart'} />
|
updateCart={() => 'dont need update shopping cart'} />
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
@ -153,4 +169,4 @@ const StoreCartWrapper: React.FC<StoreCartProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Application.Components.component('storeCart', react2angular(StoreCartWrapper, ['onError', 'currentUser']));
|
Application.Components.component('storeCart', react2angular(StoreCartWrapper, ['onError', 'currentUser', 'userLogin']));
|
||||||
|
@ -5,11 +5,12 @@ import { FabAlert } from '../base/fab-alert';
|
|||||||
import CouponAPI from '../../api/coupon';
|
import CouponAPI from '../../api/coupon';
|
||||||
import { Coupon } from '../../models/coupon';
|
import { Coupon } from '../../models/coupon';
|
||||||
import { User } from '../../models/user';
|
import { User } from '../../models/user';
|
||||||
|
import FormatLib from '../../lib/format';
|
||||||
|
|
||||||
interface CouponInputProps {
|
interface CouponInputProps {
|
||||||
amount: number,
|
amount: number,
|
||||||
user?: User,
|
user?: User,
|
||||||
onChange?: (coupon: Coupon) => void
|
onChange?: (coupon?: Coupon) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
@ -42,7 +43,7 @@ export const CouponInput: React.FC<CouponInputProps> = ({ user, amount, onChange
|
|||||||
if (res.type === 'percent_off') {
|
if (res.type === 'percent_off') {
|
||||||
mgs.push({ type: 'info', message: t('app.shared.coupon_input.the_coupon_has_been_applied_you_get_PERCENT_discount', { PERCENT: res.percent_off }) });
|
mgs.push({ type: 'info', message: t('app.shared.coupon_input.the_coupon_has_been_applied_you_get_PERCENT_discount', { PERCENT: res.percent_off }) });
|
||||||
} else {
|
} else {
|
||||||
mgs.push({ type: 'info', message: t('app.shared.coupon_input.the_coupon_has_been_applied_you_get_AMOUNT_CURRENCY', { AMOUNT: res.amount_off, CURRENCY: 'euro' }) });
|
mgs.push({ type: 'info', message: t('app.shared.coupon_input.the_coupon_has_been_applied_you_get_AMOUNT_CURRENCY', { AMOUNT: res.amount_off, CURRENCY: FormatLib.currencySymbol() }) });
|
||||||
}
|
}
|
||||||
if (res.validity_per_user === 'once') {
|
if (res.validity_per_user === 'once') {
|
||||||
mgs.push({ type: 'warning', message: t('app.shared.coupon_input.coupon_validity_once') });
|
mgs.push({ type: 'warning', message: t('app.shared.coupon_input.coupon_validity_once') });
|
||||||
@ -58,7 +59,10 @@ export const CouponInput: React.FC<CouponInputProps> = ({ user, amount, onChange
|
|||||||
setCoupon(null);
|
setCoupon(null);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setMessages([{ type: 'danger', message: t(`app.shared.coupon_input.unable_to_apply_the_coupon_because_${state}`) }]);
|
setMessages([{ type: 'danger', message: t(`app.shared.coupon_input.unable_to_apply_the_coupon_because_${state}`) }]);
|
||||||
|
onChange(null);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
onChange(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import { ComputePriceResult } from '../../models/price';
|
|||||||
import { Wallet } from '../../models/wallet';
|
import { Wallet } from '../../models/wallet';
|
||||||
import FormatLib from '../../lib/format';
|
import FormatLib from '../../lib/format';
|
||||||
import { Order } from '../../models/order';
|
import { Order } from '../../models/order';
|
||||||
|
import { computePriceWithCoupon } from '../../lib/coupon';
|
||||||
|
|
||||||
export interface GatewayFormProps {
|
export interface GatewayFormProps {
|
||||||
onSubmit: () => void,
|
onSubmit: () => void,
|
||||||
@ -114,7 +115,7 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
|||||||
if (order && order?.user?.id) {
|
if (order && order?.user?.id) {
|
||||||
WalletAPI.getByUser(order.user.id).then((wallet) => {
|
WalletAPI.getByUser(order.user.id).then((wallet) => {
|
||||||
setWallet(wallet);
|
setWallet(wallet);
|
||||||
const p = { price: order.total, price_without_coupon: order.total };
|
const p = { price: computePriceWithCoupon(order.total, order.coupon), price_without_coupon: order.total };
|
||||||
setPrice(p);
|
setPrice(p);
|
||||||
setRemainingPrice(new WalletLib(wallet).computeRemainingPrice(p.price));
|
setRemainingPrice(new WalletLib(wallet).computeRemainingPrice(p.price));
|
||||||
setReady(true);
|
setReady(true);
|
||||||
|
@ -89,7 +89,8 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
|
|||||||
try {
|
try {
|
||||||
let res;
|
let res;
|
||||||
if (order) {
|
if (order) {
|
||||||
res = await CheckoutAPI.payment(order.token);
|
res = await CheckoutAPI.payment(order);
|
||||||
|
res = res.order;
|
||||||
} else {
|
} else {
|
||||||
res = await LocalPaymentAPI.confirmPayment(cart);
|
res = await LocalPaymentAPI.confirmPayment(cart);
|
||||||
}
|
}
|
||||||
@ -120,6 +121,9 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
|
|||||||
* Get the type of the main item in the cart compile
|
* Get the type of the main item in the cart compile
|
||||||
*/
|
*/
|
||||||
const mainItemType = (): string => {
|
const mainItemType = (): string => {
|
||||||
|
if (order) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
return Object.keys(cart.items[0])[0];
|
return Object.keys(cart.items[0])[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export const PayzenForm: React.FC<PayzenFormProps> = ({ onSubmit, onSuccess, onE
|
|||||||
} else if (paymentSchedule) {
|
} else if (paymentSchedule) {
|
||||||
return await PayzenAPI.chargeCreateToken(cart, customer);
|
return await PayzenAPI.chargeCreateToken(cart, customer);
|
||||||
} else if (order) {
|
} else if (order) {
|
||||||
const res = await CheckoutAPI.payment(order.token);
|
const res = await CheckoutAPI.payment(order);
|
||||||
return res.payment as CreateTokenResponse;
|
return res.payment as CreateTokenResponse;
|
||||||
} else {
|
} else {
|
||||||
return await PayzenAPI.chargeCreatePayment(cart, customer);
|
return await PayzenAPI.chargeCreatePayment(cart, customer);
|
||||||
@ -97,7 +97,7 @@ export const PayzenForm: React.FC<PayzenFormProps> = ({ onSubmit, onSuccess, onE
|
|||||||
if (paymentSchedule) {
|
if (paymentSchedule) {
|
||||||
return await PayzenAPI.confirmPaymentSchedule(event.clientAnswer.orderDetails.orderId, transaction.uuid, cart);
|
return await PayzenAPI.confirmPaymentSchedule(event.clientAnswer.orderDetails.orderId, transaction.uuid, cart);
|
||||||
} else if (order) {
|
} else if (order) {
|
||||||
const res = await CheckoutAPI.confirmPayment(order.token, event.clientAnswer.orderDetails.orderId);
|
const res = await CheckoutAPI.confirmPayment(order, event.clientAnswer.orderDetails.orderId);
|
||||||
return res.order;
|
return res.order;
|
||||||
} else {
|
} else {
|
||||||
return await PayzenAPI.confirm(event.clientAnswer.orderDetails.orderId, cart);
|
return await PayzenAPI.confirm(event.clientAnswer.orderDetails.orderId, cart);
|
||||||
|
@ -12,6 +12,7 @@ import { CardPaymentModal } from '../card-payment-modal';
|
|||||||
import PriceAPI from '../../../api/price';
|
import PriceAPI from '../../../api/price';
|
||||||
import { ComputePriceResult } from '../../../models/price';
|
import { ComputePriceResult } from '../../../models/price';
|
||||||
import { Order } from '../../../models/order';
|
import { Order } from '../../../models/order';
|
||||||
|
import { computePriceWithCoupon } from '../../../lib/coupon';
|
||||||
|
|
||||||
interface PaymentModalProps {
|
interface PaymentModalProps {
|
||||||
isOpen: boolean,
|
isOpen: boolean,
|
||||||
@ -47,7 +48,7 @@ export const PaymentModal: React.FC<PaymentModalProps> = ({ isOpen, toggleModal,
|
|||||||
// refresh the price when the cart changes
|
// refresh the price when the cart changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (order) {
|
if (order) {
|
||||||
setPrice({ price: order.total, price_without_coupon: order.total });
|
setPrice({ price: computePriceWithCoupon(order.total, order.coupon), price_without_coupon: order.total });
|
||||||
} else {
|
} else {
|
||||||
PriceAPI.compute(cart).then(price => {
|
PriceAPI.compute(cart).then(price => {
|
||||||
setPrice(price);
|
setPrice(price);
|
||||||
|
@ -44,7 +44,7 @@ export const StripeForm: React.FC<GatewayFormProps> = ({ onSubmit, onSuccess, on
|
|||||||
try {
|
try {
|
||||||
if (!paymentSchedule) {
|
if (!paymentSchedule) {
|
||||||
if (order) {
|
if (order) {
|
||||||
const res = await CheckoutAPI.payment(order.token, paymentMethod.id);
|
const res = await CheckoutAPI.payment(order, paymentMethod.id);
|
||||||
if (res.payment) {
|
if (res.payment) {
|
||||||
await handleServerConfirmation(res.payment as PaymentConfirmation);
|
await handleServerConfirmation(res.payment as PaymentConfirmation);
|
||||||
} else {
|
} else {
|
||||||
@ -90,7 +90,7 @@ export const StripeForm: React.FC<GatewayFormProps> = ({ onSubmit, onSuccess, on
|
|||||||
// The PaymentIntent can be confirmed again on the server
|
// The PaymentIntent can be confirmed again on the server
|
||||||
try {
|
try {
|
||||||
if (order) {
|
if (order) {
|
||||||
const confirmation = await CheckoutAPI.confirmPayment(order.token, result.paymentIntent.id);
|
const confirmation = await CheckoutAPI.confirmPayment(order, result.paymentIntent.id);
|
||||||
await handleServerConfirmation(confirmation.order);
|
await handleServerConfirmation(confirmation.order);
|
||||||
} else {
|
} else {
|
||||||
const confirmation = await StripeAPI.confirmIntent(result.paymentIntent.id, cart);
|
const confirmation = await StripeAPI.confirmIntent(result.paymentIntent.id, cart);
|
||||||
|
@ -4,12 +4,24 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
Application.Controllers.controller('CartController', ['$scope', 'CSRF', 'growl', '$state',
|
Application.Controllers.controller('CartController', ['$scope', 'CSRF', 'growl',
|
||||||
function ($scope, CSRF, growl, $state) {
|
function ($scope, CSRF, growl) {
|
||||||
/* PRIVATE SCOPE */
|
/* PRIVATE SCOPE */
|
||||||
|
|
||||||
/* PUBLIC SCOPE */
|
/* PUBLIC SCOPE */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the modal dialog allowing the user to log into the system
|
||||||
|
*/
|
||||||
|
$scope.userLogin = function () {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!$scope.isAuthenticated()) {
|
||||||
|
$scope.login();
|
||||||
|
$scope.$apply();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback triggered in case of error
|
* Callback triggered in case of error
|
||||||
*/
|
*/
|
||||||
|
13
app/frontend/src/javascript/lib/coupon.ts
Normal file
13
app/frontend/src/javascript/lib/coupon.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Coupon } from '../models/coupon';
|
||||||
|
|
||||||
|
export const computePriceWithCoupon = (price: number, coupon?: Coupon): number => {
|
||||||
|
if (!coupon) {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
if (coupon.type === 'percent_off') {
|
||||||
|
return price - (price * coupon.percent_off / 100.00);
|
||||||
|
} else if (coupon.type === 'amount_off' && price > coupon.amount_off) {
|
||||||
|
return price - coupon.amount_off;
|
||||||
|
}
|
||||||
|
return price;
|
||||||
|
};
|
@ -32,4 +32,11 @@ export default class FormatLib {
|
|||||||
static price = (price: number): string => {
|
static price = (price: number): string => {
|
||||||
return new Intl.NumberFormat(Fablab.intl_locale, { style: 'currency', currency: Fablab.intl_currency }).format(price);
|
return new Intl.NumberFormat(Fablab.intl_locale, { style: 'currency', currency: Fablab.intl_currency }).format(price);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return currency symbol for currency setting
|
||||||
|
*/
|
||||||
|
static currencySymbol = (): string => {
|
||||||
|
return new Intl.NumberFormat('fr', { style: 'currency', currency: Fablab.intl_currency }).formatToParts()[2].value;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
import { TDateISO } from '../typings/date-iso';
|
import { TDateISO } from '../typings/date-iso';
|
||||||
import { PaymentConfirmation } from './payment';
|
import { PaymentConfirmation } from './payment';
|
||||||
import { CreateTokenResponse } from './payzen';
|
import { CreateTokenResponse } from './payzen';
|
||||||
import { User } from './user';
|
import { UserRole } from './user';
|
||||||
|
import { Coupon } from './coupon';
|
||||||
|
|
||||||
export interface Order {
|
export interface Order {
|
||||||
id: number,
|
id: number,
|
||||||
token: string,
|
token: string,
|
||||||
statistic_profile_id?: number,
|
statistic_profile_id?: number,
|
||||||
user?: User,
|
user?: {
|
||||||
|
id: number,
|
||||||
|
role: UserRole
|
||||||
|
name?: string,
|
||||||
|
},
|
||||||
operator_profile_id?: number,
|
operator_profile_id?: number,
|
||||||
reference?: string,
|
reference?: string,
|
||||||
state?: string,
|
state?: string,
|
||||||
payment_state?: string,
|
payment_state?: string,
|
||||||
total?: number,
|
total?: number,
|
||||||
|
coupon?: Coupon,
|
||||||
created_at?: TDateISO,
|
created_at?: TDateISO,
|
||||||
order_items_attributes: Array<{
|
order_items_attributes: Array<{
|
||||||
id: number,
|
id: number,
|
||||||
|
@ -15,5 +15,5 @@
|
|||||||
|
|
||||||
|
|
||||||
<section class="m-lg">
|
<section class="m-lg">
|
||||||
<store-cart current-user="currentUser" on-error="onError" on-success="onSuccess" />
|
<store-cart current-user="currentUser" user-login="userLogin" on-error="onError" on-success="onSuccess" />
|
||||||
</section>
|
</section>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
class Coupon < ApplicationRecord
|
class Coupon < ApplicationRecord
|
||||||
has_many :invoices
|
has_many :invoices
|
||||||
has_many :payment_schedule
|
has_many :payment_schedule
|
||||||
|
has_many :orders
|
||||||
|
|
||||||
after_create :create_gateway_coupon
|
after_create :create_gateway_coupon
|
||||||
before_destroy :delete_gateway_coupon
|
before_destroy :delete_gateway_coupon
|
||||||
@ -82,7 +83,7 @@ class Coupon < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def users
|
def users
|
||||||
invoices.map(&:user)
|
invoices.map(&:user).concat(orders.map(&:user)).uniq(&:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def users_ids
|
def users_ids
|
||||||
@ -104,5 +105,4 @@ class Coupon < ApplicationRecord
|
|||||||
def delete_gateway_coupon
|
def delete_gateway_coupon
|
||||||
PaymentGatewayService.new.delete_coupon(id)
|
PaymentGatewayService.new.delete_coupon(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Order is a model for the user hold information of order
|
# Order is a model for the user hold information of order
|
||||||
class Order < ApplicationRecord
|
class Order < PaymentDocument
|
||||||
belongs_to :statistic_profile
|
belongs_to :statistic_profile
|
||||||
belongs_to :operator_profile, class_name: 'InvoicingProfile'
|
belongs_to :operator_profile, class_name: 'InvoicingProfile'
|
||||||
|
belongs_to :coupon
|
||||||
has_many :order_items, dependent: :destroy
|
has_many :order_items, dependent: :destroy
|
||||||
|
has_one :payment_gateway_object, as: :item
|
||||||
|
|
||||||
ALL_STATES = %w[cart in_progress ready canceled return].freeze
|
ALL_STATES = %w[cart in_progress ready canceled return].freeze
|
||||||
enum state: ALL_STATES.zip(ALL_STATES).to_h
|
enum state: ALL_STATES.zip(ALL_STATES).to_h
|
||||||
@ -14,8 +16,19 @@ class Order < ApplicationRecord
|
|||||||
|
|
||||||
validates :token, :state, presence: true
|
validates :token, :state, presence: true
|
||||||
|
|
||||||
def set_wallet_transaction(amount, transaction_id)
|
before_create :add_environment
|
||||||
self.wallet_amount = amount
|
|
||||||
self.wallet_transaction_id = transaction_id
|
delegate :user, to: :statistic_profile
|
||||||
|
|
||||||
|
def footprint_children
|
||||||
|
order_items
|
||||||
|
end
|
||||||
|
|
||||||
|
def paid_by_card?
|
||||||
|
!payment_gateway_object.nil? && payment_method == 'card'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.columns_out_of_footprint
|
||||||
|
%w[payment_method]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,6 +15,7 @@ class PaymentGatewayObject < ApplicationRecord
|
|||||||
belongs_to :machine, foreign_type: 'Machine', foreign_key: 'item_id'
|
belongs_to :machine, foreign_type: 'Machine', foreign_key: 'item_id'
|
||||||
belongs_to :space, foreign_type: 'Space', foreign_key: 'item_id'
|
belongs_to :space, foreign_type: 'Space', foreign_key: 'item_id'
|
||||||
belongs_to :training, foreign_type: 'Training', foreign_key: 'item_id'
|
belongs_to :training, foreign_type: 'Training', foreign_key: 'item_id'
|
||||||
|
belongs_to :order, foreign_type: 'Order', foreign_key: 'item_id'
|
||||||
|
|
||||||
belongs_to :payment_gateway_object # some objects may require a reference to another object for remote recovery
|
belongs_to :payment_gateway_object # some objects may require a reference to another object for remote recovery
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ class PaymentSchedule < PaymentDocument
|
|||||||
belongs_to :coupon
|
belongs_to :coupon
|
||||||
belongs_to :invoicing_profile
|
belongs_to :invoicing_profile
|
||||||
belongs_to :statistic_profile
|
belongs_to :statistic_profile
|
||||||
belongs_to :operator_profile, foreign_key: :operator_profile_id, class_name: 'InvoicingProfile'
|
belongs_to :operator_profile, class_name: 'InvoicingProfile'
|
||||||
|
|
||||||
has_many :payment_schedule_items
|
has_many :payment_schedule_items
|
||||||
has_many :payment_gateway_objects, as: :item
|
has_many :payment_gateway_objects, as: :item
|
||||||
@ -61,9 +61,7 @@ class PaymentSchedule < PaymentDocument
|
|||||||
payment_schedule_objects.find_by(main: true)
|
payment_schedule_objects.find_by(main: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user
|
delegate :user, to: :invoicing_profile
|
||||||
invoicing_profile.user
|
|
||||||
end
|
|
||||||
|
|
||||||
# for debug & used by rake task "fablab:maintenance:regenerate_schedules"
|
# for debug & used by rake task "fablab:maintenance:regenerate_schedules"
|
||||||
def regenerate_pdf
|
def regenerate_pdf
|
||||||
|
@ -6,10 +6,6 @@ class CartPolicy < ApplicationPolicy
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_customer?
|
|
||||||
user.privileged?
|
|
||||||
end
|
|
||||||
|
|
||||||
%w[add_item remove_item set_quantity].each do |action|
|
%w[add_item remove_item set_quantity].each do |action|
|
||||||
define_method "#{action}?" do
|
define_method "#{action}?" do
|
||||||
return user.privileged? || (record.statistic_profile_id == user.statistic_profile.id) if user
|
return user.privileged? || (record.statistic_profile_id == user.statistic_profile.id) if user
|
||||||
|
10
app/policies/checkout_policy.rb
Normal file
10
app/policies/checkout_policy.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Check the access policies for API::CheckoutController
|
||||||
|
class CheckoutPolicy < ApplicationPolicy
|
||||||
|
%w[payment confirm_payment].each do |action|
|
||||||
|
define_method "#{action}?" do
|
||||||
|
return user.privileged? || (record.statistic_profile_id == user.statistic_profile.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,19 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# Provides methods for create cart
|
|
||||||
class Cart::CreateService
|
|
||||||
def call(user)
|
|
||||||
token = GenerateTokenService.new.call(Order)
|
|
||||||
order_param = {
|
|
||||||
token: token,
|
|
||||||
state: 'cart',
|
|
||||||
total: 0
|
|
||||||
}
|
|
||||||
if user
|
|
||||||
order_param[:statistic_profile_id] = user.statistic_profile.id if user.member?
|
|
||||||
|
|
||||||
order_param[:operator_profile_id] = user.invoicing_profile.id if user.privileged?
|
|
||||||
end
|
|
||||||
Order.create!(order_param)
|
|
||||||
end
|
|
||||||
end
|
|
60
app/services/cart/find_or_create_service.rb
Normal file
60
app/services/cart/find_or_create_service.rb
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Provides methods for find or create a cart
|
||||||
|
class Cart::FindOrCreateService
|
||||||
|
def call(order_token, user)
|
||||||
|
order = Order.find_by(token: order_token, state: 'cart')
|
||||||
|
|
||||||
|
if order && user && ((user.member? && order.statistic_profile_id.present? && order.statistic_profile_id != user.statistic_profile.id) ||
|
||||||
|
(user.privileged? && order.operator_profile_id.present? && order.operator_profile_id != user.invoicing_profile.id))
|
||||||
|
order = nil
|
||||||
|
end
|
||||||
|
order = nil if order && !user && order.statistic_profile_id.present?
|
||||||
|
if order && order.statistic_profile_id.present? && Order.where(statistic_profile_id: order.statistic_profile_id,
|
||||||
|
payment_state: 'paid').where('created_at > ?', order.created_at).last.present?
|
||||||
|
order = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if order.nil?
|
||||||
|
if user&.member?
|
||||||
|
last_paid_order = Order.where(statistic_profile_id: user.statistic_profile.id,
|
||||||
|
payment_state: 'paid').last
|
||||||
|
order = if last_paid_order
|
||||||
|
Order.where(statistic_profile_id: user.statistic_profile.id,
|
||||||
|
state: 'cart').where('created_at > ?', last_paid_order.created_at).last
|
||||||
|
else
|
||||||
|
Order.where(statistic_profile_id: user.statistic_profile.id, state: 'cart').last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if user&.privileged?
|
||||||
|
last_paid_order = Order.where(operator_profile_id: user.invoicing_profile.id,
|
||||||
|
payment_state: 'paid').last
|
||||||
|
order = if last_paid_order
|
||||||
|
Order.where(operator_profile_id: user.invoicing_profile.id,
|
||||||
|
state: 'cart').where('created_at > ?', last_paid_order.created_at).last
|
||||||
|
else
|
||||||
|
Order.where(operator_profile_id: user.invoicing_profile.id, state: 'cart').last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if order
|
||||||
|
order.update(statistic_profile_id: user.statistic_profile.id) if order.statistic_profile_id.nil? && user&.member?
|
||||||
|
order.update(operator_profile_id: user.invoicing_profile.id) if order.operator_profile_id.nil? && user&.privileged?
|
||||||
|
return order
|
||||||
|
end
|
||||||
|
|
||||||
|
token = GenerateTokenService.new.call(Order)
|
||||||
|
order_param = {
|
||||||
|
token: token,
|
||||||
|
state: 'cart',
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
if user
|
||||||
|
order_param[:statistic_profile_id] = user.statistic_profile.id if user.member?
|
||||||
|
|
||||||
|
order_param[:operator_profile_id] = user.invoicing_profile.id if user.privileged?
|
||||||
|
end
|
||||||
|
Order.create!(order_param)
|
||||||
|
end
|
||||||
|
end
|
@ -1,10 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# Provides methods for admin set customer to order
|
|
||||||
class Cart::SetCustomerService
|
|
||||||
def call(order, user_id)
|
|
||||||
user = User.find(user_id)
|
|
||||||
order.update(statistic_profile_id: user.statistic_profile.id)
|
|
||||||
order.reload
|
|
||||||
end
|
|
||||||
end
|
|
@ -4,31 +4,35 @@
|
|||||||
class Checkout::PaymentService
|
class Checkout::PaymentService
|
||||||
require 'pay_zen/helper'
|
require 'pay_zen/helper'
|
||||||
require 'stripe/helper'
|
require 'stripe/helper'
|
||||||
|
include Payments::PaymentConcern
|
||||||
|
|
||||||
def payment(order, operator, payment_id = '')
|
def payment(order, operator, coupon_code, payment_id = '')
|
||||||
raise Cart::OutStockError unless Orders::OrderService.new.in_stock?(order, 'external')
|
raise Cart::OutStockError unless Orders::OrderService.new.in_stock?(order, 'external')
|
||||||
|
|
||||||
raise Cart::InactiveProductError unless Orders::OrderService.new.all_products_is_active?(order)
|
raise Cart::InactiveProductError unless Orders::OrderService.new.all_products_is_active?(order)
|
||||||
|
|
||||||
if operator.member?
|
CouponService.new.validate(coupon_code, order.statistic_profile.user)
|
||||||
|
|
||||||
|
amount = debit_amount(order)
|
||||||
|
if operator.privileged? || amount.zero?
|
||||||
|
Payments::LocalService.new.payment(order, coupon_code)
|
||||||
|
elsif operator.member?
|
||||||
if Stripe::Helper.enabled?
|
if Stripe::Helper.enabled?
|
||||||
Payments::StripeService.new.payment(order, payment_id)
|
Payments::StripeService.new.payment(order, coupon_code, payment_id)
|
||||||
elsif PayZen::Helper.enabled?
|
elsif PayZen::Helper.enabled?
|
||||||
Payments::PayzenService.new.payment(order)
|
Payments::PayzenService.new.payment(order, coupon_code)
|
||||||
else
|
else
|
||||||
raise Error('Bad gateway or online payment is disabled')
|
raise Error('Bad gateway or online payment is disabled')
|
||||||
end
|
end
|
||||||
elsif operator.privileged?
|
|
||||||
Payments::LocalService.new.payment(order)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_payment(order, operator, payment_id = '')
|
def confirm_payment(order, operator, coupon_code, payment_id = '')
|
||||||
if operator.member?
|
if operator.member?
|
||||||
if Stripe::Helper.enabled?
|
if Stripe::Helper.enabled?
|
||||||
Payments::StripeService.new.confirm_payment(order, payment_id)
|
Payments::StripeService.new.confirm_payment(order, coupon_code, payment_id)
|
||||||
elsif PayZen::Helper.enabled?
|
elsif PayZen::Helper.enabled?
|
||||||
Payments::PayzenService.new.confirm_payment(order, payment_id)
|
Payments::PayzenService.new.confirm_payment(order, coupon_code, payment_id)
|
||||||
else
|
else
|
||||||
raise Error('Bad gateway or online payment is disabled')
|
raise Error('Bad gateway or online payment is disabled')
|
||||||
end
|
end
|
||||||
|
@ -32,6 +32,18 @@ class PaymentDocumentService
|
|||||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# remove information about refunds (R[text])
|
||||||
|
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||||
|
# remove information about payment schedule (S[text])
|
||||||
|
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
||||||
|
elsif document.is_a? Order
|
||||||
|
# information about online selling (X[text])
|
||||||
|
if document.paid_by_card?
|
||||||
|
reference.gsub!(/X\[([^\]]+)\]/, '\1')
|
||||||
|
else
|
||||||
|
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
# remove information about refunds (R[text])
|
# remove information about refunds (R[text])
|
||||||
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||||
# remove information about payment schedule (S[text])
|
# remove information about payment schedule (S[text])
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
class Payments::LocalService
|
class Payments::LocalService
|
||||||
include Payments::PaymentConcern
|
include Payments::PaymentConcern
|
||||||
|
|
||||||
def payment(order)
|
def payment(order, coupon_code)
|
||||||
o = payment_success(order, 'local')
|
o = payment_success(order, coupon_code, 'local')
|
||||||
{ order: o }
|
{ order: o }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,19 +9,34 @@ module Payments::PaymentConcern
|
|||||||
wallet_amount >= total_amount ? total_amount : wallet_amount
|
wallet_amount >= total_amount ? total_amount : wallet_amount
|
||||||
end
|
end
|
||||||
|
|
||||||
def debit_amount(order)
|
def debit_amount(order, coupon_code = nil)
|
||||||
total = order.total
|
total = CouponService.new.apply(order.total, coupon_code, order.statistic_profile.user)
|
||||||
wallet_debit = get_wallet_debit(order.statistic_profile.user, total)
|
wallet_debit = get_wallet_debit(order.statistic_profile.user, total)
|
||||||
total - wallet_debit
|
total - wallet_debit
|
||||||
end
|
end
|
||||||
|
|
||||||
def payment_success(order, payment_method = '')
|
def payment_success(order, coupon_code, payment_method = '', payment_id = nil, payment_type = nil)
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
|
order.paid_total = debit_amount(order, coupon_code)
|
||||||
|
coupon = Coupon.find_by(code: coupon_code)
|
||||||
|
order.coupon_id = coupon.id if coupon
|
||||||
WalletService.debit_user_wallet(order, order.statistic_profile.user)
|
WalletService.debit_user_wallet(order, order.statistic_profile.user)
|
||||||
order.update(state: 'in_progress', payment_state: 'paid', payment_method: payment_method)
|
order.operator_profile_id = order.statistic_profile.user.invoicing_profile.id if order.operator_profile.nil?
|
||||||
|
order.payment_method = if order.total == order.wallet_amount
|
||||||
|
'wallet'
|
||||||
|
else
|
||||||
|
payment_method
|
||||||
|
end
|
||||||
|
order.state = 'in_progress'
|
||||||
|
order.payment_state = 'paid'
|
||||||
|
if payment_id && payment_type
|
||||||
|
order.payment_gateway_object = PaymentGatewayObject.new(gateway_object_id: payment_id, gateway_object_type: payment_type)
|
||||||
|
end
|
||||||
order.order_items.each do |item|
|
order.order_items.each do |item|
|
||||||
ProductService.update_stock(item.orderable, 'external', 'sold', -item.quantity, item.id)
|
ProductService.update_stock(item.orderable, 'external', 'sold', -item.quantity, item.id)
|
||||||
end
|
end
|
||||||
|
order.reference = order.generate_reference
|
||||||
|
order.save
|
||||||
order.reload
|
order.reload
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,8 +8,8 @@ class Payments::PayzenService
|
|||||||
require 'pay_zen/service'
|
require 'pay_zen/service'
|
||||||
include Payments::PaymentConcern
|
include Payments::PaymentConcern
|
||||||
|
|
||||||
def payment(order)
|
def payment(order, coupon_code)
|
||||||
amount = debit_amount(order)
|
amount = debit_amount(order, coupon_code)
|
||||||
|
|
||||||
raise Cart::ZeroPriceError if amount.zero?
|
raise Cart::ZeroPriceError if amount.zero?
|
||||||
|
|
||||||
@ -23,16 +23,16 @@ class Payments::PayzenService
|
|||||||
{ order: order, payment: { formToken: result['answer']['formToken'], orderId: id } }
|
{ order: order, payment: { formToken: result['answer']['formToken'], orderId: id } }
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_payment(order, payment_id)
|
def confirm_payment(order, coupon_code, payment_id)
|
||||||
client = PayZen::Order.new
|
client = PayZen::Order.new
|
||||||
payzen_order = client.get(payment_id, operation_type: 'DEBIT')
|
payzen_order = client.get(payment_id, operation_type: 'DEBIT')
|
||||||
|
|
||||||
if payzen_order['answer']['transactions'].any? { |transaction| transaction['status'] == 'PAID' }
|
if payzen_order['answer']['transactions'].any? { |transaction| transaction['status'] == 'PAID' }
|
||||||
o = payment_success(order, 'card')
|
o = payment_success(order, coupon_code, 'card', payment_id, 'PayZen::Order')
|
||||||
{ order: o }
|
{ order: o }
|
||||||
else
|
else
|
||||||
order.update(payment_state: 'failed')
|
order.update(payment_state: 'failed')
|
||||||
{ order: order, payment_error: payzen_order['answer'] }
|
{ order: order, payment: { error: { statusText: payzen_order['answer'] } } }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,8 +5,8 @@ class Payments::StripeService
|
|||||||
require 'stripe/service'
|
require 'stripe/service'
|
||||||
include Payments::PaymentConcern
|
include Payments::PaymentConcern
|
||||||
|
|
||||||
def payment(order, payment_id)
|
def payment(order, coupon_code, payment_id)
|
||||||
amount = debit_amount(order)
|
amount = debit_amount(order, coupon_code)
|
||||||
|
|
||||||
raise Cart::ZeroPriceError if amount.zero?
|
raise Cart::ZeroPriceError if amount.zero?
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ class Payments::StripeService
|
|||||||
)
|
)
|
||||||
|
|
||||||
if intent&.status == 'succeeded'
|
if intent&.status == 'succeeded'
|
||||||
o = payment_success(order, 'card')
|
o = payment_success(order, coupon_code, 'card', intent.id, intent.class.name)
|
||||||
return { order: o }
|
return { order: o }
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -33,14 +33,14 @@ class Payments::StripeService
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_payment(order, payment_id)
|
def confirm_payment(order, coupon_code, payment_id)
|
||||||
intent = Stripe::PaymentIntent.confirm(payment_id, {}, { api_key: Setting.get('stripe_secret_key') })
|
intent = Stripe::PaymentIntent.confirm(payment_id, {}, { api_key: Setting.get('stripe_secret_key') })
|
||||||
if intent&.status == 'succeeded'
|
if intent&.status == 'succeeded'
|
||||||
o = payment_success(order, 'card')
|
o = payment_success(order, coupon_code, 'card', intent.id, intent.class.name)
|
||||||
{ order: o }
|
{ order: o }
|
||||||
else
|
else
|
||||||
order.update(payment_state: 'failed')
|
order.update(payment_state: 'failed')
|
||||||
{ order: order, payment_error: 'payment failed' }
|
{ order: order, payment: { error: { statusText: 'payment failed' } } }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -23,7 +23,7 @@ class ProductService
|
|||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.update_stock(product, stock_type, reason, quantity, order_item_id)
|
def self.update_stock(product, stock_type, reason, quantity, order_item_id = nil)
|
||||||
remaining_stock = product.stock[stock_type] + quantity
|
remaining_stock = product.stock[stock_type] + quantity
|
||||||
product.product_stock_movements.create(stock_type: stock_type, reason: reason, quantity: quantity, remaining_stock: remaining_stock,
|
product.product_stock_movements.create(stock_type: stock_type, reason: reason, quantity: quantity, remaining_stock: remaining_stock,
|
||||||
date: DateTime.current,
|
date: DateTime.current,
|
||||||
|
@ -159,7 +159,6 @@ Rails.application.routes.draw do
|
|||||||
put 'add_item', on: :collection
|
put 'add_item', on: :collection
|
||||||
put 'remove_item', on: :collection
|
put 'remove_item', on: :collection
|
||||||
put 'set_quantity', on: :collection
|
put 'set_quantity', on: :collection
|
||||||
put 'set_customer', on: :collection
|
|
||||||
end
|
end
|
||||||
resources :checkout, only: %i[] do
|
resources :checkout, only: %i[] do
|
||||||
post 'payment', on: :collection
|
post 'payment', on: :collection
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
class AddFootprintAndEnvironmentToOrder < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :orders, :footprint, :string
|
||||||
|
add_column :orders, :environment, :string
|
||||||
|
end
|
||||||
|
end
|
5
db/migrate/20220826140921_add_coupon_id_to_order.rb
Normal file
5
db/migrate/20220826140921_add_coupon_id_to_order.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddCouponIdToOrder < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_reference :orders, :coupon, index: true, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
5
db/migrate/20220826175129_add_paid_total_to_order.rb
Normal file
5
db/migrate/20220826175129_add_paid_total_to_order.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddPaidTotalToOrder < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :orders, :paid_total, :integer
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2022_08_26_093503) do
|
ActiveRecord::Schema.define(version: 2022_08_26_175129) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "fuzzystrmatch"
|
enable_extension "fuzzystrmatch"
|
||||||
@ -471,6 +471,11 @@ ActiveRecord::Schema.define(version: 2022_08_26_093503) do
|
|||||||
t.integer "wallet_amount"
|
t.integer "wallet_amount"
|
||||||
t.integer "wallet_transaction_id"
|
t.integer "wallet_transaction_id"
|
||||||
t.string "payment_method"
|
t.string "payment_method"
|
||||||
|
t.string "footprint"
|
||||||
|
t.string "environment"
|
||||||
|
t.bigint "coupon_id"
|
||||||
|
t.integer "paid_total"
|
||||||
|
t.index ["coupon_id"], name: "index_orders_on_coupon_id"
|
||||||
t.index ["operator_profile_id"], name: "index_orders_on_operator_profile_id"
|
t.index ["operator_profile_id"], name: "index_orders_on_operator_profile_id"
|
||||||
t.index ["statistic_profile_id"], name: "index_orders_on_statistic_profile_id"
|
t.index ["statistic_profile_id"], name: "index_orders_on_statistic_profile_id"
|
||||||
end
|
end
|
||||||
@ -1165,6 +1170,7 @@ ActiveRecord::Schema.define(version: 2022_08_26_093503) do
|
|||||||
add_foreign_key "invoices", "wallet_transactions"
|
add_foreign_key "invoices", "wallet_transactions"
|
||||||
add_foreign_key "invoicing_profiles", "users"
|
add_foreign_key "invoicing_profiles", "users"
|
||||||
add_foreign_key "order_items", "orders"
|
add_foreign_key "order_items", "orders"
|
||||||
|
add_foreign_key "orders", "coupons"
|
||||||
add_foreign_key "orders", "invoicing_profiles", column: "operator_profile_id"
|
add_foreign_key "orders", "invoicing_profiles", column: "operator_profile_id"
|
||||||
add_foreign_key "orders", "statistic_profiles"
|
add_foreign_key "orders", "statistic_profiles"
|
||||||
add_foreign_key "organizations", "invoicing_profiles"
|
add_foreign_key "organizations", "invoicing_profiles"
|
||||||
|
Loading…
Reference in New Issue
Block a user