dd-trace
Version:
Datadog APM tracing client for JavaScript
107 lines (87 loc) • 3.46 kB
JavaScript
const shimmer = require('../../datadog-shimmer')
const { channel, addHook } = require('./helpers/instrument')
const checkoutSessionCreateFinishCh = channel('datadog:stripe:checkoutSession:create:finish')
const paymentIntentCreateFinishCh = channel('datadog:stripe:paymentIntent:create:finish')
const constructEventFinishCh = channel('datadog:stripe:constructEvent:finish')
function wrapSessionCreate (create) {
return function wrappedSessionCreate (...args) {
const promise = create.apply(this, args)
if (!checkoutSessionCreateFinishCh.hasSubscribers) return promise
return promise.then((result) => {
checkoutSessionCreateFinishCh.publish(result)
return result
})
}
}
function wrapPaymentIntentCreate (create) {
return function wrappedPaymentIntentCreate (...args) {
const promise = create.apply(this, args)
if (!paymentIntentCreateFinishCh.hasSubscribers) return promise
return promise.then((result) => {
paymentIntentCreateFinishCh.publish(result)
return result
})
}
}
function wrapConstructEvent (constructEvent) {
return function wrappedConstructEvent (...args) {
const result = constructEvent.apply(this, args)
// no need to check for hasSubscribers,
// if it's false, the publish function will be noop
constructEventFinishCh.publish(result)
return result
}
}
function wrapConstructEventAsync (constructEventAsync) {
return function wrappedConstructEventAsync (...args) {
const promise = constructEventAsync.apply(this, args)
if (!constructEventFinishCh.hasSubscribers) return promise
return promise.then((result) => {
constructEventFinishCh.publish(result)
return result
})
}
}
function instrumentStripeInstance (stripe) {
if (typeof stripe.checkout?.sessions?.create === 'function') {
shimmer.wrap(stripe.checkout.sessions, 'create', wrapSessionCreate)
}
if (typeof stripe.paymentIntents?.create === 'function') {
shimmer.wrap(stripe.paymentIntents, 'create', wrapPaymentIntentCreate)
}
if (typeof stripe.webhooks?.constructEvent === 'function') {
shimmer.wrap(stripe.webhooks, 'constructEvent', wrapConstructEvent)
}
if (typeof stripe.webhooks?.constructEventAsync === 'function') {
shimmer.wrap(stripe.webhooks, 'constructEventAsync', wrapConstructEventAsync)
}
}
// stripe <22: the constructor mutates this (when invoked with 'new') and
// returns nothing; without 'new' it delegates to 'new Stripe(...)' and returns
// that result. We need to instrument whichever object actually got populated
function wrapLegacyStripe (Stripe) {
return function wrappedStripe (...args) {
const result = Stripe.apply(this, args)
const stripe = this instanceof Stripe ? this : result
instrumentStripeInstance(stripe)
return stripe
}
}
// stripe >=22: the constructor is a factory that always returns a fresh Stripe
// instance regardless of 'new', so we just instrument and forward the result
function wrapStripe (Stripe) {
return function wrappedStripe (...args) {
const stripe = Stripe.apply(this, args)
instrumentStripeInstance(stripe)
return stripe
}
}
addHook({
name: 'stripe',
versions: ['9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '>=20.0.0 <22'],
}, Stripe => shimmer.wrapFunction(Stripe, wrapLegacyStripe))
addHook({
name: 'stripe',
versions: ['>=22'],
}, Stripe => shimmer.wrapFunction(Stripe, wrapStripe))