昨天用了Postman先小试身手,从测试环境取得了必要的发语词Nonce的值(还记得这个60秒就会过期了吧),现在要来了解一下正式使用API沟通的时候会需要的一些流程。
接下来其实不会以永丰开发规格书的顺序来实作,会以我认为较符合情境顺序的方式来说明。首先我们在呼叫API前,要先拆解一下所需要的必要项目。
在规格书中会看到这张图:
主要说明的是每一次的API呼叫(不管你是要用哪一种服务,举凡是要建立订单用信用卡支付、还是要查询付款状态等),都会需要的6个栏位以及其来源提供方和组成的方式。
所以先搞定这个麻烦的起手式组成,接下来才能好好的来看应用面。
除了开发规格书有整理的表面,我也针对这6个必要栏位作说明:(我认为要先解释Message後,才解释Sign比较清楚)
参数 | 说明 |
---|---|
Version | API版本,目前能传的为固定值:"1.0.0" |
ShopNo | 我们从永丰取得的商店代号固定值:"NA0249_001" |
APIService | 要呼叫的API服务名称,目前有四支可呼叫:"OrderCreate""OrderQuery""OrderPayQuery" |
Nonce | 从Nonce API服务中取得,每次呼叫都要记得拿新的放入。 |
Message | 经AES加密演算法後的交易讯息内容密文 注:运算细节文章中再来说明 |
Sign | 安全签章,主要用途是API接收端可用来验证上面的Message在传递过程中没有被非法篡改过。注:运算细节文章中再来说明 |
很显然的,除了那些固定或选择性范围的值之外,我们必须花一点篇幅来解释一下的是Message
和Sign
这两个栏位,这两个看似复杂的栏位也是银行依金管会所颁布的「电子银行业务安全控管作业基准」的安全设计原则中有关「讯息隐密性」以及「讯息完整性」、「无法否认传送讯息」等而对应的设计。
Message栏位的组成,依开发规格书说明如下:
可还原的原始交易
讯息内文
+HashID (32 位元值 )
+IV (16 位元值)
三要素进行 AES CBC 加密产生 。
因此从这句话来拆解,又可分为其中三个要素:
先准备好上面三个要素後,再把它们用AES加密演算法(CBC)
掺在一起做成撒尿牛丸(?),上面所需要的Message
栏位就是这样来的。
不过在看了开发规格书中的加/解密范例网址,「Hash ID」这个名称似乎有些前後命名不一致,在该网址中是称作「AES Key」,我比较偏好的是这个命名。
总之,在AES-CBC加密过程会需要加密金钥(AES Key)
以及初始向量(IV)
,将原本明文的内文加密成密文。
接下来就是一一把这过程所需的要素准备好。
依据要呼叫的不同API服务,会有定义所需要传递给永丰API银行端的一套讯息内文栏位。
我们就先以建立订单交易 (信用卡订单)
作为范例,目前我们的重点是在於理解Message与Sign的产生过程,因此讯息内文可先以开发规格书中的范例作内容即可(先不用理解每个JSON栏位的意义),後续我们套用到自己的情境时再来讲究内容的设计与选用。
{
"ShopNo": "NA0249_001",
"OrderNo": "C201804300001",
"Amount": 50000,
"CurrencyID": "TWD",
"PayType": "C",
"CardParam": { "AutoBilling": "Y" },
"PrdtName": "信用卡订单",
"ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient/Store/Return",
"BackendURL": "http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess"
}
以上就是我们所需要的讯息内文
的JSON内容,接着往下看。
也就是上面谈到的「AES Key」(加密金钥),这个Hash ID栏位的组成,依开发规格书说明如下:
Hash ID 是透过位元运算(XOR)将四组 Hash 计算产出的, 将 A1/A2 以 XOR 运 算所得的字串, 再 与 B1/B2 以 XOR 运算 出来的字串,二个相加後将英文转换为大写, 为长度为 32 的字串 (例 : 17D8E6558DC60E702A6B57E1B9B7060D)
快把Email中收到的四个Hash码拿出来,前两个一组做XOR运算,後两个一组XOR运算,再把两组运算後的字串拼接在一起後,转成全大写,这就是我们需要的Hash ID。
我们直接使用Python来跑一次:
A1, A2, B1, B2 = "86D50DEF3EB7400E", "01FD27C09E5549E5", "9E004965F4244953", "7FB3385F414E4F91"
def bytes_xor_to_hexstring(ba1, ba2):
return bytes([a ^ b for a, b in zip(ba1, ba2)]).hex()
ba_xor_A = bytes_xor_to_hexstring(bytes.fromhex(A1), bytes.fromhex(A2))
ba_xor_B = bytes_xor_to_hexstring(bytes.fromhex(B1), bytes.fromhex(B2))
hash_id = "{}{}".format(ba_xor_A, ba_xor_B).upper()
print(hash_id)
#output: 87282A2FA0E209EBE1B3713AB56A06C2
在上面的bytes_xor_to_hexstring()
function中,主要是可接受两个一组的Hash参数值(bytes型别),并将这两组bytes使用zip()将每个同index的元素组成两两一组,再进行XOR
的运算,最终将这样的运算结果以Python独特的List comprehension产生新的XOR结果的List。
由於XOR後会是一般的List,因此需要使用bytes()转型後,再使用hex()方法将其结果转换为16进位(HEX)字串。
在呼叫上述function之前,需要使用bytes.fromhex()将16进位样式的字串转换成bytes,才可传入bytes_xor_to_hexstring()
中。
最後将A1/A2产生的字串结果和B1/B2产生的字串结果作字串拼接起来再一起使用upper()转成大写,这就是我们的目标Hash ID的值。
Production版本的扩充建议:
若是真的要运用在Production环境,可以考虑到以下几点:
- 进行四个Hash值的特定长度以及16进位字多的检查
- 两两进行比对时确认长度相同 (第一点若有实作,其实已可满足)
- 这四个值即使看起来不会有所变动,但仍然应从外部属性档中读入,而非写死在程序码中
另外补充说明一下,既然是AES的对称加密,因此我们辛苦产生的这把金钥:Hash ID,既然原先的4组Hash代码是由永丰提供给我们,就表示对於我们这个商家而言,他们也必然会拥有和我们一样的Hash ID。以便我们在未来作加密後的API沟通的内容,永丰API在收到密文的内容是有办法使用解密法解开,还原回原文。
好罗,把AES-CBC加密需要的金钥(Hash ID)也准备好了,接下来就是IV值的准备!明日再写。
>>: D3JsDay04一同来见识 D3起手式—用D3写Helloworld
switch 主要功能是依据不同的条件去执行其动作 他基本型态会长这样 switch (expres...
Redis持久化 Redis持久化模式->AOF AOF (Append Only File)...
昨天介绍了一些名词,今天继续提Node.js Node.js一点入门 今天直接贴上程序码,再去做解释...
使用首页、输入画面、输出画面等三个基础画面,来熟悉画面之间跳转及资料移动的原理。 本练习不涉及业务...
继续介绍昨天主流程里的副程序吧。 今日要点: 》Google Docs 转换 API Bluepr...