Ionic React
Integrate Commerce in Ionic React with Capgo InAppBrowser
Our Commerce SDK works in Ionic React apps that use Capacitor by opening Commerce in a managed native WebView and bridging JavaScript callbacks to your app.
Do not embed Commerce in a same-page <iframe>. Your Ionic app and commerce.bidali.com are different origins, so you cannot inject window.bidaliProvider into an iframe due to the browser same-origin policy. Use a native WebView that can inject JavaScript instead.
Step 1: Obtain your API Key
To get set up with API Keys please email us at support@bidali.com with your request. We will provide you with API keys for our sandbox and production environments.
The Commerce URLs to use in the webview will be:
| Environment | URL |
|---|---|
| Sandbox | https://commerce.staging.bidali.com/dapp?key=YOUR_API_KEY |
| Production | https://commerce.bidali.com/dapp?key=YOUR_API_KEY |
When specifying the accepted payment options and providing balances, Bidali uses a unique internal identifier named protocol. You can view the list of supported protocols below.
| Environment | URL |
|---|---|
| Sandbox | https://docs.bidali.com/commerce/currencies?environment=testnet |
| Production | https://docs.bidali.com/commerce/currencies |
Step 2: Install Capgo InAppBrowser and open Commerce
The official Capacitor @capacitor/inappbrowser plugin does not currently expose the JavaScript injection APIs Bidali needs (executeScript / document-start injection).
Use @capgo/capacitor-inappbrowser instead. Install the plugin major version that matches your app's Capacitor major version (for example Capgo 8.x with Capacitor 8.x).
npm install @capgo/capacitor-inappbrowser
# match Capacitor major, e.g. @capgo/capacitor-inappbrowser@8
npx cap syncOpen Commerce with InAppBrowser.openWebView() and inject window.bidaliProvider before page JavaScript runs:
const { id } = await InAppBrowser.openWebView({
url: `https://commerce.staging.bidali.com/dapp?key=${API_KEY}`,
isPresentAfterPageLoad: true,
preShowScript: providerScript,
preShowScriptInjectionTime: 'documentStart',
showToolbar: false,
});preShowScriptInjectionTime: 'documentStart' is the closest equivalent to React Native's injectedJavaScriptBeforeContentLoaded.
See full sample code below.
Bridge mapping vs other platforms:
| Web → wallet | Wallet → Web | |
|---|---|---|
| React Native | ReactNativeWebView.postMessage() | injectJavaScript() |
| Flutter | BidaliChannel.postMessage() | runJavaScript() |
| Ionic React | mobileApp.postMessage() | executeScript() |
Step 3: Add a bidaliProvider onto the window
window.bidaliProvider = {
key: 'YOUR API KEY', // This is provided by Bidali
name: 'YOUR WALLET NAME', // This will be provided by Bidali with your apiKey
paymentCurrencies: ['stellar', 'stellarusdc'],
brandColor: '#35D07F',
email: 'test@test.com',
phoneNumber: '15554321234',
balances: {
stellar: '5',
usdcstellar: '20'
},
onPaymentRequest: function (paymentRequest) {
window.mobileApp.postMessage({
detail: {
method: 'onPaymentRequest',
data: paymentRequest
}
});
},
openUrl: function (url) {
window.mobileApp.postMessage({
detail: {
method: 'openUrl',
data: { url: url }
}
});
}
};Capgo injects window.mobileApp into managed WebViews. Listen for 'messageFromWebview' in your Ionic app to receive those messages.
window.bidaliProvider must have the following properties:
| Property | Type | Description |
|---|---|---|
name | String | The name of the wallet integrating (Provided by Bidali) |
key | String | This is your Bidali Commerce Publishable Key. The unique and public key that identifies your organization. It looks like pk_viqah4squsnuw6p0e1ue.(Provided by Bidali) |
paymentCurrencies | String[] | Limits the payment options to the currencies you list. It takes an Array of protocol strings (Bidali's unique identifier for each currency) |
brandColor | String | Hex color used to theme CTA button backgrounds, ex: #35D07F |
limitToCountries | String[] | Country codes of the countries to limit the products to, ex: [ "US", "CA" ] |
email | String | Allows us to pre-populate their email, user has to confirm during checkout flow |
phoneNumber | String | Allows us to pre-populate the phone number for mobile topups, the user has the chance to change this |
balances | object | The balances for the allowed currencies, ex. { stellar: '25.2', usdcstellar: '2000' } |
onPaymentRequest | Function | Provide a handler for this function that displays a payment confirmation modal for the user |
openUrl | Function | Opens a URL to our support docs, etc. Ideally should open in an external browser or a new in-app modal browser |
Step 4: Handle onPaymentRequest
Add a handler for onPaymentRequest that shows the user a payment confirmation modal using the information in the paymentRequest. If an extraId field is present on the paymentRequest it must be sent with the transaction (ex. as a memo).
This is what a paymentRequest looks like
{
amount: '0.0187',
symbol: 'ETH', // This is the payment currency your user chose
protocol: 'ethereum', // This is Bidali's unique identifier for a currency
address: '0x220b48eda85a0a067c769f0442dd2f4e03ba625a',
chargeId: 'vIQ807gvR',
extraId: 'vIQ807gvR',
extraIdName: 'Memo text',
description: '$50 Amazon US',
lineItems: [
{
name: 'Amazon US',
quantity: 1,
amount: '$50 USD',
image: 'https://assets.bidali.com/commerce/brands/amazon-com-us.png'
}
]
}A paymentRequest has the following properties:
| Property | Type | Description |
|---|---|---|
address | String | The address to send to |
amount | String | The amount to send, as a user readable string (ex. "25") |
symbol | String | The symbol of the currency the user has chosen to pay with, ex: USDC. Not unique to a currency: USDC on Ethereum and USDC on Stellar both have the symbol USDC |
protocol | String | The protocol of the currency the user has chosen to pay with, this is unique for each currency. ex: usdcstellar |
description | String | A comma-delimited list of products being purchased. Can be used on your payment approval screen so the user sees what they are approving payment for. Ex: $50 USD Amazon US, 2x $25 USD Apple US |
lineItems | Array | A list of products being purchased. Just like with description, you can use this on your payment approval screen. A line item looks like { name: 'Apple US', quantity: 1, amount: '$5 USD', image: 'https://assets.bidali.com/commerce/brands/apple.png' } |
chargeId | String | The users order reference, helpful to reference the order in the future or if there are any issues. Ex: vIQ807gvR |
extraId | String | The extraId that must be passed for the payment to be credited appropriately to the order. See memo / note requirements by network. |
extraIdName | String | The name of the extraId that must be sent with the transaction. See memo / note requirements by network. |
Networks that need a memo
On these networks, the payment request includes an extra field that MUST be sent with the transaction for the payment to be credited.
| Network | extraIdName |
|---|---|
| Stellar | Memo text |
| Solana | Memo |
| Algorand | Note/Memo |
| Cosmos | Memo |
| Osmosis | Memo |
Step 5: Notify Bidali of payment status
When the user sends the transaction call window.bidaliProvider.paymentSent() inside the WebView. If they cancel payment / dismiss the modal then call window.bidaliProvider.paymentCancelled().
await InAppBrowser.executeScript({
id,
code: `window.bidaliProvider.paymentSent();`,
});
await InAppBrowser.executeScript({
id,
code: `window.bidaliProvider.paymentCancelled();`,
});Step 6: Update balances
After a user sends a transaction or their balances change, update the balances object on window.bidaliProvider via executeScript:
async function updateBidaliBalances(
id: string,
balances: Record<string, string>
) {
await InAppBrowser.executeScript({
id,
code: `
window.bidaliProvider.balances = ${JSON.stringify(balances)};
`,
});
}Example code
Here’s sample Ionic React + Capacitor code that opens Commerce with Capgo InAppBrowser, injects window.bidaliProvider, and handles onPaymentRequest via messageFromWebview.
import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
import { Browser } from '@capacitor/browser';
const API_KEY = 'YOUR_API_KEY';
const WALLET_NAME = 'YOUR WALLET NAME';
const balances = {
stellar: '5.2',
usdcstellar: '2000',
};
const user = {
email: 'test@test.com',
phoneNumber: '15554321234',
};
export async function openBidali() {
const providerScript = `
window.bidaliProvider = {
key: ${JSON.stringify(API_KEY)},
name: ${JSON.stringify(WALLET_NAME)},
paymentCurrencies: ['stellar', 'usdcstellar'],
brandColor: '#35D07F',
email: ${JSON.stringify(user.email)},
phoneNumber: ${JSON.stringify(user.phoneNumber)},
balances: ${JSON.stringify(balances)},
onPaymentRequest: function(paymentRequest) {
window.mobileApp.postMessage({
detail: {
method: 'onPaymentRequest',
data: paymentRequest
}
});
},
openUrl: function(url) {
window.mobileApp.postMessage({
detail: {
method: 'openUrl',
data: { url: url }
}
});
}
};
`;
const messageListener = await InAppBrowser.addListener(
'messageFromWebview',
async (event) => {
const payload = event.detail;
if (!payload) return;
switch (payload.method) {
case 'onPaymentRequest': {
const paymentRequest = payload.data as {
amount: string;
address: string;
symbol: string;
protocol: string;
description: string;
chargeId: string;
extraId?: string;
};
// Show your wallet's native transaction confirmation UI here.
// const result = await wallet.sendTransaction(...)
// Notify Bidali of the payment status:
// await InAppBrowser.executeScript({
// id,
// code: 'window.bidaliProvider.paymentSent();',
// });
// or
// await InAppBrowser.executeScript({
// id,
// code: 'window.bidaliProvider.paymentCancelled();',
// });
console.log('Bidali payment request', paymentRequest);
break;
}
case 'openUrl': {
const { url } = payload.data as { url: string };
await Browser.open({ url });
break;
}
}
}
);
const { id } = await InAppBrowser.openWebView({
url: `https://commerce.staging.bidali.com/dapp?key=${API_KEY}`,
// Inject window.bidaliProvider before Bidali app JS executes
isPresentAfterPageLoad: true,
preShowScript: providerScript,
preShowScriptInjectionTime: 'documentStart',
showToolbar: false,
});
return { id, messageListener };
}
export async function updateBidaliBalances(
id: string,
nextBalances: Record<string, string>
) {
await InAppBrowser.executeScript({
id,
code: `window.bidaliProvider.balances = ${JSON.stringify(nextBalances)};`,
});
}Please send any feedback to support@bidali.com