feat(plugin-stripe): update plugin stripe for v3 (#6019)

This commit is contained in:
Paul
2024-05-02 16:19:27 -03:00
committed by GitHub
parent c0ae287d46
commit 75a95469b2
19 changed files with 104 additions and 99 deletions

View File

@@ -24,8 +24,8 @@
"exports": { "exports": {
".": { ".": {
"import": "./src/index.ts", "import": "./src/index.ts",
"require": "./src/index.ts", "types": "./src/index.ts",
"types": "./src/index.ts" "default": "./src/index.ts"
} }
}, },
"main": "./src/index.ts", "main": "./src/index.ts",
@@ -36,12 +36,14 @@
"types.d.ts" "types.d.ts"
], ],
"scripts": { "scripts": {
"build": "echo \"Build temporarily disabled.\" && exit 0", "build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
"build:swc": "swc ./src -d ./dist --config-file .swcrc", "build:swc": "swc ./src -d ./dist --config-file .swcrc",
"build:types": "tsc --emitDeclarationOnly --outDir dist", "build:types": "tsc --emitDeclarationOnly --outDir dist",
"clean": "rimraf {dist,*.tsbuildinfo}", "clean": "rimraf {dist,*.tsbuildinfo}",
"prepublishOnly": "pnpm clean && pnpm turbo run build && pnpm test", "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
"test": "echo 'No tests available.'" "lint": "eslint src",
"lint:fix": "eslint --fix --ext .ts,.tsx src",
"prepublishOnly": "pnpm clean && pnpm turbo build"
}, },
"dependencies": { "dependencies": {
"@payloadcms/ui": "workspace:*", "@payloadcms/ui": "workspace:*",
@@ -51,17 +53,19 @@
}, },
"devDependencies": { "devDependencies": {
"@payloadcms/eslint-config": "workspace:*", "@payloadcms/eslint-config": "workspace:*",
"@payloadcms/next": "workspace:*",
"@payloadcms/translations": "workspace:*",
"@payloadcms/ui": "workspace:*",
"@types/express": "^4.17.9", "@types/express": "^4.17.9",
"@types/lodash.get": "^4.4.7", "@types/lodash.get": "^4.4.7",
"@types/react": "18.2.74", "@types/react": "18.2.74",
"@types/uuid": "^9.0.0", "@types/uuid": "^9.0.0",
"payload": "workspace:*", "payload": "workspace:*"
"prettier": "^2.7.1",
"webpack": "^5.78.0"
}, },
"peerDependencies": { "peerDependencies": {
"payload": "workspace:*", "@payloadcms/translations": "workspace:*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0" "@payloadcms/ui": "workspace:*",
"payload": "workspace:*"
}, },
"publishConfig": { "publishConfig": {
"exports": { "exports": {
@@ -72,6 +76,7 @@
} }
}, },
"main": "./dist/index.js", "main": "./dist/index.js",
"registry": "https://registry.npmjs.org/",
"types": "./dist/index.d.ts" "types": "./dist/index.d.ts"
}, },
"homepage:": "https://payloadcms.com" "homepage:": "https://payloadcms.com"

View File

@@ -1,10 +1,10 @@
import type { Config } from 'payload/config' import type { Config } from 'payload/config'
import type { SanitizedStripeConfig, StripeConfig } from './types' import type { SanitizedStripeConfig, StripeConfig } from './types.js'
import { getFields } from './fields/getFields' import { getFields } from './fields/getFields.js'
const stripePlugin = export const stripePlugin =
(incomingStripeConfig: StripeConfig) => (incomingStripeConfig: StripeConfig) =>
(config: Config): Config => { (config: Config): Config => {
const { collections } = config const { collections } = config
@@ -42,5 +42,3 @@ const stripePlugin =
}), }),
} }
} }
export default stripePlugin

View File

@@ -1,8 +1,8 @@
import type { CollectionConfig, Field } from 'payload/types' import type { CollectionConfig, Field } from 'payload/types'
import type { SanitizedStripeConfig } from '../types' import type { SanitizedStripeConfig } from '../types.js'
import { LinkToDoc } from '../ui/LinkToDoc' import { LinkToDoc } from '../ui/LinkToDoc.js'
interface Args { interface Args {
collection: CollectionConfig collection: CollectionConfig
@@ -39,13 +39,12 @@ export const getFields = ({ collection, stripeConfig, syncConfig }: Args): Field
type: 'ui', type: 'ui',
admin: { admin: {
components: { components: {
Field: (args) => Field: LinkToDoc,
LinkToDoc({ },
...args, custom: {
isTestKey: stripeConfig.isTestKey, isTestKey: stripeConfig.isTestKey,
nameOfIDField: 'stripeID', nameOfIDField: 'stripeID',
stripeResourceType: syncConfig.stripeResourceType, stripeResourceType: syncConfig.stripeResourceType,
}),
}, },
position: 'sidebar', position: 'sidebar',
}, },

View File

@@ -3,11 +3,12 @@ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload/typ
import { APIError } from 'payload/errors' import { APIError } from 'payload/errors'
import Stripe from 'stripe' import Stripe from 'stripe'
import type { StripeConfig } from '../types' import type { StripeConfig } from '../types.js'
import { deepen } from '../utilities/deepen' import { deepen } from '../utilities/deepen.js'
const stripeSecretKey = process.env.STRIPE_SECRET_KEY const stripeSecretKey = process.env.STRIPE_SECRET_KEY
// api version can only be the latest, stripe recommends ts ignoring it
const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' }) const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' })
type HookArgsWithCustomCollection = Omit< type HookArgsWithCustomCollection = Omit<
@@ -52,12 +53,15 @@ export const createNewInStripe: CollectionBeforeValidateHookWithArgs = async (ar
if (syncConfig) { if (syncConfig) {
// combine all fields of this object and match their respective values within the document // combine all fields of this object and match their respective values within the document
let syncedFields = syncConfig.fields.reduce((acc, field) => { let syncedFields = syncConfig.fields.reduce(
(acc, field) => {
const { fieldPath, stripeProperty } = field const { fieldPath, stripeProperty } = field
acc[stripeProperty] = dataRef[fieldPath] acc[stripeProperty] = dataRef[fieldPath]
return acc return acc
}, {} as Record<string, any>) },
{} as Record<string, any>,
)
syncedFields = deepen(syncedFields) syncedFields = deepen(syncedFields)
@@ -72,6 +76,7 @@ export const createNewInStripe: CollectionBeforeValidateHookWithArgs = async (ar
try { try {
// NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources // NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create( const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(
// @ts-expect-error
syncedFields, syncedFields,
) )
@@ -105,6 +110,7 @@ export const createNewInStripe: CollectionBeforeValidateHookWithArgs = async (ar
// NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources // NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create( const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(
// @ts-expect-error
syncedFields, syncedFields,
) )

View File

@@ -3,9 +3,10 @@ import type { CollectionAfterDeleteHook, CollectionConfig } from 'payload/types'
import { APIError } from 'payload/errors' import { APIError } from 'payload/errors'
import Stripe from 'stripe' import Stripe from 'stripe'
import type { StripeConfig } from '../types' import type { StripeConfig } from '../types.js'
const stripeSecretKey = process.env.STRIPE_SECRET_KEY const stripeSecretKey = process.env.STRIPE_SECRET_KEY
// api version can only be the latest, stripe recommends ts ignoring it
const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' }) const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' })
type HookArgsWithCustomCollection = Omit<Parameters<CollectionAfterDeleteHook>[0], 'collection'> & { type HookArgsWithCustomCollection = Omit<Parameters<CollectionAfterDeleteHook>[0], 'collection'> & {

View File

@@ -3,11 +3,12 @@ import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload/types
import { APIError } from 'payload/errors' import { APIError } from 'payload/errors'
import Stripe from 'stripe' import Stripe from 'stripe'
import type { StripeConfig } from '../types' import type { StripeConfig } from '../types.js'
import { deepen } from '../utilities/deepen' import { deepen } from '../utilities/deepen.js'
const stripeSecretKey = process.env.STRIPE_SECRET_KEY const stripeSecretKey = process.env.STRIPE_SECRET_KEY
// api version can only be the latest, stripe recommends ts ignoring it
const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' }) const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' })
type HookArgsWithCustomCollection = Omit< type HookArgsWithCustomCollection = Omit<
@@ -39,12 +40,15 @@ export const syncExistingWithStripe: CollectionBeforeChangeHookWithArgs = async
if (syncConfig) { if (syncConfig) {
if (operation === 'update') { if (operation === 'update') {
// combine all fields of this object and match their respective values within the document // combine all fields of this object and match their respective values within the document
let syncedFields = syncConfig.fields.reduce((acc, field) => { let syncedFields = syncConfig.fields.reduce(
(acc, field) => {
const { fieldPath, stripeProperty } = field const { fieldPath, stripeProperty } = field
acc[stripeProperty] = data[fieldPath] acc[stripeProperty] = data[fieldPath]
return acc return acc
}, {} as Record<string, any>) },
{} as Record<string, any>,
)
syncedFields = deepen(syncedFields) syncedFields = deepen(syncedFields)

View File

@@ -9,7 +9,10 @@ import { syncExistingWithStripe } from './hooks/syncExistingWithStripe.js'
import { stripeREST } from './routes/rest.js' import { stripeREST } from './routes/rest.js'
import { stripeWebhooks } from './routes/webhooks.js' import { stripeWebhooks } from './routes/webhooks.js'
const stripePlugin = export { LinkToDoc } from './ui/LinkToDoc.js'
export { stripeProxy } from './utilities/stripeProxy.js'
export const stripePlugin =
(incomingStripeConfig: StripeConfig) => (incomingStripeConfig: StripeConfig) =>
(config: Config): Config => { (config: Config): Config => {
const { collections } = config const { collections } = config
@@ -112,5 +115,3 @@ const stripePlugin =
endpoints, endpoints,
} }
} }
export default stripePlugin

View File

@@ -1,9 +0,0 @@
export const createNewInStripe = () => null
export const deleteFromStripe = () => null
export const stripeREST = () => null
export const stripeWebhooks = () => null
export const syncExistingWithStripe = () => null
export default {
raw: () => {}, // mock express fn
}

View File

@@ -33,7 +33,9 @@ export const stripeREST = async (args: {
} }
responseJSON = await stripeProxy({ responseJSON = await stripeProxy({
// @ts-expect-error
stripeArgs, stripeArgs,
// @ts-expect-error
stripeMethod, stripeMethod,
stripeSecretKey, stripeSecretKey,
}) })

View File

@@ -19,6 +19,7 @@ export const stripeWebhooks = async (args: {
if (stripeWebhooksEndpointSecret) { if (stripeWebhooksEndpointSecret) {
const stripe = new Stripe(stripeSecretKey, { const stripe = new Stripe(stripeSecretKey, {
// api version can only be the latest, stripe recommends ts ignoring it
apiVersion: '2022-08-01', apiVersion: '2022-08-01',
appInfo: { appInfo: {
name: 'Stripe Payload Plugin', name: 'Stripe Payload Plugin',
@@ -26,17 +27,14 @@ export const stripeWebhooks = async (args: {
}, },
}) })
const body = await req.text()
const stripeSignature = req.headers.get('stripe-signature') const stripeSignature = req.headers.get('stripe-signature')
if (stripeSignature) { if (stripeSignature) {
let event: Stripe.Event | undefined let event: Stripe.Event | undefined
try { try {
event = stripe.webhooks.constructEvent( event = stripe.webhooks.constructEvent(body, stripeSignature, stripeWebhooksEndpointSecret)
await req.text(),
stripeSignature,
stripeWebhooksEndpointSecret,
)
} catch (err: unknown) { } catch (err: unknown) {
const msg: string = err instanceof Error ? err.message : JSON.stringify(err) const msg: string = err instanceof Error ? err.message : JSON.stringify(err)
req.payload.logger.error(`Error constructing Stripe event: ${msg}`) req.payload.logger.error(`Error constructing Stripe event: ${msg}`)

View File

@@ -1,17 +1,15 @@
'use client'
import type { CustomComponent } from 'payload/config'
import type { UIField } from 'payload/types' import type { UIField } from 'payload/types'
import { useFieldProps } from '@payloadcms/ui/forms/FieldPropsProvider'
// import CopyToClipboard from 'payload/dist/admin/components/elements/CopyToClipboard' // import CopyToClipboard from 'payload/dist/admin/components/elements/CopyToClipboard'
import { useFormFields } from '@payloadcms/ui/forms/Form' import { useFormFields } from '@payloadcms/ui/forms/Form'
import React from 'react' import React from 'react'
export const LinkToDoc: React.FC< export const LinkToDoc: CustomComponent<UIField> = () => {
UIField & { const { custom } = useFieldProps()
isTestKey: boolean const { isTestKey, nameOfIDField, stripeResourceType } = custom
nameOfIDField: string
stripeResourceType: string
}
> = (props) => {
const { isTestKey, nameOfIDField, stripeResourceType } = props
const field = useFormFields(([fields]) => fields[nameOfIDField]) const field = useFormFields(([fields]) => fields[nameOfIDField])
const { value: stripeID } = field || {} const { value: stripeID } = field || {}

View File

@@ -1,7 +1,7 @@
import lodashGet from 'lodash.get' import lodashGet from 'lodash.get'
import Stripe from 'stripe' import Stripe from 'stripe'
import type { StripeProxy } from '../types' import type { StripeProxy } from '../types.js'
export const stripeProxy: StripeProxy = async ({ stripeArgs, stripeMethod, stripeSecretKey }) => { export const stripeProxy: StripeProxy = async ({ stripeArgs, stripeMethod, stripeSecretKey }) => {
const stripe = new Stripe(stripeSecretKey, { const stripe = new Stripe(stripeSecretKey, {

View File

@@ -1,8 +1,8 @@
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types' import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types.js'
import { deepen } from '../utilities/deepen' import { deepen } from '../utilities/deepen.js'
type HandleCreatedOrUpdated = ( type HandleCreatedOrUpdated = (
args: Parameters<StripeWebhookHandler>[0] & { args: Parameters<StripeWebhookHandler>[0] & {
@@ -62,12 +62,15 @@ export const handleCreatedOrUpdated: HandleCreatedOrUpdated = async (args) => {
const foundDoc = payloadQuery.docs[0] as any const foundDoc = payloadQuery.docs[0] as any
// combine all properties of the Stripe doc and match their respective fields within the document // combine all properties of the Stripe doc and match their respective fields within the document
let syncedData = syncConfig.fields.reduce((acc, field) => { let syncedData = syncConfig.fields.reduce(
(acc, field) => {
const { fieldPath, stripeProperty } = field const { fieldPath, stripeProperty } = field
acc[fieldPath] = stripeDoc[stripeProperty] acc[fieldPath] = stripeDoc[stripeProperty]
return acc return acc
}, {} as Record<string, any>) },
{} as Record<string, any>,
)
syncedData = deepen({ syncedData = deepen({
...syncedData, ...syncedData,

View File

@@ -1,4 +1,4 @@
import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types' import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types.js'
type HandleDeleted = ( type HandleDeleted = (
args: Parameters<StripeWebhookHandler>[0] & { args: Parameters<StripeWebhookHandler>[0] & {
@@ -58,6 +58,7 @@ export const handleDeleted: HandleDeleted = async (args) => {
if (logs) payload.logger.info(`- Deleting Payload document with ID: '${foundDoc.id}'...`) if (logs) payload.logger.info(`- Deleting Payload document with ID: '${foundDoc.id}'...`)
try { try {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
payload.delete({ payload.delete({
id: foundDoc.id, id: foundDoc.id,
collection: collectionSlug, collection: collectionSlug,

View File

@@ -1,9 +1,9 @@
import type { StripeWebhookHandler } from '../types' import type { StripeWebhookHandler } from '../types.js'
import { handleCreatedOrUpdated } from './handleCreatedOrUpdated' import { handleCreatedOrUpdated } from './handleCreatedOrUpdated.js'
import { handleDeleted } from './handleDeleted' import { handleDeleted } from './handleDeleted.js'
export const handleWebhooks: StripeWebhookHandler = async (args) => { export const handleWebhooks: StripeWebhookHandler = (args) => {
const { event, payload, stripeConfig } = args const { event, payload, stripeConfig } = args
if (stripeConfig?.logs) if (stripeConfig?.logs)
@@ -21,7 +21,7 @@ export const handleWebhooks: StripeWebhookHandler = async (args) => {
if (syncConfig) { if (syncConfig) {
switch (method) { switch (method) {
case 'created': { case 'created': {
await handleCreatedOrUpdated({ void handleCreatedOrUpdated({
...args, ...args,
resourceType, resourceType,
stripeConfig, stripeConfig,
@@ -30,7 +30,7 @@ export const handleWebhooks: StripeWebhookHandler = async (args) => {
break break
} }
case 'updated': { case 'updated': {
await handleCreatedOrUpdated({ void handleCreatedOrUpdated({
...args, ...args,
resourceType, resourceType,
stripeConfig, stripeConfig,
@@ -39,7 +39,7 @@ export const handleWebhooks: StripeWebhookHandler = async (args) => {
break break
} }
case 'deleted': { case 'deleted': {
await handleDeleted({ void handleDeleted({
...args, ...args,
resourceType, resourceType,
stripeConfig, stripeConfig,

16
pnpm-lock.yaml generated
View File

@@ -1188,9 +1188,6 @@ importers:
lodash.get: lodash.get:
specifier: ^4.4.2 specifier: ^4.4.2
version: 4.4.2 version: 4.4.2
react:
specifier: ^18.0.0
version: 18.2.0
stripe: stripe:
specifier: ^10.2.0 specifier: ^10.2.0
version: 10.17.0 version: 10.17.0
@@ -1201,6 +1198,12 @@ importers:
'@payloadcms/eslint-config': '@payloadcms/eslint-config':
specifier: workspace:* specifier: workspace:*
version: link:../eslint-config-payload version: link:../eslint-config-payload
'@payloadcms/next':
specifier: workspace:*
version: link:../next
'@payloadcms/translations':
specifier: workspace:*
version: link:../translations
'@types/express': '@types/express':
specifier: ^4.17.9 specifier: ^4.17.9
version: 4.17.21 version: 4.17.21
@@ -1216,12 +1219,6 @@ importers:
payload: payload:
specifier: workspace:* specifier: workspace:*
version: link:../payload version: link:../payload
prettier:
specifier: ^2.7.1
version: 2.8.8
webpack:
specifier: ^5.78.0
version: 5.91.0(@swc/core@1.4.13)(esbuild@0.19.12)(webpack-cli@5.1.4)
packages/richtext-lexical: packages/richtext-lexical:
dependencies: dependencies:
@@ -13875,6 +13872,7 @@ packages:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
hasBin: true hasBin: true
dev: false
/prettier@3.2.5: /prettier@3.2.5:
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}

View File

@@ -52,7 +52,7 @@ const packageWhitelist = [
'plugin-redirects', 'plugin-redirects',
'plugin-search', 'plugin-search',
'plugin-seo', 'plugin-seo',
// 'plugin-stripe', 'plugin-stripe',
// 'plugin-sentry', // 'plugin-sentry',
] ]

View File

@@ -1,6 +1,7 @@
import type { CollectionConfig } from 'payload/types' import type { CollectionConfig } from 'payload/types'
import { LinkToDoc } from '../../../packages/plugin-stripe/src/ui/LinkToDoc.js' import { LinkToDoc } from '@payloadcms/plugin-stripe'
import { customersSlug } from '../shared.js' import { customersSlug } from '../shared.js'
export const Customers: CollectionConfig = { export const Customers: CollectionConfig = {
@@ -31,13 +32,12 @@ export const Customers: CollectionConfig = {
type: 'ui', type: 'ui',
admin: { admin: {
components: { components: {
Field: (args) => Field: LinkToDoc,
LinkToDoc({ },
...args, custom: {
isTestKey: process.env.PAYLOAD_PUBLIC_IS_STRIPE_TEST_KEY === 'true', isTestKey: process.env.PAYLOAD_PUBLIC_IS_STRIPE_TEST_KEY === 'true',
nameOfIDField: `${args.path}.stripeSubscriptionID`, nameOfIDField: `stripeSubscriptionID`,
stripeResourceType: 'subscriptions', stripeResourceType: 'subscriptions',
}),
}, },
}, },
label: 'Link', label: 'Link',

View File

@@ -1,4 +1,4 @@
import stripePlugin from '@payloadcms/plugin-stripe' import { stripePlugin } from '@payloadcms/plugin-stripe'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js' import { devUser } from '../credentials.js'