Day19 - [丰收款] 防止掉单小帮手,以时间条件查询交易订单

细数一下之前实作的API功能,有建立订单以及选择支付方式(ATM虚拟帐号、信用卡付款),拿到永丰API透过ReturnURLBackendURL回传给我们的PayToken去问回付款状态,现在即将进入最後一个永丰API的功能:订单交易查询

若是在上面透过ReturnURLBackendURL回传PayToken失效 (任何可能的原因),永丰API还提供了一个主动由电商带时间区间条件来查询订单状态的功能。

开发规格书中提到的使用时机:

收款纪录都可透过此API查询,若有交易迟未收到丰收款的主动通知,您可透过本API介面,利用条件筛选,主动发起查询,减少掉单问题。

而注意事项如下:

A. 订单查询时至少要选择其一条件 (1)订单编号 (2)收款方式 (3)交易起迄 (4)付款状态
B. 每次查询笔数上限为 300 笔,建议条件范围不要设定过大。

参数名称 说明
ShopNo 商店号码
OrderNo 商户订单编号
PayType 收款方式
OrderDateTimeS 交易日期(起),例如 2017/5/3 00:00 则带 201705030000
OrderDateTimeE 交易日期(迄),例如 2017/5/3 23:59 则带 201705032359
PayDateTimeS 付款日期(起),例如 2017/5/3 00:00 则带 201705030000
PayDateTimeE 付款日期(迄),例如 2017/5/3 23:59 则带 201705032359
PayFlag 依付款状态为条件查询ATM - Y:已转帐/N:未转帐/O:逾期;信用卡 - Y:已请款/N:未请款/O:逾期

流程说明

我们可以提供多种条件组合来查询订单,在查询结果是会出现符合结果的多笔订单内容,因此和先前的response不同之处在於我们需要从OrderList多性中拿回List的结果。

合理的用法如下:

  1. 带入OrderNo与PayFlag,直接查询该订单的状态
  2. 带入交易起迄区间,这个方式比较倾向用来作电商後台定期排程,来防止掉单最後一道防线
    但由於会拿回太多的订单List,因此这个方式非有必要应避免这样处理。

由於我实际呼叫後,发现铁人赛的参数者都是共用相同的ShopNo,因此用时间区间会把一脱拉库与自己不相关的订单也都取回。所以我主要会以单笔OrderNo进行查询。

查询订单资料类别OrderQueryMessage

class OrderQueryMessage(ApiMessage):
    def __init__(self):
        super().__init__("OrderQuery")
        self.query_cond = None

    def set_query_conditions(self, orderno="", pay_type="",
                             order_date_time_s="", order_date_time_e="",
                             pay_date_time_s="", pay_date_time_e="", pay_flag=""):
        self.query_cond = {
            "ShopNo": self.shop_no,
            "OrderNo": orderno,
            "PayType": pay_type,
            "OrderDateTimeS": order_date_time_s,
            "OrderDateTimeE": order_date_time_e,
            "PayDateTimeS": pay_date_time_s,
            "PayDateTimeE": pay_date_time_e,
            "PayFlag": pay_flag
        }

        print("- Query_data: {}".format(self.query_cond))
        super()._set_plain_msg_to_proc_msg_sign(self.query_cond)

    def query_orders(self):
        print("Send API...")
        return super()._send_api()

如同先前作法一样,我们会将这个订单查询拆成两个方法叫用,一个是先设定所需要的查询条件set_query_conditions(),设定完後再呼叫实际叫用API的query_orders()方法。

这里我们将条件参数都设定了预设值,主要是希望到时使用时可依带明确参数方式传值进来,增加使用的弹性。

查订单回传值类别ResponseOrderQuery

class ResponseOrderQuery(ResponseMessage):
    def __init__(self, resp_json, hash_id):
        super().__init__(resp_json, hash_id)

在这边的回传值不像上次会直接更新Payment资料表,因此暂时留空,我们目标只希望把结果取回就好。这需要商业逻辑先行定义好,是否需要启动连带的更新动作,则可将取回的资料在後续进行订单付款状态更新。

[Model]查询订单方法query_orders_by_conditions

def query_orders_by_conditions(orderno="", pay_type="",
                             order_date_time_s="", order_date_time_e="",
                             pay_date_time_s="", pay_date_time_e="", pay_flag=""):
    query_api = OrderQueryMessage()
    query_api.set_query_conditions(orderno=orderno, pay_type=pay_type,
                             order_date_time_s=order_date_time_s, order_date_time_e=order_date_time_e,
                             pay_date_time_s=pay_date_time_s, pay_date_time_e=pay_date_time_e, pay_flag=pay_flag)
    resp = ResponseOrderQuery(query_api.query_orders(), query_api.hash_id)
    print("-- Response: " + str(resp.dec_resp_json))
    return str(resp.dec_resp_json)

[View]查询单笔订单

def order_query(request):
    resp = query_orders_by_conditions(pay_type="A", orderno="A202110507397", pay_flag="Y")
    return HttpResponse(resp)

例如,我们以固定的OrderNo进行查询,而且仅希望ATM虚拟帐户,且付款成功的才回传回来。

结果如下:

{
   "ShopNo":"NA0249_001",
   "Date":"202110032253",
   "Status":"S",
   "Description":"S0000 – 处理成功",
   "OrderList":[
      {
         "OrderNo":"A202110507397",
         "TSNo":"NA024900000369",
         "TSDate":"202110012243",
         "PayDate":"202110012245",
         "Amount":79900,
         "PayType":"A",
         "PayStatus":"1A400",
         "ExpireDate":"202110112359",
         "RefundFlag":"N",
         "RefundStatus":"",
         "RefundDate":"",
         "PrdtName":"虚拟帐号订单",
         "ATMParam":{
            "AtmPayNo":"99922530175613",
            "WebAtmURL":"https://sandbox.sinopac.com/QPay.WebPaySite/Bridge/PayWebATM?TD=NA024900000369&TK=3a83b2d6-76d0-4e66-a32b-66d55ceb74f3",
            "OtpURL":"https://sandbox.sinopac.com/QPay.WebPaySite/Bridge/PayOTP?TD=NA024900000369&TK=3a83b2d6-76d0-4e66-a32b-66d55ceb74f3",
            "BankNo":"807",
            "AcctNo":"01700100003411"
         },
         "RefundAmount":0
      }
   ]
}

虽然仅回传一笔回来,不过一样会放在List型别的OrderList中,需要再加工将资料取出。

另外最後再提一下,若是在ReturnURL或BackendURL有成功被回传的情况下,基本上我们是可以作到自动化更新付款状态,因此当顾客进到会员後台查询他的订单内容时,是会即时更新。但难免在例如我们提供的服务器有异常时,例如BackendURL有十分钟重新丢一次,至多五次的retry机制。因此如果在永丰API尝试回呼我们的服务器但未果时,而且也超过上限,我们总不会希望订单的付款状态永远无法更新。

在这个状态下,才会使用今天介绍的这支主动询问订单程序。可以写成排程,类似是每几小时进行询问尚未更新的状态,但一样的,我们也是需要有自己的retry上限与逻辑。这部份都是在商务逻辑上需要设计与实作的,不可能永无止尽一直去照三餐询问一个可能永远不会有结果的消息。(如此悲伤)

目前已把所有功能都完成了,接下来看是要作更贴近电商模拟情境的实作,还是用剩下的天数来写Shioaji证券程序交易呢?就让我们看下去吧。


<<:  【Tableau Desktop入门】免费2小时基础操作体验

>>:  18 - Rest Client - HTTP 请求工具

JavaScript学习日记 : Day2 - 动态型别+弱型别

1. 静态型别 VS. 动态型别 1.1 静态型别的例子 以Java为例: int x 在宣告x变数...

Day-03 认识Android模拟器

本次要来介绍如何建立Android Studio上的模拟器,以及有哪些优缺点。 首先我认为最大的优点...

Day 4 初始化的 RSpec 资料夹剖析

该文章同步发布於:我的部落格 利用 Command line 来创建你的 RSpec 资料夹 一样...

[Day 28] Crypto 小替换

嘿嘿 到了第28八天啦 雀跃的心情 就像在京都 看着漫天散落的粉嫩樱花 今天这题超简单 因为我要去补...

D4: [漫画]工程师太师了-第2.5话

工程师太师了: 第2.5话 杂记: 报名的时候没注意到有最低300个字限制阿~~~~(抱头 本来只想...