Virtual Stadium widgets can request the user's previously shared bets for users to share in chat. This is handled through the betHistory provider function that must be implemented and registered with the adapter.
To integrate bet sharing with Virtual Stadium:
Implement a betHistory provider function (promise, callback, or return style).
Register the adapter with the betHistory option.
Handle requests for user bets, optionally filtering by events or already shared IDs.
Return bet objects in the expected format.
The widgets will call your provider function when users interact with bet sharing features.
The betHistory function receives arguments about the user and context, and returns a list of shared bets.
userId string required
The ID of the user whose bets to retrieve.
channelId string optional
Channel for which tickets are requested.
events Event[] optional
Array of event objects used to filter the returned bets. It is recommended that only bets that contain at least one individual bet related to any of the specified events should be included in the response.
alreadySharedBetIds string[] optional
Array of bet IDs that have already been shared, to avoid duplicates in the response.
bets BetSlip[] required
Array of user's placed tickets filtered by the provided arguments, containing all necessary data to enable bet reproduction through copy bet functionality.
Callback-based Provider:
function betHistoryCallback(args, callback) {
// Validate required parameters
if (!args.userId) {
return callback(new Error('userId required'));
}
// Fetch user's shared bets from your backend API or tickets system
// Implement filtering logic based on provided arguments
fetchUserBets(args.userId, (error, allBets) => {
if (error) {
return callback(error);
}
let filteredBets = allBets;
// Filter by events if provided
if (args.events && args.events.length > 0) {
const eventIds = args.events.map(event => event.id);
filteredBets = filteredBets.filter(bet =>
bet.bets.some(betItem => eventIds.includes(betItem.event.id))
);
}
// Exclude already shared bets if provided
if (args.alreadySharedBetIds && args.alreadySharedBetIds.length > 0) {
filteredBets = filteredBets.filter(bet =>
!args.alreadySharedBetIds.includes(bet.id)
);
}
// Convert filteredBets to expected structure (example transformation)
const bets = filteredBets.map(bet => ({
id: bet.id || 'sr:bet-share-event:single',
betSlipId: bet.betSlipId || 'sr:bet-share-event:single',
betType: bet.betType || 'single',
currency: bet.currency || 'USD',
combinedOdds: bet.combinedOdds || { decimalValue: 2.55, displayValue: '2.55' },
stake: bet.stake || { value: '50.00' },
payout: bet.payout || { value: '127.50' },
bets: bet.bets || [{
id: 'xxx',
markets: [{
name: 'Total',
outcomes: [{
name: 'Under 1.5',
odds: 13.37
}]
}],
odds: { decimalValue: 13.37 },
event: { name: 'Match name' }
}]
}));
callback(null, { bets });
});
}Promise-based Provider:
function betHistoryPromise(args) {
return new Promise((resolve, reject) => {
// Validate required parameters
if (!args.userId) {
return reject(new Error('userId required'));
}
// Fetch user's shared bets from your backend API or tickets system
// Implement filtering logic based on provided arguments
fetchUserBets(args.userId)
.then(allBets => {
let filteredBets = allBets;
// Filter by events if provided
if (args.events && args.events.length > 0) {
const eventIds = args.events.map(event => event.id);
filteredBets = filteredBets.filter(bet =>
bet.bets.some(betItem => eventIds.includes(betItem.event.id))
);
}
// Exclude already shared bets if provided
if (args.alreadySharedBetIds && args.alreadySharedBetIds.length > 0) {
filteredBets = filteredBets.filter(bet =>
!args.alreadySharedBetIds.includes(bet.id)
);
}
// Convert filteredBets to expected structure (example transformation)
const bets = filteredBets.map(bet => ({
id: bet.id || 'sr:bet-share-event:single',
betSlipId: bet.betSlipId || 'sr:bet-share-event:single',
betType: bet.betType || 'single',
currency: bet.currency || 'USD',
combinedOdds: bet.combinedOdds || { decimalValue: 2.55, displayValue: '2.55' },
stake: bet.stake || { value: '50.00' },
payout: bet.payout || { value: '127.50' },
bets: bet.bets || [{
id: 'xxx',
markets: [{
name: 'Total',
outcomes: [{
name: 'Under 1.5',
odds: 13.37
}]
}],
odds: { decimalValue: 13.37 },
event: { name: 'Match name' }
}]
}));
resolve({ bets });
})
.catch(error => reject(error));
});
}Synchronous Return Provider:
function betHistoryReturn(args) {
// Validate required parameters
if (!args.userId) {
throw new Error('userId required');
}
// Fetch user's shared bets synchronously from your backend or tickets system
let allBets = fetchUserBetsSync(args.userId);
let filteredBets = allBets;
// Filter by events if provided
if (args.events && args.events.length > 0) {
const eventIds = args.events.map(event => event.id);
filteredBets = filteredBets.filter(bet =>
bet.bets.some(betItem => eventIds.includes(betItem.event.id))
);
}
// Exclude already shared bets if provided
if (args.alreadySharedBetIds && args.alreadySharedBetIds.length > 0) {
filteredBets = filteredBets.filter(bet =>
!args.alreadySharedBetIds.includes(bet.id)
);
}
// Convert filteredBets to expected structure (example transformation)
const bets = filteredBets.map(bet => ({
id: bet.id || 'sr:bet-share-event:single',
betSlipId: bet.betSlipId || 'sr:bet-share-event:single',
betType: bet.betType || 'single',
currency: bet.currency || 'USD',
combinedOdds: bet.combinedOdds || { decimalValue: 2.55, displayValue: '2.55' },
stake: bet.stake || { value: '50.00' },
payout: bet.payout || { value: '127.50' },
bets: bet.bets || [{
id: 'xxx',
markets: [{
name: 'Total',
outcomes: [{
name: 'Under 1.5',
odds: 13.37
}]
}],
odds: { decimalValue: 13.37 },
event: { name: 'Match name' }
}]
}));
return { bets };
}For the complete Bet Data Structure reference, see Bet Data Structure.
Once the betHistory function is implemented, register it with the Virtual Stadium adapter.
Pass the function in the options object when calling SIR('registerAdapter').
Register Adapter with betHistory:
SIR('registerAdapter', 'myAdapter', {
betHistory: betHistoryPromise // or betHistoryCallback
});Here's a complete example based on the client integration file, showing how to set up bet sharing in a real application.
This example uses a promise-based provider and includes sample bet data.
Complete HTML Integration (adapted from betInsightTest.html):
<!DOCTYPE html>
<html>
<head>
<script src="https://widgets.sir.sportradar.com/.../widgetloader"></script>
</head>
<body>
<script>
// Sample bet data
const sampleBet = {
id: 'sr:bet-share-event:single',
betSlipId: 'sr:bet-share-event:single',
betType: 'single',
currency: 'USD',
combinedOdds: { decimalValue: 2.55, displayValue: '2.55' },
stake: { value: '50.00' },
payout: { value: '127.50' },
bets: [{
id: 'xxx',
markets: [{
name: 'Total',
outcomes: [{
name: 'Under 1.5',
odds: 13.37
}]
}],
odds: { decimalValue: 13.37 },
event: { name: 'Match name' }
}]
};
// Promise-based betHistory provider
function betHistoryPromise(args) {
return Promise.resolve({
bets: [sampleBet]
});
}
// Register adapter
SIR('registerAdapter', 'myAdapter', {
betHistory: betHistoryPromise
});
// Add Virtual Stadium widget
/**
* TODO(developer): Replace 'your-jwt-token' with your actual JWT token
* and 'your-channel' with your actual channel ID before running this code.
*/
SIR('addWidget', '.vs-container', 'virtualStadium', {
jwt: 'your-jwt-token',
channelId: 'your-channel'
});
</script>
<div class="vs-container"></div>
</body>
</html>For information on implementing the copy bet functionality that works alongside bet sharing, see the Copy Bet Button documentation.