iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
永豐金融APIs

openAPI 對接實務系列 第 4

[day4] 安全簽章 - 產生訂單 & 簽章(Sign)

準備訊息文本

依照參數說明,建立訂單的資料結構(DAY3-參考),詳細參數規格可以在永豐API技術規格文件內找到,此處先以訂單建立(OrderCreate)API進行測試,訂單細節如下:

故使用的參數為:

以Python實作:

neworder = APIModel.ReqOrderCreate(ShopNo="NA0249_001", OrderNo="2021091500001", Amount=40400, \
            PrdtName="IPhone 13 Pro Max 256g", ReturnURL="https://0.0.0.0/store/Return", \
                BackendURL="https://0.0.0.0/bakcend", PayType="C", AutoBilling="Y", PayTypeSub="ONE")

這邊吐槽一下信用卡API測試環境只能測試一次付清,否則會跳E0602 - 收款方式未啟用錯誤,另外還有預計自動請款天數,在設定自動請款時按照說明書應該會忽略,結果會跳日期範圍不正確錯誤,請款日期範圍也不是文件上的1-21天,而是1-7天

計算簽章Sign

在取得Nonce、HashID、訊息文本等三個參數後,才能夠進行安全簽章(Sign)計算,計算方式概述:

  1. 移除訊息文本中空的參數與節點參數(如留空的參數與ATMParam與CardParam)
  2. 將參數以不分大小寫方式遞增排序
  3. 以[參數名稱1]=[數值1]&[參數名稱2]=[數值2].....的方式串接參數組成字串
  4. 加上Nonce與HashID
  5. 進行字串SHA256計算

以上節iPhone的信用卡訂單為範例,產生的訊息文本為:

{
    "ShopNo": "NA0249_001",
    "OrderNo": "2021091500002",
    "Amount": 40400,
    "CurrencyID": "TWD",
    "PrdtName": "IPhone 13 Pro Max 256g",
    "Memo": "",
    "Param1": "",
    "Param2": "",
    "Param3": "",
    "ReturnURL": "https://0.0.0.0/store/Return",
    "BackendURL": "https://0.0.0.0/bakcend",
    "PayType": "C",
    "ATMParam": {
        "ExpireDate": ""
    },
    "CardParam": {
        "AutoBilling": "Y",
        "ExpBillingDays": 7,
        "ExpMinutes": 10,
        "PayTypeSub": "ONE"
    }
}

進行步驟1.移除訊息文本中空的參數與節點參數(如留空的參數與ATMParam與CardParam):

{
    "ShopNo": "NA0249_001",
    "OrderNo": "2021091500002",
    "Amount": 40400,
    "CurrencyID": "TWD",
    "PrdtName": "IPhone 13 Pro Max 256g",
    "ReturnURL": "https://0.0.0.0/store/Return",
    "BackendURL": "https://0.0.0.0/bakcend",
    "PayType": "C",
}

進行步驟2.將參數以不分大小寫方式遞增排序、3. 以[參數名稱1]=[數值1]&[參數名稱2]=[數值2].....的方式串接參數組成字串,記得去掉末尾的'&'

order = "Amount=40400&BackendURL=https://0.0.0.0/bakcend&CurrencyID=TWD&OrderNo=2021091500002&PayType=C&PrdtName=IPhone 13 Pro Max 256g&ReturnURL=https://0.0.0.0/store/Return&ShopNo=NA0249_001"

進行步驟4. 加上Nonce與HashID

nonce = "NjM3NjczMjQwNzM1NTAuOTo5ZWE1MmFhYzk0NDgwMzljY2RiNjhjOGU0MGU3ODc0NzFiMTIwNmNkMTViZWU4MzUwZDU3Zjg1M2VhNjIwODRj"
HashID = "17D8E6558DC60E702A6B57E1B9B7060D"

str_before_sign = "Amount=40400&BackendURL=https://0.0.0.0/bakcend&CurrencyID=TWD&OrderNo=2021091500002&PayType=C&PrdtName=IPhone 13 Pro Max 256g&ReturnURL=https://0.0.0.0/store/Return&ShopNo=NA0249_001NjM3NjczMjQwNzM1NTAuOTo5ZWE1MmFhYzk0NDgwMzljY2RiNjhjOGU0MGU3ODc0NzFiMTIwNmNkMTViZWU4MzUwZDU3Zjg1M2VhNjIwODRj17D8E6558DC60E702A6B57E1B9B7060D"

進行步驟5. 進行字串SHA256計算

sign = "3c883d53f7732ec7c3f8c9d0232691545855b60507b199094a4cde71c911f522"

以下為Python完整實作:

'''
Sample Env.ini
[App]
Version = 1.0.0
ShopNo = BA0026_001
A1 = 86D50DEF3EB7400E
A2 = 01FD27C09E5549E5
B1 = 9E004965F4244953
B2 = 7FB3385F414E4F91

[Server]
Api_URL = https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order
Nonce_URL = https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Nonce
'''
def main():
  env = ConfigParser()
  env.read('env.ini')
  Hash = SimpleNamespace(A1 = env['App']['A1'], A2 = env['App']['A2'], B1 = env['App']['B1'], B2 = env['App']['B2'])
  cfg = SimpleNamespace(Version = env['App']['Version'], ShopNo = env['App']['ShopNo'], HashID = HashID(Hash))
  neworder = APIModel.ReqOrderCreate(ShopNo="NA0249_001", OrderNo="2021091500002", Amount=40400, PrdtName="IPhone 13 Pro Max 256g", ReturnURL="https://0.0.0.0/store/Return",                       BackendURL="https://0.0.0.0/bakcend", PayType="C", AutoBilling="Y", PayTypeSub="ONE")
  OrderCreate(neworder, cfg)

def OrderCreate(origin, cfg):
  nonce = GetNonce(cfg)
  sign = GenSign(origin, nonce, cfg.HashID)

def GetNonce(cfg):
  payload = json.dumps({"ShopNo":cfg.ShopNo}, indent=4)
  resp = APIPm.sendreq(url=APIPm.nonceservice, data=payload)
  return json.loads(resp.text)['Nonce']

def GenSign(origin, Nonce:str, HashID:str):
  SignStr = ""
  #不分大小寫排序變數
  for parm in sorted(origin.__dict__, key=lambda v: v.upper()):
      val = origin.__dict__[parm]
      # print(f"Parm:{parm}, Val:{val}, Types:{type(val)}")
      if(not type(val) == SimpleNamespace and origin.__dict__[parm] != None):
          if(type(val) == str and not val):continue
          SignStr = SignStr + f"{parm}={origin.__dict__[parm]}&"
  SignStr = SignStr.removesuffix('&') + Nonce + HashID
  sign = hashlib.sha256(SignStr.encode('utf-8')).hexdigest().upper()
  return sign

如何產生簽章大家都會了嗎;阿好像忘了寫怎麼在Python內丟Request,明天補寫,斯米麻賽


上一篇
[Day3] 安全簽章 - XOR加密(HashID)
下一篇
[day5] Python發送Request接收Response與永豐API串接參數
系列文
openAPI 對接實務30

尚未有邦友留言

立即登入留言