Skip to main content

Pi SDK Django

Integrating Pi Network into a Django backend revolves around managing two primary types of transactions: User-to-App (U2A) and App-to-User (A2U). The backend serves as a secure verifier and ledger, ensuring no client-side spoofing occurs before logging transactions to the blockchain.

The Transaction Flow

User-to-App (U2A) Flow

This flow handles users purchasing items or services with Pi.

  • Initialization: The user triggers a payment via the client application. The Pi JS SDK opens the native wallet, requiring user confirmation.
  • Pre-Approval: The SDK fires an event (onReadyForServerApproval). The client frontend sends the paymentId via an HTTP POST request to your Django backend.
  • Verification & Approval: Django checks if the paymentId is valid and the items in the order match the backend database. If valid, Django hits the ://minepi.com[payment_id]/approve endpoint.
  • Completion: The Pi blockchain processes the transaction. The SDK then fires an onReadyForServerCompletion event. The frontend sends the txid (transaction ID) to Django, which verifies the transaction and calls the complete endpoint on the Pi API.

App-to-User (A2U) Flow

This flow enables your application to send Pi directly to users (e.g., refunds or rewards).

  • Request Initiation: Your application generates an A2U request via Django.
  • Direct Submission: Because it originates from your server, it does not require client-side approval. Django uses a secure Server API Key to submit payment to api.minepi.com/v2/payments.

Core Django Models

To manage this ecosystem safely, your Django models (usually in models.py) must track both users and the ledger of transactions.

from django.db import models
from django.contrib.auth.models import User
import uuid

class PiAppUser(models.Model):
"""Stores Pi Network user identifiers and links them to the native Django User model."""
user = models.OneToOneField(User, on_delete=models.CASCADE)
pi_uid = models.CharField(max_length=100, unique=True, help_text="Unique Pi Network User ID")
kyc_verified = models.BooleanField(default=False)

def __str__(self):
return f"Pi User {self.pi_uid}"

class PiTransaction(models.Model):
"""Tracks state of Pi Network transactions flowing through the app."""
STATUS_CHOICES = (
('CREATED', 'Created'),
('APPROVED', 'Approved'),
('COMPLETED', 'Completed'),
('FAILED', 'Failed'),
)

transaction_id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
pi_app_user = models.ForeignKey(PiAppUser, on_delete=models.SET_NULL, null=True)
pi_payment_id = models.CharField(max_length=100, unique=True, help_text="Payment ID from Pi API")
txid = models.CharField(max_length=100, blank=True, null=True, help_text="Blockchain Transaction ID")
amount = models.DecimalField(max_digits=15, decimal_places=6)

Key Backend Considerations

  • Idempotency: Always store the payment_id and the txid as soon as they are returned from the Pi Network API to guarantee transactions aren't processed twice in the event of dropped network packets.
  • Webhooks & Polling: While the JS SDK handles the flow natively, your Django backend should securely update its own internal ledger after calling completion APIs to maintain reliable local data.
  • Security: Keep your App Server API key completely hidden within Django's .env or settings files and never expose it to frontend clients.

Core Token Expiration Architecture

In Pi Network application development, Pioneer access tokens are dynamic, short-lived strings that expire naturally to limit security risks. When building a Django-native backend, your application must handle token verification, expiration, and user session management because the Pi Developer SDK relies on your server to validate these tokens against the Pi Platform APIs.

  • Frontend Responsibility: The Pi Network JavaScript SDK captures the user's access token inside the Pi Browser environment.
  • Backend Responsibility: Your native Django application acts as the backend service that receives this token from your frontend via an authorization header (Authorization: Bearer <token).
  • Dynamic Validation: Rather than hardcoding an expiration check into Django, Django validates the token by making a server-to-server HTTP request to the Pi Platform's /me endpoint.

Developer Best Practices

  • Never Cache Tokens Silos: Do not save the Pioneer access token directly into your Django database as a long-term credential, since its dynamic lifecycle means it will quickly become stale.
  • Map to Django Sessions: Once the Pi API returns a valid uid, map that unique identifier to a standard native Django User model or issue a short-lived Django REST Framework JWT for your app's internal sessions.
  • Intercept Frontend Expiry: Configure your frontend JavaScript to watch for 401 errors returned by your Django server. If a 401 hits your frontend, trigger the Pi SDK's built-in authenticating methods to silently fetch a fresh, unexpired native token from the Pi Browser ecosystem.

Payment API rate limits

Pi Network's payment API rate limits cap the number of requests your application can send to the Pi servers within a specific timeframe (like per second). This protects the network from overload.

When handling the Pi App Platform APIs, your backend will primarily interact with two payment steps:

  1. Approve Payment: Using the paymentID, this call verifies the transaction using your Server API Key.
  2. Complete Payment: Using the txID (transaction ID).

If your app makes too many calls in a short period, the server will block the excess requests and return an HTTP 429 Too Many Requests error.

Best Practices & Solutions

To avoid running into HTTP 429 errors when using the API, follow these development guidelines:

  • Implement Exponential Backoff: If you receive a 429 error, pause. Wait a few seconds before automatically retrying.
  • Avoid Aggressive Polling: Instead of continuously requesting payment statuses, rely on event-driven callbacks (such as the onReadyForServerCompletion method in the Pi SDK) to trigger your API calls.
  • Cache Responses: Temporarily save successful API responses in your own local database instead of making a brand new API request for the same information.

The asynchronous lifecycle of a Pi Network

Transaction with a Django backend typically follows this strict workflow:

Pi Browser (Client) ] [ Django Server (Backend) ]
│ │
├─► window.Pi.createPayment() │
│ (User confirms in wallet) │
│ │
├─► onReadyForServerApproval(paymentId) ───────►│ (Async POST request)
│ │ 1. Verify with Pi API
│ │ 2. Save Pending DB State
│◄──────────────────────────────────────────────┤ 3. Return 200 OK Approval
│ │
│ (Pi Blockchain submits Tx) │
│ │
├─► onReadyForServerCompletion(txid) ──────────►│ (Async POST request)
│ │ 1. Submit completion to Pi
│ │ 2. Mark DB as Paid/Fulfilled
│◄──────────────────────────────────────────────┤ 3. Return Success

Pi Network applications run inside an iframe within the Pi Browser sandbox, which modern web browsers treat as a cross-site, third-party context. By default, Django enforces privacy and security restrictions that block session and CSRF cookies from being sent or saved under these exact conditions.

  1. The SameSite Cookie Restriction: Modern browsers default cookie policies to SameSite='Lax'. This means they refuse to pass authorization headers or cookies if your Django backend runs on a different domain than the parent Pi App container. Force Django to explicit None values, permitting cookies inside external iframes.
SESSION_COOKIE_SAMESITE = 'None'
CSRF_COOKIE_SAMESITE = 'None'
  1. Required Secure HTTPS Flag: Browsers will reject any cookie assigned SameSite='None' unless it is accompanied by a Secure flag. This mandates that your Pi App communicates strictly via HTTPS. Activate forced HTTPS cookie flags.
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
  1. Sandbox Opaque Origin & CSRF Headers: The Pi Sandbox strips original request headers or treats them as anonymous/null origins. When an app submits a POST request, Django’s CSRF verification checks the Referer and Origin headers, which fails inside the sandbox. Explicitly trust the incoming Pi Network domains.
CSRF_TRUSTED_ORIGINS = [
'https://pinet.com',
'https://*.pinet.com',
]
  1. Clickjacking Protection Bypass: Django includes a middleware layer (django.middleware.clickjacking.XFrameOptionsMiddleware) that passes X-Frame-Options: DENY by default, blocking the browser from displaying the site in an iframe entirely. Remove django.middleware.clickjacking.XFrameOptionsMiddleware from your MIDDLEWARE array in settings.py. Alternatively, use the @xframe_options_exempt decorator on the specific views exposed to the Pi App
  2. Google Privacy Sandbox & CHIPS (Partitioned Cookies): As major web engines completely phase out traditional third-party cookies, standard SameSite=None attributes can fail. Browsers require CHIPS (Cookies Having Independent Partitioned State). For modern setups, your cookies need the Partitioned attribute. If your Django version does not natively configure partitioned parameters across all sessions yet, append them manually using custom middleware.

Packages

The pi-sdk-django package handles the server-side Pi Network payment lifecycle, authentication helpers, and ad reward verification for Django apps. It pairs with pi-sdk-react (frontend) to give you a complete, idiomatic Pi integration.

Required packages

LayerPackageInstall
Backend (Django)pi-sdk-djangopip install pi-sdk-django
Frontend (React)pi-sdk-reactnpm install pi-sdk-react

Do not substitute pi-sdk-django with raw requests / httpx calls to the Pi Platform API. The managed package handles payment lifecycle state, error recovery, and incomplete-payment resolution that are easy to miss when calling the API directly.

This guide demonstrates how to integrate the Pi SDK into a Django app. This example shows how to initialize the Pi SDK, authenticate a Pioneer, and create a payment request inside a Pi app.

The pi-sdk-django package is part of the “Ten Minutes to Transactions” effort described in this video.

If you, or your GenAI agent, are planning to use the Django for your app, it is highly suggested that you use this package rather than implement transaction processing by hand with the core Pi SDK. The three way handshake between client, server, and the Pi servers required is provded for you.

Note: Pi SDK authentication and payment features require the application to run inside the Pi Browser.


Django Quick Start

Register your application with Pi Network

While this process is covered in the Getting Started Guide, here is a brief reminder of the steps you need to take. Application registration is also discussed in the video.

  • Open your Pi Mining app.
  • Click the hamburger (☰).
  • Select “Pi Utilities”.
  • Click the “Develop” icon followed by the “New App” icon.
  • Provide name and description of your app and submit.
  • Then click the “Configuration” icon.
  • Set the app URL to something valid, but does not necessarily exist.
  • Set the development URL to be http://localhost:8000. The actual port is between you and your development server.
  • Submit your changes.
  • Get your API key.
  • Register a wallet for your app.

1\. Add pi-sdk-django to your requirements

pi-sdk-django is required for the backend. Add it to requirements.txt:

pi-sdk-django>=<version>

Then install:

pip install -r requirements.txt

Common mistake: Using requests or httpx to call https://api.minepi.com/v2/ directly instead of installing pi-sdk-django. This bypasses the official SDK and will cause your implementation to fail the Pi integration review. See Common Mistakes.

Bash

pip install pi-sdk-django

2\. Run the Pi component and API scaffolder

Bash

pi-sdk-django-install

This will generate:

  • routes/pi_payment/ directory with individual route handlers
  • routes/pi_payment/index.ts \- Router that exports all routes
  • app.example.ts \- Example Django app setup

3\. Load the Pi SDK script and call Pi.init() (Frontend)

Add the Pi SDK script tag to your HTML before any Pi SDK calls, and call Pi.init() before React mounts. Without this, window.Pi is undefined and all SDK calls will throw. See Common Mistakes — Mistake 1.

<!-- index.html -->
<head>
<!-- REQUIRED: load Pi SDK before any Pi SDK calls -->
<script src="https://sdk.minepi.com/pi-sdk.js"></script>
</head>
// main.tsx — before ReactDOM.createRoot(...)
window.Pi.init({ version: '2.0', sandbox: true }); // sandbox: false in production

The CDN script is needed in sandbox/desktop environments. Apps running inside the Pi Browser receive window.Pi natively.

4\. Frontend Integration

Use pi-sdk-react hooks on your frontend. Never call window.Pi.authenticate() or window.Pi.createPayment() directly — the hooks own those calls.

import { usePiConnection, usePiPurchase } from 'pi-sdk-react';

// Auth — accessToken comes directly from the hook (not window.PiSdkBase)
const { connected, accessToken } = usePiConnection();

// Payment — hook handles approve + complete internally
const purchase = usePiPurchase({ amount: 1, memo: 'Unlock feature', metadata: {} });

For more detail see pi-sdk-react.

5\. Set Environment Variables

PI_API_KEY=your_pi_api_key_here

API Endpoints

pi-sdk-django mounts the following endpoints (matching usePiPurchase defaults):

  • POST /pi_payment/approve — Phase I: approve with Pi Platform API
  • POST /pi_payment/complete — Phase III: complete and deliver feature
  • POST /pi_payment/cancel — cancel a payment
  • POST /pi_payment/incomplete — resolve stuck payments