Skip to content

Commit

Permalink
feat(checkoutdotcom): adding new saga for adding checkoutdotcom card
Browse files Browse the repository at this point in the history
  • Loading branch information
pedroapfilho committed Jan 11, 2022
1 parent 419a14c commit 2357e6e
Show file tree
Hide file tree
Showing 11 changed files with 348 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default ({ api, coreSagas, networks }) => {

return function* buySellSaga() {
yield takeEvery(actionTypes.form.CHANGE, buySellSagas.formChanged)
yield takeLatest(actions.registerCard, buySellSagas.registerBSCard)
yield takeLatest(actions.activateCard.type, buySellSagas.activateBSCard)
yield takeLatest(actions.addCard.type, buySellSagas.addCardDetails)
yield takeLatest(actions.addCardFinished, buySellSagas.addCardFinished)
Expand All @@ -26,7 +27,7 @@ export default ({ api, coreSagas, networks }) => {
yield takeLatest(actions.confirmOrder.type, buySellSagas.confirmOrder)
yield takeLatest(actions.fetchBalance.type, buySellSagas.fetchBSBalances)
yield takeLatest(actions.deleteCard.type, buySellSagas.deleteBSCard)
yield takeLatest(actions.fetchCard.type, buySellSagas.fetchBSCard)
yield takeLatest(actions.createCard.type, buySellSagas.createBSCard)
yield takeLatest(actions.fetchCards.type, buySellSagas.fetchBSCards)
yield takeLatest(actions.fetchFiatEligible.type, buySellSagas.fetchFiatEligible)
yield takeLatest(actions.fetchSDDEligibility.type, buySellSagas.fetchSDDEligible)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
FiatType,
OrderType,
ProductTypes,
ProviderDetailsType,
SwapOrderType,
WalletFiatType,
WalletOptionsType
Expand Down Expand Up @@ -91,34 +90,82 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
})
const { fetchBankTransferAccounts } = brokerageSagas({ api })

const registerBSCard = function* ({ payload }: ReturnType<typeof A.registerCard>) {
try {
const { paymentMethodTokens } = payload
const userDataR = selectors.modules.profile.getUserData(yield select())
const billingAddressForm: T.BSBillingAddressFormValuesType | undefined = yield select(
selectors.form.getFormValues(FORMS_BS_BILLING_ADDRESS)
)
const userData = userDataR.getOrFail('NO_USER_ADDRESS')
const address = billingAddressForm || userData.address

if (!address) throw new Error('NO_USER_ADDRESS')

// We always use CHECKOUTDOTCOM for now
// and we need 3DS to create a card
yield put(A.setStep({ step: '3DS_HANDLER_CHECKOUTDOTCOM' }))

yield put(A.addCardLoading())

// This creates the card on the backend
yield put(A.createCard(paymentMethodTokens))
yield take([A.createCardSuccess.type, A.createCardFailure.type])

// Check if the card was created
const cardR = S.getBSCard(yield select())
const card = cardR.getOrFail('CARD_CREATION_FAILED')

// This is for the 0 dollar payment
yield put(A.activateCard(card))
yield take([A.activateCardSuccess.type, A.activateCardFailure.type])

yield put(
A.addCardSuccess({
payment_state: null,
processing_errors: null
})
)
} catch (e) {
const error = errorHandler(e)

yield put(
A.setStep({
step: 'DETERMINE_CARD_PROVIDER'
})
)

yield put(A.addCardFailure(error))
}
}

const activateBSCard = function* ({ payload }: ReturnType<typeof A.activateCard>) {
let providerDetails: ProviderDetailsType
try {
yield put(A.activateCardLoading())
const domainsR = selectors.core.walletOptions.getDomains(yield select())
const domains = domainsR.getOrElse({
walletHelper: 'https://wallet-helper.blockchain.com'
} as WalletOptionsType['domains'])
if (payload.partner === 'EVERYPAY') {
providerDetails = yield call(
api.activateBSCard,
payload.id,
`${domains.walletHelper}/wallet-helper/everypay/#/response-handler`
)
yield put(A.activateCardSuccess(providerDetails))
} else {
throw new Error('UNKNOWN_PARTNER')
}

const redirectUrl = `${domains.walletHelper}/wallet-helper/3ds-payment-success/#/`

const providerDetails = yield call(api.activateBSCard, {
cardBeneficiaryId: payload.id,
redirectUrl
})

yield put(A.activateCardSuccess(providerDetails))
} catch (e) {
const error = errorHandler(e)
yield put(A.activateCardFailure(error))
}
}

const fetchBSCardSDD = function* (billingAddress: T.BSBillingAddressFormValuesType) {
let card: BSCardType
// TODO remove once EveryPay is deprecated
// START LEGACY CARD CREATION
const createBSCardSDD = function* (billingAddress: T.BSBillingAddressFormValuesType) {
try {
yield put(A.fetchCardLoading())
yield put(A.createCardLoading())
const state = yield select()
let currency = selectors.core.settings.getCurrency(state).getOrElse('USD')
const origin = S.getOrigin(state)
Expand All @@ -134,32 +181,33 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne

if (!billingAddress) throw new Error('NO_USER_ADDRESS')

card = yield call(
api.createBSCard,
const card = yield call(api.createBSCard, {
address: billingAddress,
currency,
{
...billingAddress
},
userData.email
)
yield put(A.fetchCardSuccess(card))
email: userData.email
})
yield put(A.createCardSuccess(card))
} catch (e) {
const error = errorHandler(e)
yield put(A.fetchCardFailure(error))
yield put(A.createCardFailure(error))
}
}

const addCardDetails = function* () {
try {
// Get card
const formValues: T.BSAddCardFormValuesType = yield select(
selectors.form.getFormValues(FORM_BS_ADD_EVERYPAY_CARD)
)

// Check if card exists
const existingCardsR = S.getBSCards(yield select())
const existingCards = existingCardsR.getOrElse([] as Array<BSCardType>)
const nextCardAlreadyExists = getNextCardExists(existingCards, formValues)

if (nextCardAlreadyExists) throw new Error('CARD_ALREADY_SAVED')

// 3DS validation
yield put(
A.setStep({
step: '3DS_HANDLER_EVERYPAY'
Expand All @@ -168,15 +216,14 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
yield put(A.addCardLoading())

let waitForAction = true
// Create card
if (formValues.billingaddress && !formValues.sameAsBillingAddress) {
yield call(fetchBSCardSDD, formValues.billingaddress)
yield call(createBSCardSDD, formValues.billingaddress)
waitForAction = false
} else {
yield put(A.fetchCard())
yield put(A.createCard({}))
}
if (waitForAction) {
yield take([A.fetchCardSuccess.type, A.fetchCardFailure.type])
yield take([A.createCardSuccess.type, A.createCardFailure.type])
}
const cardR = S.getBSCard(yield select())
const card = cardR.getOrFail('CARD_CREATION_FAILED')
Expand All @@ -187,6 +234,11 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne

const providerDetailsR = S.getBSProviderDetails(yield select())
const providerDetails = providerDetailsR.getOrFail('CARD_ACTIVATION_FAILED')

if (!providerDetails.everypay) {
throw new Error('CARD_ACTIVATION_FAILED')
}

const [nonce] = yield call(api.generateUUIDs, 1)

const response: { data: Everypay3DSResponseType } = yield call(
Expand Down Expand Up @@ -226,6 +278,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
yield take(A.fetchCardsSuccess.type)
yield put(actions.modals.closeAllModals())
}
// END LEGACY CARD CREATION

const cancelBSOrder = function* ({ payload }: ReturnType<typeof A.cancelOrder>) {
try {
Expand Down Expand Up @@ -576,10 +629,9 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
}
}

const fetchBSCard = function* () {
let card: BSCardType
const createBSCard = function* ({ payload }: ReturnType<typeof A.createCard>) {
try {
yield put(A.fetchCardLoading())
yield put(A.createCardLoading())
const currency = S.getFiatCurrency(yield select())
if (!currency) throw new Error(NO_FIAT_CURRENCY)

Expand All @@ -592,18 +644,16 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
const address = billingAddressForm || userData.address
if (!address) throw new Error('NO_USER_ADDRESS')

card = yield call(
api.createBSCard,
const card = yield call(api.createBSCard, {
address,
currency,
{
...address
},
userData.email
)
yield put(A.fetchCardSuccess(card))
email: userData.email,
paymentMethodTokens: payload
})
yield put(A.createCardSuccess(card))
} catch (e) {
const error = errorHandler(e)
yield put(A.fetchCardFailure(error))
yield put(A.createCardFailure(error))
}
}

Expand Down Expand Up @@ -1491,7 +1541,10 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
)
yield put(A.fetchAccumulatedTradesSuccess(accumulatedTradesResponse.tradesAccumulated))
} catch (e) {
yield put(A.fetchAccumulatedTradesFailure(e))
// This is a workaround while I don't find a solution for the fetchAccumulatedTradesFailure type
const error = e as string

yield put(A.fetchAccumulatedTradesFailure(error))
}
}

Expand Down Expand Up @@ -1537,12 +1590,12 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
confirmBSFundsOrder,
confirmOrder,
confirmOrderPoll,
createBSCard,
createBSCardSDD,
createBSOrder,
deleteBSCard,
fetchAccumulatedTrades,
fetchBSBalances,
fetchBSCard,
fetchBSCardSDD,
fetchBSCards,
fetchBSOrders,
fetchBSPairs,
Expand All @@ -1568,6 +1621,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
pollBSBalances,
pollBSCard,
pollBSOrder,
registerBSCard,
setFiatTradingCurrency,
setStepChange,
showModal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ const buySellSlice = createSlice({
action: PayloadAction<{ order: BSOrderType; paymentMethodId: BSCardType['id'] }>
) => {},
confirmOrderPoll: (state, action: PayloadAction<BSOrderType>) => {},
createCard: (state, action: PayloadAction<{ [key: string]: string }>) => {},
createCardFailure: (state, action: PayloadAction<string>) => {
state.card = Remote.Failure(action.payload)
},
createCardLoading: (state) => {
state.card = Remote.Loading
},
createCardSuccess: (state, action: PayloadAction<BSCardType>) => {
state.card = Remote.Success(action.payload)
},
createOrder: (
state,
action: PayloadAction<{
Expand Down Expand Up @@ -238,16 +248,6 @@ const buySellSlice = createSlice({
fetchBuyQuoteSuccess: (state, action: PayloadAction<BuyQuoteStateType>) => {
state.buyQuote = Remote.Success(action.payload)
},
fetchCard: () => {},
fetchCardFailure: (state, action: PayloadAction<string>) => {
state.card = Remote.Failure(action.payload)
},
fetchCardLoading: (state) => {
state.card = Remote.Loading
},
fetchCardSuccess: (state, action: PayloadAction<BSCardType>) => {
state.card = Remote.Success(action.payload)
},
fetchCards: (state, action: PayloadAction<boolean>) => {},
// cards fetch fails so often in staging that this is a temp fix
fetchCardsFailure: (state, action: PayloadAction<string>) => {
Expand Down Expand Up @@ -437,6 +437,10 @@ const buySellSlice = createSlice({
pollBalances: () => {},
pollCard: (state, action: PayloadAction<BSCardType['id']>) => {},
pollOrder: (state, action: PayloadAction<string>) => {},
registerCard: (
state,
action: PayloadAction<{ paymentMethodTokens: { [key: string]: string } }>
) => {},
setBuyCrypto: (state, action: PayloadAction<string>) => {},
setFiatCurrency: (state, action: PayloadAction<FiatType>) => {
state.fiatCurrency = action.payload
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { connect, ConnectedProps } from 'react-redux'
import { bindActionCreators, Dispatch } from 'redux'

Expand All @@ -11,6 +11,8 @@ import { getData } from './selectors'
import Success from './template.success'

const AddCardCheckoutDotCom = (props: Props) => {
const [isError, setError] = useState(false)

const handlePostMessage = async ({
data
}: {
Expand All @@ -29,20 +31,22 @@ const AddCardCheckoutDotCom = (props: Props) => {

if (data.action === 'ADD_CARD') {
if (data.status === 'ERROR') {
// eslint-disable-next-line no-console
console.error('ERROR')
setError(true)
}

if (data.status === 'SUCCESS' && data.token) {
if (data.status === 'SUCCESS') {
const paymentMethodTokens = props.checkoutDotComAccountCodes?.reduce((prev, curr) => {
if (!data.token) return prev

return {
...prev,
[curr]: data.token
}
}, {})

// eslint-disable-next-line no-console
console.log(paymentMethodTokens)
if (!paymentMethodTokens) throw new Error('No payment method tokens')

props.buySellActions.registerCard({ paymentMethodTokens })
}
}
}
Expand All @@ -53,6 +57,10 @@ const AddCardCheckoutDotCom = (props: Props) => {
return () => window.removeEventListener('message', handlePostMessage, false)
})

if (isError) {
return <DataError />
}

return props.data.cata({
Failure: (e) => (
<DataError message={{ message: e }} onClick={props.buySellActions.fetchPaymentMethods} />
Expand Down

0 comments on commit 2357e6e

Please sign in to comment.