Skip to content

Commit

Permalink
feat: Add Klarna components
Browse files Browse the repository at this point in the history
  • Loading branch information
acasazza committed Apr 12, 2022
1 parent 87f2520 commit 21bbc01
Show file tree
Hide file tree
Showing 2 changed files with 324 additions and 0 deletions.
229 changes: 229 additions & 0 deletions src/components/KlarnaPayment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import React, {
FunctionComponent,
SyntheticEvent,
useContext,
useEffect,
useRef,
useState,
} from 'react'
import PaymentMethodContext from '#context/PaymentMethodContext'
import {
CardElement,
Elements,
useElements,
useStripe,
} from '@stripe/react-stripe-js'
import {
StripeCardElementOptions,
StripeElementLocale,
} from '@stripe/stripe-js'
import { PaymentMethodConfig } from '#reducers/PaymentMethodReducer'
import { PaymentSourceProps } from './PaymentSource'
import Parent from './utils/Parent'
import OrderContext from '#context/OrderContext'
import { setCustomerOrderParam } from '#utils/localStorage'

export type KlarnaConfig = {
// containerClassName?: string
// hintLabel?: string
// name?: string
// options?: StripeCardElementOptions
[key: string]: any
}

// type KlarnaPaymentFormProps = {
// options?: StripeCardElementOptions
// templateCustomerSaveToWallet?: PaymentSourceProps['templateCustomerSaveToWallet']
// }

// const defaultOptions = {
// style: {
// base: {
// fontSize: '16px',
// color: '#424770',
// '::placeholder': {
// color: '#aab7c4',
// },
// },
// invalid: {
// color: '#9e2146',
// },
// },
// hidePostalCode: true,
// }

// const StripePaymentForm: FunctionComponent<KlarnaPaymentFormProps> = ({
// options = defaultOptions,
// templateCustomerSaveToWallet,
// }) => {
// const ref = useRef<null | HTMLFormElement>(null)
// const {
// setPaymentSource,
// paymentSource,
// currentPaymentMethodType,
// setPaymentMethodErrors,
// setPaymentRef,
// } = useContext(PaymentMethodContext)
// const { order } = useContext(OrderContext)
// const stripe = useStripe()
// const elements = useElements()
// useEffect(() => {
// if (ref.current && stripe && elements) {
// ref.current.onsubmit = () =>
// onSubmit(ref.current as any, stripe, elements)
// setPaymentRef({ ref })
// }
// return () => {
// setPaymentRef({ ref: { current: null } })
// }
// }, [ref, stripe, elements])
// const onSubmit = async (
// event: SyntheticEvent<HTMLFormElement>,
// stripe: any,
// elements: any
// ): Promise<boolean> => {
// if (!stripe) return false

// const savePaymentSourceToCustomerWallet =
// // @ts-ignore
// event?.elements?.['save_payment_source_to_customer_wallet']?.checked
// if (savePaymentSourceToCustomerWallet)
// setCustomerOrderParam(
// '_save_payment_source_to_customer_wallet',
// savePaymentSourceToCustomerWallet
// )

// const cardElement = elements && elements.getElement(CardElement)
// if (cardElement) {
// const billingInfo = order?.billing_address
// const email = order?.customer_email
// const billing_details = {
// name: billingInfo?.full_name,
// email,
// phone: billingInfo?.phone,
// address: {
// city: billingInfo?.city,
// country: billingInfo?.country_code,
// line1: billingInfo?.line_1,
// postal_code: billingInfo?.zip_code,
// state: billingInfo?.state_code,
// },
// }
// const { paymentMethod } = await stripe.createPaymentMethod({
// type: 'card',
// card: cardElement,
// billing_details,
// })
// // @ts-ignore
// if (paymentSource?.client_secret) {
// const { error, paymentIntent } = await stripe.confirmCardPayment(
// // @ts-ignore
// paymentSource.client_secret,
// {
// payment_method: {
// card: cardElement,
// billing_details,
// },
// }
// )
// if (error) {
// console.error(error)
// setPaymentMethodErrors([
// {
// code: 'PAYMENT_INTENT_AUTHENTICATION_FAILURE',
// resource: 'payment_methods',
// field: currentPaymentMethodType,
// message: error.message as string,
// },
// ])
// } else {
// if (
// paymentIntent &&
// paymentMethod &&
// paymentSource &&
// currentPaymentMethodType
// ) {
// try {
// await setPaymentSource({
// paymentSourceId: paymentSource.id,
// paymentResource: currentPaymentMethodType,
// attributes: {
// options: {
// ...(paymentMethod as Record<string, any>),
// setup_future_usage: 'off_session',
// },
// },
// })
// return true
// } catch (e) {
// return false
// }
// }
// }
// }
// }
// return false
// }

// return (
// <form ref={ref} onSubmit={(e) => onSubmit(e, stripe, elements)}>
// <CardElement options={{ ...defaultOptions, ...options }} />
// {templateCustomerSaveToWallet && (
// <Parent {...{ name: 'save_payment_source_to_customer_wallet' }}>
// {templateCustomerSaveToWallet}
// </Parent>
// )}
// </form>
// )
// }

type KlarnaPaymentProps = PaymentMethodConfig['klarnaPayment'] &
JSX.IntrinsicElements['div'] &
Partial<PaymentSourceProps['templateCustomerSaveToWallet']> & {
show?: boolean
clientToken: string
locale?: string
}

export default function KlarnaPayment({
clientToken,
show,
options,
locale = 'auto',
...p
}: KlarnaPaymentProps) {
const [isLoaded, setIsLoaded] = useState(false)
const [stripe, setStripe] = useState(null)
const {
containerClassName,
templateCustomerSaveToWallet,
fonts = [],
...divProps
} = p
useEffect(() => {
if (show && clientToken) {
const { loadStripe } = require('@stripe/stripe-js')
const getStripe = async () => {
const res = await loadStripe(clientToken, {
locale,
})
setStripe(res)
setIsLoaded(true)
}
getStripe()
}
return () => {
setIsLoaded(false)
}
}, [show, clientToken])
return isLoaded && stripe ? (
<div className={containerClassName} {...divProps}>
<Elements stripe={stripe} options={{ fonts }}>
{/* <StripePaymentForm
options={options}
templateCustomerSaveToWallet={templateCustomerSaveToWallet}
/> */}
</Elements>
</div>
) : null
}
95 changes: 95 additions & 0 deletions src/components/gateways/KlarnaGateway.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { GatewayBaseType } from '#components/PaymentGateway'
import StripePayment from '#components/StripePayment'
import CustomerContext from '#context/CustomerContext'
import OrderContext from '#context/OrderContext'
import PaymentMethodChildrenContext from '#context/PaymentMethodChildrenContext'
import PaymentMethodContext from '#context/PaymentMethodContext'
import PaymentSourceContext from '#context/PaymentSourceContext'
import {
getPaymentConfig,
PaymentResource,
} from '#reducers/PaymentMethodReducer'
import isEmpty from 'lodash/isEmpty'
import React, { Fragment, useContext } from 'react'
import PaymentCardsTemplate from '../utils/PaymentCardsTemplate'

type KlarnaGateway = GatewayBaseType

export default function KlarnaGateway(props: KlarnaGateway) {
const {
readonly,
showCard,
handleEditClick,
children,
templateCustomerCards,
show,
loading,
onClickCustomerCards,
loaderComponent,
templateCustomerSaveToWallet,
...p
} = props
const { order } = useContext(OrderContext)
const { payment } = useContext(PaymentMethodChildrenContext)
const { payments, isGuest } = useContext(CustomerContext)
const { currentPaymentMethodId, config, paymentSource } =
useContext(PaymentMethodContext)
const paymentResource: PaymentResource = 'klarna_payments'
const locale = order?.language_code

if (!readonly && payment?.id !== currentPaymentMethodId) return null

// @ts-ignore
const clientToken = paymentSource?.client_token
const stripeConfig = config
? getPaymentConfig<'stripePayment'>(paymentResource, config)
: {}
const customerPayments =
!isEmpty(payments) && payments
? payments.filter((customerPayment) => {
return customerPayment.payment_source?.type === 'klarna_payments'
})
: []

if (readonly || showCard) {
// @ts-ignore
const card = paymentSource?.options?.card as Record<string, any>
const value = { ...card, showCard, handleEditClick, readonly }
return isEmpty(card) ? null : (
<PaymentSourceContext.Provider value={value}>
{children}
</PaymentSourceContext.Provider>
)
}
if (!isGuest && templateCustomerCards) {
return (
<Fragment>
{isEmpty(customerPayments) ? null : (
<div className={p.className}>
<PaymentCardsTemplate {...{ paymentResource, customerPayments }}>
{templateCustomerCards}
</PaymentCardsTemplate>
</div>
)}
<StripePayment
show={show}
templateCustomerSaveToWallet={templateCustomerSaveToWallet}
clientToken={clientToken}
locale={locale}
{...stripeConfig}
/>
</Fragment>
)
}

return clientToken && !loading ? (
<StripePayment
show={show}
clientToken={clientToken}
locale={locale}
{...stripeConfig}
/>
) : (
loaderComponent
)
}

0 comments on commit 21bbc01

Please sign in to comment.