Created
October 20, 2023 01:07
-
-
Save sibelius/bb95610d888f294b7ab0d3064f773e13 to your computer and use it in GitHub Desktop.
useOpenpix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useState } from 'react'; | |
const value = 100; | |
const PixDynamic = () => { | |
const [giftbackValue, setGiftbackValue] = useState(null); | |
const [giftbackHash, setGiftbackHash] = useState(null); | |
const [shopperId, setShopperId] = useState(null); | |
const [modal, setModal] = useState<boolean>(true); | |
// generate a new transactionID on mount | |
const [correlationID, setCorrelationID] = useState(() => | |
getDefaultTransactionId(), | |
); | |
const onClick = () => { | |
const payload = { | |
correlationID, | |
value: value, | |
modal, | |
shopperId, | |
giftbackValue, | |
giftbackHash, | |
comment: 'good', | |
customer: { name: 'Dan', taxID: '31324227036' }, | |
expiresIn: 35 * 60, | |
// enable auto close on success | |
// closeOnSuccess: true, | |
}; | |
window.$openpix.push(['pix', payload]); | |
}; | |
const isOpenPixLoaded = !!window.$openpix?.addEventListener; | |
useEffect(() => { | |
if (isOpenPixLoaded) { | |
const logEvents = (e) => { | |
// eslint-disable-next-line | |
console.log('logEvents: ', e); | |
if (e.type === EventType.PAYMENT_STATUS) { | |
if (e.data.status === 'COMPLETED') { | |
setCorrelationID(getDefaultTransactionId()); | |
// wait 2 seconds to close modal | |
// setTimeout(() => { | |
// window.$openpix.push([ | |
// 'close', | |
// ]); | |
// }, 2000); | |
} | |
} | |
}; | |
const giftbackApplyEvent = (e) => { | |
// eslint-disable-next-line | |
console.log('logEvents: ', e); | |
if (e.type === EventType.GIFTBACK_APPLY) { | |
const { shopper, giftbackValue, giftbackHash } = e.data; | |
if (giftbackValue) { | |
setGiftbackValue(giftbackValue); | |
} | |
if (shopper?.id) { | |
setShopperId(shopper.id); | |
} | |
if (giftbackHash) { | |
setGiftbackHash(giftbackHash); | |
} | |
} | |
}; | |
const unsubscribe = window.$openpix.addEventListener(logEvents); | |
const unsubscribeGiftback = | |
window.$openpix.addEventListener(giftbackApplyEvent); | |
return () => { | |
unsubscribe && unsubscribe(); | |
unsubscribeGiftback && unsubscribeGiftback(); | |
}; | |
} | |
}, [isOpenPixLoaded]); | |
return ( | |
<BoxFlex sx={{ flexDirection: 'column', gap: 16 }}> | |
<BoxFlex sx={{ flexDirection: 'column' }}> | |
<h2>Dynamic Pix React</h2> | |
<form | |
onChange={(e) => { | |
const value = e.target.value; | |
setModal(value === 'modal'); | |
}} | |
> | |
<BoxFlex sx={{ justifyContent: 'space-between' }}> | |
<label htmlFor='renderType'> | |
<input | |
type='radio' | |
id='html' | |
name='type' | |
value='modal' | |
defaultChecked | |
/> | |
Modal | |
</label> | |
<label htmlFor='renderType'> | |
<input type='radio' id='html' name='type' value='div' /> | |
Div | |
</label> | |
</BoxFlex> | |
</form> | |
<Button mt='10px' onClick={onClick}> | |
Pay with Pix | |
</Button> | |
</BoxFlex> | |
<div id='openpix'></div> | |
</BoxFlex> | |
); | |
}; | |
export default PixDynamic; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect } from 'react'; | |
import { useScript } from './useScript'; | |
import config from './config'; | |
export type IOpenPixApi = { | |
generateStatic: (options: any) => any; | |
status: () => void; | |
addEventListener: () => void; | |
}; | |
declare global { | |
interface Window { | |
$openpix: unknown[] & IOpenPixApi; | |
} | |
} | |
export const useOpenPix = () => { | |
useEffect(() => { | |
window.$openpix = []; | |
window.$openpix.push(['config', { appID: config.OPEN_PIX_APP_ID }]); | |
}, []); | |
const scriptURL = config.OPEN_PIX_URL; | |
// eslint-disable-next-line | |
const [loaded, error] = useScript({ src: scriptURL }); | |
useEffect(() => { | |
if (!error) { | |
return; | |
} | |
// eslint-disable-next-line | |
console.log('OpenPix not loaded'); | |
}, [error]); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState, useEffect } from 'react'; | |
export interface ScriptProps { | |
src: HTMLScriptElement['src'] | null; | |
checkForExisting?: boolean; | |
[key: string]: any; | |
} | |
type ErrorState = ErrorEvent | null; | |
type ScriptStatus = { | |
loading: boolean; | |
error: ErrorState; | |
scriptEl: HTMLScriptElement; | |
}; | |
type ScriptStatusMap = { | |
[key: string]: ScriptStatus; | |
}; | |
// Previously loading/loaded scripts and their current status | |
export const scripts: ScriptStatusMap = {}; | |
// Check for existing <script> tags with this src. If so, update scripts[src] | |
// and return the new status; otherwise, return undefined. | |
const checkExisting = (src: string): ScriptStatus | undefined => { | |
const existing: HTMLScriptElement | null = document.querySelector( | |
`script[src="${src}"]`, | |
); | |
if (existing) { | |
// Assume existing <script> tag is already loaded, | |
// and cache that data for future use. | |
return (scripts[src] = { | |
loading: false, | |
error: null, | |
scriptEl: existing, | |
}); | |
} | |
return undefined; | |
}; | |
export const useScript = ({ | |
src, | |
checkForExisting = true, | |
...attributes | |
}: ScriptProps): [boolean, ErrorState] => { | |
// Check whether some instance of this hook considered this src. | |
let status: ScriptStatus | undefined = src ? scripts[src] : undefined; | |
// If requested, check for existing <script> tags with this src | |
// (unless we've already loaded the script ourselves). | |
if (!status && checkForExisting && src && isBrowser) { | |
status = checkExisting(src); | |
} | |
const [loading, setLoading] = useState<boolean>( | |
status ? status.loading : Boolean(src), | |
); | |
const [error, setError] = useState<ErrorState>(status ? status.error : null); | |
useEffect(() => { | |
// Nothing to do on server, or if no src specified, or | |
// if loading has already resolved to "loaded" or "error" state. | |
if (!isBrowser || !src || !loading || error) return; | |
// Check again for existing <script> tags with this src | |
// in case it's changed since mount. | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
status = scripts[src]; | |
if (!status && checkForExisting) { | |
status = checkExisting(src); | |
} | |
// Determine or create <script> element to listen to. | |
let scriptEl: HTMLScriptElement; | |
if (status) { | |
scriptEl = status.scriptEl; | |
} else { | |
scriptEl = document.createElement('script'); | |
scriptEl.src = src; | |
Object.keys(attributes).forEach((key) => { | |
if (scriptEl[key] === undefined) { | |
scriptEl.setAttribute(key, attributes[key]); | |
} else { | |
scriptEl[key] = attributes[key]; | |
} | |
}); | |
status = scripts[src] = { | |
loading: true, | |
error: null, | |
scriptEl: scriptEl, | |
}; | |
} | |
// `status` is now guaranteed to be defined: either the old status | |
// from a previous load, or a newly created one. | |
const handleLoad = () => { | |
if (status) status.loading = false; | |
setLoading(false); | |
}; | |
const handleError = (error: ErrorEvent) => { | |
if (status) status.error = error; | |
setError(error); | |
}; | |
scriptEl.addEventListener('load', handleLoad); | |
scriptEl.addEventListener('error', handleError); | |
document.body.appendChild(scriptEl); | |
return () => { | |
scriptEl.removeEventListener('load', handleLoad); | |
scriptEl.removeEventListener('error', handleError); | |
}; | |
// we need to ignore the attributes as they're a new object per call, so we'd never skip an effect call | |
}, [src]); | |
return [loading, error]; | |
}; | |
const isBrowser = | |
typeof window !== 'undefined' && typeof window.document !== 'undefined'; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment