mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #11573 from overleaf/ii-payment-page-migration-three-d-secure
[web] Payment page migration 3DS GitOrigin-RevId: b1049882c0f9fd17af2d2974e3f59b01af353c8f
This commit is contained in:
parent
6811b9085a
commit
f6ddb95ae5
4 changed files with 65 additions and 9 deletions
|
@ -36,5 +36,3 @@ block content
|
||||||
)
|
)
|
||||||
p !{translate("for_visa_mastercard_and_discover", {}, ['strong', 'strong', 'strong'])}
|
p !{translate("for_visa_mastercard_and_discover", {}, ['strong', 'strong', 'strong'])}
|
||||||
p !{translate("for_american_express", {}, ['strong', 'strong', 'strong'])}
|
p !{translate("for_american_express", {}, ['strong', 'strong', 'strong'])}
|
||||||
|
|
||||||
+studentCheckModal
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FormGroup, ControlLabel } from 'react-bootstrap'
|
import { FormGroup, ControlLabel } from 'react-bootstrap'
|
||||||
import classnames from 'classnames'
|
|
||||||
import { CardElementChangeState } from '../../../../../../../types/recurly/elements'
|
import { CardElementChangeState } from '../../../../../../../types/recurly/elements'
|
||||||
import { ElementsInstance } from 'recurly__recurly-js'
|
import { ElementsInstance } from 'recurly__recurly-js'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
type CardElementProps = {
|
type CardElementProps = {
|
||||||
className?: string
|
className?: string
|
||||||
|
|
|
@ -15,6 +15,7 @@ import CompanyDetails from './company-details'
|
||||||
import CouponCode from './coupon-code'
|
import CouponCode from './coupon-code'
|
||||||
import TosAgreementNotice from './tos-agreement-notice'
|
import TosAgreementNotice from './tos-agreement-notice'
|
||||||
import SubmitButton from './submit-button'
|
import SubmitButton from './submit-button'
|
||||||
|
import ThreeDSecure from './three-d-secure'
|
||||||
import getMeta from '../../../../../utils/meta'
|
import getMeta from '../../../../../utils/meta'
|
||||||
import { postJSON } from '../../../../../infrastructure/fetch-json'
|
import { postJSON } from '../../../../../infrastructure/fetch-json'
|
||||||
import * as eventTracking from '../../../../../infrastructure/event-tracking'
|
import * as eventTracking from '../../../../../infrastructure/event-tracking'
|
||||||
|
@ -215,6 +216,22 @@ function CheckoutPanel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleThreeDToken = (token: TokenPayload) => {
|
||||||
|
// on SCA verification success: show payment UI in processing mode and
|
||||||
|
// resubmit the payment with the new token final success or error will be
|
||||||
|
// handled by `completeSubscription`
|
||||||
|
completeSubscription(null, undefined, token)
|
||||||
|
setGenericError('')
|
||||||
|
setThreeDSecureActionTokenId(undefined)
|
||||||
|
setIsProcessing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleThreeDError = (error: RecurlyError) => {
|
||||||
|
// on SCA verification error: show payment UI with the error message
|
||||||
|
setGenericError(`Error: ${error.message}`)
|
||||||
|
setThreeDSecureActionTokenId(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
const handlePaymentMethod = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handlePaymentMethod = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setPaymentMethod(e.target.value)
|
setPaymentMethod(e.target.value)
|
||||||
}
|
}
|
||||||
|
@ -265,6 +282,13 @@ function CheckoutPanel() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{threeDSecureActionTokenId && (
|
||||||
|
<ThreeDSecure
|
||||||
|
actionTokenId={threeDSecureActionTokenId}
|
||||||
|
onToken={handleThreeDToken}
|
||||||
|
onError={handleThreeDError}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className={classnames({ hidden: threeDSecureActionTokenId })}>
|
<div className={classnames({ hidden: threeDSecureActionTokenId })}>
|
||||||
<PriceSwitchHeader
|
<PriceSwitchHeader
|
||||||
planCode={plan.planCode}
|
planCode={plan.planCode}
|
||||||
|
|
|
@ -1,18 +1,52 @@
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert } from 'react-bootstrap'
|
import { Alert } from 'react-bootstrap'
|
||||||
|
import { RecurlyError, TokenPayload } from 'recurly__recurly-js'
|
||||||
|
|
||||||
function ThreeDSecure() {
|
type ThreeDSecureProps = {
|
||||||
|
actionTokenId: string
|
||||||
|
onToken: (token: TokenPayload) => void
|
||||||
|
onError: (error: RecurlyError) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function ThreeDSecure({ actionTokenId, onToken, onError }: ThreeDSecureProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const container = useRef<HTMLDivElement>(null)
|
||||||
|
const recurlyContainer = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// scroll the UI into view (timeout needed to make sure the element is
|
||||||
|
// visible)
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
container.current?.scrollIntoView()
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!recurly || !recurlyContainer.current) return
|
||||||
|
|
||||||
|
// instanciate and configure Recurly 3DSecure flow
|
||||||
|
const risk = recurly.Risk()
|
||||||
|
const threeDSecure = risk.ThreeDSecure({ actionTokenId })
|
||||||
|
|
||||||
|
threeDSecure.on('token', onToken)
|
||||||
|
threeDSecure.on('error', onError)
|
||||||
|
threeDSecure.attach(recurlyContainer.current)
|
||||||
|
}, [actionTokenId, onToken, onError])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="three-d-secure-container--react">
|
<div className="three-d-secure-container--react" ref={container}>
|
||||||
<Alert bsStyle="info" className="small" aria-live="assertive">
|
<Alert bsStyle="info" className="small" aria-live="assertive">
|
||||||
<strong>{t('card_must_be_authenticated_by_3dsecure')}</strong>
|
<strong>{t('card_must_be_authenticated_by_3dsecure')}</strong>
|
||||||
</Alert>
|
</Alert>
|
||||||
<div className="three-d-secure-recurly-container">
|
<div
|
||||||
{/* {threeDSecureFlowError && <>{threeDSecureFlowError.message}</>} */}
|
className="three-d-secure-recurly-container"
|
||||||
{/* <ThreeDSecureAction {...props} /> */}
|
ref={recurlyContainer}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue