Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

frontend: use postmessage to communicate with the iframe #3146

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 107 additions & 88 deletions frontends/web/public/btcdirect/fiat-to-coin.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,104 +10,123 @@

<script lang="js">
;(() => {
const {
address,
apiKey,
baseCurrency,
mode,
quoteCurrency,
} = window.frameElement?.dataset;

if (mode === 'debug') {
console.info(window.frameElement?.dataset);

if (window.top === window) {
showError('Unexpected error');
return;
}

if ( // this should never happen, but if it does we stop here
!address
|| !baseCurrency
|| !quoteCurrency
) {
document.body.append(
Object.assign(document.createElement('h1'), {
style: 'color: red; padding: 1rem;',
textContent: `Unexpected error:
const onMessage = (event) => {
switch (event.data?.action) {
case 'configuration':
const {
address,
apiKey,
baseCurrency,
mode,
quoteCurrency,
} = event.data || {};

if ( // this should never happen, but if it does we stop here
!address
|| !baseCurrency
|| !quoteCurrency
) {
showError(`Unexpected error:
${!address ? 'Address missing' : ''}
${!baseCurrency ? 'BaseCurrency missing' : ''}
${!quoteCurrency ? 'QuoteCurrency missing' : ''}
`
})
);
return;
}
`);
return;
}
const currency = baseCurrency.toUpperCase();

const currency = baseCurrency.toUpperCase();
// add the btcdirect CSS
document.head.appendChild(
Object.assign(document.createElement('link'), {
href: (
mode === 'production'
? 'https://cdn.btcdirect.eu/fiat-to-coin/fiat-to-coin.css'
: 'https://cdn-sandbox.btcdirect.eu/fiat-to-coin/fiat-to-coin.css'
),
rel: 'stylesheet',
})
);

// add the btcdirect CSS
document.head.appendChild(
Object.assign(document.createElement('link'), {
href: (
// add the btcdirect script
(function (btc, d, i, r, e, c, t) {
btc[r] = btc[r] || function () {
(btc[r].q = btc[r].q || []).push(arguments)
};
c = d.createElement(i);
c.id = r; c.src = e; c.async = true;
c.type = 'module'; c.dataset.btcdirect = '';
t = d.getElementsByTagName(i)[0];
t.parentNode.insertBefore(c, t);
})(window, document, 'script', 'btcdirect',
mode === 'production'
? 'https://cdn.btcdirect.eu/fiat-to-coin/fiat-to-coin.css'
: 'https://cdn-sandbox.btcdirect.eu/fiat-to-coin/fiat-to-coin.css'
),
rel: 'stylesheet',
})
);

// add the btcdirect script
(function (btc, d, i, r, e, c, t) {
btc[r] = btc[r] || function () {
(btc[r].q = btc[r].q || []).push(arguments)
};
c = d.createElement(i);
c.id = r; c.src = e; c.async = true;
c.type = 'module'; c.dataset.btcdirect = '';
t = d.getElementsByTagName(i)[0];
t.parentNode.insertBefore(c, t);
})(window, document, 'script', 'btcdirect',
mode === 'production'
? 'https://cdn.btcdirect.eu/fiat-to-coin/fiat-to-coin.js'
: 'https://cdn-sandbox.btcdirect.eu/fiat-to-coin/fiat-to-coin.js'
);

btcdirect('init', {
token: apiKey,
debug: mode === 'debug',
locale: window.frameElement?.dataset.locale || 'en-US',
theme: window.frameElement?.dataset.theme || 'light',
});

// fiat to coin order
btcdirect('wallet-addresses', {
addresses: {
address,
currency,
id: 'BitBox',
walletName: 'BitBox'
}
});

btcdirect('set-parameters',
mode === 'production' ? {
baseCurrency: currency,
fixedCurrency: true,
quoteCurrency,
// paymentMethod: any of 'bancontact', 'bankTransfer', 'creditCard', 'giropay', 'iDeal', 'sofort', 'applepay'
showWalletAddress: false,
} : {
baseCurrency: currency,
fixedCurrency: true,
paymentMethod: 'sofort', // sandbox currently only supports sofort payment method
quoteCurrency,
showWalletAddress: false,
? 'https://cdn.btcdirect.eu/fiat-to-coin/fiat-to-coin.js'
: 'https://cdn-sandbox.btcdirect.eu/fiat-to-coin/fiat-to-coin.js'
);

btcdirect('init', {
token: apiKey,
debug: mode === 'debug',
locale: window.frameElement?.dataset.locale || 'en-GB',
theme: window.frameElement?.dataset.theme || 'light',
Copy link
Collaborator Author

@thisconnect thisconnect Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: take locale and theme from onMessage "configuration" / event.data

Copy link
Collaborator Author

@thisconnect thisconnect Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rebased this branch without the backend commit and fixed "locale" and "theme" in #3152

});

// fiat to coin order
btcdirect('wallet-addresses', {
addresses: {
address,
currency,
id: 'BitBox',
walletName: 'BitBox'
}
});

btcdirect('set-parameters',
mode === 'production' ? {
baseCurrency: currency,
fixedCurrency: true,
quoteCurrency,
// paymentMethod: any of 'bancontact', 'bankTransfer', 'creditCard', 'giropay', 'iDeal', 'sofort', 'applepay'
showWalletAddress: false,
} : {
baseCurrency: currency,
fixedCurrency: true,
paymentMethod: 'sofort', // sandbox currently only supports sofort payment method
quoteCurrency,
showWalletAddress: false,
}
);

window.addEventListener('btcdirect-embeddable-fiat-to-coin-order-confirmed', (event) => {
console.log('btcdirect-embeddable-fiat-to-coin-order-confirmed', event);
// Handle the event
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you could directly post a message to pass the order confirmation, maybe? And leave the TODO in the handler inside btcdirect.tsx. No strong opinion, tho

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only tested once, this event is fired after the user clicked proceed to payment and not really useful this way, I'll just remove.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed in #3152

// Note that the sent information from the widget is found inside event.detail
});

break;
}
);
};

window.addEventListener('message', onMessage);

// Request the parent to send attributes
window.parent.postMessage({
action: 'request-configuration'
}, '*');

window.addEventListener('btcdirect-embeddable-fiat-to-coin-order-confirmed', (event) => {
console.log('btcdirect-embeddable-fiat-to-coin-order-confirmed', event);
// Handle the event
// Note that the sent information from the widget is found inside event.detail
});
function showError(message) {
document.body.append(
Object.assign(document.createElement('h1'), {
style: 'color: red; padding: 1rem;',
textContent: message,
})
);
}

})();
</script>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the file we host on shiftcrypto.io and is already deployed bitboxapp.shiftcrypto.io/widgets/btcdirect/v1/fiat-to-coin.html

but in order for it to communicate with the react app we need the changes in this PR in btcdirect.tsx

45 changes: 33 additions & 12 deletions frontends/web/src/routes/exchange/btcdirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { useState, useEffect, createRef, useContext, useRef } from 'react';
import { useState, useEffect, createRef, useContext, useRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { getBTCDirectInfo } from '@/api/exchanges';
import { AppContext } from '@/contexts/AppContext';
Expand Down Expand Up @@ -94,15 +94,45 @@ export const BTCDirect = ({ accounts, code }: TProps) => {
}, 200);
};

const locale = i18n.resolvedLanguage ? localeMapping[i18n.resolvedLanguage] : 'en-GB';

const onMessage = useCallback((event: MessageEvent) => {
// if (!isDevserver && event.origin !== 'https://shiftcrypto.io') { return; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this commented?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update once we have the backend part, the correct subdomain is https://bitboxapp.shiftcrypto.io.

If you don't mind I'll continue in #3152

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in #3152


if (!account || !btcdirectInfo?.success) {
return;
}
switch (event.data.action) {
case 'request-configuration':
event.source?.postMessage({
action: 'configuration',
address,
locale,
theme: isDarkMode ? 'dark' : 'light',
baseCurrency: getCoinCode(account.coinCode),
quoteCurrency: 'EUR',
mode: isTesting || debug ? 'debug' : 'production',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? I didn't test it (no pun intended 😇 ) but I think that isTesting is true for testnet, but we could use mainnet on the sandbox too, right? Maybe this is the cause of the apikey error you mentioned here? #3142 (comment)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed in DM it would be great to align with the backend, so we need something like isProdservers in the frontend.

Alternative: maybethe backend could pass 'debug' or 'production' in another field in some existing api call.

apiKey: btcdirectInfo.apiKey,
}, {
targetOrigin: event.origin
});
break;
}

}, [account, address, btcdirectInfo, locale, isDarkMode, isTesting]);

useEffect(() => {
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
});

if (!account || !config) {
return null;
}

const hasOnlyBTCAccounts = accounts.every(({ coinCode }) => isBitcoinOnly(coinCode));
const translationContext = hasOnlyBTCAccounts ? 'bitcoin' : 'crypto';

const locale = i18n.resolvedLanguage ? localeMapping[i18n.resolvedLanguage] : 'en-GB';

return (
<div className="contentWithGuide">
<div className="container">
Expand Down Expand Up @@ -137,15 +167,6 @@ export const BTCDirect = ({ accounts, code }: TProps) => {
frameBorder="0"
className={`${style.iframe} ${!iframeLoaded ? style.hide : ''}`}
allow="camera"
data-locale={locale}
data-theme={isDarkMode ? 'dark' : 'light'}
data-base-currency={getCoinCode(account.coinCode)}
data-quote-currency={'EUR'}
data-address={address}
data-mode={
isTesting || debug ? 'debug' : 'production'
}
data-api-key={btcdirectInfo.apiKey}
src={btcdirectInfo.url}>
</iframe>
)}
Expand Down