程序内支付(IAP)编程及例子解析
本文介绍如何使用Nokia IAP api开发程序内支付应用
Contents |
Introduction
1.指定程序内支付物品
程序内支付的物品必须有Nokia商店收费。你必须在Nokia商店注册物品名,描述,和价格点。Nokia商店对每个物品提供一个ID标识,来给API调用。
环境
在开始开发前,你就可以标定程序内支付物品。 向Nokia商店提交信息后,就可以被程序内支付API访问到。为避免泄露你的产品信息,你可以提交一个零食的描述用于测试,当发布时再改成真正的描述。你可以设一个很低的价格,以降低测试消费。
步骤
1. 登陆 http://publish.ovi.com.
2. 创建一个新的 Qt Based Application. 你不能向一个已有的内容物品中添加程序内支付功能。
3. 在内部物品名中添加IAP字样(不是外部显示名)。
4. 勾选程序内支付物品选择框。如果Ovi publish没有显示这个选择框,那么你的账号不能访问程序内支付的功能,请联系Nokia申请这个权限.
5.提交所有媒质信息,点Create。
6. 点In-app purchases选项卡。为第一个支付物品提交名字,描述,和价格点。
7. 点Create。
8. 点Add in-app purchase给其他物品提交信息。Ovi pulish 为每个物品显示一个ID,在你的程序中使用这个ID标定每个物品。
9. 点Submit(提交)。 将这些信息上传到Nokia商店。这样你都可以放我那支付数据,并向在线服务器提交支付申请,以测试你的应用。之后你可以根据需要修改这些媒质信息。
页面结果
现在你有了支付物品及其ID,接下来将这些ID用在:
- 资源文件夹的名字,如设置开发环境章节中的描述。
- API调用,如使用程序内支付API章节里的描述。你可以决定如何存储这些ID,及你的应用如何访问你的存储。示例应用章节中演示了如何从资源子文件夹中读取ID。
2.设置开发环境
你程序的私有文件夹中必须有几个特殊文件,并适当设置你的Qt开发环境。
设置私有文件夹
你的程序的私有文件夹不能被其他程序访问。你有一个默认的私有文件夹:
<drive>:\private\<UID>\
其中<drive>是盘符(如, C:,D:),<UID>时候全局唯一标示号,是你的symbian程序的UID。8位16进制数,没有0x前缀。在这个私有文件夹中放两个文件
1. IAP_VARIANTID.txt: 包含000000(6个0)的文本。这是程序内支付API所要求的。 你的程序提交以后,Ovi Publish在这个文本文件中写入数据,用于DRM的访问。
2. TEST_MODE.txt: 用来模拟支付,绕过真实付款。去掉这个文件将访问在线Nokia商店进行真实支付。 参考测试应用获取具体细节。
设置DRM保护
通过以下步骤你可以使用 OMA DRM 2.0 保护你的文件:
1. 申请免费签名服务。 之后我们可以打开你提交的应用,添加DRM保护然后签名 再打包。申请免费签名后,Nokia商店提供给你UID,你用这个UID来编码你的私有文件夹路径。如何申请参考: http://www.developer.nokia.com/Distribute/Packaging_and_signing.xhtml.
2. 设置外部资源文件。适当组织你的程序访问外部资源的结构。例如,一个游戏中每关的数据和图片文件。将这些文件放在适当的子文件夹中:
- <drive>:\private\<UID>\drm\data\ — 保护需要支付的应用的基本资源文件
- <drive>:\private\<UID>\drm\data\resourceId_XXXXXX\ — 保护程序内支付的资源文件
其中XXXXXX是6位程序内ID,由之前Ovi Publish提供。一个子资源文件夹的例子是: D:\private\ef80c1a8\data\resourceId_593032.
设置Qt开发环境
如果你刚开始接触Qt SDK, 请先学习如何设置Qt 开发环境,及如何编译应用。参见Portal:Qt (Chinese)
开发程序内支付应用,需要如下设置Qt应用的 pro文件。
CONFIG += mobility \
iapppurchasing \
symbian: {
TARGET.UID3 = 0xee774112
ICON = icon.svg
TARGET.EPOCSTACKSIZE = 0x14000
TARGET.EPOCHEAPSIZE = 0x020000 \
0x800000
DEPENDPATH += ./Symbian
SOURCES += Symbian/drmfile_p.cpp
HEADERS += Symbian/drmfile_p.h
LIBS += -lcaf \
-lcafutils \
-lapmime
# capabilities required for IAP API
TARGET.CAPABILITY += NetworkServices
# IAP configuration
addConfigFiles.sources = ./unlocked_data/IAP_VARIANTID.txt ./data/TEST_MODE.txt
addConfigFiles.path = .
DEPLOYMENT += addConfigFiles
# resources to be DRM protected
addDrm.sources = ./data
addDrm.path = ./drm
DEPLOYMENT += addDrm
# resources no need to be DRM protected
addUnlocked.sources = ./unlocked_data/unlocked_resources
addUnlocked.path = .
DEPLOYMENT += addUnlocked
}
如下构建程序: 1.运行qmake,在pkg文件中应该生成以下依赖语句:
; Has dependency on IAP component
(0x200345C8), 0, 1, 1, {IAP}
2.编译运行产生 sis包。
3.使用程序内支付API
使用程序内支付API,你需要在你的应用中实现购买内容和恢复内容的场景。
处理异步事件
程序内支付API的方法都是异步方法。你的应用先调用一个程序内支付方法。当API完成方法请求,API返回一个或多个信号。你的应用中用slot来处理每个可能的信号,
有两种函数调用的方法:
- 一次发一个:当一个调用结束后发出下一个。例如,你请求一个物品的信息,然后请求下一个。推荐这个方法,因为你可以很容易处理到来的信号。也更容易处理延后的调用反应。
- 并发的:同时调用多个方法。例如,用一个循环访问所有物品的媒质信息,然后按返回顺序处理返回信号。这种方法比较快速,但是回复顺序不可控(例如你请求A,然后B,但是接到B回复然后接到A回复)。你需要建立请求与回复之间的关系,并确保所有期望的回复都收到。
请参看示例应用如何连接signal,slot和发出调用。 下面的表列出你能调用的方法,和返回的信号。
| API function | Returned signals | Used with built-in DRM or not built-in DRM content |
| getProductData() | productDataReceived() | Both |
| purchaseProduct() | purchaseCompleted() and purchaseFlowFinished() | Both |
| getUserAndDeviceId() | userAndDeviceDataReceived() | Not built-in |
| getRestorableProducts() | restorableProductsReceived() | Built-in |
| restoreProduct() | restorationCompleted() (and restorationFlowFinished() , not needed in the application.) | Built-in |
执行一个销售流程
1.要弹出程序的待售目录,调用getProductData() 并传递产品ID。API返回productDataReceived()信号。用你的应用的slot连接这个信号并处理。
重复这个步骤以显示你的待售物品。
2.将待售物品显示给用户,至少显示品名。显示一个购买的按钮或其他方法以让用户触发购买。连接clicked()信号到一个slot比如,buyProduct()。
3.在buyProduct()slot中,调用purchaseProduct()方法,并传递商品ID。
4.这时程序内支付API接手,开始Nokia商店支付流程。这个过程中你的程序UI变灰色在背景,下面的步骤不需处理,只描述一下:
a.如果用户没有登录Nokia商店,API弹出Nokia商店登录对话框。API也显示相应的对话框供用户确认购买。API选择支付方法,通常是Nokia账户的首选支付方法。
b. 当用户支付完毕,API发送purchaseCompleted() 信号,并传递状态值。
c.发送purchaseCompleted() 信号后,API显示交易的输出对话框,或者是支付成功信息,或错误信息。
d. 当用户点击OK忽略交易输出,API发出purchaseFlowFinished() 信号给你的应用。
5.收到 purchaseFlowFinished() 信号时,你的应用重新获得了UI控制权。如果支付成功,开始让用户访问购买物品内容的过程。
- 如果内容是程序内的DRM保护的,IAP API将其自动解锁
- 如果内容不是由DRM保护,你的应用需要自己处理内容。
- 如果你使用后台服务器下载内容到用户手机,服务器需要首先调用Purchase Ticket Verification API来验证产品已经售出。
恢复已买物品
你可以使用程序内支付API恢复DRM加密内容的证书。恢复内容的过程取决于你是否使用了内建的DRM保护。
- 要恢复内建DRM保护的内容,Nokia商店重新部署DRM key到手机。Nokia商店可以保存关于可恢复的内容的信息也可以限制恢复的次数。
- 要恢复其他内容,你可以决定支持哪种类型的恢复,你可以使用getUserANdDeviceId()来获取用户和当前使用设备的信息,你可以使用这些信息来决定用户可否恢复。
4.使用Purchase Ticket Verification API
使用 Purchase Ticket Verification (PTV) API来验证购买票的有效性。
PTV API通过HTTPS访问Nokia商店获取XML。如果你使用后台服务器向用户手机提供下载服务,服务器可使用PTV API来向Nokia商店验证商品是否已售出。 1.向Nokia商店发送PurchaseVerificationRequest 请求 2.Nokia商店返回PurchaseVerificationResponse ,其中结果的属性表明支付是否成功。 请求和回复使用UTF-8编码,由PTV API XML语法描述。
所有PTV API请求使用HTTPS POST方法发送。请求XML设置to form参数内容。调用者必须设置Content-Type HTTP头参数为application/x-www-form-urlencoded并包含charset参数如 Content-Type: application/x-www-form-urlencoded; charset=UTF-8。API不需要额外的验证。
API调用返回下面的HTTP状态值:
- 返回 200 如果调用成功。返回体包含XML回复文本。
- 返回非200则调用失败。不同的错误原因返回值不同。
你可以通过发送购买验证请求来验证购买是否成功。
购买验证请求
要验证购买票相关的购买是否成功,向Nokia商店发送PurchaseVerificationRequest 。请求URL是/iap/1.0/purchases/verify?method=GET。
在请求中,使用一个PurchaseTicket 或二进制XML 元素。PurchaseTicket 是标示单个购买的一个结构体。二进制的值是PurchaseTicket 值得64位编码的值。 下表列出了PurchaseTicket 元素的属性,
| Attribute | Data type | Description |
| transactionId | IdType | The unique identification of the purchase transaction. |
| transactionTime | xs:datetime | The date and time of the purchase in the UTC time zone, for example 2010-12-22T08:47:59.000Z |
| productId | IdType | The product ID of the purchased product. |
| applicationId | IdType | The identifier of the application. |
| accountId | HashType | The hashed value of the user's account ID. |
| imei | HashType | The hashed value of the user's IMEI. |
| imsi | HashType | The hashed value of the user's IMSI. |
| signature | SignatureType | Used for verifying data integrity. The value is a hexadecimal string presentation of an SHA–1 hash. The hash is calculated over a string that is derived by concatenating the other PurchaseTicket attribute values, in the following order: transactionId, transactionTime, productId, applicationId, accountId, imei, imsi. |
下表列出PurchaseTicket 属性使用的数据类型。
| Data type | Description |
| IdType | A string value, length between 1 and 128 characters. |
| HashType | A string value, length 128 characters. |
| SignatureType | A string value, length 40 characters. |
PurchaseTicket 元素示例:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?>
<PurchaseVerificationRequest xmlns=”http://payment.ovi.com/iap”>
<PurchaseTicket
transactionId="TH4B7CJE6P629"
transactionTime="2010-12-22T08:47:59.000Z"
productId="IAP_Resource_1"
applicationId="IAP_Product_1"
accountId="c738d9a3e5cbe57abac12c2e342bf5cdee19536e72de81532be680d9bcf1a17d6fb5f382467b78bcec0f669b09f753bc392586c29d99c7e6316d2b62a617e35b"
imei="d892bd0af1b5c124753be58ec7e3091cab2ecadc201069b63a3a7baace31edf04dee79f72a9ffb969093da62745ad9f42f4a3100bcbf56e550d3502efa846bf7"
imsi="9efd65ed7dbb77cb7d9c96503158a3139f0346576dd80f00d22c0076e11c1c34d3bde4877446eb6b4d122265f5bed8b75f7b4ae8d8c2cee7e724230b4a930136"
signature="99c10faffc36e944dd20a2f71ff55c91276690af"
</PurchaseTicket>
</PurchaseVerificationRequest>
上例中的签名如下计算:
SHA1(“TH4B7CJE6P6292010-12-22T08:47:59.000ZIAP_Resource_1IAP_Product_1c738d9a3e5cbe57abac12c2e342bf5cdee19536e72de81532be680d9bcf1a17d6fb5f382467b78bcec0f669b09f753bc392586c29d99c7e6316d2b62a617e35bd892bd0af1b5c124753be58ec7e3091cab2ecadc201069b63a3a7baace31edf04dee79f72a9ffb969093da62745ad9f42f4a3100bcbf56e550d3502efa846bf79efd65ed7dbb77cb7d9c96503158a3139f0346576dd80f00d22c0076e11c1c34d3bde4877446eb6b4d122265f5bed8b75f7b4ae8d8c2cee7e724230b4a930136”).
二进制元素示例:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?>
<PurchaseVerificationRequest xmlns=”http://payment.ovi.com/iap”>
<Binary>PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxQdXJjaGFzZVRpY2tldCBzaWduYXR1cmU9IjRhOTRiMWUzMzljZmI5MjkzMjdlNTJhYzBiZjRiNGUyNzAwYjEwMjUiIGltc2k9Ijg0ODI0YWE5NTM2NjBkNjkxZjlkZTQxNWQ2NzJkMGY3NTg4MTgzODQiIGltZWk9ImUxZDAwOWIwMjBkNGQzMTBmNGUzODEwN2I1MWQ4YjAyYWEyYjI2MDUiIGFjY291bnRJZD0iYzM2YzYyODVhZmQzNWYwYTkwZDg2MWE2Y2Q0ZTVmMTQyNjc5NWNiZCIgcHJvZHVjdElkPSJJQVBfUmVzb3VyY2VfMSIgYXBwbGljYXRpb25JZD0iSUFQX1Byb2R1Y3RfMSIgdHJhbnNhY3Rpb25UaW1lPSIyMDEwLTExLTAyIDA5OjI0OjM0IFVUQyIgdHJhbnNhY3Rpb25JZD0iMDdjMzI4NGUtODhiMS00MzYxLTljZDYtNTUxMzkwMzk5NTA2IiB4bWxucz0iaHR0cDovL3BheW1lbnQub3ZpLmNvbS9pYXAiLz4=</Binary>
</PurchaseVerificationRequest>
购买 验证回复
如果请求成功(返回HTTP状态值200),Nokia商店返回PurchaseVerificationResponse。回复中PurchaseVerificationResultType 元素的属性表明购买是否成功。下表列出可能的属性值。
| Value | Description |
| OK | The payment succeeded. |
| Failed | The payment failed. |
| Refunded | The payment was refunded. |
| InvalidPurchaseTicket | The PurchaseTicket element in the request contained invalid data. For example, the signature was invalid. |
例子:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PurchaseVerificationResponse xmlns="http://payment.ovi.com/iap" result="OK" />
5.使用DRM保护内置资源
你可以使用 OMA DRM 2.0 来保护你程序中的可购买资源。
如果你从你的后台服务器发布你的支付物品,你需要自己保护这些物品,如果你随程序发布物品,你可以请Nokia运用OMA DRM 2.0来保护这些物品。OMA DRM 2.0是数字版权管理技术,由Open Mobile Alliance定义来管理数字媒体的发布。
DRM 如何工作
你提交包含支付物品的应用到Nokia Publish后,Nokia Publish解开安装包,使用OMA DRM 2.0加密预先定义在工程文件夹中的资源文件,根据需要签名文件,然后重新打包发布,你可以加密资源文件和核心资源文件,例如,你有一个游戏,前两关在游戏在初始销售,后面的关卡需要额外支付。每一关的资源文件(数据,图片文件)可单独使用OMA DRM 2.0加密。 如果是服务,那么可以这样设计,使用一些访问服务的门控口令,口令保存在文本文件中,用 OMA DRM 2.0保护。
OMA DRM 2.0 保护如何工作:
1. 消费者 -- 从Nokia商店下载你的应用
2. Nokia商店 -- 如果应用不免费,则进行支付流程,然后发送应用
3. 消费者手机,DRM代理 -- 收到支付确认,获取加密核心文件的访问权限
4. 证书密钥管理器 -- 认证设备,然后发送加密核心文件的访问权限
5. 消费者手机,DRM代理 -- 注册访问权限
6. 消费者 -- 使用应用,解密核心文件前调用DRM代理检查访问权限
7. 消费者 -- 做一次程序内支付
8. Nokia 商店 -- 进行支付流程
9. 消费者手机,DRM代理 -- 收到支付确认,获得加密的支付物品文件的权限
10. 证书密钥管理器 -- 认证设备,然后发送加密支付物品文件的访问权限
11. 消费者手机,DRM代理 -- 注册访问权限
12. 消费者 -- 使用程序内支付,解密程序内支付文件前调用DRM代理检查访问权限
设置开发环境
设置外部资源文件采用DRM保护的细节,参见设置开发环境章节。
访问DRM保护的文件
要访问受DRM保护的资源文件,你的应用需要使用内容访问框架(CAF)API,使用设备的DRM代理来检查访问权限。更多CAF API信息,参看:
Qt应用要访问加密文件,使用DRMFile类包裹CAF API, 在例子程序 \Examples\TryAndBuyExample\中有一个Qt 转换类DRMFile,打开、读或关闭的文件如下代码所示:
#include "DRMFile.h"
// create a file using the DRMFile class
DRMFile file;
// try to access the encrypted file
int error = file.open(protectedFileName[0]);
// if the device can access the encrypted file
if(!error)
{
// process the file data
:
file.close();
}
// if the device cannot access the encrypted file
else
{
file.close();
// check the cause of the error
if(file.isDRMError(error))
{
:
}
}
完整代码请参考\Examples\TryAndBuyExample Note: 接入未受保护的文件资源既可以通过CAF API也可以试Symbian C++或者Qt的文件接入方法。
6 .设置应用的工程文件
编译构建应用前,需要在Qt的pro文件中加入依赖的lib和需要的能力,例如\Examples\TryAndBuyExample中:
CONFIG += mobility \
iapppurchasing \
symbian:{
LIBS += -lcaf \
-lcafutils \
-lapmime
# capabilities required for IAP API
TARGET.CAPABILITY += NetworkServices
}
同时需要在pro中加入需要打包部署到设备中的文件:
# IAP configuration
addConfigFiles.sources = ./unlocked_data/IAP_VARIANTID.txt #./data/TEST_MODE.txt
addConfigFiles.path = .
DEPLOYMENT += addConfigFiles
addDrm.sources = ./data
addDrm.path = ./drm
DEPLOYMENT += addDrm
addUnlocked.sources = ./unlocked_data/unlocked_resources
addUnlocked.path = .
DEPLOYMENT += addUnlocked
正确的设置应用程序的工程文件才能确保程序正确编译并正常运行,如果你是Qt的初学者,关于Qt的工程文件请参考帮助文档:[1]
Summary
使用程序内支付进行编程需要去实现一些特定的步骤,参照本文以及API包中提供的例子可以帮助你实现应用的程序内支付功能。


(no comments yet)