Search code examples
flasksafaricreate-react-app

Flask/React app, downloaded binary (pkpass) file differs from the same file on the server


I'm building a React app that hits a Flask server for Stripe payment processing and Apple Wallet pass generation. My server script generates the file, and saves it to a directory, then returns the name of the pkpass file to the client, which attempts to download it and load it into Wallet.

Passes seem to be generating correctly. I can download them (sftp) and drop them on the Simulator and they open up just fine. I can un-zip them and all the necessary components seem to be there and seem valid.

However, with the web app, I cannot get them to download and install into Wallet. On my iPhone, I get a simple "Safari could not download the file" error.

If I use Safari on the desktop, the pkpass file gets downloaded to my Mac. When I drag & drop it on the simulator, I see the following error in Console:

BOM could not extract archive: Couldn't read PKZip signature

Failed to add pass: 'file:///Users/tpoulsen/Downloads/ch_1FPYBoAzcfAbJsLnXDsVRcrc.pkpass' Error Domain=PKPassKitErrorDomain Code=1 "The pass cannot be read because it isn’t valid." UserInfo={NSLocalizedDescription=The pass cannot be read because it isn’t valid., NSUnderlyingError=0x6000016dbbd0 {Error Domain=PKPassKitErrorDomain Code=1 "(null)"}}.

I used VBinDiff to compare the downloads (via sftp and Safari) and the files are quite different. So, the problem appears to be either with my Flask app or my web app.

Here's my Flask route

@application.route("/passes/<path:fname>")
def passes_proxy(fname):
    """static passes serve"""
    return send_from_directory("passes", fname, mimetype="application/vnd.apple.pkpass")

And in my component:

paymentRequest.on('token', async (ev) => {
  try {
    const response = await fetch('https://my_server.com/charge', {
      method: 'POST',
      body: JSON.stringify({
        token: ev.token.id,
        amount: totalInCents,
        description: purchasedItems.join(',\n')
      }),
      headers: {'content-type': 'application/json'},
    });
    if (!response.ok) {
      throw new Error('Network response was not ok.');
    }
    ev.complete('success');
    const pkpass = await response.json();
    download(`https://my_server.com/passes/${pkpass.filename}`, pkpass.filename, "application/vnd.apple.pkpass");
  } catch (error) {
    throw new Error("There was a problem processing your payment");
  }
});

Tech involved:

  • create-react-app / React
  • Flask with wallet-py3k for pass generation
  • download.js to download the generated file

Solution

  • Got it. I was being too fancy. Instead of using download.js to grab the file, I simply needed to:

    window.location.href = `https://my_server.com/passes/${pkpass.filename}`;