Skip to content

Commit

Permalink
feat: use payment provider based on URL param
Browse files Browse the repository at this point in the history
  • Loading branch information
jbranchaud authored and kodiakhq[bot] committed Mar 26, 2024
1 parent 80352de commit c263156
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 17 deletions.
5 changes: 4 additions & 1 deletion apps/testing-javascript/src/pages/thanks/purchase.tsx
Expand Up @@ -27,10 +27,13 @@ import {paymentOptions} from '../api/skill/[...skillRecordings]'
export const getServerSideProps: GetServerSideProps = async (context) => {
const {query} = context

const provider =
(query.provider instanceof Array ? query.provider[0] : query.provider) ||
'stripe'
const session_id =
query.session_id instanceof Array ? query.session_id[0] : query.session_id

const paymentProvider = paymentOptions.providers.stripe
const paymentProvider = paymentOptions.getProvider(provider)

if (!session_id || !paymentProvider) {
return {
Expand Down
5 changes: 4 additions & 1 deletion apps/testing-javascript/src/pages/welcome/index.tsx
Expand Up @@ -18,12 +18,15 @@ import {paymentOptions} from '../api/skill/[...skillRecordings]'

export const getServerSideProps: GetServerSideProps = async ({req, query}) => {
const {purchaseId: purchaseQueryParam, upgrade} = query
const provider =
(query.provider instanceof Array ? query.provider[0] : query.provider) ||
'stripe'
const session_id =
query.session_id instanceof Array ? query.session_id[0] : query.session_id
const token = await getToken({req})
const {getPurchaseDetails} = getSdk()

const paymentProvider = paymentOptions.providers.stripe
const paymentProvider = paymentOptions.getProvider(provider)

let purchaseId = purchaseQueryParam

Expand Down
36 changes: 28 additions & 8 deletions packages/commerce-server/src/providers/default-payment-options.ts
Expand Up @@ -47,20 +47,27 @@ export type StripeProviderFunction = (
) => StripeProvider

type Paypal = 'paypal-client'
type PaypalProvider = {name: 'paypal'; paymentClient: Paypal}
type PaypalProvider = {
name: 'paypal'
paymentClient: Paypal
} & PaymentProviderFunctionality
type PaypalProviderFunction = (options: {
paypalSecretKey: string
}) => PaypalProvider

type PaymentProviderOptions = StripeProvider | PaypalProvider

type SupportedProviders = {
stripe?: StripeProvider
paypal?: PaypalProvider
}
export type PaymentOptions = {
providers: {
stripe?: StripeProvider
paypal?: PaypalProvider
}
getProvider: (providerName: string) => PaymentProvider | undefined
providers: SupportedProviders
}

type ProviderNames = Readonly<Array<keyof PaymentOptions['providers']>>

// Two concepts for the providers:
// 1. We have the Payment Provider Functions (factories?) that take a few config values
// 2. We have the Payment Provider Options which are the resulting object of the above function
Expand All @@ -69,10 +76,23 @@ export const defaultPaymentOptions = (options: {
stripeProvider?: StripeProvider
paypalProvider?: PaypalProvider
}): PaymentOptions => {
const supportedProviderNames: ProviderNames = ['stripe', 'paypal'] as const

const providers: SupportedProviders = {
stripe: options.stripeProvider,
paypal: options.paypalProvider,
}

return {
providers: {
stripe: options.stripeProvider,
paypal: options.paypalProvider,
getProvider: (providerName: string) => {
for (const supportedProviderName of supportedProviderNames) {
if (providerName === supportedProviderName) {
return providers[supportedProviderName]
}
}

return undefined
},
providers,
}
}
Expand Up @@ -299,7 +299,7 @@ export const processStripeWebhook = async (
if (nextAuthOptions) {
await sendServerEmail({
email,
callbackUrl: `${process.env.NEXT_PUBLIC_URL}/welcome?purchaseId=${purchase.id}`,
callbackUrl: `${process.env.NEXT_PUBLIC_URL}/welcome?purchaseId=${purchase.id}&provider=stripe`,
nextAuthOptions,
type: 'purchase',
})
Expand Down
28 changes: 22 additions & 6 deletions packages/skill-api/src/core/services/stripe-checkout.ts
Expand Up @@ -7,7 +7,7 @@ import {
getSdk,
prisma,
} from '@skillrecordings/database'
import {first} from 'lodash'
import {first, isEmpty} from 'lodash'
import {add} from 'date-fns'
import {
getCalculatedPrice,
Expand All @@ -24,6 +24,24 @@ import {

const {stripe: defaultStripe} = defaultStripeContext

const buildSearchParams = (params: object) => {
// implementing this instead of using `URLSearchParams` because that API
// does URL encoding of values in the URL like the curly braces in
// `session_id={CHECKOUT_SESSION_ID}` which needs to get passed to stripe
// as is.
if (isEmpty(params)) {
return ''
} else {
const paramsAsString = Object.entries(params)
.map(([key, value]) => {
return `${key}=${value}`
})
.join('&')

return paramsAsString
}
}

/**
* Given a specific user we want to lookup their Stripe
* customer ID and if one doesn't exist we will
Expand Down Expand Up @@ -414,17 +432,15 @@ export async function stripeCheckout({
}

if (isUpgrade) {
const queryParamString = new URLSearchParams({
const queryParamString = buildSearchParams({
...baseQueryParams,
upgrade: 'true',
}).toString()
})
const url = `${process.env.NEXT_PUBLIC_URL}/welcome?${queryParamString}`

return url
} else {
const queryParamString = new URLSearchParams(
baseQueryParams,
).toString()
const queryParamString = buildSearchParams(baseQueryParams)

const url = `${process.env.NEXT_PUBLIC_URL}/thanks/purchase?${queryParamString}`
return url
Expand Down

0 comments on commit c263156

Please sign in to comment.