-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Expand file tree
/
Copy pathconfirmOrder.ts
More file actions
149 lines (124 loc) · 4.24 KB
/
confirmOrder.ts
File metadata and controls
149 lines (124 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import Stripe from 'stripe'
import type { PaymentAdapter } from '../../../types/index.js'
import type { StripeAdapterArgs } from './index.js'
type Props = {
apiVersion?: Stripe.StripeConfig['apiVersion']
appInfo?: Stripe.StripeConfig['appInfo']
secretKey: StripeAdapterArgs['secretKey']
}
export const confirmOrder: (props: Props) => NonNullable<PaymentAdapter>['confirmOrder'] =
(props) =>
async ({
cartsSlug = 'carts',
customersSlug = 'users',
data,
ordersSlug = 'orders',
req,
transactionsSlug = 'transactions',
}) => {
const payload = req.payload
const { apiVersion, appInfo, secretKey } = props || {}
const customerEmail = data.customerEmail
const paymentIntentID = data.paymentIntentID as string
if (!secretKey) {
throw new Error('Stripe secret key is required')
}
if (!paymentIntentID) {
throw new Error('PaymentIntent ID is required')
}
const stripe = new Stripe(secretKey, {
// API version can only be the latest, stripe recommends ts ignoring it
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - ignoring since possible versions are not type safe, only the latest version is recognised
apiVersion: apiVersion || '2025-03-31.basil',
appInfo: appInfo || {
name: 'Stripe Payload Plugin',
url: 'https://payloadcms.com',
},
})
try {
let customer = (
await stripe.customers.list({
email: customerEmail,
})
).data[0]
if (!customer?.id) {
customer = await stripe.customers.create({
email: customerEmail,
})
}
// Find our existing transaction by the payment intent ID
const transactionsResults = await payload.find({
collection: transactionsSlug,
req,
where: {
'stripe.paymentIntentID': {
equals: paymentIntentID,
},
},
})
const transaction = transactionsResults.docs[0]
if (!transactionsResults.totalDocs || !transaction) {
throw new Error('No transaction found for the provided PaymentIntent ID')
}
const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentID)
if (paymentIntent.status !== 'succeeded') {
throw new Error(`Payment not completed.`)
}
const cartID = paymentIntent.metadata.cartID
const cartItemsSnapshot = paymentIntent.metadata.cartItemsSnapshot
? JSON.parse(paymentIntent.metadata.cartItemsSnapshot)
: undefined
const shippingAddress = paymentIntent.metadata.shippingAddress
? JSON.parse(paymentIntent.metadata.shippingAddress)
: undefined
if (!cartID) {
throw new Error('Cart ID not found in the PaymentIntent metadata')
}
if (!cartItemsSnapshot || !Array.isArray(cartItemsSnapshot)) {
throw new Error('Cart items snapshot not found or invalid in the PaymentIntent metadata')
}
const order = await payload.create({
collection: ordersSlug,
data: {
amount: paymentIntent.amount,
currency: paymentIntent.currency.toUpperCase(),
...(req.user?.collection === customersSlug
? { customer: req.user.id }
: { customerEmail }),
items: cartItemsSnapshot,
shippingAddress,
status: 'processing',
transactions: [transaction.id],
},
req,
})
const timestamp = new Date().toISOString()
await payload.update({
id: cartID,
collection: cartsSlug,
data: {
purchasedAt: timestamp,
},
req,
})
await payload.update({
id: transaction.id,
collection: transactionsSlug,
data: {
order: order.id,
status: 'succeeded',
},
req,
})
return {
message: 'Payment initiated successfully',
orderID: order.id,
transactionID: transaction.id,
...(order.accessToken ? { accessToken: order.accessToken } : {}),
}
} catch (error) {
payload.logger.error({ err: error, msg: 'Error confirming order with Stripe' })
throw new Error(error instanceof Error ? error.message : 'Unknown error initiating payment')
}
}