diff --git a/js/civicrmStripeConfirm.js b/js/civicrmStripeConfirm.js
index c406e02b71887878a60b7c18070b0c0497ebc443..6684b0e087f412b319ad28fc22c8988b3e46020b 100644
--- a/js/civicrmStripeConfirm.js
+++ b/js/civicrmStripeConfirm.js
@@ -1,117 +1,124 @@
 /**
- * This handles confirmation actions on the "Thankyou" pages for contribution/event workflows.
+ * This handles confirmation actions on the "Thankyou" pages for
+ * contribution/event workflows.
  */
-CRM.$(function($) {
-  debugging("civicrmStripeConfirm loaded");
+(function($, CRM, _, undefined) {
 
-  if (typeof CRM.vars.stripe === 'undefined') {
-    debugging('CRM.vars.stripe not defined! Not a Stripe processor?');
-    return;
-  }
-  switch (CRM.vars.stripe.paymentIntentStatus) {
-    case 'succeeded':
-    case 'cancelled':
-      debugging('paymentIntent: ' + CRM.vars.stripe.paymentIntentStatus);
-      return;
-  }
+  var confirm = {
+    scriptName: 'stripeconfirm',
+    stripe: null,
+    stripeLoading: false,
 
-  checkAndLoad();
+    handleServerResponse: function(result) {
+      CRM.payment.debugging(confirm.scriptName, 'handleServerResponse');
+      if (result.error) {
+        // Show error from server on payment form
+        CRM.payment.debugging(confirm.scriptName, result.error.message);
+      }
+      else
+        if (result.requires_action) {
+          // Use Stripe.js to handle required card action
+          confirm.handleAction(result);
+        }
+        else {
+          // All good, nothing more to do
+          CRM.payment.debugging(confirm.scriptName, 'success - payment captured');
+        }
+    },
 
-  if (typeof stripe === 'undefined') {
-    stripe = Stripe(CRM.vars.stripe.publishableKey);
-  }
+    handleAction: function(response) {
+      switch (CRM.vars.stripe.paymentIntentMethod) {
+        case 'automatic':
+          confirm.stripe.handleCardPayment(response.payment_intent_client_secret)
+            .then(function (result) {
+              if (result.error) {
+                // Show error in payment form
+                confirm.handleCardConfirm();
+              }
+              else {
+                // The card action has been handled
+                CRM.payment.debugging(confirm.scriptName, 'card payment success');
+                confirm.handleCardConfirm();
+              }
+            });
+          break;
 
-  handleCardConfirm();
+        case 'manual':
+          confirm.stripe.handleCardAction(response.payment_intent_client_secret)
+            .then(function (result) {
+              if (result.error) {
+                // Show error in payment form
+                confirm.handleCardConfirm();
+              }
+              else {
+                // The card action has been handled
+                CRM.payment.debugging(confirm.scriptName, 'card action success');
+                confirm.handleCardConfirm();
+              }
+            });
+          break;
+      }
+    },
 
-  // On initial load...
-  var stripe;
-  var stripeLoading = false;
+    handleCardConfirm: function() {
+      CRM.payment.debugging(confirm.scriptName, 'handle card confirm');
+      // Send paymentMethod.id to server
+      var url = CRM.url('civicrm/stripe/confirm-payment');
+      $.post(url, {
+        payment_intent_id: CRM.vars.stripe.paymentIntentID,
+        capture: true,
+        id: CRM.vars.stripe.id,
+        description: document.title,
+        csrfToken: CRM.vars.stripe.csrfToken,
+      }).then(function (result) {
+        // Handle server response (see Step 3)
+        confirm.handleServerResponse(result);
+      });
+    },
 
-  // Disable the browser "Leave Page Alert" which is triggered because we mess with the form submit function.
-  window.onbeforeunload = null;
+    checkAndLoad: function () {
+      if (typeof Stripe === 'undefined') {
+        if (confirm.stripeLoading) {
+          return;
+        }
+        confirm.stripeLoading = true;
+        CRM.payment.debugging(confirm.scriptName, 'Stripe.js is not loaded!');
 
-  function handleServerResponse(result) {
-    debugging('handleServerResponse');
-    if (result.error) {
-      // Show error from server on payment form
-      // displayError(result);
-    } else if (result.requires_action) {
-      // Use Stripe.js to handle required card action
-      handleAction(result);
-    } else {
-      // All good, nothing more to do
-      debugging('success - payment captured');
-    }
-  }
+        $.getScript("https://js.stripe.com/v3", function () {
+          CRM.payment.debugging(confirm.scriptName, 'Script loaded and executed.');
+          confirm.stripeLoading = false;
 
-  function handleAction(response) {
-    switch (CRM.vars.stripe.paymentIntentMethod) {
-      case 'automatic':
-        stripe.handleCardPayment(response.payment_intent_client_secret)
-          .then(function (result) {
-            if (result.error) {
-              // Show error in payment form
-              handleCardConfirm();
-            }
-            else {
-              // The card action has been handled
-              debugging('card payment success');
-              handleCardConfirm();
-            }
-          });
-        break;
+          if (confirm.stripe === null) {
+            confirm.stripe = Stripe(CRM.vars.stripe.publishableKey);
+          }
+        });
+      }
+    },
+  };
 
-      case 'manual':
-        stripe.handleCardAction(response.payment_intent_client_secret)
-          .then(function (result) {
-            if (result.error) {
-              // Show error in payment form
-              handleCardConfirm();
-            }
-            else {
-              // The card action has been handled
-              debugging('card action success');
-              handleCardConfirm();
-            }
-          });
-        break;
-    }
+  if (typeof CRM.payment === 'undefined') {
+    CRM.payment = {};
   }
 
-  function handleCardConfirm() {
-    debugging('handle card confirm');
-    // Send paymentMethod.id to server
-    var url = CRM.url('civicrm/stripe/confirm-payment');
-    $.post(url, {
-      payment_intent_id: CRM.vars.stripe.paymentIntentID,
-      capture: true,
-      id: CRM.vars.stripe.id,
-    }).then(function (result) {
-      // Handle server response (see Step 3)
-      handleServerResponse(result);
-    });
-  }
+  CRM.payment.stripeConfirm = confirm;
 
-  function checkAndLoad() {
-    if (typeof Stripe === 'undefined') {
-      if (stripeLoading) {
-        return;
-      }
-      stripeLoading = true;
-      debugging('Stripe.js is not loaded!');
+  CRM.payment.debugging(confirm.scriptName, 'civicrmStripeConfirm loaded');
 
-      $.getScript("https://js.stripe.com/v3", function () {
-        debugging("Script loaded and executed.");
-        stripeLoading = false;
-      });
-    }
+  if (typeof CRM.vars.stripe === 'undefined') {
+    CRM.payment.debugging(confirm.scriptName, 'CRM.vars.stripe not defined! Not a Stripe processor?');
+    return;
   }
-
-  function debugging(errorCode) {
-    // Uncomment the following to debug unexpected returns.
-    if ((typeof(CRM.vars.stripe) === 'undefined') || (Boolean(CRM.vars.stripe.jsDebug) === true)) {
-      console.log(new Date().toISOString() + ' civicrm_stripe.js: ' + errorCode);
-    }
+  switch (CRM.vars.stripe.paymentIntentStatus) {
+    case 'succeeded':
+    case 'cancelled':
+      CRM.payment.debugging(confirm.scriptName, 'paymentIntent: ' + CRM.vars.stripe.paymentIntentStatus);
+      return;
   }
 
-});
+  confirm.checkAndLoad();
+  confirm.handleCardConfirm();
+
+  // Disable the browser "Leave Page Alert" which is triggered because we mess with the form submit function.
+  window.onbeforeunload = null;
+
+}(jQuery, CRM, _));
diff --git a/stripe.php b/stripe.php
index 433a1148f970aebdde59c763056a03d49dcbe2a4..2579870fcd0a2e64a9dc379c250316761c856322 100644
--- a/stripe.php
+++ b/stripe.php
@@ -162,7 +162,7 @@ function stripe_civicrm_buildForm($formName, &$form) {
           'path' => \Civi::resources()->getPath(E::LONG_NAME, 'js/civicrmStripeConfirm.js'),
           'mimetype' => 'application/javascript',
         ]
-      ));
+      ), -1000);
 
       // This is a fairly nasty way of matching and retrieving our paymentIntent as it is no longer available.
       $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String');