1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-07 01:54:16 +01:00

handle successfull stripe payment

This commit is contained in:
Sylvain 2020-12-07 13:49:11 +01:00
parent 1e43dc9518
commit 96b1cfcbc7
5 changed files with 33 additions and 40 deletions

View File

@ -4,10 +4,11 @@ import { PaymentMethod } from "@stripe/stripe-js";
import PaymentAPI from '../api/payment'; import PaymentAPI from '../api/payment';
import { CartItems, PaymentConfirmation } from '../models/payment'; import { CartItems, PaymentConfirmation } from '../models/payment';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Reservation } from '../models/reservation';
interface StripeFormProps { interface StripeFormProps {
onSubmit: () => void, onSubmit: () => void,
onSuccess: (paymentMethod: PaymentMethod) => void, onSuccess: (result: PaymentMethod|PaymentConfirmation) => void,
onError: (message: string) => void, onError: (message: string) => void,
className?: string, className?: string,
processPayment?: boolean, processPayment?: boolean,
@ -48,7 +49,7 @@ export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onE
if (processPayment) { if (processPayment) {
// process the full payment pipeline, including SCA validation // process the full payment pipeline, including SCA validation
const res = await PaymentAPI.confirm(paymentMethod.id, cartItems); const res = await PaymentAPI.confirm(paymentMethod.id, cartItems);
await handleServerConfirmation(res, paymentMethod); await handleServerConfirmation(res);
} else { } else {
// we don't want to process the payment, only return the payment method // we don't want to process the payment, only return the payment method
onSuccess(paymentMethod); onSuccess(paymentMethod);
@ -58,8 +59,12 @@ export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onE
/** /**
* Process the server response about the Strong-customer authentication (SCA) * Process the server response about the Strong-customer authentication (SCA)
* @param response can be a PaymentConfirmation, or a Reservation (if the reservation succeeded), or a Subscription (if the subscription succeeded)
* @see app/controllers/api/payments_controller.rb#on_reservation_success
* @see app/controllers/api/payments_controller.rb#on_subscription_success
* @see app/controllers/api/payments_controller.rb#generate_payment_response
*/ */
const handleServerConfirmation = async (response: PaymentConfirmation, paymentMethod: PaymentMethod) => { const handleServerConfirmation = async (response: PaymentConfirmation|any) => {
if (response.error) { if (response.error) {
if (response.error.statusText) { if (response.error.statusText) {
onError(response.error.statusText); onError(response.error.statusText);
@ -76,13 +81,13 @@ export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onE
// The PaymentIntent can be confirmed again on the server // The PaymentIntent can be confirmed again on the server
try { try {
const confirmation = await PaymentAPI.confirm(result.paymentIntent.id, cartItems); const confirmation = await PaymentAPI.confirm(result.paymentIntent.id, cartItems);
await handleServerConfirmation(confirmation, paymentMethod); await handleServerConfirmation(confirmation);
} catch (e) { } catch (e) {
onError(e); onError(e);
} }
} }
} else { } else {
onSuccess(paymentMethod); onSuccess(response);
} }
} }

View File

@ -23,7 +23,7 @@ import { StripeForm } from './stripe-form';
import stripeLogo from '../../../images/powered_by_stripe.png'; import stripeLogo from '../../../images/powered_by_stripe.png';
import mastercardLogo from '../../../images/mastercard.png'; import mastercardLogo from '../../../images/mastercard.png';
import visaLogo from '../../../images/visa.png'; import visaLogo from '../../../images/visa.png';
import { CartItems } from '../models/payment'; import { CartItems, PaymentConfirmation } from '../models/payment';
import WalletAPI from '../api/wallet'; import WalletAPI from '../api/wallet';
import PriceAPI from '../api/price'; import PriceAPI from '../api/price';
@ -33,7 +33,7 @@ declare var Fablab: IFablab;
interface StripeModalProps { interface StripeModalProps {
isOpen: boolean, isOpen: boolean,
toggleModal: () => void, toggleModal: () => void,
afterSuccess: (paymentMethod: PaymentMethod) => void, afterSuccess: (result: PaymentMethod|PaymentConfirmation) => void,
cartItems: CartItems, cartItems: CartItems,
currentUser: User, currentUser: User,
schedule: PaymentSchedule, schedule: PaymentSchedule,
@ -140,9 +140,9 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
/** /**
* After sending the form with success, process the resulting payment method * After sending the form with success, process the resulting payment method
*/ */
const handleFormSuccess = async (paymentMethod: PaymentMethod): Promise<void> => { const handleFormSuccess = async (result: PaymentMethod|PaymentConfirmation): Promise<void> => {
setSubmitState(false); setSubmitState(false);
afterSuccess(paymentMethod); afterSuccess(result);
} }
/** /**

View File

@ -735,19 +735,6 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* @param user {Object} user associated with the slot * @param user {Object} user associated with the slot
*/ */
const updateMachineSlot = function (slot, reservation, user) { const updateMachineSlot = function (slot, reservation, user) {
/* TODO, FIXME
machines.js.erb:741 Uncaught (in promise) TypeError: Cannot read property 'slots' of undefined
at updateMachineSlot (machines.js.erb:741)
at machines.js.erb:664
at Object.forEach (angular.js:386)
at Scope.$scope.afterPayment (machines.js.erb:652)
at afterPayment (cart.js:884)
at $scope.afterStripeSuccess (cart.js:338)
at _callee$ (stripe-modal.tsx:145)
at tryCatch (runtime.js:63)
at Generator.invoke [as _invoke] (runtime.js:293)
at Generator.next (runtime.js:118)
*/
angular.forEach(reservation.slots, function (s) { angular.forEach(reservation.slots, function (s) {
if (slot.start.isSame(s.start_at)) { if (slot.start.isSame(s.start_at)) {
slot.slot_id = s.id; slot.slot_id = s.id;

View File

@ -70,13 +70,13 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
// Payment schedule // Payment schedule
$scope.schedule = { $scope.schedule = {
requested_schedule: false, // does the user requests a payment schedule for his subscription requested_schedule: false, // does the user requests a payment schedule for his subscription
payment_schedule: null // the effective computed payment schedule payment_schedule: undefined // the effective computed payment schedule
}; };
// online payments (stripe) // online payments (stripe)
$scope.stripe = { $scope.stripe = {
showModal: false, showModal: false,
cartItems: null cartItems: undefined
}; };
// currently logged-in user // currently logged-in user
@ -327,10 +327,11 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
/** /**
* Invoked atfer a successful Stripe payment * Invoked atfer a successful Stripe payment
* @param result {*} may be a reservation or a subscription
*/ */
$scope.afterStripeSuccess = () => { $scope.afterStripeSuccess = (result) => {
$scope.toggleStripeModal(); $scope.toggleStripeModal();
afterPayment($scope.reservation); afterPayment(result);
}; };
/* PRIVATE SCOPE */ /* PRIVATE SCOPE */
@ -638,9 +639,9 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
/** /**
* Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object * Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
* @param request {{reservation: object}|{subscription: object}} as returned by mkReservation() * @param request {{reservation: *}|{subscription: *}} as returned by mkReservation()
* @param coupon {Object} Coupon as returned from the API * @param coupon {{code: string}} Coupon as returned from the API
* @return {{reservation:Object, subscription: Object, coupon_code:string}} * @return {CartItems}
*/ */
const mkRequestParams = function (request, coupon) { const mkRequestParams = function (request, coupon) {
return Object.assign({ return Object.assign({
@ -870,28 +871,28 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
/** /**
* Actions to run after the payment was successful * Actions to run after the payment was successful
* @param reservation {Object} may be a reservation or a subscription * @param paymentResult {*} may be a reservation or a subscription
*/ */
const afterPayment = function (reservation) { const afterPayment = function (paymentResult) {
// we set the cart content as 'paid' to display a summary of the transaction // we set the cart content as 'paid' to display a summary of the transaction
$scope.events.paid = $scope.events.reserved; $scope.events.paid = $scope.events.reserved;
$scope.amountPaid = $scope.amountTotal; $scope.amountPaid = $scope.amountTotal;
// we call the external callback if present // we call the external callback if present
if (typeof $scope.afterPayment === 'function') { $scope.afterPayment(reservation); } if (typeof $scope.afterPayment === 'function') { $scope.afterPayment(paymentResult); }
// we reset the coupon, and the cart content, and we unselect the slot // we reset the coupon, and the cart content, and we unselect the slot
$scope.coupon.applied = null; $scope.coupon.applied = undefined;
if ($scope.slot) { if ($scope.slot) {
// reservation (+ subscription) // reservation (+ subscription)
$scope.slot = null; $scope.slot = undefined;
$scope.events.reserved = []; $scope.events.reserved = [];
} else { } else {
// subscription only // subscription only
$scope.events = {}; $scope.events = {};
} }
$scope.paidPlan = $scope.selectedPlan; $scope.paidPlan = $scope.selectedPlan;
$scope.selectedPlan = null; $scope.selectedPlan = undefined;
$scope.schedule.requested_schedule = false; $scope.schedule.requested_schedule = false;
$scope.schedule.payment_schedule = null; $scope.schedule.payment_schedule = undefined;
}; };
/** /**

View File

@ -6,7 +6,7 @@ export interface PaymentConfirmation {
success?: boolean, success?: boolean,
error?: { error?: {
statusText: string statusText: string
}, }
} }
export enum PaymentMethod { export enum PaymentMethod {
@ -15,12 +15,12 @@ export enum PaymentMethod {
} }
export interface CartItems { export interface CartItems {
reservation: Reservation, reservation?: Reservation,
subscription: { subscription?: {
plan_id: number, plan_id: number,
user_id: number, user_id: number,
payment_schedule: boolean, payment_schedule: boolean,
payment_method: PaymentMethod payment_method: PaymentMethod
}, },
coupon_code: string coupon_code?: string
} }