在你的 Firefox OS 应用中实现应用内支付

在 Mozilla,我们一直在努力将支付引入网络。我们认为开发者应该拥有一个轻松的应用程序货币化方式。这也给了我们机会提供更深层的平台集成,允许使用运营商计费等方式,除了信用卡/借记卡之外。

我们之前在 navigator.mozPay() 用于网页支付 中讨论过应用内支付,但这是一篇从应用开发者角度概述的文章。这篇文章还包括一些更新,例如 Firefox 应用商店中的真实支付现已上线。支付不同于仅仅是付费应用,付费应用在首次购买时向用户收取一次费用。应用内支付是一种不同的流程,允许你为应用内的特定商品向客户收费。

架构概述

目前,支付仅在 Firefox OS 设备上启用。我们正在努力在其他平台上启用支付,甚至在桌面 Firefox 上启用,但这还需要一段时间。在本文的其余部分,我们将假设使用 Firefox OS 环境。

有关应用内支付的 MDN 页面 对流程进行了很好的概述,但这里有一个更简短的概述。你需要一个服务器来生成支付请求,因为否则黑客可以轻松地将价格改为 0。服务器创建 JWT 令牌,将其传递给你的应用,你的应用使用该令牌调用 navigator.mozPay。用户将被带到内置支付流程,完成后,你的服务器将收到通知。之前关于支付的 Hacks 文章 更深入地探讨了更低级别的机制和支付的理由。

剩下的工作流程由你处理:在成功购买商品后更新应用程序,将购买与帐户关联等。

获取模拟密钥

为了使用支付,你需要使用应用商店为你生成的应用密钥和密钥。如果你只想模拟支付,你可以轻松地 获取测试密钥和密钥。你只需要创建一个帐户(如果你还没有帐户的话)。

你只能使用该密钥模拟支付。当你准备使用真钱时,你需要将你的应用提交到应用商店,设置支付,并获取真实的密钥和密钥。所有这些内容将在下面详细说明。

设置服务器端代码

你需要做的第一件事是设置服务器端代码。在 几种不同的语言 中存在一个 “mozpay” 模块,它为你实现了支付流程。如果你的编程语言中不存在它,你需要使用 JWT 模块 并进行更多操作。查看 mozpay-js 中相当 简单的代码,以了解你需要做什么的示例。

我为 Firefox OS 写了一个名为 Webfighter 的游戏,它使用 node.js 作为后端,因此我在服务器端使用了 mozpay-js。Webfighter 是一款带有商店的射击游戏,你可以在商店里购买更多武器和飞船,它对于开发者来说是一个很好的参考,可以查看使用支付的实际代码。下面的示例取自 Webfighter。 你可以在这里查看 Webfighter 的源代码。

首先,需要并配置 mozpay 模块

var pay = require('mozpay');
var settings = require('settings');

pay.configure({
    mozPayKey: settings.payKey,
    mozPaySecret: settings.paySecret,
    mozPayAudience: 'marketplace.firefox.com',
    mozPayType: 'mozilla/payments/pay/v1',
    mozPayRoutePrefix: '/mozpay'
});

settings 模块只是一个包含 JSON 的文件,它指定了应用的配置。将配置分隔开以便于你轻松地在不同的环境(如开发环境和生产环境)中进行切换,这样很不错。

使用你从应用商店获取的密钥和密钥。如果你使用的是 Mozilla 以外的支付服务器,你应该只更改 audience 和 type 选项。routePrefix 是可选的,它指定了支付服务器回传的 URL 前缀。

接下来,你需要定义一个路由,用于创建支付交易中使用的数据。客户端是执行实际支付请求的人(这样你就不必处理信用卡详细信息,甚至可以通过运营商计费进行收费)。你只需要创建一个表示购买的对象。本示例使用 express.js 作为 http 服务器。

var purchaseQueue = {};

app.post('/sign-jwt', function(req, res) {
    var token = 'o' + Date.now();
    var item = getItem(req.body.name);

    var jwt = pay.request({
        id: item.name,
        name: item.name,
        description: item.description,
        icons: { '64': settings.url + item.icon },
        pricePoint: item.price,
        productData: token,
        postbackURL: settings.url + 'mozpay/postback',
        chargebackURL: settings.url + 'mozpay/chargeback',
        simulate: { result: 'postback' }
    });

    // Keep track of which JWT objects we are waiting on
    purchaseQueue[token] = 'processing';

    res.send(JSON.stringify({
        jwt: jwt,
        token: token
    });
});

再次注意 settings 对象;它只是一个 dict,它包含有关我们的应用的一些配置属性。settings.url 是我们的应用托管的基本 URL,因此所有传入的 URL 都是绝对的。

我们调用 getItem 来获取有关商品的详细信息(我们在这里不定义它)。我发现定义一个包含所有可用商品信息的 JSON 文件是最容易的。你可以在 GitHub 上看到 Webfighter 中可用商品的 JSON

对于购买请求,有 几个字段id 只是一个唯一的标识符,我们简单地使用产品名称。其他字段描述了产品,例如 icons,它列出了各种尺寸的图标。pricePoint 是一个数字,它映射到每个可用地区的特定价格,你应该查看 价格点表 找到你需要的那一个。例如,价格点为 10 表示在美国的价格为 0.99 美元。

simulate 参数告诉支付系统只模拟请求。如果传递了该参数,则不会进行实际收费,你将经历模拟流程。这对开发非常有用。**请记住**,在生产环境中要删除此标志。

你可以在 productData 中存储任何想要的内容,你可以在处理事件时使用它。这里我生成了一个用于跟踪请求的任意令牌,并将其存储在全局对象 purchaseQueue 中。我们在处理事件时使用它。

接下来,订阅 mozpay 模块的 postback 事件

pay.on('postback', function(data) {
    // the payment was successful
    var req = data.request;
    purchaseQueue[req.productData] = 'success';
});

你也可以订阅 chargeback 事件。当客户对收费提出争议并退回款项时,就会发生退款。这可能会在一个月后发生,并且难以处理。Webfighter 会跟踪退款,但应用程序不会检查它们并撤销商品。你需要定期或在启动时检查用户是否仍然拥有对所有已购买商品的访问权限。

pay.on('chargeback', function(data) {
    var req = data.request;
    purchaseQueue[req.productData] = 'chargeback';
});

服务器端的代码就这些。

在客户端使用 mozPay

在客户端,你需要发布到 /sign-swt 并使用 mozPay 启动购买。在下面的代码中,我们检查 mozPay 是否可用,如果不可用,我们只是将商品提供给用户。请记住,这只是一个测试应用程序!一旦购买启动,我们就开始轮询服务器,直到我们收到成功的购买信息。

function buy(name) {
    // Purchase an item by requesting a JWT object from the
    // server, and posting it to the mozPay API
    $.post('/sign-jwt', { name: name }, function(res) {
        if(navigator.mozPay) {
            var req = navigator.mozPay([res.jwt]);

            req.onerror = function() {
                console.log('mozPay error: ' + this.error.name);
                clearPolling();
            };

            // Poll to see when the payment is complete
            startPolling();
        }
        else {
            alert('in-app payments unavailable, so giving it to you for free');
            onPurchase(name);
        }
    });
}

以下是轮询函数,它们等待服务器响应成功的购买信息。请注意,我们只检查成功的购买信息。用户将在本机支付屏幕中收到任何错误的通知,当他们关闭屏幕时,mozPay 请求对象上会触发一个错误事件,我们处理该事件并停止轮询。查看上面的代码,其中包含 req.onerror

function pollQueue(token, name) {
    // Poll the server every second to see the status of our
    // payment request

    $.get('/purchaseQueue?token=' + token, function(res) {
        if(res == 'success') {
            onPurchase(name);
            clearPolling();
        }
    });
}

var pollTimer;
function startPolling() {
    pollTimer = setInterval(function() { pollQueue(res.token, name); }, 1000);
}

function clearPolling() {
    if(pollTimer) {
        clearInterval(pollTimer);
    }
    pollTimer = null;
}

当成功购买商品并且应为用户启用商品时,我们调用 onPurchase。你应该创建该函数并编写你的应用特定的代码。

真实支付:将应用提交到 Firefox 应用商店

当你准备运行真实支付时,在服务器创建支付请求时删除 simulate 字段,并将 你的应用提交 到 Firefox 应用商店。你需要提交应用,以便你能够连接真实支付。

获得报酬:配置银行账户详细信息和支付选项

你的应用提交后,编辑你的应用,点击左侧的 “兼容性和支付”,选择 “付费/应用内”。在 “支付账户” 下,添加你的银行账户。困扰我的一件事是需要我的银行的 SWIFT 代码。将来,应用商店有望自动填写它,但目前,你可以在 theswiftcodes.com 上查找它。

在 “价格和国家/地区” 下,你可以设置你的应用的价格。你可能只想设置 “免费,含应用内购买”,这就是 Webfighter 的设置方式。此外,你可以选择性地选择你的应用可用的区域。带有支付的应用只在启用了支付的区域可用。

使用真实的密钥和密钥

现在点击左侧边栏中的 “应用内支付” 链接。你将看到一个屏幕,你可以在其中获取用于真实支付的新密钥和密钥。在服务器上的 configure 调用中使用它们。不要分享你的密钥。

在撰写本文时,运营商计费只在特定国家/地区可用。查看 支付状态页面,了解它在哪些地方可用。如果它不可用,用户将看到信用卡屏幕。

使用你的应用赚点钱吧!

我们希望你能够成功地参与到应用程序生态系统和经济中。请告诉我们你的体验!我们很乐意在 应用商店邮件列表 上回答任何问题。

关于 kumar303

Kumar 为各种项目开发 Mozilla 网络服务和工具,例如支持 Firefox 扩展 的工具。他也开发很多 随机开源项目

更多 kumar303 的文章…

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道师和编辑。他经常发表演讲和撰写博客,主题包括 HTML5、JavaScript 和开放网络。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直从事 Web 前端开发工作,分别在瑞典和纽约市。他还在 http://robertnyman.com 上定期发布博客,热爱旅行和结识新朋友。

更多来自 Robert Nyman [名誉编辑] 的文章…


17 条评论

  1. Progi1984

    这篇文章写得很好。

    当您有一个打包的应用程序时,没有服务器端。您如何在打包的应用程序中实现应用内支付?

    2013 年 11 月 26 日 14:18

    1. James Long

      您仍然可以在打包的应用程序中使用服务器,应用程序本身并不依赖于它。据我所知,目前您需要一个服务器来创建和签署支付数据。当支付处理完成后,支付服务器会向您发送一个事件,所以从这个角度来看,您肯定需要一个服务器来处理该回调。

      服务器只处理支付处理,仅此而已。您的应用程序的其余部分可以在脱机状态下工作。

      2013 年 11 月 26 日 14:31

      1. Progi1984

        那么,没有服务器就无法实现应用内支付吗?

        2013 年 11 月 26 日 14:45

        1. James Long

          是的,因为支付服务器如何将事件发送回您以确认支付已处理?没有服务器,它没有可以发布的 URL。

          您可以使用类似 Heroku 的工具,它有免费的轻量级计划,仅用于管理应用内支付。

          2013 年 11 月 26 日 15:16

          1. Progi1984

            如果我理解正确,开发者会将其银行帐户连接到应用商店。

            用户进入应用程序,想要购买应用内商品。他点击“支付”按钮。这会向 /sign-jwt 发送请求以获取签名的 JWT。支付使用此签名的 JWT 并进入支付流程。支付成功或失败后,会向回调 URL(成功)或回退 URL(失败)发送请求。

            是这样吗?

            2013 年 11 月 26 日 15:52

          2. James Long

            不完全是。您的工作流程是正确的,但您需要一个服务器来处理 /sign-jwt 请求。应用商店不会为您提供任何托管服务。

            2013 年 11 月 26 日 16:45

          3. Progi1984

            除了 /sign-jwt 请求之外,流程是这样的吗?

            您有用于管理此功能的后端 PHP 吗?

            2013 年 11 月 27 日 00:05

          4. James Long

            还没有 PHP,请阅读上面的“服务器端”部分。

            2013 年 11 月 27 日 08:26

        2. Kumar McMillan

          我们计划为打包的应用程序(没有服务器)提供一种进行支付的方式。该功能仍在开发中,因此还没有时间表。您可以关注此 bug 以获取更新 https://bugzilla.mozilla.org/show_bug.cgi?id=910270

          2013 年 11 月 26 日 18:24

  2. Stuart Langridge

    一个简短的问题。从 https://mdn.org.cn/en-US/docs/Mozilla/Marketplace/App_pricing?redirectlocale=en-US&redirectslug=Web%2FApps%2FPublishing%2FApp_pricing 中可以清楚地看出,如果我在我编写的应用程序中使用应用内支付,那么我的财务关系与支付提供商之间,而不是与 Mozilla 之间。这对 Moz 来说是合理的,当然;去掉中间人,等等,我想这意味着如果我收到付款时遇到问题,我会向支付提供商提出投诉,而不是 Mozilla 团队。但是,如果是这样的话……Mozilla 为什么会抽取一部分付款?通常在这种情况下,应用商店的运营商会抽取一部分付款,但我的财务关系是与他们之间的;也就是说,如果出现财务问题,我会向运行应用商店的人员投诉,并由他们处理。Moz 从该角色中退出了(也许是明智之举)……我付给 Mozilla 的这部分费用是用于什么呢?

    2013 年 11 月 26 日 14:22

    1. Andy McKay

      至少 Mozilla 需要支付支付流程的网站开发、基础设施、营销、维护和测试费用,以及支付支持的联系点。

      虽然开发人员和支付提供商之间的关系是作为中介的 Mozilla 之间的,但 Mozilla 负有某些责任。我们会尽力让每个人满意。

      2013 年 11 月 26 日 14:49

      1. Stuart Langridge

        说得有道理。这是否意味着任何想要建立替代应用商店的人都需要承担这些支付责任才能做到这一点?

        2013 年 11 月 26 日 15:49

        1. Andy McKay

          我认为这完全取决于建立该应用商店的人以及他们想要如何实现支付。

          2013 年 11 月 26 日 17:28

          1. Stuart Langridge

            Andy,
            我想我并不理解 navigator.mozPay 如何工作;如果我建立一个替代应用商店,似乎每个应用程序都需要知道它是在哪个应用商店中出售的,以便知道要与哪个应用商店的支付服务通信?所以在有多个应用商店而不是只有一个(一直有人说这是目标;这不是一个唯一的应用商店锁定)的情况下,我应该如何编写应用程序?我是否需要为我要发布的每个应用商店创建应用程序的独立分支?

            2013 年 11 月 26 日 18:22

        2. Kumar McMillan

          您不必为每个应用商店创建应用程序的分支。但您确实需要在每个应用商店中设置一个支付帐户。您仍然可以使用相同的 mozPay() API 来处理每个应用商店。在当前设计中,您只需传递一个为支持的每个应用商店签名的 JWT 数组。将来,我们可能会改进 API 以适应多个应用商店的情况,但您绝对不必创建应用程序的分支。

          2013 年 11 月 26 日 18:32

          1. Stuart Langridge

            啊,明白了;所以,在我的服务器端,我只需传递一个包含密钥/密钥/应用商店对象的数组到 pay.configure 中吗?这很有道理。而应用程序并不关心;它只是将从服务器获取的 JWT 传递出去?

            2013 年 11 月 26 日 18:41

          2. Kumar McMillan

            是的,应用程序只是将签名的 JWT 传递给 mozPay()。您不能在客户端上签名 JWT,因为没有安全的方法来处理私钥以进行签名。

            2013 年 11 月 26 日 18:43

本文的评论已关闭。