Skip to main content

Note: The author is an amateur developer who wanted to explore and document creating a Web3 app on the Pi platform. This post is a colorful journey and step-by-step breakdown of his Pi app development experience. It can inform developers how easy and intuitive it is to build a Pi app – even from the perspective of an inexperienced developer.

Enjoy reading this intriguing piece. Developers, see for yourself how fast it is to bring your app to tens of millions of the Pi community members! For more information, find the formal Documentation of the Pi Platform here, and a comprehensive Community Developer Guide here.

Language, and as he calls them, the “stunts,” are the author’s own. Reader discretion advised.

A step-by-step beginner’s guide to developing on the Pi platform

I’ve dabbled in Python development before, but I still suck at it. Pi Blockchain, here I come. Game on! 

If you want to skip straight to the step-by-step, I’ll be sad, but I understand. Here’s a table of contents:


Or, you can grab all of the code for the final app
here: https://github.com/pi-apps/insult-o-meter

My previous attempts at coding have been… rough. Half-hearted attempts, unfinished jobs. You name it. 

So when I decided to write a blockchain app on the Pi platform and create a beginner’s guide, I started sweating. Oh, gods, I thought. Here we go again.

Spoilers: I was pleasantly surprised. It was, dare I say, kind of fun. Here’s my step-by-step guide to building a blockchain app with Pi.

What You Need

To follow along and/or to just grab my code and figure it out, you need:

  • Some knowledge of Python 3.0
  • Enough javascript knowledge to change text on a page
  • Enough javascript knowledge to cut and paste code without breaking anything
  • An iPhone or Android device

Meet Your Developer

I am:

  • A very, very, very amateur Python developer. I still get confused when installing libraries. My most common ChatGPT prompt is, “What’s wrong with this code?”
  • An even more amateur javascript developer. Actually, I don’t know a shred of javascript except what I copy and paste from Medium tutorials
  • A 28-year digital marketer with declining brain plasticity

Also, I don’t know developer-speak. To me, a token is how I rode the subway in the 1990s. So pardon me if I occasionally say stuff like “that API thingie.” I include examples. Hopefully, it’ll make sense.

What I Built

I considered doing something basic, or wholesome, or basic and wholesome.

Nah.

Meet the Insult-o-meter. Get three creative insults, ready to deliver, all for the price of .01 PiCoin:

insultometer image

Trust me, it’s on-brand.

Tools I Used

I used:

All on my trusty Macbook Pro, running Sonoma.

About The Pi SDK

The Pi SDK connects your app to all the Pi currency out there. Real crypto nerds will punch me in the spleen for this analogy, but the SDK is a credit card terminal in your store.

Here’s how it works:

  1. When someone visits your page, Pi kicks things off. The SDK connects to your app, confirms the user gave permission to access their Pi wallet, etc. If your app was a real store, this is when you turn the lights on and unlock the door
  2. Check for incomplete payments. There’s no great analogy for this. Don’t forget about this step. I did, and I suffered for it
  3. Patiently wait for the user to do something. You’re behind the counter or wandering around, straightening things on shelves while customers browse
  4. Create a payment. Pi sets up the transaction the same way you ask a customer for their credit card
  5. Then, do a whole bunch of stuff behind the scenes. You’ve swiped the credit card. Payment info is sent to the payment provider, which confirms that credit card number, checks for funds, and, if there are sufficient funds, transfers currency from the customer to you
  6. Return status. Finally, Pi says, “OK, all done. You’ve got coin!”

Setting Up The Python Environment

In case you’re not a Python expert like me (cough), here’s how I set up my environment:

  1. Open Terminal
  2. Run pipenv shell. That creates an enclosed environment where I can safely break things in an isolated environment. If you haven’t used it, check it out here. There are other virtual environment tools out there, but this one doesn’t break my brain
  3. Install the libraries listed above

That’s it.

Building The Python App

I won’t bore you with the details. I built a simple webpage with a Flask backend. The app reads insults from a text file, grabs a random insult, and passes it to the webpage using some basic javascript. 

For now, we’re just building the app without SDK connectivity. That means three routes:

  • /get_quote grabs a random quote from the text file. It doesn’t connect to Pi. It does connect to some javascript that counts the number of requested quotes. If the user has asked for more than three quotes, the javascript displays a modal with a button that connects to the approve route
  • /back is boring

Pretty straightforward. You can get much fancier.

Be sure to note the app URL:

building python app

You’ll need this when you set up the app on Pi.

Registering Your New App On Pi

If you have the step-by-step, this is easy. I’m a helpful guy, so here’s the step-by-step:

Note: You’ll be using two apps: the Pi Browser, a web browser built on the Pi platform, and the Pi Mining App, which is how you generate all that Pi goodness. You’ll jump back and forth. It might drive you slightly bonkers, but it does make sense in the long run. Once you get used to it, it makes the workflow easier. Trust me. I wouldn’t lead you astray.

1. Get The Pi Mining App

Before you do anything else, you need to install the Pi Mining App. It’s a little app that runs on your iPhone or Android device. Get it on:

Google Play Store: https://play.google.com/store/apps/details?id=com.blockchainvault, or 

App Store: https://itunes.apple.com/us/app/pi-network/id1445472541

It also lets you mine Pi currency. No GPU required.

Go ahead. I’ll wait.

All set? On to the next step.

2. Get the Pi Browser

Next you need to download the Pi Browser, a mobile web browser which gives you access to all applications built using the Pi SDK. 

Google Play Store: https://play.google.com/store/apps/details?id=pi.browser 

Apple App Store: https://apps.apple.com/us/app/pi-browser/id1560911608 

All set.

Type in your app name and description. For App Network I used Pi Testnet. No way in hell I was going to try to launch this thing in the real world. Plus, Testnet transactions are 100% pure, fake Pi, so you’re not charging anyone for a buggy app. I recommend you do the same.

You should see this handy screen:

The checklist shows where you’re at in the process of launching your app on Mainnet.

Right now, we need to set up a wallet.

le sigh

I went back and reset to http://127.0.0.1:3000, and everything worked.

Integrating The Python App With Pi

This is really, really hard.

I’m kidding. I kid. It’s easy. Just add the following to your Python app’s HTML code:

Javascript
<script src="https://sdk.minepi.com/pi-sdk.js"></script>
<script> Pi.init({ version: "2.0", sandbox: true }) </script>

Note: The sandbox flag is “true” only when running your app in the Pi Sandbox. 

And then add this. It authenticates the user:

 Javascript
<script> 
    // Authenticate the user, and get permission to request payments from them:
    const scopes = ;

    // Read more about this callback in the SDK reference:
    function onIncompletePaymentFound(payment) { 
        paymentId = payment.identifier
        txid = payment.transaction.txid
        $.post('/payment/complete',
                {
                    paymentId: paymentId,
                    txid: txid,
                    debug: 'cancel'
                }
            )
    };

    Pi.authenticate(scopes, onIncompletePaymentFound).then(function(auth) {
console.log('woot!');
    }).catch(function(error) {
      console.error(error);
    });
</script>

Finally, add this to your Flask app.py:

Python
header = {
    'Authorization': "Key YOUR-API-KEY"
}

That provides your API key to the SDK. Please practice safe variable inclusion.

first app load

Note: This assumes you’ve got a Pi account with a little Pi in it, which you should. You installed the Pi Mining app and Pi Browser, after all.

Time So Far: < 2 hours, Including Pet Care

Not counting building the Python app, I just:

  1. Set up a Pi wallet
  2. Started mining Pi (because why not?)
  3. Set up an app
  4. Fed my cat. She got really impatient at this point
  5. Got all the necessary stuff to integrate with the Pi Blockchain
  6. Walked my dog. He was hopping around like a toddler in potty training
  7. Dried my dog’s feet before he tracked mud all over the house
  8. Stopped my dog from cornering my cat in the bedroom, where the cat would have flayed him alive
  9. Got my app up and running in the Pi sandbox

It took more time to write out the steps.

You’ll probably be faster than this Gen X coder. If you say it took you less than 1 hour, I’ll call you a showoff.

Setting Up Payment Processing

This is the “real” work. In quotes, because once you know how to do it, it’s pretty straightforward. I’m going to go through the step-by-step in a moment. For now, just paste this in:

1. Add This Code To Your HTML Page

Paste this in after the Pi.init code:

 Javascript
<script>
    // we're doing payments
    const Pi = window.Pi;
    // main payments function
    function createPayment() {
        const paymentData = {
            amount: .01,
            memo: "This buys you three more insults! What a deal!!!",
            metadata: { insultid: 123456 }
        };
        // the SDK does all this like magic
        const paymentCallbacks = {
            onReadyForServerApproval: (paymentDTO) => {
                paymentId = paymentDTO
                $.post('/payment/approve', {
                    paymentId: paymentId,
                    accessToken: accessToken
                })
            },
            onReadyForServerCompletion: (paymentDTO, txid) => {
                paymentId = paymentDTO
                txid = txid
                $.post('/payment/complete',
                        {
                            paymentId: paymentId,
                            txid: txid,
                            debug: 'complete'
                        }
                    )
            },
            onCancel: (paymentDTO) => {
                paymentId = paymentDTO.identifier
                $.post('/payment/complete',
                        {
                            paymentId: paymentId,
                            debug: 'cancel'
                        }
                    )
            },
            onError: (paymentDTO) => {
                console.log('There was an error ', paymentDTO)
                paymentId = paymentDTO.identifier
                $.post('/payment/error',
                        {
                            paymentDTO: paymentDTO,
                            paymentId: paymentId,
                            debug: 'error'
                        }
                    )
            },
            onIncompletePaymentFound: function(paymentDTO)
            {
                paymentId = paymentDTO.identifier
                console.log('onIncompletePaymentFound', paymentId)
                $.post('/payment/complete',
                        {
                            paymentId: paymentId,
                            txid: paymentDTO.transaction.txid
                        }
                    )
             }
        };
        Pi.createPayment(paymentData, paymentCallbacks);
    }
</script>

2. Add This Code To app.py

This sets up the Flask routes I mentioned before. These are just the Pi routes. You’ll need to set up any app-specific routes. For me, those are get_quote and back. Or just download the whole repository.

Python
@app.route('/payment/approve', methods=)
def approve():
    # Build the header for user authentication
    accessToken = request.form.get('accessToken')
    userheader = {
        'Authorization': f"Bearer {accessToken}"
    }
    paymentId = request.form.get('paymentId')
    approveurl = f"https://api.minepi.com/v2/payments/{paymentId}/approve"
    response = requests.post(approveurl, headers=header)
    userurl = "https://api.minepi.com/v2/me"
    userresponse = requests.get(userurl, headers=userheader)
    userjson = json.loads(userresponse.text)
    return(response.text)

@app.route('/payment/complete', methods=)
def complete():   
    # Build the header for user authentication
    accessToken = request.form.get('accessToken')
    userheader = {
        'Authorization': f"Bearer {accessToken}"
    }
    paymentId = request.form.get('paymentId')
    txid = request.form.get('txid')
    userurl = "https://api.minepi.com/v2/me"
    userresponse = requests.get(userurl, headers=userheader)
    data = {'txid': txid}
    approveurl = f"https://api.minepi.com/v2/payments/{paymentId}/complete"
    response = requests.post(approveurl, headers=header, data=data)
    return(response.text)

@app.route('/payment/cancel', methods=)
def cancel():    
    paymentId = request.form.get('paymentId')
    approveurl = f"https://api.minepi.com/v2/payments/{paymentId}/cancel"
    response = requests.post(approveurl, headers=header)
    return(response.text)

@app.route('/payment/error', methods=)
def error():    
    paymentId = request.form.get('paymentId')
    approveurl = f"https://api.minepi.com/v2/payments/{paymentId}/cancel"
    response = requests.post(approveurl, headers=header)
    return(response.text)

@app.route('/me', methods=)
def getme():
    userurl = "https://api.minepi.com/v2/me"
    response = requests.post(userurl, headers=header)
    return(response.text)

These new routes do the following:

  • /payment/approve connects to the Pi SDK and kicks off the entire payment/approval process. It’s fired by the javascript createPayment function when the user clicks ‘Pay.’ At this point, all sorts of magical stuff happens on the Pi platform. If the payment is approved, the SDK calls the complete route
  • /payment/complete grabs a transaction ID (the unique transaction string), and fires the me route
  • /payment/cancel and error are exactly what they sound like, except that I’m lazy, so both routes send a request to the SDK’s cancel URL

3. Make Sure They Connect

To connect your app to the Pi SDK, all you need to do is have something fire createPayment. For me, it’s the “Pay” button:

 Javascript
<a href="#" id="resetBtn" class="btn btn-primary mt-3" OnClick='javascript:createPayment();'>Pay</a>

When someone clicks “Pay,” that calls createPayment, which then calls the /payments/approve Flask route. Then the magic happens.

Time So Far: < 4 Hours, Minus Stupidity

At this point, I’ve got a working app. It shows me insults, charges me Pi, and does all the stuff in between.

That took about 4 more hours, with some extra time for:

  1. Mismatched brackets because I’m That Developer
  2. Some kind of grinding war of attrition between my cat and my dog that involved the cat on a coffee table, dangling her tail in front of Alfred, who then went insane trying to grab it
  3. A total failure on my part to understand what the SDK is really doing. Had I given even a little thought to that, I could’ve had this up and running far faster

I would save you that pain. Read the next section for a walkthrough of how this all works:

What It’s All Doing

At this point, you’re probably dizzy. That’s a lot of cutting-and-pasting or (hopefully) looking at the code repository. But very little insight into how this is actually working. Here’s a step-by-step walkthrough:

  1. First visit. The page opens, the SDK loads with Pi.init
  2. Pi.authenticate is called by the app and checks whether we’re, well, authenticated. If not, Pi shows a security warning and asks the user for permission to continue
  3. Assuming it all works out and the user gives permission, Pi.authenticate grabs user.username and writes it to the page for a nice, personalized feel
  4. The user clicks “Gimme an Insult” a few times. Each click calls the /get_quote route
  5. If the user has clicked more than three times, the app reveals a modal with a payment request (yes, this is pathetic and easy to evade with a popup blocker – this is for science, OK?)
  6. The user is so thrilled with the quality of the insults that they OK payment by clicking Pay
  7. When they do that, the modal calls createPayment
  8. createPayment calls onReadyForServerApproval, which fires the /payment/approve route in the Flask app, sending Flask the paymentId and accessToken
  9. The /payment/approve route sends a request with the payment ID and access token to the API
  10. In response, the API (hopefully) says, “OK, all good,” at which point the SDK calls onReadyForServerCompletion
  11. That calls the /payment/complete Flask route, passing it paymentId and, most important, txid (transaction id)
  12. The /payment/complete Flask route processes the payment

About onIncompletePaymentFound

This little function nearly drove me batty. It’s a great utility, checking for incomplete transactions and letting you call other functions. I didn’t understand that at first. 

A couple of times, I broke stuff, and I ended up with transactions submitted but not completed. That led to an error-laden toilet bowl of death where I tried to debug, tried to submit another payment, failed, tried to debug again… It wasn’t pretty.

For my purposes, I had onIncompletePaymentFound call /payment/complete. Now, any time there’s a half-done transaction, the next time that user loads the app, it finishes the transaction and lets them keep generating insults. 

These are tiny transactions, and it’s a test, so I’d rather just finish the transaction and keep testing. Plus, I want to ensure a steady flow of insults into the global economy.

In production, you’ll want to cancel the transaction or do something fancier.

Using Authentication: An Aside

The code provided already does all this. This is here for explanation.

Building the app wasn’t enough. Noooooo. I wanted to show the username in a couple of places. To do that, I had to use authentication. Normally, I hear “authentication,” and I think of the nightmare that is OAUTH. Here, though, the SDK did most of the work. 

Change Authentication Javascript and Add A <span>

I changed the code block that authenticates the user to this:

 Javascript
// Authenticate the user, and get permission to request payments from them (added 'username' to provide username to API):
const scopes = ;
var accessToken
var username


// Read more about this callback in the SDK reference:
function onIncompletePaymentFound(payment) { 
    paymentId = payment.identifier
    txid = payment.transaction.txid
    $.post('/payment/complete',
            {
                paymentId: paymentId,
                txid: txid,
                debug: 'cancel'
            }
        )
};

Pi.authenticate(scopes, onIncompletePaymentFound).then(function(auth) {
  accessToken = auth.accessToken
  username = auth.user.username
  $('#username').text(username); // writes username to the page
}).catch(function(error) {
  console.error(error);
});

accessToken = auth.accessToken
username = auth.user.username
$('#username').text(username); // writes username to the page
}).catch(function(error) {
  console.error(error);
});
ian app example

I also added a <span> with the id #username. That’s where the user’s account name appears on the page.

Add /me To My Flask Routes

In app.py, I added the “/me” route: 

Python
@app.route('/me', methods=)
def getme():
    userurl = "https://api.minepi.com/v2/me"
    response = requests.post(userurl, headers=header)
    return(response.text)

This makes the call to the API and retrieves the user’s account name.

I also added a few lines of code to the /payments/complete route. But because I’m lazy, I already included that in the cut-and-paste block above. If you download the entire app repository, it’s all set.

Done!

That’s it! I now have a complete app. It provides a few insults, personalizes them, and even automatically copies a prewritten message (insult included) for easy emailing, posting, or anything else:

ian app done

Debugging, Ian-Style

I used console.log. A lot. If you’re a beginner like me, learn more about it here.

Quest Complete

Did I level up? I’m not sure. But I never, ever thought I’d be able to incorporate blockchain technology into anything I built. I didn’t realize it was as simple as adding some javascript or that I could use my old friend Python to do the work.

So it’s a tie: Blockchain 1, Ian 1

Both the cat and the dog received treats for being (sort of) well-behaved, so they get 2 points each.

Pi Network
Start mining. Easy as Pi!