diff --git a/app/frontend/packs/application.js.erb b/app/frontend/packs/application.js.erb
index 0c3ef4dd8..d4c1eb9b3 100644
--- a/app/frontend/packs/application.js.erb
+++ b/app/frontend/packs/application.js.erb
@@ -91,6 +91,7 @@ importAll(require.context('../src/javascript/controllers/', true, /.*/));
 importAll(require.context('../src/javascript/services/', true, /.*/));
 importAll(require.context('../src/javascript/directives/', true, /.*/));
 importAll(require.context('../src/javascript/filters/', true, /.*/));
+importAll(require.context('../src/javascript/typings/', true, /.*/));
 
 importAll(require.context('../images', true));
 importAll(require.context('../templates', true));
diff --git a/app/frontend/src/javascript/components/stripe-form.tsx b/app/frontend/src/javascript/components/stripe-form.tsx
new file mode 100644
index 000000000..10c785a75
--- /dev/null
+++ b/app/frontend/src/javascript/components/stripe-form.tsx
@@ -0,0 +1,64 @@
+import React, { FormEvent } from 'react';
+import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js';
+import { PaymentMethod } from "@stripe/stripe-js";
+
+interface StripeFormProps {
+  onSubmit: () => void,
+  onSuccess: (paymentMethod: PaymentMethod) => void,
+  onError: (message: string) => void,
+}
+
+export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onError, children }) => {
+
+  const stripe = useStripe();
+  const elements = useElements();
+
+  /**
+   * Handle the submission of the form. Depending on the configuration, it will create the payment method on Stripe,
+   * or it will process a payment with the inputted card.
+   */
+  const handleSubmit = async (event: FormEvent): Promise<void> => {
+    event.preventDefault();
+    onSubmit();
+
+    // Stripe.js has not loaded yet
+    if (!stripe || !elements) { return; }
+
+    const cardElement = elements.getElement(CardElement);
+    const { error, paymentMethod } = await stripe.createPaymentMethod({
+      type: 'card',
+      card: cardElement,
+    });
+
+    if (error) {
+      onError(error.message);
+    } else {
+      onSuccess(paymentMethod);
+    }
+  }
+
+  /**
+   * Options for the Stripe's card input
+   */
+  const cardOptions = {
+    style: {
+      base: {
+        fontSize: '16px',
+        color: '#424770',
+        '::placeholder': { color: '#aab7c4' }
+      },
+      invalid: {
+        color: '#9e2146',
+        iconColor: '#9e2146'
+      },
+    },
+    hidePostalCode: true
+  };
+
+  return (
+    <form onSubmit={handleSubmit} id="stripe-form">
+      <CardElement options={cardOptions} />
+      {children}
+    </form>
+  );
+}
diff --git a/app/frontend/src/javascript/components/stripe-modal.tsx b/app/frontend/src/javascript/components/stripe-modal.tsx
index 90b5f209c..ef048479e 100644
--- a/app/frontend/src/javascript/components/stripe-modal.tsx
+++ b/app/frontend/src/javascript/components/stripe-modal.tsx
@@ -2,8 +2,7 @@
  * This component enables the user to input his card data.
  */
 
-import React, { ChangeEvent, FormEvent, ReactNode, useState } from 'react';
-import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js';
+import React, { ChangeEvent, ReactNode, useEffect, useState } from 'react';
 import { react2angular } from 'react2angular';
 import { Loader } from './loader';
 import { IApplication } from '../models/application';
@@ -19,6 +18,12 @@ import CustomAssetAPI from '../api/custom-asset';
 import { CustomAssetName } from '../models/custom-asset';
 import { PaymentSchedule } from '../models/payment-schedule';
 import { IFablab } from '../models/fablab';
+import WalletLib from '../lib/wallet';
+import { StripeForm } from './stripe-form';
+
+import stripeLogo from '../../../images/powered_by_stripe.png';
+import mastercardLogo from '../../../images/mastercard.png';
+import visaLogo from '../../../images/visa.png';
 
 declare var Application: IApplication;
 declare var Fablab: IFablab;
@@ -31,16 +36,22 @@ interface StripeModalProps {
   currentUser: User,
   wallet: Wallet,
   price: number,
-  remainingPrice: number,
   schedule: PaymentSchedule
 }
 
 const cgvFile = CustomAssetAPI.get(CustomAssetName.CgvFile);
 
-const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuccess, reservation, currentUser, wallet, price, remainingPrice, schedule }) => {
+const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuccess, reservation, currentUser, wallet, price, schedule }) => {
+  const [remainingPrice, setRemainingPrice] = useState(0);
+
+  /**
+   * Refresh the remaining price on each display
+   */
+  useEffect(() => {
+    const wLib = new WalletLib(wallet);
+    setRemainingPrice(wLib.computeRemainingPrice(price));
+  })
 
-  const stripe = useStripe();
-  const elements = useElements();
   const { t } = useTranslation('shared');
 
   const cgv = cgvFile.read();
@@ -49,32 +60,6 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
   const [submitState, setSubmitState] = useState(false);
   const [tos, setTos] = useState(false);
 
-  /**
-   * Handle the submission of the form. Depending on the configuration, it will create the payment method on Stripe,
-   * or it will process a payment with the inputted card.
-   */
-  const handleSubmit = async (event: FormEvent): Promise<void> => {
-    event.preventDefault();
-    setSubmitState(true);
-
-    // Stripe.js has not loaded yet
-    if (!stripe || !elements) { return; }
-
-    const cardElement = elements.getElement(CardElement);
-    const { error, paymentMethod } = await stripe.createPaymentMethod({
-      type: 'card',
-      card: cardElement,
-    });
-
-    setSubmitState(false);
-    if (error) {
-      setErrors(error.message);
-    } else {
-      setErrors(null);
-      afterSuccess(paymentMethod);
-    }
-  }
-
   /**
    * Check if there is currently an error to display
    */
@@ -82,13 +67,6 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
     return errors !== null;
   }
 
-  /**
-   * Change the state of the submit button: enabled/disabled
-   */
-  const toggleSubmitButton = (): void => {
-    setSubmitState(!submitState);
-  }
-
   /**
    * Check if the Terms of Sales document is set
    */
@@ -120,7 +98,6 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
   const submitButton = (): ReactNode => {
     return (
       <button type="submit"
-              onClick={toggleSubmitButton}
               disabled={submitState}
               form="stripe-form"
               className="validate-btn">
@@ -129,59 +106,60 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
     );
   }
 
-  /**
-   * Options for the Stripe's card input
-   */
-  const cardOptions = {
-    style: {
-      base: {
-        fontSize: '16px',
-        color: '#424770',
-        '::placeholder': { color: '#aab7c4' }
-      },
-      invalid: {
-        color: '#9e2146',
-        iconColor: '#9e2146'
-      },
-    },
-    hidePostalCode: true
-  };
+  const handleSubmit = (): void => {
+    setSubmitState(true);
+  }
+
+  const handleFormSuccess = (paymentMethod: PaymentMethod): void => {
+    setSubmitState(false);
+    afterSuccess(paymentMethod);
+  }
+
+  const handleFormError = (message: string): void => {
+    setSubmitState(false);
+    setErrors(message);
+  }
+
 
   return (
     <div className="stripe-modal">
       <FabModal title={t('app.shared.stripe.online_payment')} isOpen={isOpen} toggleModal={toggleModal} confirmButton={submitButton()}>
+        <WalletInfo reservation={reservation} currentUser={currentUser} wallet={wallet} price={price} />
         <StripeElements>
-          <form onSubmit={handleSubmit} id="stripe-form">
-            <WalletInfo reservation={reservation} currentUser={currentUser} wallet={wallet} price={price} remainingPrice={remainingPrice} />
-            <CardElement options={cardOptions} />
-          </form>
+          <StripeForm onSubmit={handleSubmit} onSuccess={handleFormSuccess} onError={handleFormError}>
+            {hasCgv() && <div className="terms-of-sales">
+              <input type="checkbox" id="acceptToS" name="acceptCondition" checked={tos} onChange={toggleTos} required />
+              <label htmlFor="acceptToS">{ t('app.shared.stripe.i_have_read_and_accept_') }
+                <a href={cgv.custom_asset_file_attributes.attachment_url} target="_blank">
+                  { t('app.shared.stripe._the_general_terms_and_conditions') }
+                </a>
+              </label>
+            </div>}
+          </StripeForm>
         </StripeElements>
         {hasErrors() && <div className="stripe-errors">
           {errors}
         </div>}
-        {hasCgv() && <div className="terms-of-sales">
-          <input type="checkbox" id="acceptToS" name="acceptCondition" checked={tos} onChange={toggleTos} required />
-        </div>}
         {isPaymentSchedule() && <div className="payment-schedule-info">
           <p>{ t('app.shared.stripe.payment_schedule', { DEADLINES: schedule.items.length }) }</p>
         </div>}
         <div className="stripe-modal-icons">
           <i className="fa fa-lock fa-2x m-r-sm pos-rlt" />
-          <img src="../../../images/powered_by_stripe.png" alt="powered by stripe" />
-          <img src="../../../images/mastercard.png" alt="mastercard" />
-          <img src="../../../images/visa.png" alt="visa" />
+          <img src={stripeLogo} alt="powered by stripe" />
+          <img src={mastercardLogo} alt="mastercard" />
+          <img src={visaLogo} alt="visa" />
         </div>
       </FabModal>
     </div>
   );
 }
 
-const StripeModalWrapper: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuccess, reservation, currentUser, wallet, price, remainingPrice, schedule }) => {
+const StripeModalWrapper: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuccess, reservation, currentUser, wallet, price, schedule }) => {
   return (
     <Loader>
-      <StripeModal isOpen={isOpen} toggleModal={toggleModal} afterSuccess={afterSuccess} reservation={reservation} currentUser={currentUser} wallet={wallet} price={price} remainingPrice={remainingPrice} schedule={schedule} />
+      <StripeModal isOpen={isOpen} toggleModal={toggleModal} afterSuccess={afterSuccess} reservation={reservation} currentUser={currentUser} wallet={wallet} price={price} schedule={schedule} />
     </Loader>
   );
 }
 
-Application.Components.component('stripeModal', react2angular(StripeModalWrapper, ['isOpen', 'toggleModal', 'afterSuccess', 'reservation', 'currentUser', 'wallet', 'price', 'remainingPrice', 'schedule']));
+Application.Components.component('stripeModal', react2angular(StripeModalWrapper, ['isOpen', 'toggleModal', 'afterSuccess', 'reservation', 'currentUser', 'wallet', 'price', 'schedule']));
diff --git a/app/frontend/src/javascript/components/wallet-info.tsx b/app/frontend/src/javascript/components/wallet-info.tsx
index 1baabc177..ac772b72e 100644
--- a/app/frontend/src/javascript/components/wallet-info.tsx
+++ b/app/frontend/src/javascript/components/wallet-info.tsx
@@ -2,7 +2,7 @@
  * This component displays a summary of the amount paid with the virtual wallet, for the current transaction
  */
 
-import React from 'react';
+import React, { useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
 import { react2angular } from 'react2angular';
 import { IApplication } from '../models/application';
@@ -12,6 +12,7 @@ import { Reservation } from '../models/reservation';
 import { User } from '../models/user';
 import { Wallet } from '../models/wallet';
 import { IFablab } from '../models/fablab';
+import WalletLib from '../lib/wallet';
 
 declare var Application: IApplication;
 declare var Fablab: IFablab;
@@ -21,11 +22,19 @@ interface WalletInfoProps {
   currentUser: User,
   wallet: Wallet,
   price: number,
-  remainingPrice: number,
 }
 
-export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser, wallet, price, remainingPrice}) => {
-  const {t} = useTranslation('shared');
+export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser, wallet, price}) => {
+  const { t } = useTranslation('shared');
+  const [remainingPrice, setRemainingPrice] = useState(0);
+
+  /**
+   * Refresh the remaining price on each display
+   */
+  useEffect(() => {
+    const wLib = new WalletLib(wallet);
+    setRemainingPrice(wLib.computeRemainingPrice(price));
+  })
 
   /**
    * Return the formatted localized amount for the given price (eg. 20.5 => "20,50 €")
@@ -111,13 +120,12 @@ export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser,
   );
 }
 
-const WalletInfoWrapper: React.FC<WalletInfoProps> = ({currentUser, reservation, price, remainingPrice, wallet}) => {
+const WalletInfoWrapper: React.FC<WalletInfoProps> = ({currentUser, reservation, price, wallet}) => {
   return (
     <Loader>
-      <WalletInfo currentUser={currentUser} reservation={reservation} price={price}
-                  remainingPrice={remainingPrice} wallet={wallet}/>
+      <WalletInfo currentUser={currentUser} reservation={reservation} price={price} wallet={wallet}/>
     </Loader>
   );
 }
 
-Application.Components.component('walletInfo', react2angular(WalletInfoWrapper, ['currentUser', 'price', 'remainingPrice', 'reservation', 'wallet']));
+Application.Components.component('walletInfo', react2angular(WalletInfoWrapper, ['currentUser', 'price', 'reservation', 'wallet']));
diff --git a/app/frontend/src/javascript/directives/cart.js b/app/frontend/src/javascript/directives/cart.js
index 9fb27622a..c87d8e6d6 100644
--- a/app/frontend/src/javascript/directives/cart.js
+++ b/app/frontend/src/javascript/directives/cart.js
@@ -808,7 +808,7 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
                  */
                 $scope.ok = function () {
                   if ($scope.method.payment_method === 'stripe') {
-                    return payByStripe(reservation);
+                    return $scope.toggleStripeModal();
                   }
                   $scope.attempting = true;
                   // save subscription (if there's only a subscription selected)
diff --git a/app/frontend/src/javascript/lib/wallet.ts b/app/frontend/src/javascript/lib/wallet.ts
new file mode 100644
index 000000000..57338f20d
--- /dev/null
+++ b/app/frontend/src/javascript/lib/wallet.ts
@@ -0,0 +1,20 @@
+import { Wallet } from '../models/wallet';
+
+export default class WalletLib {
+  private wallet: Wallet;
+
+  constructor (wallet: Wallet) {
+    this.wallet = wallet;
+  }
+
+  /**
+   * Return the price remaining to pay, after we have used the maximum possible amount in the wallet
+   */
+  computeRemainingPrice = (price: number): number => {
+    if (this.wallet.amount > price) {
+      return 0;
+    } else {
+      return price - this.wallet.amount;
+    }
+  }
+}
diff --git a/app/frontend/src/javascript/typings/import-png.d.ts b/app/frontend/src/javascript/typings/import-png.d.ts
new file mode 100644
index 000000000..6a81ab5d7
--- /dev/null
+++ b/app/frontend/src/javascript/typings/import-png.d.ts
@@ -0,0 +1,4 @@
+declare module "*.png" {
+  const value: any;
+  export default value;
+}
diff --git a/app/frontend/templates/shared/valid_reservation_modal.html b/app/frontend/templates/shared/valid_reservation_modal.html
index caf38e405..6f0c21bc4 100644
--- a/app/frontend/templates/shared/valid_reservation_modal.html
+++ b/app/frontend/templates/shared/valid_reservation_modal.html
@@ -43,13 +43,18 @@
     <wallet-info current-user="currentUser"
                  reservation="reservation"
                  price="price"
-                 remaining-price="amount"
                  wallet="wallet"/>
   </div>
 </div>
 <div class="modal-footer">
   <button class="btn btn-info" ng-click="ok()" ng-disabled="attempting" ng-bind-html="validButtonName"></button>
   <button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
-  <!--TODO, continuer l'intégration de la modal -->
-  <stripe-modal is-open="isOpenStripeModal" toggle-modal="toggleStripeModal" after-success="afterCreatePaymentMethod" reservation="reservation" current-user="currentUser" wallet="wallet" price="" />
+  <stripe-modal is-open="isOpenStripeModal"
+                toggle-modal="toggleStripeModal"
+                after-success="afterCreatePaymentMethod"
+                reservation="reservation"
+                current-user="currentUser"
+                wallet="wallet"
+                price="price"
+                schedule="schedule" />
 </div>
diff --git a/app/frontend/templates/stripe/payment_modal.html b/app/frontend/templates/stripe/payment_modal.html
index 1198fe235..0d174d8ca 100644
--- a/app/frontend/templates/stripe/payment_modal.html
+++ b/app/frontend/templates/stripe/payment_modal.html
@@ -14,7 +14,6 @@
             <wallet-info current-user="currentUser"
                          reservation="reservation"
                          price="price"
-                         remaining-price="amount"
                          wallet="wallet"/>
           </div>
           <div id="card-element"></div>
diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml
index cbeab9acc..e0bf0ecbc 100644
--- a/config/locales/app.shared.en.yml
+++ b/config/locales/app.shared.en.yml
@@ -117,7 +117,7 @@ en:
       #stripe payment modal
       stripe:
         online_payment: "Online payment"
-        i_have_read_and_accept_: "I have read, and accept"
+        i_have_read_and_accept_: "I have read, and accept "
         _the_general_terms_and_conditions: "the general terms and conditions."
         credit_amount_for_pay_reservation: "{amount} {currency} remains to be paid to confirm your reservation"
         client_credit_amount_for_pay_reservation: "{amount} {currency} remains to be paid to confirm reservation of client"
diff --git a/config/locales/app.shared.es.yml b/config/locales/app.shared.es.yml
index 7ebf121ab..ec53f78a0 100644
--- a/config/locales/app.shared.es.yml
+++ b/config/locales/app.shared.es.yml
@@ -117,7 +117,7 @@ es:
       #stripe payment modal
       stripe:
         online_payment: "Online payment"
-        i_have_read_and_accept_: "He leido y acepto"
+        i_have_read_and_accept_: "He leido y acepto "
         _the_general_terms_and_conditions: "Los términos y condiciones."
         credit_amount_for_pay_reservation: "{amount} {currency} falta por pagar para efectuar su reserva"
         client_credit_amount_for_pay_reservation: "{amount} {currency} falta por pagar para efectuar la reserva del cliente"
diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml
index d89540fdb..a706d473a 100644
--- a/config/locales/app.shared.fr.yml
+++ b/config/locales/app.shared.fr.yml
@@ -117,7 +117,7 @@ fr:
       #stripe payment modal
       stripe:
         online_payment: "Paiement en ligne"
-        i_have_read_and_accept_: "J'ai bien pris connaissance, et accepte"
+        i_have_read_and_accept_: "J'ai bien pris connaissance, et accepte "
         _the_general_terms_and_conditions: "les conditions générales de vente."
         credit_amount_for_pay_reservation: "Il vous reste {amount} {currency} à payer pour valider votre réservation"
         client_credit_amount_for_pay_reservation: "Il reste {amount} {currency} à payer pour valider la réservation"
diff --git a/config/locales/app.shared.pt.yml b/config/locales/app.shared.pt.yml
index fa6c5f8e2..bd15eabf5 100755
--- a/config/locales/app.shared.pt.yml
+++ b/config/locales/app.shared.pt.yml
@@ -117,7 +117,7 @@ pt:
       #stripe payment modal
       stripe:
         online_payment: "Online payment"
-        i_have_read_and_accept_: "Eu li e aceito"
+        i_have_read_and_accept_: "Eu li e aceito "
         _the_general_terms_and_conditions: "os termos e condições."
         credit_amount_for_pay_reservation: "{amount} {currency} a ser pago para confirmar sua inscrição"
         client_credit_amount_for_pay_reservation: "{amount} {currency}  a ser pago para confirmar a inscrição do cliente"
diff --git a/config/webpack/environment.js b/config/webpack/environment.js
index 9546e42f4..4240dac80 100644
--- a/config/webpack/environment.js
+++ b/config/webpack/environment.js
@@ -32,7 +32,6 @@ environment.loaders.prepend('js', js);
 environment.loaders.append('html', html);
 environment.loaders.append('sass', sass);
 environment.loaders.append('uiTour', uiTour);
-environment.loaders.insert('foo', jsErb, { alter: 'bar' });
 
 environment.splitChunks();