@payloadcms/plugin-stripe 0.0.19 → 3.0.0-alpha.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +3 -285
  2. package/dist/admin.d.ts +4 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/admin.js +34 -0
  5. package/dist/admin.js.map +1 -0
  6. package/dist/fields/getFields.d.ts +12 -0
  7. package/dist/fields/getFields.d.ts.map +1 -0
  8. package/dist/fields/getFields.js +46 -0
  9. package/dist/fields/getFields.js.map +1 -0
  10. package/dist/hooks/createNewInStripe.d.ts +7 -2
  11. package/dist/hooks/createNewInStripe.d.ts.map +1 -0
  12. package/dist/hooks/createNewInStripe.js +70 -124
  13. package/dist/hooks/createNewInStripe.js.map +1 -1
  14. package/dist/hooks/deleteFromStripe.d.ts +7 -2
  15. package/dist/hooks/deleteFromStripe.d.ts.map +1 -0
  16. package/dist/hooks/deleteFromStripe.js +30 -87
  17. package/dist/hooks/deleteFromStripe.js.map +1 -1
  18. package/dist/hooks/syncExistingWithStripe.d.ts +7 -2
  19. package/dist/hooks/syncExistingWithStripe.d.ts.map +1 -0
  20. package/dist/hooks/syncExistingWithStripe.js +42 -94
  21. package/dist/hooks/syncExistingWithStripe.js.map +1 -1
  22. package/dist/index.d.ts +5 -3
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +88 -166
  25. package/dist/index.js.map +1 -1
  26. package/dist/payload-stripe-plugin.postman_collection.json +301 -0
  27. package/dist/routes/rest.d.ts +4 -6
  28. package/dist/routes/rest.d.ts.map +1 -0
  29. package/dist/routes/rest.js +33 -76
  30. package/dist/routes/rest.js.map +1 -1
  31. package/dist/routes/webhooks.d.ts +4 -6
  32. package/dist/routes/webhooks.d.ts.map +1 -0
  33. package/dist/routes/webhooks.js +49 -94
  34. package/dist/routes/webhooks.js.map +1 -1
  35. package/dist/types.d.ts +138 -11
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +2 -2
  38. package/dist/types.js.map +1 -1
  39. package/dist/ui/LinkToDoc.d.ts +4 -7
  40. package/dist/ui/LinkToDoc.d.ts.map +1 -0
  41. package/dist/ui/LinkToDoc.js +29 -31
  42. package/dist/ui/LinkToDoc.js.map +1 -1
  43. package/dist/utilities/deepen.d.ts +1 -0
  44. package/dist/utilities/deepen.d.ts.map +1 -0
  45. package/dist/utilities/deepen.js +9 -16
  46. package/dist/utilities/deepen.js.map +1 -1
  47. package/dist/utilities/stripeProxy.d.ts +2 -1
  48. package/dist/utilities/stripeProxy.d.ts.map +1 -0
  49. package/dist/utilities/stripeProxy.js +37 -88
  50. package/dist/utilities/stripeProxy.js.map +1 -1
  51. package/dist/webhooks/handleCreatedOrUpdated.d.ts +3 -2
  52. package/dist/webhooks/handleCreatedOrUpdated.d.ts.map +1 -0
  53. package/dist/webhooks/handleCreatedOrUpdated.js +115 -196
  54. package/dist/webhooks/handleCreatedOrUpdated.js.map +1 -1
  55. package/dist/webhooks/handleDeleted.d.ts +2 -1
  56. package/dist/webhooks/handleDeleted.d.ts.map +1 -0
  57. package/dist/webhooks/handleDeleted.js +44 -102
  58. package/dist/webhooks/handleDeleted.js.map +1 -1
  59. package/dist/webhooks/index.d.ts +2 -1
  60. package/dist/webhooks/index.d.ts.map +1 -0
  61. package/dist/webhooks/index.js +46 -90
  62. package/dist/webhooks/index.js.map +1 -1
  63. package/package.json +50 -45
  64. package/types.d.ts +1 -1
  65. package/types.js +1 -1
  66. package/dist/extendWebpackConfig.d.ts +0 -3
  67. package/dist/extendWebpackConfig.js +0 -31
  68. package/dist/extendWebpackConfig.js.map +0 -1
  69. package/dist/mocks/serverModule.d.ts +0 -2
  70. package/dist/mocks/serverModule.js +0 -6
  71. package/dist/mocks/serverModule.js.map +0 -1
package/README.md CHANGED
@@ -1,289 +1,7 @@
1
1
  # Payload Stripe Plugin
2
2
 
3
- [![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-stripe)](https://www.npmjs.com/package/@payloadcms/plugin-stripe)
4
-
5
3
  A plugin for [Payload](https://github.com/payloadcms/payload) to connect [Stripe](https://stripe.com) and Payload.
6
4
 
7
- Core features:
8
-
9
- - Hides your Stripe credentials when shipping SaaS applications
10
- - Allows restricted keys through [Payload access control](https://payloadcms.com/docs/access-control/overview)
11
- - Enables a two-way communication channel between Stripe and Payload
12
- - Proxies the [Stripe REST API](https://stripe.com/docs/api)
13
- - Proxies [Stripe webhooks](https://stripe.com/docs/webhooks)
14
- - Automatically syncs data between the two platforms
15
-
16
- ## Installation
17
-
18
- ```bash
19
- yarn add @payloadcms/plugin-stripe
20
- # OR
21
- npm i @payloadcms/plugin-stripe
22
- ```
23
-
24
- ## Basic Usage
25
-
26
- In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin with [options](#options):
27
-
28
- ```js
29
- import { buildConfig } from "payload/config";
30
- import stripePlugin from "@payloadcms/plugin-stripe";
31
-
32
- const config = buildConfig({
33
- plugins: [
34
- stripePlugin({
35
- stripeSecretKey: process.env.STRIPE_SECRET_KEY,
36
- }),
37
- ],
38
- });
39
-
40
- export default config;
41
- ```
42
-
43
- ### Options
44
-
45
- - `stripeSecretKey`: string
46
-
47
- Required. Your Stripe secret key.
48
-
49
- - `sync`: array
50
-
51
- Optional. An array of sync configs. This will automatically configure a sync between Payload collections and Stripe resources on create, delete, and update. See [sync](#sync) for more details.
52
-
53
- - `stripeWebhooksEndpointSecret`: string
54
-
55
- Optional. Your Stripe webhook endpoint secret. This is needed only if you wish to sync data _from_ Stripe _to_ Payload.
56
-
57
- - `rest`: boolean
58
-
59
- Optional. When `true`, opens the `/api/stripe/rest` endpoint. See [endpoints](#endpoints) for more details.
60
-
61
- - `webhooks`: object | function
62
-
63
- Optional. Either a function to handle all webhooks events, or an object of Stripe webhook handlers, keyed to the name of the event. See [webhooks](#webhooks) for more details or for a list of all available webhooks, see [here](https://stripe.com/docs/cli/trigger#trigger-event).
64
-
65
- - `logs`: boolean
66
-
67
- Optional. When `true`, logs sync events to the console as they happen.
68
-
69
- ## Sync
70
-
71
- This option will setup a basic sync between Payload collections and Stripe resources for you automatically. It will create all the necessary hooks and webhooks handlers, so the only thing you have to do is map your Payload fields to their corresponding Stripe properties. As documents are created, updated, and deleted from either Stripe or Payload, the changes are reflected on either side.
72
-
73
- > NOTE: If you wish to enable a _two-way_ sync, be sure to setup [`webhooks`](#webhooks) and pass the `stripeWebhooksEndpointSecret` through your config.
74
-
75
- > NOTE: Due to limitations in the Stripe API, this currently only works with top-level fields. This is because every Stripe object is a separate entity, making it difficult to abstract into a simple reusable library. In the future, we may find a pattern around this. But for now, cases like that will need to be hard-coded. See the [demo](./demo) for an example of this.
76
-
77
- ```js
78
- import { buildConfig } from "payload/config";
79
- import stripePlugin from "@payloadcms/plugin-stripe";
80
-
81
- const config = buildConfig({
82
- plugins: [
83
- stripePlugin({
84
- stripeSecretKey: process.env.STRIPE_SECRET_KEY,
85
- stripeWebhooksEndpointSecret: process.env.STRIPE_WEBHOOKS_ENDPOINT_SECRET,
86
- sync: [
87
- {
88
- collection: "customers",
89
- stripeResourceType: "customers",
90
- stripeResourceTypeSingular: "customer",
91
- fields: [
92
- {
93
- fieldPath: "name", // this is a field on your own Payload config
94
- stripeProperty: "name", // use dot notation, if applicable
95
- },
96
- ],
97
- },
98
- ],
99
- }),
100
- ],
101
- });
102
-
103
- export default config;
104
- ```
105
-
106
- Using `sync` will do the following:
107
-
108
- - Adds and maintains a `stripeID` read-only field on each collection, this is a field generated _by Stripe_ and used as a cross-reference
109
- - Adds a direct link to the resource on Stripe.com
110
- - Adds and maintains an `skipSync` read-only flag on each collection to prevent infinite syncs when hooks trigger webhooks
111
- - Adds the following hooks to each collection:
112
- - `beforeValidate`: `createNewInStripe`
113
- - `beforeChange`: `syncExistingWithStripe`
114
- - `afterDelete`: `deleteFromStripe`
115
- - Handles the following Stripe webhooks
116
- - `STRIPE_TYPE.created`: `handleCreatedOrUpdated`
117
- - `STRIPE_TYPE.updated`: `handleCreatedOrUpdated`
118
- - `STRIPE_TYPE.deleted`: `handleDeleted`
119
-
120
- ### Endpoints
121
-
122
- The following custom endpoints are automatically opened for you:
123
-
124
- > NOTE: the `/api` part of these routes may be different based on the settings defined in your Payload config.
125
-
126
- - #### `POST /api/stripe/rest`
127
-
128
- If `rest` is true, proxies the [Stripe REST API](https://stripe.com/docs/api) behind [Payload access control](https://payloadcms.com/docs/access-control/overview) and returns the result. If you need to proxy the API server-side, use the [stripeProxy](#node) function.
129
-
130
- ```js
131
- const res = await fetch(`/api/stripe/rest`, {
132
- method: "POST",
133
- credentials: "include",
134
- headers: {
135
- "Content-Type": "application/json",
136
- // Authorization: `JWT ${token}` // NOTE: do this if not in a browser (i.e. curl or Postman)
137
- },
138
- body: JSON.stringify({
139
- stripeMethod: "stripe.subscriptions.list",
140
- stripeArgs: [
141
- {
142
- customer: "abc",
143
- },
144
- ],
145
- }),
146
- });
147
- ```
148
-
149
- - #### `POST /stripe/webhooks`
150
-
151
- Returns an http status code. This is where all Stripe webhook events are sent to be handled. See [webhooks](#webhooks).
152
-
153
- ### Webhooks
154
-
155
- [Stripe webhooks](https://stripe.com/docs/webhooks) are used to sync from Stripe to Payload. Webhooks listen for events on your Stripe account so you can trigger reactions to them. Follow the steps below to enable webhooks.
156
-
157
- Development:
158
-
159
- 1. Login using Stripe cli `stripe login`
160
- 1. Forward events to localhost `stripe listen --forward-to localhost:3000/stripe/webhooks`
161
- 1. Paste the given secret into your `.env` file as `STRIPE_WEBHOOKS_ENDPOINT_SECRET`
162
-
163
- Production:
164
-
165
- 1. Login and [create a new webhook](https://dashboard.stripe.com/test/webhooks/create) from the Stripe dashboard
166
- 1. Paste `YOUR_DOMAIN_NAME/api/stripe/webhooks` as the "Webhook Endpoint URL"
167
- 1. Select which events to broadcast
168
- 1. Paste the given secret into your `.env` file as `STRIPE_WEBHOOKS_ENDPOINT_SECRET`
169
- 1. Then, handle these events using the `webhooks` portion of this plugin's config:
170
-
171
- ```js
172
- import { buildConfig } from "payload/config";
173
- import stripePlugin from "@payloadcms/plugin-stripe";
174
-
175
- const config = buildConfig({
176
- plugins: [
177
- stripePlugin({
178
- stripeSecretKey: process.env.STRIPE_SECRET_KEY,
179
- stripeWebhooksEndpointSecret: process.env.STRIPE_WEBHOOKS_ENDPOINT_SECRET,
180
- webhooks: {
181
- "customer.subscription.updated": ({ event, stripe, stripeConfig }) => {
182
- // do something...
183
- },
184
- },
185
- // NOTE: you can also catch all Stripe webhook events and handle the event types yourself
186
- // webhooks: (event, stripe, stripeConfig) => {
187
- // switch (event.type): {
188
- // case 'customer.subscription.updated': {
189
- // // do something...
190
- // break;
191
- // }
192
- // default: {
193
- // break;
194
- // }
195
- // }
196
- // }
197
- }),
198
- ],
199
- });
200
-
201
- export default config;
202
- ```
203
-
204
- For a full list of available webhooks, see [here](https://stripe.com/docs/cli/trigger#trigger-event).
205
-
206
- ### Node
207
-
208
- On the server you should interface with Stripe directly using the [stripe](https://www.npmjs.com/package/stripe) npm module. That might look something like this:
209
-
210
- ```js
211
- import Stripe from "stripe";
212
-
213
- const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
214
- const stripe = new Stripe(stripeSecretKey, { apiVersion: "2022-08-01" });
215
-
216
- export const MyFunction = async () => {
217
- try {
218
- const customer = await stripe.customers.create({
219
- email: data.email,
220
- });
221
-
222
- // do something...
223
- } catch (error) {
224
- console.error(error.message);
225
- }
226
- };
227
- ```
228
-
229
- Alternatively, you can interface with the Stripe using the `stripeProxy`, which is exactly what the `/api/stripe/rest` endpoint does behind-the-scenes. Here's the same example as above, but piped through the proxy:
230
-
231
- ```js
232
- import { stripeProxy } from "@payloadcms/plugin-stripe";
233
-
234
- export const MyFunction = async () => {
235
- try {
236
- const customer = await stripeProxy({
237
- stripeSecretKey: process.env.STRIPE_SECRET_KEY,
238
- stripeMethod: "customers.create",
239
- stripeArgs: [
240
- {
241
- email: data.email,
242
- },
243
- ],
244
- });
245
-
246
- if (customer.status === 200) {
247
- // do something...
248
- }
249
-
250
- if (customer.status >= 400) {
251
- throw new Error(customer.message);
252
- }
253
- } catch (error) {
254
- console.error(error.message);
255
- }
256
- };
257
- ```
258
-
259
- ## TypeScript
260
-
261
- All types can be directly imported:
262
-
263
- ```js
264
- import {
265
- StripeConfig,
266
- StripeWebhookHandler,
267
- StripeProxy,
268
- ...
269
- } from '@payloadcms/plugin-stripe/types';
270
- ```
271
-
272
- ### Development
273
-
274
- For development purposes, there is a full working example of how this plugin might be used in the [demo](./demo) of this repo. This demo can be developed locally using any Stripe account, you just need a working API key. Then:
275
-
276
- ```bash
277
- git clone git@github.com:payloadcms/plugin-stripe.git \
278
- cd plugin-stripe && yarn \
279
- cd demo && yarn \
280
- cp .env.example .env \
281
- vim .env \ # add your Stripe creds to this file
282
- yarn dev
283
- ```
284
-
285
- Now you have a running Payload server with this plugin installed, so you can authenticate and begin hitting the routes. To do this, open [Postman](https://www.postman.com/) and import [our config](https://github.com/payloadcms/plugin-stripe/blob/main/src/payload-stripe-plugin.postman_collection.json). First, login to retrieve your Payload access token. This token is automatically attached to the header of all other requests.
286
-
287
- ## Screenshots
288
-
289
- <!-- ![screenshot 1](https://github.com/@payloadcms/plugin-stripe/blob/main/images/screenshot-1.jpg?raw=true) -->
5
+ - [Source code](https://github.com/payloadcms/payload/tree/main/packages/plugin-stripe)
6
+ - [Documentation](https://payloadcms.com/docs/plugins/stripe)
7
+ - [Documentation source](https://github.com/payloadcms/payload/tree/main/docs/plugins/stripe.mdx)
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'payload/config';
2
+ import type { StripeConfig } from './types.js';
3
+ export declare const stripePlugin: (incomingStripeConfig: StripeConfig) => (config: Config) => Config;
4
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAE5C,OAAO,KAAK,EAAyB,YAAY,EAAE,MAAM,YAAY,CAAA;AAIrE,eAAO,MAAM,YAAY,yBACA,YAAY,cAC1B,MAAM,KAAG,MAmCjB,CAAA"}
package/dist/admin.js ADDED
@@ -0,0 +1,34 @@
1
+ import { getFields } from './fields/getFields.js';
2
+ export const stripePlugin = (incomingStripeConfig)=>(config)=>{
3
+ const { collections } = config;
4
+ // set config defaults here
5
+ const stripeConfig = {
6
+ ...incomingStripeConfig,
7
+ // TODO: in the next major version, default this to `false`
8
+ rest: incomingStripeConfig?.rest ?? true,
9
+ sync: incomingStripeConfig?.sync || []
10
+ };
11
+ // NOTE: env variables are never passed to the client, but we need to know if `stripeSecretKey` is a test key
12
+ // unfortunately we must set the 'isTestKey' property on the config instead of using the following code:
13
+ // const isTestKey = stripeConfig.stripeSecretKey?.startsWith('sk_test_');
14
+ return {
15
+ ...config,
16
+ collections: collections?.map((collection)=>{
17
+ const syncConfig = stripeConfig.sync?.find((sync)=>sync.collection === collection.slug);
18
+ if (syncConfig) {
19
+ const fields = getFields({
20
+ collection,
21
+ stripeConfig,
22
+ syncConfig
23
+ });
24
+ return {
25
+ ...collection,
26
+ fields
27
+ };
28
+ }
29
+ return collection;
30
+ })
31
+ };
32
+ };
33
+
34
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/admin.ts"],"sourcesContent":["import type { Config } from 'payload/config'\n\nimport type { SanitizedStripeConfig, StripeConfig } from './types.js'\n\nimport { getFields } from './fields/getFields.js'\n\nexport const stripePlugin =\n (incomingStripeConfig: StripeConfig) =>\n (config: Config): Config => {\n const { collections } = config\n\n // set config defaults here\n const stripeConfig: SanitizedStripeConfig = {\n ...incomingStripeConfig,\n // TODO: in the next major version, default this to `false`\n rest: incomingStripeConfig?.rest ?? true,\n sync: incomingStripeConfig?.sync || [],\n }\n\n // NOTE: env variables are never passed to the client, but we need to know if `stripeSecretKey` is a test key\n // unfortunately we must set the 'isTestKey' property on the config instead of using the following code:\n // const isTestKey = stripeConfig.stripeSecretKey?.startsWith('sk_test_');\n\n return {\n ...config,\n collections: collections?.map((collection) => {\n const syncConfig = stripeConfig.sync?.find((sync) => sync.collection === collection.slug)\n\n if (syncConfig) {\n const fields = getFields({\n collection,\n stripeConfig,\n syncConfig,\n })\n return {\n ...collection,\n fields,\n }\n }\n\n return collection\n }),\n }\n }\n"],"names":["getFields","stripePlugin","incomingStripeConfig","config","collections","stripeConfig","rest","sync","map","collection","syncConfig","find","slug","fields"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAIA,SAASA,SAAS,QAAQ,wBAAuB;AAEjD,OAAO,MAAMC,eACX,CAACC,uBACD,CAACC;QACC,MAAM,EAAEC,WAAW,EAAE,GAAGD;QAExB,2BAA2B;QAC3B,MAAME,eAAsC;YAC1C,GAAGH,oBAAoB;YACvB,2DAA2D;YAC3DI,MAAMJ,sBAAsBI,QAAQ;YACpCC,MAAML,sBAAsBK,QAAQ,EAAE;QACxC;QAEA,6GAA6G;QAC7G,wGAAwG;QACxG,0EAA0E;QAE1E,OAAO;YACL,GAAGJ,MAAM;YACTC,aAAaA,aAAaI,IAAI,CAACC;gBAC7B,MAAMC,aAAaL,aAAaE,IAAI,EAAEI,KAAK,CAACJ,OAASA,KAAKE,UAAU,KAAKA,WAAWG,IAAI;gBAExF,IAAIF,YAAY;oBACd,MAAMG,SAASb,UAAU;wBACvBS;wBACAJ;wBACAK;oBACF;oBACA,OAAO;wBACL,GAAGD,UAAU;wBACbI;oBACF;gBACF;gBAEA,OAAOJ;YACT;QACF;IACF,EAAC"}
@@ -0,0 +1,12 @@
1
+ import type { CollectionConfig, Field } from 'payload/types';
2
+ import type { SanitizedStripeConfig } from '../types.js';
3
+ interface Args {
4
+ collection: CollectionConfig;
5
+ stripeConfig: SanitizedStripeConfig;
6
+ syncConfig: {
7
+ stripeResourceType: string;
8
+ };
9
+ }
10
+ export declare const getFields: ({ collection, stripeConfig, syncConfig }: Args) => Field[];
11
+ export {};
12
+ //# sourceMappingURL=getFields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getFields.d.ts","sourceRoot":"","sources":["../../src/fields/getFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAE5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAIxD,UAAU,IAAI;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,YAAY,EAAE,qBAAqB,CAAA;IACnC,UAAU,EAAE;QACV,kBAAkB,EAAE,MAAM,CAAA;KAC3B,CAAA;CACF;AAED,eAAO,MAAM,SAAS,6CAA8C,IAAI,KAAG,KAAK,EAyC/E,CAAA"}
@@ -0,0 +1,46 @@
1
+ import { LinkToDoc } from '../ui/LinkToDoc.js';
2
+ export const getFields = ({ collection, stripeConfig, syncConfig })=>{
3
+ const stripeIDField = {
4
+ name: 'stripeID',
5
+ type: 'text',
6
+ admin: {
7
+ position: 'sidebar',
8
+ readOnly: true
9
+ },
10
+ label: 'Stripe ID',
11
+ saveToJWT: true
12
+ };
13
+ const skipSyncField = {
14
+ name: 'skipSync',
15
+ type: 'checkbox',
16
+ admin: {
17
+ position: 'sidebar',
18
+ readOnly: true
19
+ },
20
+ label: 'Skip Sync'
21
+ };
22
+ const docUrlField = {
23
+ name: 'docUrl',
24
+ type: 'ui',
25
+ admin: {
26
+ components: {
27
+ Field: LinkToDoc
28
+ },
29
+ custom: {
30
+ isTestKey: stripeConfig.isTestKey,
31
+ nameOfIDField: 'stripeID',
32
+ stripeResourceType: syncConfig.stripeResourceType
33
+ },
34
+ position: 'sidebar'
35
+ }
36
+ };
37
+ const fields = [
38
+ ...collection.fields,
39
+ stripeIDField,
40
+ skipSyncField,
41
+ docUrlField
42
+ ];
43
+ return fields;
44
+ };
45
+
46
+ //# sourceMappingURL=getFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/fields/getFields.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload/types'\n\nimport type { SanitizedStripeConfig } from '../types.js'\n\nimport { LinkToDoc } from '../ui/LinkToDoc.js'\n\ninterface Args {\n collection: CollectionConfig\n stripeConfig: SanitizedStripeConfig\n syncConfig: {\n stripeResourceType: string\n }\n}\n\nexport const getFields = ({ collection, stripeConfig, syncConfig }: Args): Field[] => {\n const stripeIDField: Field = {\n name: 'stripeID',\n type: 'text',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n label: 'Stripe ID',\n saveToJWT: true,\n }\n\n const skipSyncField: Field = {\n name: 'skipSync',\n type: 'checkbox',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n label: 'Skip Sync',\n }\n\n const docUrlField: Field = {\n name: 'docUrl',\n type: 'ui',\n admin: {\n components: {\n Field: LinkToDoc,\n },\n custom: {\n isTestKey: stripeConfig.isTestKey,\n nameOfIDField: 'stripeID',\n stripeResourceType: syncConfig.stripeResourceType,\n },\n position: 'sidebar',\n },\n }\n\n const fields = [...collection.fields, stripeIDField, skipSyncField, docUrlField]\n\n return fields\n}\n"],"names":["LinkToDoc","getFields","collection","stripeConfig","syncConfig","stripeIDField","name","type","admin","position","readOnly","label","saveToJWT","skipSyncField","docUrlField","components","Field","custom","isTestKey","nameOfIDField","stripeResourceType","fields"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAIA,SAASA,SAAS,QAAQ,qBAAoB;AAU9C,OAAO,MAAMC,YAAY,CAAC,EAAEC,UAAU,EAAEC,YAAY,EAAEC,UAAU,EAAQ;IACtE,MAAMC,gBAAuB;QAC3BC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,UAAU;YACVC,UAAU;QACZ;QACAC,OAAO;QACPC,WAAW;IACb;IAEA,MAAMC,gBAAuB;QAC3BP,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,UAAU;YACVC,UAAU;QACZ;QACAC,OAAO;IACT;IAEA,MAAMG,cAAqB;QACzBR,MAAM;QACNC,MAAM;QACNC,OAAO;YACLO,YAAY;gBACVC,OAAOhB;YACT;YACAiB,QAAQ;gBACNC,WAAWf,aAAae,SAAS;gBACjCC,eAAe;gBACfC,oBAAoBhB,WAAWgB,kBAAkB;YACnD;YACAX,UAAU;QACZ;IACF;IAEA,MAAMY,SAAS;WAAInB,WAAWmB,MAAM;QAAEhB;QAAeQ;QAAeC;KAAY;IAEhF,OAAOO;AACT,EAAC"}
@@ -1,7 +1,12 @@
1
1
  import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload/types';
2
- import type { StripeConfig } from '../types';
3
- export type CollectionBeforeValidateHookWithArgs = (args: Parameters<CollectionBeforeValidateHook>[0] & {
2
+ import type { StripeConfig } from '../types.js';
3
+ type HookArgsWithCustomCollection = Omit<Parameters<CollectionBeforeValidateHook>[0], 'collection'> & {
4
+ collection: CollectionConfig;
5
+ };
6
+ export type CollectionBeforeValidateHookWithArgs = (args: HookArgsWithCustomCollection & {
4
7
  collection?: CollectionConfig;
5
8
  stripeConfig?: StripeConfig;
6
9
  }) => void;
7
10
  export declare const createNewInStripe: CollectionBeforeValidateHookWithArgs;
11
+ export {};
12
+ //# sourceMappingURL=createNewInStripe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createNewInStripe.d.ts","sourceRoot":"","sources":["../../src/hooks/createNewInStripe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAKnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAQ/C,KAAK,4BAA4B,GAAG,IAAI,CACtC,UAAU,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,EAC3C,YAAY,CACb,GAAG;IACF,UAAU,EAAE,gBAAgB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,oCAAoC,GAAG,CACjD,IAAI,EAAE,4BAA4B,GAAG;IACnC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,YAAY,CAAC,EAAE,YAAY,CAAA;CAC5B,KACE,IAAI,CAAA;AAET,eAAO,MAAM,iBAAiB,EAAE,oCA8G/B,CAAA"}
@@ -1,130 +1,76 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
1
+ import { APIError } from 'payload/errors';
2
+ import Stripe from 'stripe';
3
+ import { deepen } from '../utilities/deepen.js';
4
+ const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
5
+ // api version can only be the latest, stripe recommends ts ignoring it
6
+ const stripe = new Stripe(stripeSecretKey || '', {
7
+ apiVersion: '2022-08-01'
8
+ });
9
+ export const createNewInStripe = async (args)=>{
10
+ const { collection, data, operation, req, stripeConfig } = args;
11
+ const { logs, sync } = stripeConfig || {};
12
+ const payload = req?.payload;
13
+ const dataRef = data || {};
14
+ if (process.env.NODE_ENV === 'test') {
15
+ dataRef.stripeID = 'test';
16
+ return dataRef;
36
17
  }
37
- };
38
- var __importDefault = (this && this.__importDefault) || function (mod) {
39
- return (mod && mod.__esModule) ? mod : { "default": mod };
40
- };
41
- Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.createNewInStripe = void 0;
43
- var errors_1 = require("payload/errors");
44
- var stripe_1 = __importDefault(require("stripe"));
45
- var deepen_1 = require("../utilities/deepen");
46
- var stripeSecretKey = process.env.STRIPE_SECRET_KEY;
47
- var stripe = new stripe_1.default(stripeSecretKey || '', { apiVersion: '2022-08-01' });
48
- var createNewInStripe = function (args) { return __awaiter(void 0, void 0, void 0, function () {
49
- var req, operation, data, collection, stripeConfig, _a, sync, logs, payload, dataRef, collectionSlug_1, syncConfig, syncedFields, stripeResource, error_1, msg, stripeResource, error_2, msg;
50
- var _b, _c;
51
- return __generator(this, function (_d) {
52
- switch (_d.label) {
53
- case 0:
54
- req = args.req, operation = args.operation, data = args.data, collection = args.collection, stripeConfig = args.stripeConfig;
55
- _a = stripeConfig || {}, sync = _a.sync, logs = _a.logs;
56
- payload = req === null || req === void 0 ? void 0 : req.payload;
57
- dataRef = data || {};
58
- if (process.env.NODE_ENV === 'test') {
59
- dataRef.stripeID = 'test';
60
- return [2 /*return*/, dataRef];
61
- }
62
- if (!payload) return [3 /*break*/, 9];
63
- if (!(data === null || data === void 0 ? void 0 : data.skipSync)) return [3 /*break*/, 1];
64
- if (logs)
65
- payload.logger.info("Bypassing collection-level hooks.");
66
- return [3 /*break*/, 9];
67
- case 1:
68
- // initialize as 'false' so that all Payload admin events sync to Stripe
69
- // then conditionally set to 'true' to for events that originate from webhooks
70
- // this will prevent webhook events from triggering an unnecessary sync / infinite loop
71
- dataRef.skipSync = false;
72
- collectionSlug_1 = (collection || {}).slug;
73
- syncConfig = sync === null || sync === void 0 ? void 0 : sync.find(function (conf) { return conf.collection === collectionSlug_1; });
74
- if (!syncConfig) return [3 /*break*/, 9];
75
- syncedFields = syncConfig.fields.reduce(function (acc, field) {
76
- var fieldPath = field.fieldPath, stripeProperty = field.stripeProperty;
18
+ if (payload) {
19
+ if (data?.skipSync) {
20
+ if (logs) payload.logger.info(`Bypassing collection-level hooks.`);
21
+ } else {
22
+ // initialize as 'false' so that all Payload admin events sync to Stripe
23
+ // then conditionally set to 'true' to for events that originate from webhooks
24
+ // this will prevent webhook events from triggering an unnecessary sync / infinite loop
25
+ dataRef.skipSync = false;
26
+ const { slug: collectionSlug } = collection || {};
27
+ const syncConfig = sync?.find((conf)=>conf.collection === collectionSlug);
28
+ if (syncConfig) {
29
+ // combine all fields of this object and match their respective values within the document
30
+ let syncedFields = syncConfig.fields.reduce((acc, field)=>{
31
+ const { fieldPath, stripeProperty } = field;
77
32
  acc[stripeProperty] = dataRef[fieldPath];
78
33
  return acc;
79
34
  }, {});
80
- syncedFields = (0, deepen_1.deepen)(syncedFields);
81
- if (!(operation === 'update')) return [3 /*break*/, 5];
82
- if (logs)
83
- payload.logger.info("A '".concat(collectionSlug_1, "' document has changed in Payload with ID: '").concat(data === null || data === void 0 ? void 0 : data.id, "', syncing with Stripe..."));
84
- if (!!dataRef.stripeID) return [3 /*break*/, 5];
85
- _d.label = 2;
86
- case 2:
87
- _d.trys.push([2, 4, , 5]);
88
- return [4 /*yield*/, ((_b = stripe === null || stripe === void 0 ? void 0 : stripe[syncConfig.stripeResourceType]) === null || _b === void 0 ? void 0 : _b.create(syncedFields))];
89
- case 3:
90
- stripeResource = _d.sent();
91
- if (logs)
92
- payload.logger.info("\u2705 Successfully created new '".concat(syncConfig.stripeResourceType, "' resource in Stripe with ID: '").concat(stripeResource.id, "'."));
93
- dataRef.stripeID = stripeResource.id;
94
- // NOTE: this is to prevent sync in the "afterChange" hook
95
- dataRef.skipSync = true;
96
- return [3 /*break*/, 5];
97
- case 4:
98
- error_1 = _d.sent();
99
- msg = error_1 instanceof Error ? error_1.message : error_1;
100
- if (logs)
101
- payload.logger.error("- Error creating Stripe document: ".concat(msg));
102
- return [3 /*break*/, 5];
103
- case 5:
104
- if (!(operation === 'create')) return [3 /*break*/, 9];
105
- if (logs)
106
- payload.logger.info("A new '".concat(collectionSlug_1, "' document was created in Payload with ID: '").concat(data === null || data === void 0 ? void 0 : data.id, "', syncing with Stripe..."));
107
- _d.label = 6;
108
- case 6:
109
- _d.trys.push([6, 8, , 9]);
110
- if (logs)
111
- payload.logger.info("- Creating new '".concat(syncConfig.stripeResourceType, "' resource in Stripe..."));
112
- return [4 /*yield*/, ((_c = stripe === null || stripe === void 0 ? void 0 : stripe[syncConfig.stripeResourceType]) === null || _c === void 0 ? void 0 : _c.create(syncedFields))];
113
- case 7:
114
- stripeResource = _d.sent();
115
- if (logs)
116
- payload.logger.info("\u2705 Successfully created new '".concat(syncConfig.stripeResourceType, "' resource in Stripe with ID: '").concat(stripeResource.id, "'."));
117
- dataRef.stripeID = stripeResource.id;
118
- // IMPORTANT: this is to prevent sync in the "afterChange" hook
119
- dataRef.skipSync = true;
120
- return [3 /*break*/, 9];
121
- case 8:
122
- error_2 = _d.sent();
123
- msg = error_2 instanceof Error ? error_2.message : error_2;
124
- throw new errors_1.APIError("Failed to create new '".concat(syncConfig.stripeResourceType, "' resource in Stripe: ").concat(msg));
125
- case 9: return [2 /*return*/, dataRef];
35
+ syncedFields = deepen(syncedFields);
36
+ if (operation === 'update') {
37
+ if (logs) payload.logger.info(`A '${collectionSlug}' document has changed in Payload with ID: '${data?.id}', syncing with Stripe...`);
38
+ // NOTE: the Stripe document will be created in the "afterChange" hook, so create a new stripe document here if no stripeID exists
39
+ if (!dataRef.stripeID) {
40
+ try {
41
+ // NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
42
+ const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(// @ts-expect-error
43
+ syncedFields);
44
+ if (logs) payload.logger.info(`✅ Successfully created new '${syncConfig.stripeResourceType}' resource in Stripe with ID: '${stripeResource.id}'.`);
45
+ dataRef.stripeID = stripeResource.id;
46
+ // NOTE: this is to prevent sync in the "afterChange" hook
47
+ dataRef.skipSync = true;
48
+ } catch (error) {
49
+ const msg = error instanceof Error ? error.message : error;
50
+ if (logs) payload.logger.error(`- Error creating Stripe document: ${msg}`);
51
+ }
52
+ }
53
+ }
54
+ if (operation === 'create') {
55
+ if (logs) payload.logger.info(`A new '${collectionSlug}' document was created in Payload with ID: '${data?.id}', syncing with Stripe...`);
56
+ try {
57
+ if (logs) payload.logger.info(`- Creating new '${syncConfig.stripeResourceType}' resource in Stripe...`);
58
+ // NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
59
+ const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(// @ts-expect-error
60
+ syncedFields);
61
+ if (logs) payload.logger.info(`✅ Successfully created new '${syncConfig.stripeResourceType}' resource in Stripe with ID: '${stripeResource.id}'.`);
62
+ dataRef.stripeID = stripeResource.id;
63
+ // IMPORTANT: this is to prevent sync in the "afterChange" hook
64
+ dataRef.skipSync = true;
65
+ } catch (error) {
66
+ const msg = error instanceof Error ? error.message : error;
67
+ throw new APIError(`Failed to create new '${syncConfig.stripeResourceType}' resource in Stripe: ${msg}`);
68
+ }
69
+ }
70
+ }
126
71
  }
127
- });
128
- }); };
129
- exports.createNewInStripe = createNewInStripe;
72
+ }
73
+ return dataRef;
74
+ };
75
+
130
76
  //# sourceMappingURL=createNewInStripe.js.map