Building a Game with In-App Payments for Firefox OS

The dawn of the HTML OS has seen the rise of numerous players backed by some industry heavyweights. Tizen (Samsung and Intel), Chrome OS (Google), and Firefox OS(Mozilla) are arguably the three biggest operating systems in this realm today. They are all unique in their implementations, but the tie that binds them is the idea of embracing the Open Web and making HTML5 a first class citizen. What does this mean for you as a web developer? It means that starting today you already have the skillset needed to develop apps that run natively on each of these platforms. In this case, web development is native mobile development!

You may be asking yourself, “this is great and all, but isn’t this what PhoneGap is all about?”. Yes and no. PhoneGap provides a layer of abstraction that allows you to write a hybrid mobile app which is hosted in a native wrapper on the device. However, the apps are not truly native in the same manner that they are native in the HTML OS world. If I write an app for Firefox OS, there are no shims involved – what I write is fully supported on the device and runs with native performance and has access to the device’s native APIs.

In this post, I’m going to dive into a specific, but extremely attractive, feature of Firefox OS: the mozPay API for making in-app payments. We’re going to do this a fun way, by building a very simple canvas game and requiring our users to pay for the game before they may play (kind of like an old school arcade game – drop 25 cents in the slot to play). When you’re done with this article, you should have a sense for not only how to integrate in-app payments, but also with how easy it is to take an existing HTML5 application and move it to a platform like Firefox OS. Lets get started!

Our HTML5 Game

We aren’t going to go too deeply into what it takes to write an HTML5 game. What we will do, instead, is stand on the shoulders of giants and take advantage of some really simple open source code. We are going to use some ideas and code from Lost Decade Gamesbut enhance game play by setting the game up to utilize touch events – as well as update some of the game’s graphical elements.

We are writing an HTML5 application, so we need to create our HTML, CSS, and JavaScript assets. When we are finished we will have a fun little game that looks like this:

the game ui

Firefox OS Simulator

Before we start coding, lets take a moment to go over a key piece of the Firefox OS development story: the Firefox OS Simulator. Unlike other webkit-based iOS or Android simulators on the market today, the Firefox OS Simulator is a full-featured testing environment and even comes with some default Firefox OS apps pre-installed. After you install the simulator, the first thing you will see is the dashboard interface. This lets you validate your apps and launch them in the simulator, all within your Firefox web browser (we’ll go over how you load your app in the dashboard a little later).

simulator dashboard

For now though, lets get going with development and jump into our HTML.

The HTML

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <link href="css/kendo.mobile.all.min.css" rel="stylesheet" />
    <link href="css/main.css" rel="stylesheet" />
</head>
    <body>

        <div id="home-view" data-role="view"></div>

        <div id="payment-view" data-role="modalview" style="width:100%;height:100%">
            <div id="welcome-text">Welcome to the Firefox OS Arcade!</div>
            <br /><br />
            <a href="#" id="btn-success">Simulate a Successful Payment</a>
        </div>

        <script src="js/jquery.min.js"></script>
        <script src="js/kendo.mobile.min.js"></script>
        <script src="js/game.js"></script>

    </body>
</html>

Yes, this is the extent of the HTML for our entire application! The first thing you should notice is that while this will be a Firefox OS app, there is literally nothing in the HTML that separates this from any other HTML5 application. Very cool.

You also may notice that I’m utilizing Kendo UI Mobile in this app. This is because we need a modal window for our payment process and I happen to be comfortable with Kendo UI (full disclosure: I am a Developer Advocate for Telerik, the company responsible for Kendo UI).

Our app has two DIV elements that are Kendo UI Mobile views. No need to be confused, they just represent two pages of the site: one is the game itself, the other a modal window that will handle our payments. Other than that, everything should look pretty familiar to you as a web developer. Lets quickly move on to styling our app.

The CSS

body
{
    margin: 0;
}

#welcome-text
{
    margin-top: 50px;
    margin-left: 8px;
    font-size: 17px;
    font-weight: bold;
}

#btn-success
{
    box-shadow: inset 0px 1px 0px 0px #9acc85;
    background: linear-gradient(to bottom, #74ad5a 5%, #68a54b 100%);
    background-color: #74ad5a;
    border: 1px solid #3b6e22;
    display: inline-block;
    color: #ffffff !important;
    font-size: 15px;
    font-weight: bold;
    padding: 6px 12px;
    text-decoration: none;
    margin-left: 30px;
}

#btn-success:active
{
    position: relative;
    top: 1px;
}

Again, nothing should be too surprising here in the CSS. The majority of our custom CSS applies some pretty styling to our payment simulation button, giving us this look when we first run the app in the simulator:

app first run

We have our HTML and CSS assets completed – and everything up to this point should be straightforward for most web developers. Now lets take the opportunity to dive into our JavaScript and see how everything is tied together.

The JavaScript

The first thing we are going to do is initialize our canvas, image assets, and game objects:

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 320;
canvas.height = 460;
document.getElementById("home-view").appendChild(canvas);

// Initialize Background Image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
    bgReady = true;
};
bgImage.src = "images/bg.gif";

// Initialize Knight Image
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
    heroReady = true;
};
heroImage.src = "images/knight.png";

// Initialize First Monster Image
var monsterReady = false;
var monsterImage = new Image();
monsterImage.onload = function () {
    monsterReady = true;
};
monsterImage.src = "images/monster-1.png";

// Initialize Game Objects
var hero = {};
var monster = {};
var monstersCaught = 0;

This is going to provide us with a canvas to work with, laid out to match the dimensions of the simulator. We are also creating three basic images – the background, hero (the knight), and monster. So far so good. Next up is to add our core game functionality:

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Resets when the knight catches a monster
var reset = function () {
    monster.x = 32 + (Math.random() * (canvas.width - 64));
    monster.y = 32 + (Math.random() * (canvas.height - 64));
    monsterImage.src = "images/monster-" + getRandomInt(1, 3) + ".png";
};

// Update Game Objects
var update = function () {

    document.onmousemove = function(e) { // change to ontouchmove in production
        hero.x = e.layerX;
        hero.y = e.layerY;
    };

    // Is the knight touching the monster?
    if (
        hero.x <= (monster.x + 32)
        && monster.x <= (hero.x + 32)
        && hero.y <= (monster.y + 32)
        && monster.y <= (hero.y + 32)
    ) {
        ++monstersCaught;
        reset();
    }
};

// Draw Everything to the Canvas
var render = function () {
    if (bgReady) {
        ctx.drawImage(bgImage, 0, 0);
    }

    if (heroReady) {
        ctx.drawImage(heroImage, hero.x, hero.y);
    }

    if (monsterReady) {
        ctx.drawImage(monsterImage, monster.x, monster.y);
    }

    // Scoreboard
    ctx.fillStyle = "rgb(66, 66, 66)";
    ctx.font = "18px Arial";
    ctx.textAlign = "left";
    ctx.textBaseline = "top";
    ctx.fillText("Monsters Slain: " + monstersCaught, 22, 420);
};

Some of the highlights in this chunk of code include the render function which is what draws our images and scoreboard to the canvas. We also have an update function which is executed repeatedly throughout the lifetime of the app. This function tracks our mouse/touch movements and determines whether or not our knight is touching a monster. You’ll notice that we are using the onmousemove event handler. This is because we are limiting ourselves to the Firefox OS Simulator, which doesn’t handle touch events (since we are simulating in a desktop/mouse environment). If we were to move this to production or even test on a physical Firefox OS device, we would substitute in the ontouchmove event handler. Finally we have the reset function which simply sets up a new monster after our brave knight defeats the previous one.

We need to add just a little more JavaScript code to get our game up and running:

// The main game loop
var main = function () {
    var now = Date.now();
    var delta = now - then;
    update(delta / 1000);
    render();
    then = now;
};

// Let's play this game!
reset();
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible

// Initialize Kendo UI Mobile
var app = new kendo.mobile.Application();
$(function () {
    $("#payment-view").data("kendoMobileModalView").open();
});

We start running the game by executing the reset function and then, with the setIntervalmethod, we execute our main function every 1 millisecond to make sure we are responding constantly to the user interaction. We are also initializing our Kendo UI Mobile app, which lets us spawn our payment modal window as soon as the app is up and running.

One last step. Lets put in some temporary code that will let us close our modal window without actually simulating a payment:

$("#btn-success").click(function () {
    $("#payment-view").data("kendoMobileModalView").close();
});

And we are done! We have a completely functional HTML5 canvas game that we can play, as is, in any major browser. But this isn’t the entire reason we came here. We came to create a Firefox OS app out of this game and add in-app payments to force people to pay to play our little game. Now comes the fun part!

Creating a Firefox OS App

Lets see how easy it is to take our HTML5 application and turn it into a Firefox OS application. If you would like a more detailed tutorial on creating a Firefox OS application, I recommend you check out my post on Writing your first Firefox OS app.

There are two different types of Firefox OS apps: a hosted app and a packaged app. Ahosted app is basically a web site that is hosted on any old web server, but the application code is executed within the context of the app. Your users need to be online to access all of the app resources. A packaged app, on the other hand, is a full archive of our app that is downloaded and installed on the device. For more information on the differences between the two, I recommend you take a look at Robert Nyman’s post on Open Web Apps.

For the purposes of this project, we are going to create a hosted app. Now, to migrate an HTML5 application to a Firefox OS hosted application we need to do one thing: create amanifest file. A manifest file is simply a JSON file that contains metadata about your app. Our manifest for the app we just created could look something like this:

{
    "name": "Monster Killer",
    "description": "Kill all the monsters with this fun Firefox OS app!",
    "launch_path": "/index.html",
    "developer": {
        "name": "Rob Lauer",
        "url": "http://roblauer.me"
    },
    "icons": {
        "16": "/img/icon16.png",
        "32": "/img/icon32.png",
        "48": "/img/icon48.png",
        "64": "/img/icon64.png",
        "128": "/img/icon128.png"
    },
    "default_locale": "en"
}

In our manifest, we simply provide a name and description of the app, along with a path to the HTML file. We also provide some required developer contact information, and the location of our app launch icons.

Save this file as manifest.webapp in the root of your application. If you haven’t done so already, boot up your Firefox web browser and install the Firefox OS Simulator add-on. Once the simulator is installed, you may start it at any time by clicking on the “Firefox OS Simulator” option in the “Web Developer” menu:

open simulator

With the simulator open, I’m now going to click the Add Directory button and choose themanifest.webapp file we just created. Your app should now, like magic, open up in the simulator! You’ll notice that, by default, the Error Console will open up along with your simulator. This is a very useful tool to debug any problems with your app inside of the simulator.

So we’re happy with the final result of our app. It works in the simulator as we had hoped. We just have one more thing to do, and that is integrate in-app payments using the mozPay API:

In-App Payments Using the mozPay API (Client Side)

mozPay is a JavaScript API that is geared towards allowing app developers to provide easy and secure payment options to their users. mozPay doesn’t care about the products you are selling, it is simply a mechanism that helps facilitate a payment. Within the Firefox Marketplace you, as a developer, are able to set up an account to receive payments from a variety of payment providers of your choosing. If you are interested in more background,Kumar McMillan provides a great overview of mozPay in his post, “Introducing navigator.mozPay() For Web Payments”.

At the time of this writing, the mozPay API is not fully implemented in the Firefox Marketplace. This means that we can only simulate payments and some of the code we write today may be incompatible when the final API is released.

The first thing we are going to do is allow our users to initiate a payment within our app. We are going to replace our JavaScript code that handled the click event of the payment simulation button with this:

$("#btn-success").click(function () {
    $.post("http://yourwebsite.com/ffospayment", {})
      .done(function (signedJWT) {
          var req = navigator.mozPay([signedJWT]);
          req.onsuccess = function () {
              paymentSuccessful();
          };
          req.onerror = function() {
              alert("navigator.mozPay() error: " + this.error.name);
          };
      })
      .fail(function () {
          alert("Ajax post failed!");
      });
});

Lets walk through each step of the code to avoid any confusion. First up we are doing an ajax post to an API that is hosted on our web server (we’ll get to the server side of things in a minute). The purpose of this server-side API is to generate a signed JSON Web Token (JWT) that contains the product name and price (along with your application key/secret which, again, we will discuss in the next section). If the ajax post is successful, we callnavigator.mozPay and pass the signed JWT that we received from our server. At this point, the user will be directed to complete their payment outside of the context of the app. Finally, if the payment itself is successful, we execute the paymentSuccessful function that we will define in our app as simply:

function paymentSuccessful() {
    $("#payment-view").data("kendoMobileModalView").close();
}

…which closes our modal window. This is all of the client-side code you need to make a simple payment using the mozPay API. Lets take a look at what happens on the server side:

In-App Payments Using the mozPay API (Server Side)

Before we go any further, we’re going to need an Application Key and Application Secret. Both of these can be obtained from the Firefox Marketplace Developer Hub. We will need these when we write our server-side code so we can create a digitally signed JWT.

In my case I’m going to write a payment method using C#. There are, however, Node.JSand Python implementations available.

First up is a C# method that will generate a signed JWT (this is the method that is called in the initial ajax post from our app):

public string Get()
{
    string payload = "{" +
                           "\"iss\": YOUR_APPLICATION_KEY," +
                           "\"aud\": \"marketplace.firefox.com\"," +
                           "\"typ\": \"mozilla/payments/pay/v1\"," +
                           "\"iat\": " + (DateTime.Now - new DateTime(1970, 1, 1).ToLocalTime()).TotalSeconds + "," +
                           "\"exp\": " + (DateTime.Now.AddMinutes(10) - new DateTime(1970, 1, 1).ToLocalTime()).TotalSeconds + "," +
                           "\"request\": {" +
                           "\"  id\": \"YOUR_INTERNAL_PRODUCT_ID\"," +
                           "\"  pricePoint\": 0.25," +
                           "\"  name\": \"Arcade Game\"," +
                           "\"  description\": \"25 cents for One Game Play\"," +
                           "\"  postbackURL\": \"http://yourwebsite.com/ffospayment/postback\"," +
                           "\"  chargebackURL\": \"http://yourwebsite.com/ffospayment/chargeback\"," +
                           "\"  simulate\": {" +
                           "\"      result\": \"postback\"" +
                           "    }" +
                           "}" +
                       "}";

    const string secretKey = "YOUR_APPLICATION_SECRET";
    string token = JWT.JsonWebToken.Encode(payload, secretKey, JWT.JwtHashAlgorithm.HS256);
    return token;
}

This uses the JWT Implementation for .NET.

For more information on the individual properties above that don’t make immediate sense to you, I highly suggest you consult the API documentation which breaks down all of the properties and shows you which are mandatory vs optional.

Lets assume that our payment was successful (which it will be because we are simulating the payment). In this case, the payment provider will post a new JWT to the postbackURLwe specified above. An example of this JWT reponse is provided here:

 {
    "iss": "marketplace.firefox.com",
    "aud": APPLICATION_KEY,
    "typ": "mozilla/payments/pay/postback/v1",
    "exp": 1337370900,
    "iat": 1337360900,
    "request": {
        "id": "YOUR_INTERNAL_PRODUCT_ID",
        "pricePoint": 0.25,
        "name": "Arcade Game",
        "description": "25 cents for One Game Play",
        "postbackURL": "http://yourwebsite.com/ffospayment/postback",
        "chargebackURL": "http://yourwebsite.com/ffospayment/chargeback"
    },
    "response": {
        "transactionID": "webpay:56212uh6-6548-11c0-9089-5y8895yu5685"
    }
 }

At this point the postbackURL defined above must simply respond to this post with a plain text response that includes ONLY the transactionID, as in:

webpay:56212uh6-6548-11c0-9089-5y8895yu5685

Phew! A lot of stuff going on here. The first thing you are probably thinking is, where are the libraries that do all of this for me!? I hear you loud and clear. Within a short amount of time we are sure to see third-party open source libraries that encapsulate much of this and make it far easier for you to implement payment processing in your app. There is even anexperimental prototype that removes the server requirement altogether!

It’s also important to remember that the mozPay API is a bit of a moving target and the functionality we outlined here can (and probably will) change before it is released to the world. The important takeaway from all of this is that you at least have a high level of understanding what can be done with the mozPay API.

Conclusion

I hope through this post you have achieved some appreciation for how easy it is to take an existing HTML5 app and turn it into a native Firefox OS app. In addition, we’ve gone over the basics of the mozPay API for handling in-app payments and how you may include this functionality in your own Firefox OS app. The mozPay API is a work in progress and will only improve as time goes on, so be patient! Good luck creating your own Firefox OS apps!


猜你喜欢

转载自blog.csdn.net/ghd_214/article/details/9244943