-
Notifications
You must be signed in to change notification settings - Fork 1
Examples
Modern examples featuring hybrid app architecture and Pi Browser detection
π― All examples now include hybrid app support with smart Pi Browser detection
β¨ Key Improvements:
- β
Smart Detection - Pi Browser detection using
Pi.getPiHostAppInfo()with fallbacks - β User-Initiated Auth - No more scary popups, authentication only when users choose
- β Graceful Fallbacks - Professional UI for non-Pi Browser users
- β Better UX - Seamless experience across all environments
π Related: See our Pi Browser Detection Guide for implementation details
| π Example | π Description | π― Features | πΌ Use Case |
|---|---|---|---|
| Basic Login & Pay | Simple authentication and payments | Auth, Payments, Detection | Getting started, prototypes |
| Python Subscriptions | Flask backend with hybrid frontend | Full-stack, API integration | SaaS, subscription services |
| Full SDK Demo | Comprehensive Pi features showcase | All SDK methods, best practices | Learning, reference implementation |
π― Perfect starting point: Simple authentication and payment integration with hybrid support
π What This Example Shows:
- Smart Pi Browser detection before authentication
- User-initiated login (no scary popups)
- Graceful fallback UI for regular browsers
- Simple payment integration
| β Old Approach | β New Hybrid Approach |
|---|---|
Called Pi.authenticate() on page load |
Detects Pi Browser first, then shows login option |
| Broke in regular browsers | Works everywhere with appropriate UI |
| Scary authentication popups | User-initiated authentication flow |
Complete Example Code
// app.js - Modern hybrid approach
async function initApp() {
// 1. Initialize Pi SDK
Pi.init({ version: "2.0", sandbox: true });
// 2. Detect Pi Browser environment
const isPiBrowser = await detectPiBrowser();
if (!isPiBrowser) {
// 3a. Show fallback for regular browsers
showFallbackUI();
return;
}
// 3b. Enable Pi features for Pi Browser users
enablePiFeatures();
}
function showFallbackUI() {
document.getElementById('app').innerHTML = `
<div class="fallback-container">
<h2>π₯§ Pi Login Available in Pi Browser</h2>
<p>For full functionality including payments and Pi authentication:</p>
<div class="cta-section">
<a href="https://pinet.com/YOUR_APP" class="pi-button">
Open in Pi Browser
</a>
</div>
<div class="info-section">
<h3>What you'll get in Pi Browser:</h3>
<ul>
<li>β
Secure Pi Network authentication</li>
<li>β
Pi cryptocurrency payments</li>
<li>β
Full app functionality</li>
</ul>
</div>
</div>
`;
}
function enablePiFeatures() {
// Show Pi Browser-specific UI
document.getElementById('login-section').style.display = 'block';
document.getElementById('login-btn').onclick = handleLogin;
}
async function handleLogin() {
try {
const auth = await Pi.authenticate(['payments'], onIncompletePaymentFound);
console.log('β
Authentication successful:', auth);
// Update UI for logged-in user
document.getElementById('user-info').innerHTML = `
<h3>Welcome!</h3>
<p>User ID: ${auth.user.uid}</p>
`;
// Show payment option
document.getElementById('payment-section').style.display = 'block';
} catch (error) {
console.error('β Login failed:', error);
}
}
async function handlePayment() {
try {
const payment = await Pi.createPayment({
amount: 1.0,
memo: "Test payment",
metadata: { example: true }
}, {
onReadyForServerApproval: function(paymentId) {
console.log('Payment ready for approval:', paymentId);
},
onReadyForServerCompletion: function(paymentId, txid) {
console.log('Payment ready for completion:', paymentId, txid);
},
onCancel: function(paymentId) {
console.log('Payment cancelled:', paymentId);
},
onError: function(error, payment) {
console.error('Payment error:', error);
}
});
} catch (error) {
console.error('β Payment failed:', error);
}
}
function onIncompletePaymentFound(payment) {
console.log('Found incomplete payment:', payment);
// Handle incomplete payment logic here
}
// Initialize app when DOM is ready
document.addEventListener('DOMContentLoaded', initApp);HTML Structure:
<!DOCTYPE html>
<html>
<head>
<title>Pi Network Example - Login & Pay</title>
<script src="https://sdk.minepi.com/pi-sdk.js"></script>
</head>
<body>
<div id="app">
<div id="login-section" style="display: none;">
<button id="login-btn">Login with Pi</button>
</div>
<div id="user-info" style="display: none;"></div>
<div id="payment-section" style="display: none;">
<button onclick="handlePayment()">Make Payment (1 Pi)</button>
</div>
</div>
<script src="app.js"></script>
</body>
</html>async function detectPiBrowser() { const timeout = new Promise(resolve => setTimeout(() => resolve("timeout"), 3000));
try { if (window?.Pi?.getPiHostAppInfo) { const result = await Promise.race([window.Pi.getPiHostAppInfo(), timeout]); return result?.hostApp === "pi-browser"; } } catch (e) { console.warn("Pi SDK detection failed", e); }
const ua = navigator?.userAgent?.toLowerCase() || ""; return ua.includes("pibrowser") || document.referrer.includes("minepi.com"); }
function onIncompletePaymentFound(payment) { console.log("Found incomplete payment:", payment); }
// Initialize app document.addEventListener('DOMContentLoaded', initApp);
### 2. Python Login Pay Subscriptions
**Before:** Auto-login on page load
**After:** Detects Pi Browser, shows login button for Pi users, provides fallback message for regular browsers
```javascript
// Updated python-login-pay-subscriptions/static/app.js
class PiApp {
constructor() {
this.user = null;
this.init();
}
async init() {
const isPiBrowser = await this.detectPiBrowser();
if (!isPiBrowser) {
this.showFallbackUI();
return;
}
this.setupPiUI();
}
showFallbackUI() {
document.body.innerHTML = `
<div class="container">
<h1>Pi Subscription Service</h1>
<div class="fallback-message">
<h2>Available in Pi Browser</h2>
<p>This subscription service requires Pi Browser for secure payments.</p>
<div class="qr-code">
<img src="/static/qr-code.png" alt="Scan with Pi Browser" />
<p>Scan this QR code with Pi Browser</p>
</div>
<a href="https://pinet.com/YOUR_APP" class="pi-button">Open in Pi Browser</a>
</div>
</div>
`;
}
setupPiUI() {
document.getElementById('login-btn').style.display = 'block';
document.getElementById('login-btn').onclick = () => this.handleLogin();
}
async handleLogin() {
try {
const auth = await Pi.authenticate(['payments'], (payment) => {
console.log("Incomplete payment found:", payment);
});
this.user = auth.user;
this.updateUIForLoggedInUser();
} catch (error) {
console.error('Authentication failed:', error);
}
}
async detectPiBrowser() {
const timeout = new Promise(resolve =>
setTimeout(() => resolve("timeout"), 3000)
);
try {
if (window?.Pi?.getPiHostAppInfo) {
const result = await Promise.race([window.Pi.getPiHostAppInfo(), timeout]);
return result?.hostApp === "pi-browser";
}
} catch (e) {
console.warn("Pi SDK detection failed", e);
}
const ua = navigator?.userAgent?.toLowerCase() || "";
return ua.includes("pibrowser") || document.referrer.includes("minepi.com");
}
updateUIForLoggedInUser() {
document.getElementById('user-section').innerHTML = `
<h2>Welcome, ${this.user.uid}!</h2>
<button onclick="app.subscribe()">Subscribe for 5Ο</button>
`;
}
async subscribe() {
const payment = await Pi.createPayment({
amount: 5,
memo: "Monthly subscription",
metadata: { type: "subscription" }
}, {
onReadyForServerApproval: (paymentId) => {
// Send to Flask backend
fetch('/approve_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentId })
});
},
onReadyForServerCompletion: (paymentId, txid) => {
fetch('/complete_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentId, txid })
});
}
});
}
}
const app = new PiApp();
Before: All SDK functions available without detection After: Detects Pi Browser, hides SDK features in regular browsers, shows clear warning message
<!-- Updated Pi-Network-SDK-Example/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Pi Network SDK Example</title>
<script src="https://sdk.minepi.com/pi-sdk.js"></script>
</head>
<body>
<div id="app">
<h1>Pi Network SDK Example</h1>
<div id="detection-status">Detecting Pi Browser...</div>
<div id="pi-features" style="display: none;">
<button id="auth-btn">Authenticate</button>
<button id="payment-btn" style="display: none;">Make Payment</button>
<div id="user-info"></div>
</div>
<div id="fallback-ui" style="display: none;">
<h2>Pi Network Features</h2>
<p>This example showcases Pi Network SDK features that are available in Pi Browser.</p>
<div class="feature-list">
<h3>Available Features:</h3>
<ul>
<li>β
User Authentication</li>
<li>β
Pi Payments</li>
<li>β
App Info Access</li>
<li>β
Conversation API</li>
</ul>
</div>
<div class="cta">
<p><strong>To try these features:</strong></p>
<a href="https://pinet.com/YOUR_APP" class="pi-button">Open in Pi Browser</a>
</div>
</div>
</div>
<script>
Pi.init({ version: "2.0", sandbox: true });
async function initializeApp() {
const isPiBrowser = await detectPiBrowser();
document.getElementById('detection-status').textContent =
isPiBrowser ? 'β
Pi Browser Detected' : 'β Regular Browser Detected';
if (!isPiBrowser) {
document.getElementById('fallback-ui').style.display = 'block';
return;
}
// Show Pi features
document.getElementById('pi-features').style.display = 'block';
setupPiFeatures();
}
function setupPiFeatures() {
document.getElementById('auth-btn').onclick = async () => {
try {
const auth = await Pi.authenticate(['payments'], onIncompletePaymentFound);
document.getElementById('user-info').innerHTML = `
<h3>Authenticated User:</h3>
<p>UID: ${auth.user.uid}</p>
`;
document.getElementById('payment-btn').style.display = 'block';
} catch (error) {
console.error('Authentication failed:', error);
}
};
document.getElementById('payment-btn').onclick = async () => {
const payment = await Pi.createPayment({
amount: 1,
memo: "Test payment",
metadata: { example: true }
}, {
onReadyForServerApproval: (paymentId) => {
console.log('Payment ready for approval:', paymentId);
},
onReadyForServerCompletion: (paymentId, txid) => {
console.log('Payment completed:', paymentId, txid);
}
});
};
}
async function detectPiBrowser() {
const timeout = new Promise(resolve =>
setTimeout(() => resolve("timeout"), 3000)
);
try {
if (window?.Pi?.getPiHostAppInfo) {
const result = await Promise.race([window.Pi.getPiHostAppInfo(), timeout]);
return result?.hostApp === "pi-browser";
}
} catch (e) {
console.warn("Pi SDK detection failed", e);
}
const ua = navigator?.userAgent?.toLowerCase() || "";
return ua.includes("pibrowser") || document.referrer.includes("minepi.com");
}
function onIncompletePaymentFound(payment) {
console.log("Found incomplete payment:", payment);
}
// Initialize app when DOM is ready
document.addEventListener('DOMContentLoaded', initializeApp);
</script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.fallback-ui { background: #f5f5f5; padding: 20px; border-radius: 8px; }
.feature-list { margin: 20px 0; }
.cta { margin-top: 20px; }
.pi-button {
display: inline-block;
background: #7b2cbf;
color: white;
padding: 12px 24px;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
}
#detection-status {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
background: #e3f2fd;
}
</style>
</body>
</html>// utils/pi-browser-detection.js
class PiBrowserDetection {
// Async function to detect Pi Browser
static async detectPiBrowser() {
const timeout = new Promise(resolve =>
setTimeout(() => resolve("timeout"), 3000)
);
try {
if (window?.Pi?.getPiHostAppInfo) {
const result = await Promise.race([window.Pi.getPiHostAppInfo(), timeout]);
return result?.hostApp === "pi-browser";
}
} catch (e) {
console.warn("β Pi SDK detection failed", e);
}
// Fallback: User-Agent or Referrer
const ua = navigator?.userAgent?.toLowerCase() || "";
return ua.includes("pibrowser") || document.referrer.includes("minepi.com");
}
// Helper to create fallback UI elements
static createFallbackUI(appLink = "https://pinet.com/YOUR_APP") {
return `
<div class="pi-fallback">
<h3>Pi Features Available in Pi Browser</h3>
<p>For full functionality, open this app in Pi Browser:</p>
<a href="${appLink}" class="pi-button">Open in Pi Browser</a>
</div>
`;
}
// React-style hook simulation for vanilla JS
static createHook() {
let isInPiBrowser = null;
let listeners = [];
const notify = () => listeners.forEach(fn => fn(isInPiBrowser));
const detect = async () => {
isInPiBrowser = await this.detectPiBrowser();
notify();
};
const subscribe = (callback) => {
listeners.push(callback);
if (isInPiBrowser !== null) callback(isInPiBrowser);
return () => listeners = listeners.filter(fn => fn !== callback);
};
detect();
return { subscribe, getCurrentValue: () => isInPiBrowser };
}
}
// Export for use in other files
window.PiBrowserDetection = PiBrowserDetection;- β Hybrid App Support - Works in both Web2 and Web3 environments
- β Clean UX - No confusing popups in regular browsers
- β Proper Authentication Flow - User-initiated login only
- β Fallback UI - Clear messaging for non-Pi Browser users
- β Production Ready - Includes proper cleanup and error handling
// 1. Detect Pi Browser
const isPiBrowser = await detectPiBrowser();
// 2. Show appropriate UI
if (!isPiBrowser) {
// Show fallback UI
showFallbackMessage();
return;
}
// 3. Only then enable Pi features
enablePiFeatures();This pattern ensures your Pi Network apps work smoothly across all environments while providing the best possible experience for Pi Browser users.
- Pi Browser Detection - Complete detection guide
- SDK Reference - Full SDK documentation
- Authorization - Authentication patterns
- Payments Overview and Workflow - Payment integration
- π Home - Complete developer handbook
- π Community Support - Get help from Pi developers
- π Authorization - Authentication & security patterns
- π API Reference - Complete REST API documentation
- β‘ SDK Reference - JavaScript SDK comprehensive guide
- π Data Types - Object structures & schemas
- π Pi Browser Detection - Build hybrid Web2/Web3 apps
- π Migration Guide - Upgrade to hybrid architecture
- π» Code Examples - Production-ready samples & templates
- π What is PiNet? - Cross-platform app accessibility
- π³ What is PiWallet? - Pi Network wallet integration
- π° Payments Overview - Transaction handling guide
- π¬ Chat Rooms for Apps - Community engagement features
- π Mainnet vs. Testnet - Environment selection guide
- π Developer Terms - Terms of service
- βοΈ Social Chain ToS - Platform terms
- π Whitepaper - Pi Network foundations