kit icon indicating copy to clipboard operation
kit copied to clipboard

Support Stripe in @sveltejs/adapter-cloudflare integrations

Open fredguest opened this issue 3 years ago • 7 comments

Describe the problem

Importing Stripe https://github.com/stripe/stripe-node in a +server endpoint https://kit.svelte.dev/docs/routing#server will work fine locally, but break the build when deployed to Cloudflare.

This is likely a critical use case for many SvelteKit projects on Cloudflare, and there seems to be some awareness of the problem as there are several issues like this: https://github.com/sveltejs/kit/issues/3564#issuecomment-1126809396 on the books, but there does not seem to be a replicable, working solution in any of them.

The Cloudflare team are clearly aware of the complexities around using Stripe in Workers, and announced "native support" for Stripe here: https://blog.cloudflare.com/announcing-stripe-support-in-workers but the demo repo referenced in that blog post uses a Webpack config: https://github.com/stripe-samples/stripe-node-cloudflare-worker-template/blob/main/webpack.config.js which rules it out as a SvelteKit solution.

Describe the proposed solution

At a minimum, an official, documented example of whatever set of configs is required to allow the import of Stripe in a +server endpoint without breaking the build, for a @sveltejs/adapter-cloudflare integration, somewhere in the SvelteKit repo or docs.

Even better would be something like this for @sveltejs/adapter-cloudflare integrations: https://github.com/srmullen/sveltekit-stripe

Alternatives considered

No response

Importance

i cannot use SvelteKit without it

Additional Information

The Cloudflare team is contributing to SvelteKit, which is great to see: https://github.com/sveltejs/kit/issues/6441 - @jrf0110 do you have any thoughts on this issue, or how Cloudflare might be able to help make this integration work?

fredguest avatar Sep 03 '22 15:09 fredguest

Have you tried this?

const stripe = Stripe(<API_KEY>, {
  // This is needed to use the Fetch API rather than relying on the Node http
  httpClient: Stripe.createFetchHttpClient(),
});

ak4zh avatar Sep 04 '22 05:09 ak4zh

@ak4zh Yes, unfortunately that does not work as advertised in this case. Even using Stripe.createFetchHttpClient() you get this in the build logs:

2022-09-04T13:18:42.380596Z	
2022-09-04T13:18:42.380817Z	> Using @sveltejs/adapter-cloudflare
2022-09-04T13:18:42.578683Z	✘ [ERROR] Could not resolve "events"
2022-09-04T13:18:42.579023Z	
2022-09-04T13:18:42.579181Z	    node_modules/stripe/lib/stripe.js:48:29:
2022-09-04T13:18:42.579299Z	      48 │ const EventEmitter = require('events').EventEmitter;
2022-09-04T13:18:42.579417Z	         ╵                              ~~~~~~~~
2022-09-04T13:18:42.579527Z	
2022-09-04T13:18:42.579637Z	  The package "events" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
2022-09-04T13:18:42.579779Z	
2022-09-04T13:18:42.608271Z	✘ [ERROR] Could not resolve "http"
2022-09-04T13:18:42.608547Z	
2022-09-04T13:18:42.608684Z	    node_modules/stripe/lib/net/NodeHttpClient.js:3:21:
2022-09-04T13:18:42.608865Z	      3 │ const http = require('http');
2022-09-04T13:18:42.608999Z	        ╵                      ~~~~~~
2022-09-04T13:18:42.609114Z	
2022-09-04T13:18:42.609226Z	  The package "http" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
2022-09-04T13:18:42.60934Z	
2022-09-04T13:18:42.609459Z	✘ [ERROR] Could not resolve "events"
2022-09-04T13:18:42.609585Z	
2022-09-04T13:18:42.609696Z	    node_modules/stripe/lib/utils.js:3:29:
2022-09-04T13:18:42.609906Z	      3 │ const EventEmitter = require('events').EventEmitter;
2022-09-04T13:18:42.610148Z	        ╵                              ~~~~~~~~
2022-09-04T13:18:42.610314Z	
2022-09-04T13:18:42.610447Z	  The package "events" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
2022-09-04T13:18:42.610562Z	
2022-09-04T13:18:42.610687Z	✘ [ERROR] Could not resolve "crypto"
2022-09-04T13:18:42.610817Z	
2022-09-04T13:18:42.610953Z	    node_modules/stripe/lib/crypto/NodeCryptoProvider.js:3:23:
2022-09-04T13:18:42.611075Z	      3 │ const crypto = require('crypto');
2022-09-04T13:18:42.61119Z	        ╵                        ~~~~~~~~
2022-09-04T13:18:42.611299Z	
2022-09-04T13:18:42.611416Z	  The package "crypto" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
2022-09-04T13:18:42.611528Z	
2022-09-04T13:18:42.611707Z	✘ [ERROR] Could not resolve "https"
2022-09-04T13:18:42.611852Z	
2022-09-04T13:18:42.611978Z	    node_modules/stripe/lib/net/NodeHttpClient.js:4:22:
2022-09-04T13:18:42.612095Z	      4 │ const https = require('https');
2022-09-04T13:18:42.612204Z	        ╵                       ~~~~~~~
2022-09-04T13:18:42.612313Z	
2022-09-04T13:18:42.612423Z	  The package "https" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
2022-09-04T13:18:42.612533Z	
2022-09-04T13:18:42.612878Z	✘ [ERROR] Could not resolve "crypto"
2022-09-04T13:18:42.613101Z	
2022-09-04T13:18:42.613359Z	    node_modules/stripe/lib/utils.js:5:23:
2022-09-04T13:18:42.61356Z	      5 │ const crypto = require('crypto');
2022-09-04T13:18:42.613714Z	        ╵                        ~~~~~~~~
2022-09-04T13:18:42.613895Z	
2022-09-04T13:18:42.614097Z	  The package "crypto" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
2022-09-04T13:18:42.614313Z	
2022-09-04T13:18:42.771244Z	error during build:
2022-09-04T13:18:42.771526Z	Error: Build failed with 6 errors:
2022-09-04T13:18:42.771681Z	node_modules/stripe/lib/crypto/NodeCryptoProvider.js:3:23: ERROR: Could not resolve "crypto"
2022-09-04T13:18:42.771817Z	node_modules/stripe/lib/net/NodeHttpClient.js:3:21: ERROR: Could not resolve "http"
2022-09-04T13:18:42.771962Z	node_modules/stripe/lib/net/NodeHttpClient.js:4:22: ERROR: Could not resolve "https"
2022-09-04T13:18:42.772076Z	node_modules/stripe/lib/stripe.js:48:29: ERROR: Could not resolve "events"
2022-09-04T13:18:42.772184Z	node_modules/stripe/lib/utils.js:3:29: ERROR: Could not resolve "events"
2022-09-04T13:18:42.772302Z	...
2022-09-04T13:18:42.772443Z	    at failureErrorWithLog (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:1624:15)
2022-09-04T13:18:42.772562Z	    at /opt/buildhome/repo/node_modules/esbuild/lib/main.js:1266:28
2022-09-04T13:18:42.772669Z	    at runOnEndCallbacks (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:1179:65)
2022-09-04T13:18:42.772792Z	    at buildResponseToResult (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:1264:7)
2022-09-04T13:18:42.772918Z	    at /opt/buildhome/repo/node_modules/esbuild/lib/main.js:1377:14
2022-09-04T13:18:42.773024Z	    at /opt/buildhome/repo/node_modules/esbuild/lib/main.js:678:9
2022-09-04T13:18:42.773136Z	    at handleIncomingPacket (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:775:9)
2022-09-04T13:18:42.773242Z	    at Socket.readFromStdout (/opt/buildhome/repo/node_modules/esbuild/lib/main.js:644:7)
2022-09-04T13:18:42.773346Z	    at Socket.emit (node:events:390:28)
2022-09-04T13:18:42.77345Z	    at addChunk (node:internal/streams/readable:324:12)
2022-09-04T13:18:42.808906Z	Failed: build command exited with code: 1
2022-09-04T13:18:43.877398Z	Failed: an internal error occurred

fredguest avatar Sep 04 '22 13:09 fredguest

@fredguest I see at few places https://github.com/stripe-samples/stripe-node-cloudflare-worker-template/blob/2f10d7341cb57699eb9614e2a0dfa62b2ec6eb98/webpack.config.js#L12 it’s suggested to edit webpack config resolve.fallback and set following

 resolve: {
    fallback: { 
      // Replace these libraries with no-ops as they have no polyfills and we
      // are using fetch instead of http/https.
      "child_process": false,
      "http": false,
      "https": false,
      // Browser/worker polyfills required to replace Node libraries used by the
      // stripe-node SDK.
      "buffer": require.resolve("buffer"),
      "crypto": require.resolve("crypto-browserify"),
      "events": require.resolve("events/"),
      "path": require.resolve("path-browserify"),
      "stream": require.resolve("stream-browserify"),
    }
  },

I am not sure how to do that on vite, but you should try that.

ak4zh avatar Sep 04 '22 16:09 ak4zh

I did a dirty fix for it a while back.

I just cloned the stripe-node repo and adapted it for cloudflare workers.

I remember I set the defaults to global fetch, removed fs.

Npm install it a look at the source for reference.

I might have time to do a pr to address this this month.

https://www.npmjs.com/package/stripe-cloudflare

or you might try to use vite alias to replicate the resolve.fallback. Don’t know if this would work

https://github.com/stripe-samples/stripe-node-cloudflare-worker-template/blob/main/webpack.config.js

ghost avatar Sep 05 '22 10:09 ghost

Hey there — wondered if you’re deploying to Workers Sites or Pages. My current understanding is Pages doesn’t support node compat mode, which is required to make Stripe work ootb. (I recall a Cloudflare commit from a few months ago saying the blog post you mentioned didn’t actually work when they posted it, and node compat mode fixes it.)

more info: https://developers.cloudflare.com/workers/wrangler/configuration/

# wrangler.toml
# Add polyfills for node builtin modules and globals
node_compat = true

being in a wrangler.toml file, this means you have to use Workers Sites and not Pages. Pages is getting support for this soon I’m told. Hope this is correct/helpful!

mglikesbikes avatar Sep 25 '22 16:09 mglikesbikes

Hey @mglikesbikes, ya the @sveltejs/adapter-cloudflare adapter is specifically designed only for use with Pages, so that's what I'm using. When you use this adapter you get the benefit of auto deploys etc, but you lose the ability to fine tune things with Wrangler, so this kind of bug needs to be addressed at the integration layer.

fredguest avatar Sep 26 '22 13:09 fredguest

I did a dirty fix for it a while back.

I just cloned the stripe-node repo and adapted it for cloudflare workers.

https://www.npmjs.com/package/stripe-cloudflare

✨🎉🎇 This worked for me, thank you so much!

or you might try to use vite alias to replicate the resolve.fallback. Don’t know if this would work

https://github.com/stripe-samples/stripe-node-cloudflare-worker-template/blob/main/webpack.config.js

^^ I tried every combination to alias resolve http, https, and crypto... none of them worked.

Below is all the configs I could find, (none of these work)

import polyfillNode from 'rollup-plugin-polyfill-node';
import alias from 'esbuild-plugin-alias';

const config: UserConfig = {
	plugins: [sveltekit(), polyfillNode()],
	resolve: {
		alias: {
			http: false,
			https: false,
			crypto: 'crypto-browserify',
			events: 'events/'
		}
	},
	build: {
		commonjsOptions: {
			include: [/node_modules/, /stripe/]
		}
	},
	ssr: {
		external: ['http', 'https']
	},
	optimizeDeps: {
		include: ['stripe'],
		force: true,
		esbuildOptions: {
			plugins: [
				alias({
					http: false,
					https: false,
					crypto: 'crypto-browserify'
				})
			]
		}
	}
};

DougAnderson444 avatar Nov 22 '22 18:11 DougAnderson444

This issue has been fixed by this PR https://github.com/stripe/stripe-node/pull/1679 on the Stripe library.

Sveltekit + Cloudflare Pages Workers + Stripe now work together happily, without hacks or extra configs. 🤘

fredguest avatar Feb 16 '23 02:02 fredguest