iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0

在昨天我們度過最大難關加密了,之後應該會輕鬆許多吧?


API 呼叫流程如下
https://ithelp.ithome.com.tw/upload/images/20210914/20141787QhUztimnhR.png

步驟 1, 2, 3 目前都是我們已經會的!

第 4 步驟的 API Request 需求如下
https://ithelp.ithome.com.tw/upload/images/20210914/20141787er8UL7FoRU.png

快速說明這六個參數

  1. Version: 固定值 1.0.0
  2. ShopNo: 固定值 根據永豐給你的編號輸入
  3. APIService: 有三種變化,OrderCreate(建立訂單)、OrderQuery(訂單詳情)、OrderPayQuery(訂單付款資訊)
  4. Nonce: 利用 ShopNo 對 API 請求一個隨機值
  5. Sign: 訂單內容的簽章,避免傳送過程被竄改
  6. Message: 加密過後的訂單內容

我們今天會聚集在 API Service 的 OrderCreate 上面


OrderCreate

  • 主要功能:建立訂單
  • 訂單付款方式有兩種
    1. ATM
    2. 信用卡

我以信用卡為例,訂單內容如下

{
    "ShopNo": shop_no,  ## 商家編號
    "OrderNo": "A202109150001",  ## 訂單編號 (商家自行產生)
    "Amount": 50000,   ## 金額,最後兩位為小數 (輸入50000,實際上付款為 500.00)
    "CurrencyID": "TWD",  ## 幣別,目前只有台幣這個選項
    "PayType": "A",  ## A 代表 ATM 轉帳
    "CardParam": {  ## 信用卡的參數
        "AutoBilling": "Y",  ## 自動請款
        "ExpBillingDays": "",   ## 自動請款天數
        "ExpMinutes": "",  ## 付款連結有效時間
        "PayTypeSub": "" ## 付款子項目 (一次付清)
    },
    "PrdtName": "OrderCreate_CreditCard_Example",  ## 產品名稱
    "ReturnURL": "<URL>",  ## 付款成功時 redirect 的網頁
    "BackendURL": "<URL>"  ## 訂單完成付款時,會通知到這裡
}

只要把上面的資訊加密後輸入到下面 Message 欄位,就可以發出 Query 了!

{
    'Version': '1.0.0',
    'ShopNo': shop_no,
    'APIService': 'OrderCreate',
    'Sign': sign,
    'Nonce': nonce,
    'Message': msg
}

API 回傳的內容也跟 上面一樣,差別為內容是永豐要跟你說的訊息


程式碼非常簡單,東西填一填就可以發出 Request 了

def apiService(service, sign, nonce, msg):
	api_data = {
		'Version': '1.0.0',
		'ShopNo': shop_no,
		'APIService': service,
		'Sign': sign,
		'Nonce': nonce,
		'Message': msg
	}

	r = requests.post(order_url, json = api_data)
	resp = json.loads(r.content)

	return (resp['Sign'], resp['Nonce'], resp['Message'])
    
resp_sign, resp_nonce, resp_message = apiService('OrderCreate', signature, nonce, enc_message)

那我們今天就說到這裡,明天會說明其他兩個 API Service!


一樣在最後附上程式碼~

from __future__ import unicode_literals
import requests
import hashlib
import codecs
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

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

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

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()

def parseQueryData(msg_param):
    if type(msg_param) != dict:
        return
    
    order_message = dict(sorted(msg_param.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]

def CBCEncrypt(key, iv, data):
    cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
    return (cipher.encrypt(pad(data.encode('utf-8'), AES.block_size)))

def CBCDecrypt(key, iv, data):
    data = codecs.decode(data, "hex")
    cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
    return unpad(cipher.decrypt(data), AES.block_size)

def apiService(service, sign, nonce, msg):
	api_data = {
		'Version': '1.0.0',
		'ShopNo': shop_no,
		'APIService': service,
		'Sign': sign,
		'Nonce': nonce,
		'Message': msg
	}

	r = requests.post(order_url, json = api_data)
	resp = json.loads(r.content)

	return (resp['Sign'], resp['Nonce'], resp['Message'])

def orderCreate(order_create):
	nonce = getNonce()
	hash_id = calcHashID()
	iv = calcIV(nonce)
	content = parseQueryData(order_create)
	signature = calcSign(content, nonce, hash_id)
	msg = json.dumps(order_create, ensure_ascii=False).replace(' ','')
	enc_message = CBCEncrypt(hash_id, iv, msg).hex().upper()
	resp_sign, resp_nonce, resp_message = apiService('OrderCreate', signature, nonce, enc_message)
	resp_iv = calcIV(resp_nonce)
	temp = CBCDecrypt(hash_id, resp_iv, resp_message)
	print(json.loads(temp))


order_create = {
    "ShopNo": shop_no,
    "OrderNo": "A202109150006",
    "Amount": 51000,
    "CurrencyID": "TWD",
    "PayType": "C",
    "CardParam": {
        "AutoBilling": "Y"
    },
    "PrdtName": "OrderCreate_CreditCard_Example",
    "ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient/Store/Return",
    "BackendURL": "http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess"
}

orderCreate(order_create)

上一篇
Day04 - 隨意玩之 AES-CBC 加/解密
下一篇
Day06 - 隨意玩之 OrderQuery/OrderPayQuery
系列文
永豐 API 隨意玩6
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言