Istiaq Ahmed
Istiaq Ahmed

Reputation: 166

Stripe payment redirect_url fails after successful payment when used in office js addin due to top navigation attempt

I'm integrating stripe payment api in our office js addin product for excel. The addin type is taskpane. In the stripe js client api, the stripe.confirmPayment takes a mandatory return_url parameter which i've set to, for example, https://myaddin.azurewebsites.net/taskpane.html.

    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: "https://myaddin.azurewebsites.net/taskpane.html?payment=yes",
      },
    });
    if (error.type === "card_error" || error.type === "validation_error") {
      showMessage(error.message);
    } else {
      showMessage("An unexpected error occurred.");
    }

This would work fine in windows excel document. However, if I load the addin in a office web (microsoft 365) excel document in the browser, the redirect would be blocked by browser after successful payment because it is actually trying to navigate the top level window. The stripe payment element uses iframe which is sandboxed and doesn't have the allow-top-navigation that's why it's blocking as user's sharepoint url or ms365 url should not be redirected. How can i achieve the behavior that the taskpane addin that is hosted inside should only be redirected? Is there any alternative way of stripe payment that I should use that doen't require redirect?

Upvotes: 1

Views: 623

Answers (2)

Istiaq Ahmed
Istiaq Ahmed

Reputation: 166

Thanks to Rick's comment on my question, I finally got it to work using office js dialog api

Before the solution, let me share my understanding of the problem. The addin is loaded insided an iframe in user's sharepoint/ms365 page according to Microsoft's Documentation. Stripe is loaded in another iframe inside this iframe. Both these iframes are Sandboxed that by default (copying from the w3schools link)-

  • Treat the content as being from a unique origin
  • Prevent the content to navigate its top-level browsing context

So the stripe redirect ends up attempting to redirect the top level sharepoint url (due to unique origin) and gets blocked. This is my understanding of the problem - please correct me if i'm wrong in any part.

Therefore the trick is to open a popup office js dialog and load stripe there. It is important to load it in a separate browser window, not iframe - so that subsequent redirect after successful payment is not blocked by browser due to top navigation attempt. The information flow between the main (host) page and the child popup window is maintained by using messageParent and messageChild api's in office js. We also need a success page under the addin's hosted domain in this approach where we will redirect and from there we'll message the parent host that the payment succeeded.

Here is a brief code sample for reference:

taskpane.js (parent host)

// displayInIframe option belowis false by default, i first set it to true and
// got the same security error due to top navigation attempt, so wanted to to 
// highlight the initialization to false

let dialog;
function onPaymentButtonClicked(e){
  Office.context.ui.displayDialogAsync(
    'https://myaddin.azurewebsites.net/payment.html',
    { displayInIframe: false, height: 52, width: 33 }, 

    function (result) {
      dialog = result.value;
      dialog.addEventHandler(Microsoft.Office.WebExtension.EventType.DialogMessageReceived, processMessage);
    }
  );

function processMessage(arg) {
  console.log(`Received child msg ${arg.message}`);
  if(arg.message === "payment-successful"){
    dialog.close();
    // further processing here...
  }
  
}

payment.js (child popup)

// This is the js that operates the popup office dialog for payment
// I've skipped the payment initiation codes here that loads stripe payment 
// element and sets event handlers etc.

async function confirmPayment() {
    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: "https://myaddin.azurewebsites.net/payment-success.html",
      },
    });
    if (error.type === "card_error" || error.type === "validation_error") {
      // ...
    } else {
      // ...
    }
  }

payment-success.js (child popup)

// the underlying page is a typical payment success page that has a continue
// button and some success animations etc. This page also opens in the popup dialog
// upon successful redirection

(async () => {
    await Office.onReady();
  
    document.getElementById("btnContinue").addEventListener("click", sendMessageToParent);
  
    function sendMessageToParent(e) {
     // send message to host window so that we can close the popup and 
     // do further processing
      Office.context.ui.messageParent("payment-successful");
  }
  })();

In general, I think this popup based approach would apply to any office taskpane addin where redirection is required for any reason such as payment because redirection from taskpane would be blocked due to top navigation attempt. The experience using popup is less attractive due to the popup opening prompt eg "My Addin wants to open a new window" but it seems to be the cleanest solution available at this time.

Another alternative might be to let the error (ie the redirect block) happen i.e. catch the error and also start periodic polling (using intervals for example) as soon as payment is confirmed by user. The polling checks the status of the stripe payment intent object that was initialized in the page. Upon success, we can do further processing. This all-taskpane solution is not clean though and i don't encourage it anymore. Just an idea that might have got materialized had the popup solution not worked.

Upvotes: 2

Tarzan
Tarzan

Reputation: 1117

There's no simple solution to this. I would try using the redirect if_required option. but this doesn't really solve the issue when redirection is required.

Other than that you have really limited options. One would be to make use of Checkout Sessions and redirecting that from your add-in should be a lot more easier.

Upvotes: 0

Related Questions