Tutorial: Create a Lightning Playground app in Python


This example can be extended to other
platforms such as NodeJS / Ruby on Rails

Creating your first app

In this tutorial, we are going to create a simple skeleton for a game that integrates with the Lightning Playground for receiving and sending payments.

Generating an account

On the top menu, click on Generate to create a new account to store your earnings.

Generating app credentials

Once you've registered on the Lightning Playground, head over to the My Account page and click on Create an App
Enter some basic information about your game and click on Create.

This will generate a new app on the Playground with an App ID and an App Secret that is used to authenticate requests to the Lightning Playground API. On the detail page, scroll down to the Credentials & Integration section to view the App ID and App Secret.

Lightning Playground Developer API

This API has 3 main components, a REST API, a Webhook & a JavaScript plugin.

REST API

The REST API provides an interface to create invoices for receiving payments, and sending payments to users. It consists of two main endpoints one for each feature. The API is authenticated through a Basic Authentication using the app credentials obtained while creating the app on the Lightning Playground.

Webhook

The Webhook is used to notify your app / game when an invoice is satisfied for when a payment is completed successfully. You will need to setup an endpoint on your application and update it onto your app's page to receive events.

JavaScript Plugin

The JavaScript plugin can be added to your app / game in order to simplify the display of the invoice on your app relieving you of the need to create any additional UI for common invoice operations provided out of the box from the Lightning Playground.

We will be using a combination of all the three components to build out the skeleton of our app.

Setting up your flask application

Clone the repository available here. Let's go through the structure of the application:

sample-playground-app
│-- README.md
│-- app.py                      # Contains the route handlers for the app
└── integations
│   │-- __init__.py
│   │-- lp_client.py            # A client library for interacting with the Lightning Playground API
└── static
│   └── scripts
|   |   |-- main.js             # The main JavaScript module for running the sample application
|   |   └── lib
|   |       |-- playground.js   # A plugin for displaying invoices and claims
│   └── styles
│       |-- index.css
└── templates
│-- index.html
│-- layout.html

Let's take a look at the integrations / lp_client.py file

class LpClient():
    def __init__(self, app_id, app_secret):
        self.app_id = app_id,
        self.app_secret = app_secret
        self.auth = (app_id, app_secret)

    def __make_request__(self, path, data):
        ... # Handles POST requests to Lightning Playground API using Basic Authentication

    def create_invoice(self, amount, purpose):
        ... # Makes a request to the create invoice endpoint

    def send_payment(self, amount, recipient, purpose):
        ... # Makes a request to the send payment endpoint

We will be using this class to interact with the Lightning Playground. Now let's look at app.py:

The first part of app.py initializes the LpClient with the credentials of the app generated earlier.

lp_client = LpClient(
    app_id=os.getenv("PLAYGROUND_APP_ID"),
    app_secret=os.getenv("PLAYGROUND_APP_SECRET")
)

Creating Payment Operations

Next we initialize a route for creating invoices from the app.

@app.route('/invoice', methods=["POST"])
def invoice():
    amount = 1000
    invoice = lp_client.create_invoice(
        amount=amount,
        purpose="One round of Survivor42"
    )

The returned invoice dictionary is built from the following JSON:

{
    "id": "89d7d2741a6c49808d8d44f5a39e5609",
    "frame_url": "https://www.lightningplayground.co/approve/89d7d2741a6c49808d8d44f5a39e5609" 
}

Use the details from the invoice dictionary to store this information into the game's SQL / any other database. This route on the Flask application should now return the invoice_id and frame_url back to the UI:

return jsonify({
    "invoice_id": invoice["id"],
    "invoice_url": invoice["frame_url"]
}), 200

Navigate to static / scripts / main.js. We will setup the AJAX call on a button to the Flask route that we just created and initialize the JavaScript plugin:

// Get the play-round button
const playButton = document.getElementById("play-round")

// Attach a click event listener to the button
playButton.addEventListener('click', function(){
    fetch('/invoice', {
        method: 'POST',
        headers: {
            'Accept': 'application/json, text/plain, */*',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({})
    })
    .then(function(res){ return res.json()})
    .then(function(response){
        if(response.invoice_url){

            // Initialize the payment plugin
            lpPayments.init({
                frameUrl: response.invoice_url,
                onPaymentComplete: function(){
                    window.location.href = "/round/" + response.invoice_id
                },
                onPaymentFailed: function(){
                    window.location.reload()
                }
            })
        }
    });
})

When a payment is successful, the user will be redirected to the /round/invoice_id endpoint. But before that endpoint can be functional, we need to have our Webhook handler for receiving payment updates from the Lightning Playground API.

Creating the Webhook Events Handler

Lightning Playground can notify your application when an invoice is fulfilled, so that you can allow the user to proceed using the application. So in app.py we create a new route to handle the webhook events.

@app.route('/webhook', methods=["POST"])
def webhook():
    request_body_params = request.get_json(silent=True, force=True)

    event = request_body_params.get("event")
    invoice_id = request_body_params.get("invoice_id")
    transaction_id = request_body_params.get("transaction_id")

    if event == "PAYMENT_SUCCESS":
        #TODO:  Update the invoice information to mark it paid in the local database
        pass

    return "", 200
Install ngrok

Ngrok provides an easy way to test local applications that are used as webhooks. Once you have ngrok donwloaded, open up a terminal and type in:

./ngrok.exe http 5000

This should start a public proxy server with a URL that can be used to register the webhook on your Lightning Playground app.

Update Webhook URL on Lightning Playground

Navigate to your app on My Account and update the webhook information. If there are no credentials, refrain from updating the username and password fields.

Create the Round Handler

Once the payment is complete, we route the customer from the JavaScript plugin to the /round route which we will setup now:

@app.route('/round/<invoice_id>', methods=["GET"])
def round(invoice_id):
# TODO: Retrieve token / invoice information from database
#       Check if invoice is satisfied
#       Check if invoice is already used to complete the game
#       If all the conditions are good allow access to the game
return render_template('round.html', invoice_id=invoice_id)

Send Winnings to Customer

If your app sends payments to winners, you can use the send_payment method on the LpClient:

# When the game is completed and customer is eligible for a win:
payment_receipt = lp_client.send_payment(
    amount=1500,
    recipient=invoice_id,
    purpose="Winnings from Sample Playground App"
)

This initiates a payment directed to the user's invoice which acts as a token for receiving the winnings.

If the user paid the invoice anonymously (without a wallet on Lightning Playground) you will need to present the user with a claim. This can be detected by the response of the send_payment method:

# When the game is completed and customer is eligible for a win:
payment_receipt = lp_client.send_payment(
    amount=1500,
    recipient=invoice_id,
    purpose="Winnings from Sample Playground App"
)
if payment_receipt.get("claim"):
    return jsonify({
        "claim_url": payment_receipt["claim_url"]
    })

Navigate to static / scripts / main.js. We will setup the AJAX call on a button to the complete round endpoint and initialize the JavaScript plugin to provide the claim:

// Get the play-round button
const endButton = document.getElementById("end-round")

// Attach a click event listener to the button
endButton.addEventListener('click', function(){
    fetch('/completed', {
        method: 'POST',
        headers: {
            'Accept': 'application/json, text/plain, */*',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({})
    })
    .then(function(res){ return res.json()})
    .then(function(response){
        if(response.claim_url){
            // Initialize the payment plugin
            lpPayments.init({
                frameUrl: response.claim_url,
                onPaymentComplete: function(){
                    alert("Claim successful");
                    window.location.href = "/";
                },
                onPaymentFailed: function(){
                    alert("The payment wasn't successful. Please try again later.")
                }
            })
        }else{
            alert("Payout successful to the Lightning Playground wallet")
            window.location.href = "/";
        }
    });
})

With this skeleton as base you should be able to build a game / app that uses the Lightning Playground API for accepting and sending lightning payments.

Refer to our API specs in order to build your own integration clients.

Submitting your App

Host the app on a public / free platform like Heroku and update your entry on the Lightning Playground with the new URL.

Upload a banner image and some screenshots of your app as well.