Day03 - 随意玩之 API 讯息内文以及 Sign

今天预计讲解下面两个 (也就是下图的步骤 5)

  1. API 的 JSON 内容
  2. 把内容加上 Nonce 以及密钥 然後做 SHA256 (图片的 Sign)

https://ithelp.ithome.com.tw/upload/images/20210912/20141787zAz1Ay0TC1.png


API 的 JSON 内容

  • API 除了前面介绍的 Nonce 以外,还有就是 Order 相关的 API
  • Order 相关有分成以下三种
    1. OrderCreate (建立订单)
    2. OrderQuery (查询订单)
    3. OrderPayQuery (订单付款查询)

在 Spec 第七章有详细列出哪一种 Request 的讯息内文需要哪些东西,像是 Spec 在 OrderCreate 给的例子

{
    "ShopNo": "BA0026_001", 
    "OrderNo": "A201804270001", 
    "Amount": 50000, 
    "CurrencyID": "TWD", 
    "PayType": "A", 
    "ATMParam": { "ExpireDate": "20180502" }, 
    "CardParam": { }, 
    "ConvStoreParam": { }, 
    "PrdtName": "虚拟帐号订单", 
    "ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient/Store/Return", 
    "BackendURL": "http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess" 
}

但是实际上在 OrderCreate 必须要有的参数只有下面几项,其他会根据你的参数不同,有不同需求~

Name Type Required Note
ShopNo bytes(20) Y 商店代号
OrderNo bytes(50) Y 订单编号
Amount Int(9) Y 订单总金额
CurrencyID bytes(3) Y 币别
PrdtName bytes(60) Y 产品名称
ReturnURL bytes(255) Y 付款完成页面
PayType bytes(1) Y 收款方式

计算 Sign

https://ithelp.ithome.com.tw/upload/images/20210913/20141787ToXpKdavI1.png

如果要算出讯息的 Sign,需要注意以下几个事项。

  1. 移除所有是的值
    • 像是上面 OrderCreate 例子的 CardParamConvStoreParam
  2. 如果有多节点,不会加进去 Sign 计算
    • 像是上面 OrderCreate 例子的 ATMParam
  3. 将参数名称不分大小写由小至大排列,组成如 param1=value1&param2=value2 的字串
    • 以上面 OrderCreate 的例子会得到下面这样
    Amount=50000&BackendURL=http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess&CurrencyID=TWD&OrderNo=A201804270001&PayType=A&PrdtName=虚拟帐号订单&ReturnURL=http://10.11.22.113:8803/QPay.ApiClient/Store/Return&ShopNo=BA0026_001
    
  4. 将内文杂凑、Nonce以及 Hash ID 串在一起使用 SHA256 计算出 Hash (Sign)

下面的程序码是要让我们的讯息内文转换成要加密前的形式 (图中的内文杂凑)

def parseQueryData(msg_param):
    if type(msg_param) != dict:
        return
    
    ## 这里是将传进来的 dict 根据 key 做排序
    order_message = dict(sorted(message_content.items(), key = lambda x: x[0]))
    message = ''

    ## 这里是看是否为空值或是多节点,然後转换为要求的字串
    for k, v in order_message.items():
        if type(v) == dict or v == '':
            continue
        message += f"{k}={v}&"
    
    return message[:-1]

接着只要把前一天说的 Nonce 以及 Hash ID,将他们接在一起算 SHA256 即可获得 Sign

## msg_content 是 parseQueryData() 传回来 value
def calcSign(msg_content, nonce, hash_id):
	sign_msg = msg_content+nonce+hash_id

	s = hashlib.sha256()
	s.update(sign_msg.encode('utf-8'))
	h = s.hexdigest()
	return h.upper()

所以到目前为止,会有下面的程序码 (部分程序码有做更改,但基本上功能一样~)

import requests
import json
import hashlib

shop_no = '<shop_no>'
sinopac_hash = {
	'a1': '',
	'a2': '',
	'b1': '',
	'b2': ''
}

nonce_url = 'https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Nonce'

def getNonce():
	nonce_data = {
		'ShopNo': shop_no
	}

	r = requests.post(nonce_url, json=nonce_data)

	return json.loads(r.content)['Nonce']

def calcHashID():
	a1 = sinopac_hash['a1']
	a2 = sinopac_hash['a2']
	b1 = sinopac_hash['b1']
	b2 = sinopac_hash['b2']

	xor1 = hex(int(a1, base=16)^int(a2, base=16))
	xor2 = hex(int(b1, base=16)^int(b2, base=16))

	hash_id = xor1[2:]+xor2[2:]
	return hash_id.upper()

def calcIV(nonce):
	s = hashlib.sha256()

	s.update(nonce.encode('utf-8'))
	h = s.hexdigest()
	return h[-16:].upper()

def calcSign(msg_content, nonce, hash_id):
	sign_msg = msg_content+nonce+hash_id

	s = hashlib.sha256()
	s.update(sign_msg.encode('utf-8'))
	h = s.hexdigest()
	return h.upper()

order_create = {
    "ShopNo": shop_no, 
    "OrderNo": "A202109120008", 
    "Amount": 50000, 
    "CurrencyID": "TWD", 
    "PayType": "C", 
    "CardParam": { 
    	"AutoBilling": "Y"
	}, 
    "ConvStoreParam": { }, 
    "PrdtName": "信用卡订单", 
    "ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient-Sandbox/Store/Return", 
    "BackendURL": "https://10.11.22.113:8803/funBIZ.ApiClient/AutoPush/PushSuccess" 
}

nonce = getNonce()
hash_id = calcHashID()
iv = calcIV(nonce)
content = parseQueryData(order_create)
signature = calcSign(content, nonce, hash_id)

预告明天会利用 Python 实作 AES-CBC,敬请期待!


<<:  python证照必考题 得票数计算 选举题目 投票问题TQC+ 程序语言 Python 3 _ 409

>>:  [Day 13] .Net Task 底层(6)

[Vue2] 从初学到放弃 Day6-Template Syntax

Template Syntax Vue.js 最主要是基於HTML,在使用JQuery或者Js的时候...

【网页设计 入门 】如何使用 Bootstrap 与 Github Pages 制作 个人网站 ?

简单架设 x 不失质感 目录 源起 : 开发者网站 开发工具 : Adobe Brackets 基础...

Day10 有图有真相

Chart function 身为一个键盘柯南,最重要的技能之一就是储存和下载分析後的结果。另外c...

Java学习之路03---标识符、关键字、变数概念

架构图 前言 Java程序是一系列对象的集合,而对象之间透过彼此之间调用方法来达到开发目的,因此在认...

[Day22]Laravel 路由

Laravel 路由 基本路由 首先看到rotues资料夹里的web.php,会看到这些程序码 Ro...