我的快游戏基于cocos creator 2.0.7。
首先华为的支付流程是这样的:https://developer.huawei.com/consumer/cn/service/hms/catalog/fastgameRuntime.html?page=fastapp_fastgameRuntime_devguide_open_ability#%E6%8E%A5%E5%85%A5%E6%94%AF%E4%BB%98%E6%9C%8D%E5%8A%A1
首先我的游戏是单机游戏,没有服务器的支持。所以只能把签名算法写在本地,首先写好自己的订单信息,然后必须参与签名的字段先构造源串。在orderInfo参数中,在你的订单信息中必填的字段中选择参与签名的信息,提取出来,使用它们构造源串。例如字段url参与订单信息非必填,但是参与签名。只要你的订单信息中没有出现url,签名就可以不包含字段url。
第一步提取出的参数先构造源串。使用一个ksort算法,根据键值的ASCII码增序排序。我是在网上百度搜到的方法,拷过来单独写一个类,模块化。用到的时候调用就可以。
var SignScript ={
ksort(inputArr, sort_flags) {
// discuss at: http://phpjs.org/functions/ksort/
// original by: GeekFG (http://geekfg.blogspot.com)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Brett Zamir (http://brett-zamir.me)
// note: The examples are correct, this is a new way
// note: This function deviates from PHP in returning a copy of the array instead
// note: of acting by reference and returning true; this was necessary because
// note: IE does not allow deleting and re-adding of properties without caching
// note: of property position; you can set the ini of "phpjs.strictForIn" to true to
// note: get the PHP behavior, but use this only if you are in an environment
// note: such as Firefox extensions where for-in iteration order is fixed and true
// note: property deletion is supported. Note that we intend to implement the PHP
// note: behavior by default if IE ever does allow it; only gives shallow copy since
// note: is by reference in PHP anyways
// note: Since JS objects' keys are always strings, and (the
// note: default) SORT_REGULAR flag distinguishes by key type,
// note: if the content is a numeric string, we treat the
// note: "original type" as numeric.
// depends on: i18n_loc_get_default
// depends on: strnatcmp
// example 1: data = {d: 'lemon', a: 'orange', b: 'banana', c: 'apple'};
// example 1: data = ksort(data);
// example 1: $result = data
// returns 1: {a: 'orange', b: 'banana', c: 'apple', d: 'lemon'}
// example 2: ini_set('phpjs.strictForIn', true);
// example 2: data = {2: 'van', 3: 'Zonneveld', 1: 'Kevin'};
// example 2: ksort(data);
// example 2: $result = data
// returns 2: {1: 'Kevin', 2: 'van', 3: 'Zonneveld'}
var tmp_arr = {},
keys = [],
sorter, i, k, that = this,
strictForIn = false,
populateArr = {};
switch (sort_flags) {
case 'SORT_STRING':
// compare items as strings
sorter = function (a, b) {
return that.strnatcmp(a, b);
};
break;
case 'SORT_LOCALE_STRING':
// compare items as strings, original by the current locale (set with i18n_loc_set_default() as of PHP6)
var loc = this.i18n_loc_get_default();
sorter = this.php_js.i18nLocales[loc].sorting;
break;
case 'SORT_NUMERIC':
// compare items numerically
sorter = function (a, b) {
return ((a + 0) - (b + 0));
};
break;
// case 'SORT_REGULAR': // compare items normally (don't change types)
default:
sorter = function (a, b) {
var aFloat = parseFloat(a),
bFloat = parseFloat(b),
aNumeric = aFloat + '' === a,
bNumeric = bFloat + '' === b;
if (aNumeric && bNumeric) {
return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0;
} else if (aNumeric && !bNumeric) {
return 1;
} else if (!aNumeric && bNumeric) {
return -1;
}
return a > b ? 1 : a < b ? -1 : 0;
};
break;
}
// Make a list of key names
for (k in inputArr) {
if (inputArr.hasOwnProperty(k)) {
keys.push(k);
}
}
keys.sort(sorter);
// BEGIN REDUNDANT
this.php_js = this.php_js || {};
this.php_js.ini = this.php_js.ini || {};
// END REDUNDANT
strictForIn = this.php_js.ini['phpjs.strictForIn'] && this.php_js.ini['phpjs.strictForIn'].local_value && this.php_js
.ini['phpjs.strictForIn'].local_value !== 'off';
populateArr = strictForIn ? inputArr : populateArr;
// Rebuild array with sorted key names
for (i = 0; i < keys.length; i++) {
k = keys[i];
tmp_arr[k] = inputArr[k];
if (strictForIn) {
delete inputArr[k];
}
}
for (i in tmp_arr) {
if (tmp_arr.hasOwnProperty(i)) {
populateArr[i] = tmp_arr[i];
}
}
return strictForIn || populateArr;
}
};
module.exports = SignScript;
构造源串
//生成源串 str
var SignScript = require("SignScript");
let boo = SignScript.ksort(signInfo); //使用ksort算法根据键值排序
let str = "";
//遍历所有键值 拼接
for(let i in boo){
str += i + "=" + boo[i] + "&";
}
str = str.substring(0,str.length-1);
得到的str就是拼接好的源串,格式为a=XXX&b=XX&c=XXX,接下来进行第二步,将得到的源串使用sha256withRSA算法签名。
根据公钥加密私钥解密,私钥签名公钥验签的原则,使用私钥给源串加签名。这里我们用到jsrsasign这个东西,提供给了我们签名算法。如果需要加密则使用jsencrypt.js实现。我们这里只需要签名,所以只用jsrsasign,这里可以下载到jsrsasign,提供了例子:https://kjur.github.io/jsrsasign/,签名用到的就是KJUR.crypto.Signature类,API参考:https://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Signature.html。本地代码导入jsrsasign类,将jsrsasign.js直接放在自己的目录下,在用到签名的地方导入。我直接就用的var jsrsasign = require("jsrsasign");
实例化signature :
let signature=new jsrsasign.KJUR.crypto.Signature({alg:"SHA256withRSA","prov": "cryptojs/jsrsa","prvkeypem": keyPem});
kevPem是你的私钥,华为快应用申请的支付服务后可以看到,kevPem前后要分别加上-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY----,不然会报错。
接下来传入需要签名的字符串,也就是上边我们得到的源串:
signature.updateString(str);
签名:let keys = signature.sign();
在得到我们的签名后,还要对其进行base64编码,直接调用jsrsasign 的hex2b64方法,返回的字符串就是我们最终得到的签名值。let sign = jsrsasign.hex2b64(keys);
sign值就是我们得到的签名值。到这里签名就结束了。
将sign值赋给orderInfo的sign字段,调用华为支付方法hbs.hwPay()方法。在回调中做相应的操作就可以了。
回调传回的参数中,会返回你传过去的requestId值,requestId通过ret.result.requestId值获得。它这个回调包了两层,回调传回的ret是个obj,遍历ret只有一个,key为result,value还是一个obj,要再遍历ret.result才是到数据层。可以在回调中遍历查看键值。
得到了requestId值,再做出相应的操作,比如下发道具什么的,支付就算是完成了。